Page MenuHomeClusterLabs Projects

No OneTemporary

diff --git a/cib/callbacks.c b/cib/callbacks.c
index 5d1eabd05f..a5ddeb93c3 100644
--- a/cib/callbacks.c
+++ b/cib/callbacks.c
@@ -1,1903 +1,1902 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <clplumbing/uids.h>
#include <clplumbing/cl_uuid.h>
-#include <clplumbing/cl_malloc.h>
#include <clplumbing/Gmain_timeout.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/ipc.h>
#include <crm/common/cluster.h>
#include <crm/common/ctrl.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <cibio.h>
#include <callbacks.h>
#include <cibmessages.h>
#include <cibprimatives.h>
#include <notify.h>
#include <heartbeat.h>
extern GMainLoop* mainloop;
extern gboolean cib_shutdown_flag;
extern gboolean stand_alone;
extern const char* cib_root;
#if SUPPORT_HEARTBEAT
extern ll_cluster_t *hb_conn;
#endif
extern void cib_ha_connection_destroy(gpointer user_data);
extern enum cib_errors cib_update_counter(
crm_data_t *xml_obj, const char *field, gboolean reset);
extern void GHFunc_count_peers(
gpointer key, gpointer value, gpointer user_data);
extern enum cib_errors revision_check(
crm_data_t *cib_update, crm_data_t *cib_copy, int flags);
void initiate_exit(void);
void terminate_cib(const char *caller);
gint cib_GCompareFunc(gconstpointer a, gconstpointer b);
gboolean cib_msg_timeout(gpointer data);
void cib_GHFunc(gpointer key, gpointer value, gpointer user_data);
gboolean can_write(int flags);
HA_Message *cib_msg_copy(HA_Message *msg, gboolean with_data);
void send_cib_replace(const HA_Message *sync_request, const char *host);
void cib_process_request(
HA_Message *request, gboolean privileged, gboolean force_synchronous,
gboolean from_peer, cib_client_t *cib_client);
HA_Message *cib_construct_reply(HA_Message *request, HA_Message *output, int rc);
gboolean syncd_once = FALSE;
extern GHashTable *client_list;
int next_client_id = 0;
gboolean cib_is_master = FALSE;
extern const char *cib_our_uname;
extern unsigned long cib_num_ops, cib_num_local, cib_num_updates, cib_num_fail;
extern unsigned long cib_bad_connects, cib_num_timeouts;
extern longclock_t cib_call_time;
extern enum cib_errors cib_status;
static HA_Message *
cib_prepare_common(HA_Message *root, const char *section)
{
HA_Message *data = NULL;
/* extract the CIB from the fragment */
if(root == NULL) {
return NULL;
} else if(safe_str_eq(crm_element_name(root), XML_TAG_FRAGMENT)
|| safe_str_eq(crm_element_name(root), F_CIB_CALLDATA)) {
data = find_xml_node(root, XML_TAG_CIB, TRUE);
if(data != NULL) {
crm_debug_3("Extracted CIB from %s", TYPE(root));
} else {
crm_log_xml_debug_4(root, "No CIB");
}
} else {
data = root;
}
/* grab the section specified for the command */
if(section != NULL
&& data != NULL
&& safe_str_eq(crm_element_name(data), XML_TAG_CIB)){
int rc = revision_check(data, the_cib, 0/* call_options */);
if(rc == cib_ok) {
data = get_object_root(section, data);
if(data != NULL) {
crm_debug_3("Extracted %s from CIB", section);
} else {
crm_log_xml_debug_4(root, "No Section");
}
} else {
crm_debug_2("Revision check failed");
}
}
crm_log_xml_debug_4(root, "cib:input");
return copy_xml(data);
}
static gboolean
verify_section(const char *section)
{
if(section == NULL) {
return TRUE;
} else if(safe_str_eq(section, XML_TAG_CIB)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_STATUS)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_NODES)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_RESOURCES)) {
return TRUE;
} else if(safe_str_eq(section, XML_CIB_TAG_CONSTRAINTS)) {
return TRUE;
}
return FALSE;
}
static enum cib_errors
cib_prepare_none(HA_Message *request, HA_Message **data, const char **section)
{
*data = NULL;
*section = cl_get_string(request, F_CIB_SECTION);
if(verify_section(*section) == FALSE) {
return cib_bad_section;
}
return cib_ok;
}
static enum cib_errors
cib_prepare_data(HA_Message *request, HA_Message **data, const char **section)
{
HA_Message *input_fragment = get_message_xml(request, F_CIB_CALLDATA);
*section = cl_get_string(request, F_CIB_SECTION);
*data = cib_prepare_common(input_fragment, *section);
free_xml(input_fragment);
if(verify_section(*section) == FALSE) {
return cib_bad_section;
}
return cib_ok;
}
static enum cib_errors
cib_prepare_sync(HA_Message *request, HA_Message **data, const char **section)
{
*section = cl_get_string(request, F_CIB_SECTION);
*data = request;
if(verify_section(*section) == FALSE) {
return cib_bad_section;
}
return cib_ok;
}
static enum cib_errors
cib_prepare_diff(HA_Message *request, HA_Message **data, const char **section)
{
HA_Message *input_fragment = NULL;
const char *update = cl_get_string(request, F_CIB_GLOBAL_UPDATE);
*data = NULL;
*section = NULL;
if(crm_is_true(update)) {
input_fragment = get_message_xml(request,F_CIB_UPDATE_DIFF);
} else {
input_fragment = get_message_xml(request, F_CIB_CALLDATA);
}
CRM_CHECK(input_fragment != NULL,crm_log_message(LOG_WARNING, request));
*data = cib_prepare_common(input_fragment, NULL);
free_xml(input_fragment);
return cib_ok;
}
static enum cib_errors
cib_cleanup_query(const char *op, HA_Message **data, HA_Message **output)
{
CRM_DEV_ASSERT(*data == NULL);
return cib_ok;
}
static enum cib_errors
cib_cleanup_data(const char *op, HA_Message **data, HA_Message **output)
{
free_xml(*output);
free_xml(*data);
return cib_ok;
}
static enum cib_errors
cib_cleanup_output(const char *op, HA_Message **data, HA_Message **output)
{
free_xml(*output);
return cib_ok;
}
static enum cib_errors
cib_cleanup_none(const char *op, HA_Message **data, HA_Message **output)
{
CRM_DEV_ASSERT(*data == NULL);
CRM_DEV_ASSERT(*output == NULL);
return cib_ok;
}
static enum cib_errors
cib_cleanup_sync(const char *op, HA_Message **data, HA_Message **output)
{
/* data is non-NULL but doesnt need to be free'd */
CRM_DEV_ASSERT(*output == NULL);
return cib_ok;
}
/*
typedef struct cib_operation_s
{
const char* operation;
gboolean modifies_cib;
gboolean needs_privileges;
gboolean needs_quorum;
enum cib_errors (*prepare)(HA_Message *, crm_data_t**, const char **);
enum cib_errors (*cleanup)(crm_data_t**, crm_data_t**);
enum cib_errors (*fn)(
const char *, int, const char *,
crm_data_t*, crm_data_t*, crm_data_t**, crm_data_t**);
} cib_operation_t;
*/
/* technically bump does modify the cib...
* but we want to split the "bump" from the "sync"
*/
cib_operation_t cib_server_ops[] = {
{NULL, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
{CIB_OP_QUERY, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_query, cib_process_query},
{CIB_OP_MODIFY, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_modify},
{CIB_OP_UPDATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_change},
{CIB_OP_APPLY_DIFF,TRUE, TRUE, TRUE, cib_prepare_diff, cib_cleanup_data, cib_process_diff},
{CIB_OP_SLAVE, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_SLAVEALL, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_SYNC_ONE, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync_one},
{CIB_OP_MASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_ISMASTER, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_readwrite},
{CIB_OP_BUMP, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_bump},
{CIB_OP_REPLACE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_replace},
{CIB_OP_CREATE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_change},
{CIB_OP_DELETE, TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_delete},
{CIB_OP_DELETE_ALT,TRUE, TRUE, TRUE, cib_prepare_data, cib_cleanup_data, cib_process_change},
{CIB_OP_SYNC, FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_sync},
{CRM_OP_QUIT, FALSE, TRUE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_quit},
{CRM_OP_PING, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_output, cib_process_ping},
{CIB_OP_ERASE, TRUE, TRUE, TRUE, cib_prepare_none, cib_cleanup_output, cib_process_erase},
{CRM_OP_NOOP, FALSE, FALSE, FALSE, cib_prepare_none, cib_cleanup_none, cib_process_default},
{"cib_shutdown_req",FALSE, TRUE, FALSE, cib_prepare_sync, cib_cleanup_sync, cib_process_shutdown_req},
};
int send_via_callback_channel(HA_Message *msg, const char *token);
enum cib_errors cib_process_command(
HA_Message *request, HA_Message **reply,
crm_data_t **cib_diff, gboolean privileged);
gboolean cib_common_callback(IPC_Channel *channel, cib_client_t *cib_client,
gboolean force_synchronous, gboolean privileged);
enum cib_errors cib_get_operation_id(const HA_Message * msg, int *operation);
gboolean cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client);
int num_clients = 0;
static void
cib_ipc_connection_destroy(gpointer user_data)
{
cib_client_t *cib_client = user_data;
/* cib_process_disconnect */
if(cib_client == NULL) {
crm_debug_4("Destroying %p", user_data);
return;
}
if(cib_client->source != NULL) {
crm_debug_4("Deleting %s (%p) from mainloop",
cib_client->name, cib_client->source);
G_main_del_IPC_Channel(cib_client->source);
cib_client->source = NULL;
}
crm_debug_3("Destroying %s (%p)", cib_client->name, user_data);
num_clients--;
crm_debug_2("Num unfree'd clients: %d", num_clients);
crm_free(cib_client->name);
crm_free(cib_client->callback_id);
crm_free(cib_client->id);
crm_free(cib_client);
crm_debug_4("Freed the cib client");
return;
}
static cib_client_t *
cib_client_connect_common(
IPC_Channel *channel, const char *channel_name,
gboolean (*callback)(IPC_Channel *channel, gpointer user_data))
{
gboolean can_connect = TRUE;
cib_client_t *new_client = NULL;
crm_debug_3("Connecting channel");
if (channel == NULL) {
crm_err("Channel was NULL");
can_connect = FALSE;
cib_bad_connects++;
} else if (channel->ch_status != IPC_CONNECT) {
crm_err("Channel was disconnected");
can_connect = FALSE;
cib_bad_connects++;
} else if(channel_name == NULL) {
crm_err("user_data must contain channel name");
can_connect = FALSE;
cib_bad_connects++;
} else if(cib_shutdown_flag) {
crm_info("Ignoring new client [%d] during shutdown",
channel->farside_pid);
return NULL;
} else {
crm_malloc0(new_client, sizeof(cib_client_t));
num_clients++;
new_client->channel = channel;
new_client->channel_name = channel_name;
crm_debug_3("Created channel %p for channel %s",
new_client, new_client->channel_name);
channel->ops->set_recv_qlen(channel, 1024);
channel->ops->set_send_qlen(channel, 1024);
if(callback != NULL) {
new_client->source = G_main_add_IPC_Channel(
G_PRIORITY_DEFAULT, channel, FALSE, callback,
new_client, cib_ipc_connection_destroy);
}
crm_debug_3("Channel %s connected for client %s",
new_client->channel_name, new_client->id);
}
return new_client;
}
gboolean
cib_client_connect_rw_synch(IPC_Channel *channel, gpointer user_data)
{
cib_client_t *new_client = NULL;
new_client = cib_client_connect_common(
channel, cib_channel_rw_synchronous, cib_rw_synchronous_callback);
if(new_client == NULL) {
return FALSE;
}
return TRUE;
}
gboolean
cib_client_connect_ro_synch(IPC_Channel *channel, gpointer user_data)
{
cib_client_t *new_client = NULL;
new_client = cib_client_connect_common(
channel, cib_channel_ro_synchronous, cib_ro_synchronous_callback);
if(new_client == NULL) {
return FALSE;
}
return TRUE;
}
gboolean
cib_client_connect_rw_ro(IPC_Channel *channel, gpointer user_data)
{
cl_uuid_t client_id;
HA_Message *reg_msg = NULL;
cib_client_t *new_client = NULL;
char uuid_str[UU_UNPARSE_SIZEOF];
gboolean (*callback)(IPC_Channel *channel, gpointer user_data);
callback = cib_ro_callback;
if(safe_str_eq(user_data, cib_channel_rw)) {
callback = cib_rw_callback;
}
new_client = cib_client_connect_common(
channel,
callback==cib_ro_callback?cib_channel_ro:cib_channel_rw,
callback);
if(new_client == NULL) {
return FALSE;
}
cl_uuid_generate(&client_id);
cl_uuid_unparse(&client_id, uuid_str);
CRM_CHECK(new_client->id == NULL, crm_free(new_client->id));
new_client->id = crm_strdup(uuid_str);
cl_uuid_generate(&client_id);
cl_uuid_unparse(&client_id, uuid_str);
CRM_CHECK(new_client->callback_id == NULL, crm_free(new_client->callback_id));
new_client->callback_id = crm_strdup(uuid_str);
/* make sure we can find ourselves later for sync calls
* redirected to the master instance
*/
g_hash_table_insert(client_list, new_client->id, new_client);
reg_msg = ha_msg_new(3);
ha_msg_add(reg_msg, F_CIB_OPERATION, CRM_OP_REGISTER);
ha_msg_add(reg_msg, F_CIB_CLIENTID, new_client->id);
ha_msg_add(
reg_msg, F_CIB_CALLBACK_TOKEN, new_client->callback_id);
send_ipc_message(channel, reg_msg);
crm_msg_del(reg_msg);
return TRUE;
}
gboolean
cib_client_connect_null(IPC_Channel *channel, gpointer user_data)
{
cib_client_t *new_client = NULL;
new_client = cib_client_connect_common(
channel, cib_channel_callback, cib_null_callback);
if(new_client == NULL) {
return FALSE;
}
return TRUE;
}
gboolean
cib_rw_callback(IPC_Channel *channel, gpointer user_data)
{
gboolean result = FALSE;
result = cib_common_callback(channel, user_data, FALSE, TRUE);
return result;
}
gboolean
cib_ro_synchronous_callback(IPC_Channel *channel, gpointer user_data)
{
gboolean result = FALSE;
result = cib_common_callback(channel, user_data, TRUE, FALSE);
return result;
}
gboolean
cib_rw_synchronous_callback(IPC_Channel *channel, gpointer user_data)
{
gboolean result = FALSE;
result = cib_common_callback(channel, user_data, TRUE, TRUE);
return result;
}
gboolean
cib_ro_callback(IPC_Channel *channel, gpointer user_data)
{
gboolean result = FALSE;
result = cib_common_callback(channel, user_data, FALSE, FALSE);
return result;
}
gboolean
cib_null_callback(IPC_Channel *channel, gpointer user_data)
{
gboolean keep_connection = TRUE;
HA_Message *op_request = NULL;
HA_Message *registered = NULL;
cib_client_t *cib_client = user_data;
cib_client_t *hash_client = NULL;
const char *type = NULL;
const char *uuid_ticket = NULL;
const char *client_name = NULL;
gboolean register_failed = FALSE;
if(cib_client == NULL) {
crm_err("Discarding IPC message from unknown source"
" on callback channel.");
return FALSE;
}
while(IPC_ISRCONN(channel)) {
crm_msg_del(op_request);
if(channel->ops->is_message_pending(channel) == 0) {
break;
}
op_request = msgfromIPC_noauth(channel);
if(op_request == NULL) {
break;
}
type = cl_get_string(op_request, F_CIB_OPERATION);
if(safe_str_eq(type, T_CIB_NOTIFY) ) {
/* Update the notify filters for this client */
int on_off = 0;
ha_msg_value_int(
op_request, F_CIB_NOTIFY_ACTIVATE, &on_off);
type = cl_get_string(op_request, F_CIB_NOTIFY_TYPE);
crm_info("Setting %s callbacks for %s: %s",
type, cib_client->name, on_off?"on":"off");
if(safe_str_eq(type, T_CIB_POST_NOTIFY)) {
cib_client->post_notify = on_off;
} else if(safe_str_eq(type, T_CIB_PRE_NOTIFY)) {
cib_client->pre_notify = on_off;
} else if(safe_str_eq(type, T_CIB_UPDATE_CONFIRM)) {
cib_client->confirmations = on_off;
} else if(safe_str_eq(type, T_CIB_DIFF_NOTIFY)) {
cib_client->diffs = on_off;
} else if(safe_str_eq(type, T_CIB_REPLACE_NOTIFY)) {
cib_client->replace = on_off;
}
continue;
} else if(safe_str_neq(type, CRM_OP_REGISTER) ) {
crm_warn("Discarding IPC message from %s on callback channel",
cib_client->id);
continue;
}
uuid_ticket = cl_get_string(op_request, F_CIB_CALLBACK_TOKEN);
client_name = cl_get_string(op_request, F_CIB_CLIENTNAME);
CRM_DEV_ASSERT(uuid_ticket != NULL);
if(crm_assert_failed) {
register_failed = crm_assert_failed;
}
CRM_DEV_ASSERT(client_name != NULL);
if(crm_assert_failed) {
register_failed = crm_assert_failed;
}
if(register_failed == FALSE) {
hash_client = g_hash_table_lookup(client_list, uuid_ticket);
if(hash_client != NULL) {
crm_err("Duplicate registration request..."
" disconnecting");
register_failed = TRUE;
}
}
if(register_failed) {
crm_err("Registration request failed... disconnecting");
crm_msg_del(op_request);
return FALSE;
}
CRM_CHECK(cib_client->id == NULL, crm_free(cib_client->id));
CRM_CHECK(cib_client->name == NULL, crm_free(cib_client->name));
cib_client->id = crm_strdup(uuid_ticket);
cib_client->name = crm_strdup(client_name);
g_hash_table_insert(client_list, cib_client->id, cib_client);
crm_debug_2("Registered %s on %s channel",
cib_client->id, cib_client->channel_name);
if(safe_str_eq(cib_client->name, CRM_SYSTEM_TENGINE)) {
/* The TE is _always_ interested in these
* Enable now to avoid timing issues
*/
cib_client->diffs = TRUE;
}
registered = ha_msg_new(2);
ha_msg_add(registered, F_CIB_OPERATION, CRM_OP_REGISTER);
ha_msg_add(registered, F_CIB_CLIENTID, cib_client->id);
send_ipc_message(channel, registered);
crm_msg_del(registered);
if(channel->ch_status == IPC_CONNECT) {
break;
}
}
crm_msg_del(op_request);
if(channel->ch_status != IPC_CONNECT) {
crm_debug_2("Client disconnected");
keep_connection = cib_process_disconnect(channel, cib_client);
}
return keep_connection;
}
void
cib_common_callback_worker(HA_Message *op_request, cib_client_t *cib_client,
gboolean force_synchronous, gboolean privileged);
void
cib_common_callback_worker(HA_Message *op_request, cib_client_t *cib_client,
gboolean force_synchronous, gboolean privileged)
{
int rc = cib_ok;
int call_type = 0;
const char *op = NULL;
longclock_t call_stop = 0;
longclock_t call_start = 0;
call_start = time_longclock();
cib_client->num_calls++;
op = cl_get_string(op_request, F_CIB_OPERATION);
rc = cib_get_operation_id(op_request, &call_type);
if(rc != cib_ok) {
crm_debug("Invalid operation %s from %s/%s",
op, cib_client->name, cib_client->channel_name);
} else {
crm_debug_2("Processing %s operation from %s/%s",
op, cib_client->name, cib_client->channel_name);
}
if(rc == cib_ok) {
cib_process_request(
op_request, force_synchronous, privileged, FALSE,
cib_client);
}
call_stop = time_longclock();
cib_call_time += (call_stop - call_start);
}
gboolean
cib_common_callback(IPC_Channel *channel, cib_client_t *cib_client,
gboolean force_synchronous, gboolean privileged)
{
int lpc = 0;
HA_Message *op_request = NULL;
gboolean keep_channel = TRUE;
if(cib_client == NULL) {
crm_err("Receieved call from unknown source. Discarding.");
return FALSE;
}
if(cib_client->name == NULL) {
cib_client->name = crm_itoa(channel->farside_pid);
}
if(cib_client->id == NULL) {
cib_client->id = crm_strdup(cib_client->name);
g_hash_table_insert(client_list, cib_client->id, cib_client);
}
crm_debug_2("Callback for %s on %s channel",
cib_client->id, cib_client->channel_name);
while(IPC_ISRCONN(channel)) {
if(channel->ops->is_message_pending(channel) == 0) {
break;
}
op_request = msgfromIPC_noauth(channel);
if (op_request == NULL) {
perror("Receive failure:");
break;
}
lpc++;
crm_assert_failed = FALSE;
crm_log_message_adv(LOG_MSG, "Client[inbound]", op_request);
ha_msg_add(op_request, F_CIB_CLIENTID, cib_client->id);
ha_msg_add(op_request, F_CIB_CLIENTNAME, cib_client->name);
cib_common_callback_worker(
op_request, cib_client, force_synchronous, privileged);
crm_msg_del(op_request);
if(channel->ch_status == IPC_CONNECT) {
break;
}
}
crm_debug_2("Processed %d messages", lpc);
if(channel->ch_status != IPC_CONNECT) {
crm_debug_2("Client disconnected");
keep_channel = cib_process_disconnect(channel, cib_client);
}
return keep_channel;
}
extern void cib_send_remote_msg(void *session, HA_Message *msg);
static void
do_local_notify(HA_Message *notify_src, const char *client_id, gboolean sync_reply, gboolean from_peer)
{
/* send callback to originating child */
cib_client_t *client_obj = NULL;
HA_Message *client_reply = NULL;
enum cib_errors local_rc = cib_ok;
crm_debug_2("Performing notification");
client_reply = cib_msg_copy(notify_src, TRUE);
if(client_id != NULL) {
client_obj = g_hash_table_lookup(
client_list, client_id);
} else {
crm_debug_2("No client to sent the response to."
" F_CIB_CLIENTID not set.");
}
crm_debug_3("Sending callback to request originator");
if(client_obj == NULL) {
local_rc = cib_reply_failed;
} else if (crm_str_eq(client_obj->channel_name, "remote", FALSE)) {
crm_debug("Send message over TLS connection");
cib_send_remote_msg(client_obj->channel, client_reply);
} else {
const char *client_id = client_obj->callback_id;
crm_debug_2("Sending %ssync response to %s %s",
sync_reply?"":"an a-",
client_obj->name,
from_peer?"(originator of delegated request)":"");
if(sync_reply) {
client_id = client_obj->id;
}
local_rc = send_via_callback_channel(client_reply, client_id);
}
if(local_rc != cib_ok && client_obj != NULL) {
crm_warn("%sSync reply to %s failed: %s",
sync_reply?"":"A-",
client_obj?client_obj->name:"<unknown>", cib_error2string(local_rc));
}
ha_msg_del(client_reply);
}
static void
parse_local_options(
cib_client_t *cib_client, int call_type, int call_options, const char *host, const char *op,
gboolean *local_notify, gboolean *needs_reply, gboolean *process, gboolean *needs_forward)
{
if(cib_server_ops[call_type].modifies_cib
&& !(call_options & cib_inhibit_bcast)) {
/* we need to send an update anyway */
*needs_reply = TRUE;
} else {
*needs_reply = FALSE;
}
if(host == NULL && (call_options & cib_scope_local)) {
crm_debug_2("Processing locally scoped %s op from %s",
op, cib_client->name);
*local_notify = TRUE;
} else if(host == NULL && cib_is_master) {
crm_debug_2("Processing master %s op locally from %s",
op, cib_client->name);
*local_notify = TRUE;
} else if(safe_str_eq(host, cib_our_uname)) {
crm_debug_2("Processing locally addressed %s op from %s",
op, cib_client->name);
*local_notify = TRUE;
} else if(stand_alone) {
*needs_forward = FALSE;
*local_notify = TRUE;
*process = TRUE;
} else {
crm_debug_2("%s op from %s needs to be forwarded to %s",
op, cib_client->name,
host?host:"the master instance");
*needs_forward = TRUE;
*process = FALSE;
}
}
static gboolean
parse_peer_options(
int call_type, HA_Message *request,
gboolean *local_notify, gboolean *needs_reply, gboolean *process, gboolean *needs_forward)
{
const char *op = cl_get_string(request, F_CIB_OPERATION);
const char *originator = cl_get_string(request, F_ORIG);
const char *host = cl_get_string(request, F_CIB_HOST);
const char *reply_to = cl_get_string(request, F_CIB_ISREPLY);
const char *update = cl_get_string(request, F_CIB_GLOBAL_UPDATE);
const char *delegated = cl_get_string(request, F_CIB_DELEGATED);
if(safe_str_eq(op, "cib_shutdown_req")) {
if(reply_to != NULL) {
crm_debug("Processing %s from %s", op, host);
*needs_reply = FALSE;
} else {
crm_debug("Processing %s reply from %s", op, host);
}
return TRUE;
} else if(crm_is_true(update) && safe_str_eq(reply_to, cib_our_uname)) {
crm_debug_2("Processing global/peer update from %s"
" that originated from us", originator);
*needs_reply = FALSE;
if(cl_get_string(request, F_CIB_CLIENTID) != NULL) {
*local_notify = TRUE;
}
return TRUE;
} else if(crm_is_true(update)) {
crm_debug_2("Processing global/peer update from %s", originator);
*needs_reply = FALSE;
return TRUE;
} else if(host != NULL && safe_str_eq(host, cib_our_uname)) {
crm_debug_2("Processing request sent to us from %s", originator);
return TRUE;
} else if(delegated != NULL && cib_is_master == TRUE) {
crm_debug_2("Processing request sent to master instance from %s",
originator);
return TRUE;
} else if(reply_to != NULL && safe_str_eq(reply_to, cib_our_uname)) {
crm_debug_2("Forward reply sent from %s to local clients",
originator);
*process = FALSE;
*needs_reply = FALSE;
*local_notify = TRUE;
return TRUE;
} else if(delegated != NULL) {
crm_debug_2("Ignoring msg for master instance");
} else if(host != NULL) {
/* this is for a specific instance and we're not it */
crm_debug_2("Ignoring msg for instance on %s", crm_str(host));
} else if(reply_to == NULL && cib_is_master == FALSE) {
/* this is for the master instance and we're not it */
crm_debug_2("Ignoring reply to %s", crm_str(reply_to));
} else {
crm_err("Nothing for us to do?");
crm_log_message_adv(LOG_ERR, "Peer[inbound]", request);
}
return FALSE;
}
static void
forward_request(HA_Message *request, cib_client_t *cib_client, int call_options)
{
HA_Message *forward_msg = NULL;
const char *op = cl_get_string(request, F_CIB_OPERATION);
const char *host = cl_get_string(request, F_CIB_HOST);
forward_msg = cib_msg_copy(request, TRUE);
ha_msg_add(forward_msg, F_CIB_DELEGATED, cib_our_uname);
if(host != NULL) {
crm_debug_2("Forwarding %s op to %s", op, host);
send_cluster_message(host, crm_msg_cib, forward_msg, FALSE);
} else {
crm_debug_2("Forwarding %s op to master instance", op);
send_cluster_message(NULL, crm_msg_cib, forward_msg, FALSE);
}
if(call_options & cib_discard_reply) {
crm_debug_2("Client not interested in reply");
} else if(call_options & cib_sync_call) {
/* keep track of the request so we can time it
* out if required
*/
crm_debug_2("Registering delegated call from %s",
cib_client->id);
cib_client->delegated_calls = g_list_append(
cib_client->delegated_calls, forward_msg);
forward_msg = NULL;
}
crm_msg_del(forward_msg);
}
static void
send_peer_reply(
HA_Message *msg, crm_data_t *result_diff, const char *originator, gboolean broadcast)
{
HA_Message *reply_copy = NULL;
CRM_ASSERT(msg != NULL);
reply_copy = cib_msg_copy(msg, TRUE);
if(broadcast) {
/* this (successful) call modified the CIB _and_ the
* change needs to be broadcast...
* send via HA to other nodes
*/
int diff_add_updates = 0;
int diff_add_epoch = 0;
int diff_add_admin_epoch = 0;
int diff_del_updates = 0;
int diff_del_epoch = 0;
int diff_del_admin_epoch = 0;
char *digest = NULL;
cib_diff_version_details(
result_diff,
&diff_add_admin_epoch, &diff_add_epoch, &diff_add_updates,
&diff_del_admin_epoch, &diff_del_epoch, &diff_del_updates);
crm_debug("Sending update diff %d.%d.%d -> %d.%d.%d",
diff_del_admin_epoch,diff_del_epoch,diff_del_updates,
diff_add_admin_epoch,diff_add_epoch,diff_add_updates);
ha_msg_add(reply_copy, F_CIB_ISREPLY, originator);
ha_msg_add(reply_copy, F_CIB_GLOBAL_UPDATE, XML_BOOLEAN_TRUE);
ha_msg_mod(reply_copy, F_CIB_OPERATION, CIB_OP_APPLY_DIFF);
digest = calculate_xml_digest(the_cib, FALSE, TRUE);
ha_msg_mod(result_diff, XML_ATTR_DIGEST, digest);
/* crm_log_xml_debug(the_cib, digest); */
crm_free(digest);
add_message_xml(reply_copy, F_CIB_UPDATE_DIFF, result_diff);
crm_log_message(LOG_DEBUG_3, reply_copy);
send_cluster_message(NULL, crm_msg_cib, reply_copy, TRUE);
} else if(originator != NULL) {
/* send reply via HA to originating node */
crm_debug_2("Sending request result to originator only");
ha_msg_add(reply_copy, F_CIB_ISREPLY, originator);
send_cluster_message(originator, crm_msg_cib, reply_copy, FALSE);
}
crm_msg_del(reply_copy);
}
void
cib_process_request(
HA_Message *request, gboolean force_synchronous, gboolean privileged,
gboolean from_peer, cib_client_t *cib_client)
{
int call_type = 0;
int call_options = 0;
gboolean process = TRUE;
gboolean needs_reply = TRUE;
gboolean local_notify = FALSE;
gboolean needs_forward = FALSE;
crm_data_t *result_diff = NULL;
enum cib_errors rc = cib_ok;
HA_Message *op_reply = NULL;
const char *op = cl_get_string(request, F_CIB_OPERATION);
const char *originator = cl_get_string(request, F_ORIG);
const char *host = cl_get_string(request, F_CIB_HOST);
const char *update = cl_get_string(request, F_CIB_GLOBAL_UPDATE);
crm_debug_4("%s Processing msg %s",
cib_our_uname, cl_get_string(request, F_SEQ));
cib_num_ops++;
if(cib_num_ops == 0) {
cib_num_fail = 0;
cib_num_local = 0;
cib_num_updates = 0;
crm_info("Stats wrapped around");
}
if(host != NULL && strlen(host) == 0) {
host = NULL;
}
ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options);
crm_debug_4("Retrieved call options: %d", call_options);
if(force_synchronous) {
call_options |= cib_sync_call;
}
crm_debug_2("Processing %s message (%s) for %s...",
from_peer?"peer":"local",
from_peer?originator:cib_our_uname, host?host:"master");
rc = cib_get_operation_id(request, &call_type);
if(cib_server_ops[call_type].modifies_cib) {
cib_num_updates++;
}
if(rc != cib_ok) {
/* TODO: construct error reply */
crm_err("Pre-processing of command failed: %s",
cib_error2string(rc));
} else if(from_peer == FALSE) {
parse_local_options(cib_client, call_type, call_options, host, op,
&local_notify, &needs_reply, &process, &needs_forward);
} else if(parse_peer_options(call_type, request, &local_notify,
&needs_reply, &process, &needs_forward) == FALSE) {
return;
}
crm_debug_3("Finished determining processing actions");
if(call_options & cib_discard_reply) {
needs_reply = cib_server_ops[call_type].modifies_cib;
local_notify = FALSE;
}
if(needs_forward) {
forward_request(request, cib_client, call_options);
return;
}
if(cib_status != cib_ok) {
rc = cib_status;
crm_err("Operation ignored, cluster configuration is invalid."
" Please repair and restart: %s",
cib_error2string(cib_status));
op_reply = cib_construct_reply(request, the_cib, cib_status);
} else if(process) {
cib_num_local++;
crm_debug_2("Performing local processing:"
" op=%s origin=%s/%s,%s (update=%s)",
cl_get_string(request, F_CIB_OPERATION), originator,
cl_get_string(request, F_CIB_CLIENTID),
cl_get_string(request, F_CIB_CALLID), update);
rc = cib_process_command(
request, &op_reply, &result_diff, privileged);
crm_debug_2("Processing complete");
if(rc == cib_diff_resync || rc == cib_diff_failed
|| rc == cib_old_data) {
crm_warn("%s operation failed: %s",
crm_str(op), cib_error2string(rc));
} else if(rc != cib_ok) {
cib_num_fail++;
crm_err("%s operation failed: %s",
crm_str(op), cib_error2string(rc));
crm_log_message_adv(LOG_DEBUG, "CIB[output]", op_reply);
crm_log_message_adv(LOG_INFO, "Input message", request);
}
if(op_reply == NULL && (needs_reply || local_notify)) {
crm_err("Unexpected NULL reply to message");
crm_log_message(LOG_ERR, request);
needs_reply = FALSE;
local_notify = FALSE;
}
}
crm_debug_3("processing response cases");
if(local_notify) {
const char *client_id = cl_get_string(request, F_CIB_CLIENTID);
if(process == FALSE) {
do_local_notify(request, client_id, call_options & cib_sync_call, from_peer);
} else {
do_local_notify(op_reply, client_id, call_options & cib_sync_call, from_peer);
}
}
/* from now on we are the server */
if(needs_reply == FALSE || stand_alone) {
/* nothing more to do...
* this was a non-originating slave update
*/
crm_debug_2("Completed slave update");
} else if(rc == cib_ok
&& result_diff != NULL
&& !(call_options & cib_inhibit_bcast)) {
send_peer_reply(request, result_diff, originator, TRUE);
} else if(call_options & cib_discard_reply) {
crm_debug_4("Caller isn't interested in reply");
} else if (from_peer) {
crm_debug_2("Directing reply to %s", originator);
if(call_options & cib_inhibit_bcast) {
crm_debug_3("Request not broadcast: inhibited");
}
if(cib_server_ops[call_type].modifies_cib == FALSE || result_diff == NULL) {
crm_debug_3("Request not broadcast: R/O call");
}
if(rc != cib_ok) {
crm_debug_3("Request not broadcast: call failed: %s",
cib_error2string(rc));
}
send_peer_reply(op_reply, result_diff, originator, FALSE);
}
crm_msg_del(op_reply);
free_xml(result_diff);
return;
}
HA_Message *
cib_construct_reply(HA_Message *request, HA_Message *output, int rc)
{
int lpc = 0;
HA_Message *reply = NULL;
const char *name = NULL;
const char *value = NULL;
const char *names[] = {
F_CIB_OPERATION,
F_CIB_CALLID,
F_CIB_CLIENTID,
F_CIB_CALLOPTS
};
crm_debug_4("Creating a basic reply");
reply = ha_msg_new(8);
ha_msg_add(reply, F_TYPE, T_CIB);
for(lpc = 0; lpc < DIMOF(names); lpc++) {
name = names[lpc];
value = cl_get_string(request, name);
ha_msg_add(reply, name, value);
}
ha_msg_add_int(reply, F_CIB_RC, rc);
if(output != NULL) {
crm_debug_4("Attaching reply output");
add_message_xml(reply, F_CIB_CALLDATA, output);
}
return reply;
}
enum cib_errors
cib_process_command(HA_Message *request, HA_Message **reply,
crm_data_t **cib_diff, gboolean privileged)
{
crm_data_t *output = NULL;
crm_data_t *input = NULL;
crm_data_t *current_cib = NULL;
crm_data_t *result_cib = NULL;
int call_type = 0;
int call_options = 0;
enum cib_errors rc = cib_ok;
enum cib_errors rc2 = cib_ok;
int log_level = LOG_DEBUG_3;
crm_data_t *filtered = NULL;
const char *op = NULL;
const char *section = NULL;
gboolean config_changed = FALSE;
gboolean global_update = crm_is_true(
cl_get_string(request, F_CIB_GLOBAL_UPDATE));
CRM_ASSERT(cib_status == cib_ok);
*reply = NULL;
*cib_diff = NULL;
if(per_action_cib) {
CRM_CHECK(the_cib == NULL, free_xml(the_cib));
the_cib = readCibXmlFile(cib_root, "cib.xml", FALSE);
CRM_CHECK(the_cib != NULL, return cib_NOOBJECT);
}
current_cib = the_cib;
/* Start processing the request... */
op = cl_get_string(request, F_CIB_OPERATION);
ha_msg_value_int(request, F_CIB_CALLOPTS, &call_options);
rc = cib_get_operation_id(request, &call_type);
if(rc == cib_ok &&
cib_server_ops[call_type].needs_privileges
&& privileged == FALSE) {
/* abort */
rc = cib_not_authorized;
}
if(rc == cib_ok
&& stand_alone == FALSE
&& global_update == FALSE
&& cib_server_ops[call_type].needs_quorum
&& can_write(call_options) == FALSE) {
rc = cib_no_quorum;
}
/* prevent NUMUPDATES from being incrimented - apply the change as-is */
if(global_update) {
call_options |= cib_inhibit_bcast;
call_options |= cib_force_diff;
}
rc2 = cib_server_ops[call_type].prepare(request, &input, &section);
if(rc == cib_ok) {
rc = rc2;
}
if(rc != cib_ok) {
crm_debug_2("Call setup failed: %s", cib_error2string(rc));
goto done;
} else if(cib_server_ops[call_type].modifies_cib == FALSE) {
rc = cib_server_ops[call_type].fn(
op, call_options, section, input,
current_cib, &result_cib, &output);
CRM_CHECK(result_cib == NULL, free_xml(result_cib));
goto done;
}
/* Handle a valid write action */
if((call_options & cib_inhibit_notify) == 0) {
cib_pre_notify(call_options, op,
get_object_root(section, current_cib), input);
}
if(rc == cib_ok) {
result_cib = copy_xml(current_cib);
rc = cib_server_ops[call_type].fn(
op, call_options, section, input,
current_cib, &result_cib, &output);
fix_plus_plus_recursive(result_cib);
}
if(rc == cib_ok) {
CRM_DEV_ASSERT(result_cib != NULL);
CRM_DEV_ASSERT(current_cib != result_cib);
update_counters(__FILE__, __FUNCTION__, result_cib);
config_changed = cib_config_changed(current_cib, result_cib, &filtered);
if(global_update) {
/* skip */
CRM_CHECK(call_type == 4 || call_type == 11,
crm_err("Call type: %d", call_type);
crm_log_message(LOG_ERR, request));
crm_debug_2("Skipping update: global replace");
} else if(cib_server_ops[call_type].fn == cib_process_change
&& (call_options & cib_inhibit_bcast)) {
/* skip */
crm_debug_2("Skipping update: inhibit broadcast");
} else {
cib_update_counter(result_cib, XML_ATTR_NUMUPDATES, FALSE);
if(config_changed) {
cib_update_counter(result_cib, XML_ATTR_NUMUPDATES, TRUE);
cib_update_counter(result_cib, XML_ATTR_GENERATION, FALSE);
}
}
if(do_id_check(result_cib, NULL, TRUE, FALSE)) {
rc = cib_id_check;
if(call_options & cib_force_diff) {
crm_err("Global update introduces id collision!");
}
} else {
*cib_diff = diff_cib_object(current_cib, result_cib, FALSE);
}
}
if(rc != cib_ok) {
free_xml(result_cib);
} else {
rc = activateCibXml(result_cib, config_changed);
if(rc != cib_ok) {
crm_warn("Activation failed");
}
}
if((call_options & cib_inhibit_notify) == 0) {
const char *call_id = cl_get_string(request, F_CIB_CALLID);
const char *client = cl_get_string(request, F_CIB_CLIENTNAME);
cib_post_notify(call_options, op, input, rc, the_cib);
cib_diff_notify(call_options, client, call_id, op,
input, rc, *cib_diff);
}
if(rc == cib_dtd_validation && global_update) {
log_level = LOG_WARNING;
crm_log_xml_info(input, "cib:global_update");
} else if(rc != cib_ok) {
log_level = LOG_DEBUG_4;
} else if(cib_is_master && config_changed) {
log_level = LOG_INFO;
} else if(cib_is_master) {
log_level = LOG_DEBUG;
log_xml_diff(LOG_DEBUG_2, filtered, "cib:diff:filtered");
} else if(config_changed) {
log_level = LOG_DEBUG_2;
} else {
log_level = LOG_DEBUG_3;
}
log_xml_diff(log_level, *cib_diff, "cib:diff");
free_xml(filtered);
done:
if((call_options & cib_discard_reply) == 0) {
*reply = cib_construct_reply(request, output, rc);
}
if(call_type >= 0) {
cib_server_ops[call_type].cleanup(op, &input, &output);
}
if(per_action_cib) {
uninitializeCib();
}
return rc;
}
int
send_via_callback_channel(HA_Message *msg, const char *token)
{
cib_client_t *hash_client = NULL;
GList *list_item = NULL;
enum cib_errors rc = cib_ok;
crm_debug_3("Delivering msg %p to client %s", msg, token);
if(token == NULL) {
crm_err("No client id token, cant send message");
if(rc == cib_ok) {
rc = cib_missing;
}
} else {
/* A client that left before we could reply is not really
* _our_ error. Warn instead.
*/
hash_client = g_hash_table_lookup(client_list, token);
if(hash_client == NULL) {
crm_warn("Cannot find client for token %s", token);
rc = cib_client_gone;
} else if(hash_client->channel == NULL) {
crm_err("Cannot find channel for client %s", token);
rc = cib_client_corrupt;
} else if(hash_client->channel->ops->get_chan_status(
hash_client->channel) == IPC_DISCONNECT) {
crm_warn("Client %s has disconnected", token);
rc = cib_client_gone;
cib_num_timeouts++;
}
}
/* this is a more important error so overwriting rc is warrented */
if(msg == NULL) {
crm_err("No message to send");
rc = cib_reply_failed;
}
if(rc == cib_ok) {
list_item = g_list_find_custom(
hash_client->delegated_calls, msg, cib_GCompareFunc);
}
if(list_item != NULL) {
/* remove it - no need to time it out */
HA_Message *orig_msg = list_item->data;
crm_debug_3("Removing msg from delegated list");
hash_client->delegated_calls = g_list_remove(
hash_client->delegated_calls, orig_msg);
CRM_DEV_ASSERT(orig_msg != msg);
crm_msg_del(orig_msg);
}
if(rc == cib_ok) {
crm_debug_3("Delivering reply to client %s", token);
if(send_ipc_message(hash_client->channel, msg) == FALSE) {
crm_warn("Delivery of reply to client %s/%s failed",
hash_client->name, token);
rc = cib_reply_failed;
}
}
return rc;
}
gint cib_GCompareFunc(gconstpointer a, gconstpointer b)
{
const HA_Message *a_msg = a;
const HA_Message *b_msg = b;
int msg_a_id = 0;
int msg_b_id = 0;
ha_msg_value_int(a_msg, F_CIB_CALLID, &msg_a_id);
ha_msg_value_int(b_msg, F_CIB_CALLID, &msg_b_id);
if(msg_a_id == msg_b_id) {
return 0;
} else if(msg_a_id < msg_b_id) {
return -1;
}
return 1;
}
gboolean
cib_msg_timeout(gpointer data)
{
crm_debug_4("Checking if any clients have timed out messages");
/* g_hash_table_foreach(client_list, cib_GHFunc, NULL); */
return TRUE;
}
void
cib_GHFunc(gpointer key, gpointer value, gpointer user_data)
{
int timeout = 0; /* 1 iteration == 10 seconds */
HA_Message *msg = NULL;
HA_Message *reply = NULL;
const char *host_to = NULL;
cib_client_t *client = value;
GListPtr list = client->delegated_calls;
while(list != NULL) {
msg = list->data;
ha_msg_value_int(msg, F_CIB_TIMEOUT, &timeout);
if(timeout <= 0) {
list = list->next;
continue;
} else {
int seen = 0;
ha_msg_value_int(msg, F_CIB_SEENCOUNT, &seen);
crm_debug_4("Timeout %d, seen %d", timeout, seen);
if(seen < timeout) {
crm_debug_4("Updating seen count for msg from client %s",
client->id);
seen += 10;
ha_msg_mod_int(msg, F_CIB_SEENCOUNT, seen);
list = list->next;
continue;
}
}
cib_num_timeouts++;
host_to = cl_get_string(msg, F_CIB_HOST);
crm_warn("Sending operation timeout msg to client %s",
client->id);
reply = ha_msg_new(4);
ha_msg_add(reply, F_TYPE, T_CIB);
ha_msg_add(reply, F_CIB_OPERATION,
cl_get_string(msg, F_CIB_OPERATION));
ha_msg_add(reply, F_CIB_CALLID,
cl_get_string(msg, F_CIB_CALLID));
if(host_to == NULL) {
ha_msg_add_int(reply, F_CIB_RC, cib_master_timeout);
} else {
ha_msg_add_int(reply, F_CIB_RC, cib_remote_timeout);
}
send_ipc_message(client->channel, reply);
list = list->next;
client->delegated_calls = g_list_remove(
client->delegated_calls, msg);
crm_msg_del(msg);
crm_msg_del(reply);
}
}
gboolean
cib_process_disconnect(IPC_Channel *channel, cib_client_t *cib_client)
{
if (channel == NULL) {
CRM_DEV_ASSERT(cib_client == NULL);
} else if (cib_client == NULL) {
crm_err("No client");
} else {
CRM_DEV_ASSERT(channel->ch_status != IPC_CONNECT);
crm_debug_2("Cleaning up after client disconnect: %s/%s/%s",
crm_str(cib_client->name),
cib_client->channel_name,
cib_client->id);
if(cib_client->id != NULL) {
if(!g_hash_table_remove(client_list, cib_client->id)) {
crm_err("Client %s not found in the hashtable",
cib_client->name);
}
}
}
if(cib_shutdown_flag && g_hash_table_size(client_list) == 0) {
crm_info("All clients disconnected...");
initiate_exit();
}
return FALSE;
}
void
cib_peer_callback(HA_Message * msg, void* private_data)
{
int call_type = 0;
int call_options = 0;
const char *originator = cl_get_string(msg, F_ORIG);
const char *seq = cl_get_string(msg, F_SEQ);
const char *op = cl_get_string(msg, F_CIB_OPERATION);
crm_node_t *node = NULL;
crm_log_message_adv(LOG_MSG, "Peer[inbound]", msg);
crm_debug_2("Peer %s message (%s) from %s", op, seq, originator);
if(originator == NULL || safe_str_eq(originator, cib_our_uname)) {
crm_debug_2("Discarding %s message %s from ourselves", op, seq);
return;
}
if(crm_peer_cache == NULL) {
crm_info("Discarding %s message (%s) from %s:"
" membership not established", op, seq, originator);
return;
}
node = g_hash_table_lookup(crm_peer_cache, originator);
if(node == NULL || crm_is_member_active(node) == FALSE) {
crm_warn("Discarding %s message (%s) from %s:"
" not in our membership", op, seq, originator);
return;
}
if(cib_get_operation_id(msg, &call_type) != cib_ok) {
crm_debug("Discarding %s message (%s) from %s:"
" Invalid operation", op, seq, originator);
return;
}
crm_debug_2("Processing %s msg (%s) from %s",op, seq, originator);
ha_msg_value_int(msg, F_CIB_CALLOPTS, &call_options);
crm_debug_4("Retrieved call options: %d", call_options);
if(cl_get_string(msg, F_CIB_CLIENTNAME) == NULL) {
ha_msg_add(msg, F_CIB_CLIENTNAME, originator);
}
cib_process_request(msg, FALSE, TRUE, TRUE, NULL);
return;
}
HA_Message *
cib_msg_copy(HA_Message *msg, gboolean with_data)
{
int lpc = 0;
const char *field = NULL;
const char *value = NULL;
HA_Message *value_struct = NULL;
static const char *field_list[] = {
F_XML_TAGNAME ,
F_TYPE ,
F_CIB_CLIENTID ,
F_CIB_CALLOPTS ,
F_CIB_CALLID ,
F_CIB_OPERATION ,
F_CIB_ISREPLY ,
F_CIB_SECTION ,
F_CIB_HOST ,
F_CIB_RC ,
F_CIB_DELEGATED ,
F_CIB_OBJID ,
F_CIB_OBJTYPE ,
F_CIB_EXISTING ,
F_CIB_SEENCOUNT ,
F_CIB_TIMEOUT ,
F_CIB_CALLBACK_TOKEN ,
F_CIB_GLOBAL_UPDATE ,
F_CIB_CLIENTNAME ,
F_CIB_NOTIFY_TYPE ,
F_CIB_NOTIFY_ACTIVATE
};
static const char *data_list[] = {
F_CIB_CALLDATA ,
F_CIB_UPDATE ,
F_CIB_UPDATE_RESULT
};
HA_Message *copy = ha_msg_new(10);
CRM_ASSERT(copy != NULL);
for(lpc = 0; lpc < DIMOF(field_list); lpc++) {
field = field_list[lpc];
value = cl_get_string(msg, field);
if(value != NULL) {
ha_msg_add(copy, field, value);
}
}
for(lpc = 0; with_data && lpc < DIMOF(data_list); lpc++) {
field = data_list[lpc];
value_struct = get_message_xml(msg, field);
if(value_struct != NULL) {
add_message_xml(copy, field, value_struct);
}
free_xml(value_struct);
}
return copy;
}
enum cib_errors
cib_get_operation_id(const HA_Message * msg, int *operation)
{
int lpc = 0;
int max_msg_types = DIMOF(cib_server_ops);
const char *op = cl_get_string(msg, F_CIB_OPERATION);
for (lpc = 0; lpc < max_msg_types; lpc++) {
if (safe_str_eq(op, cib_server_ops[lpc].operation)) {
*operation = lpc;
return cib_ok;
}
}
crm_err("Operation %s is not valid", op);
*operation = -1;
return cib_operation;
}
void
cib_client_status_callback(const char * node, const char * client,
const char * status, void * private)
{
crm_node_t *member = NULL;
if(safe_str_eq(client, CRM_SYSTEM_CIB)) {
crm_info("Status update: Client %s/%s now has status [%s]",
node, client, status);
if(safe_str_eq(status, JOINSTATUS)){
status = ONLINESTATUS;
} else if(safe_str_eq(status, LEAVESTATUS)){
status = OFFLINESTATUS;
}
member = g_hash_table_lookup(crm_peer_cache, node);
if(member == NULL) {
/* Make sure it gets created */
const char *uuid = get_uuid(node);
member = crm_update_peer(0, 0, -1, 0, uuid, node, NULL, NULL);
}
crm_update_peer_proc(node, crm_proc_cib, status);
set_connected_peers(the_cib);
}
return;
}
#if SUPPORT_HEARTBEAT
extern oc_ev_t *cib_ev_token;
gboolean cib_ccm_dispatch(int fd, gpointer user_data)
{
int rc = 0;
oc_ev_t *ccm_token = (oc_ev_t*)user_data;
crm_debug_2("received callback");
rc = oc_ev_handle_event(ccm_token);
if(0 == rc) {
return TRUE;
}
crm_err("CCM connection appears to have failed: rc=%d.", rc);
/* eventually it might be nice to recover and reconnect... but until then... */
crm_err("Exiting to recover from CCM connection failure");
exit(2);
return FALSE;
}
int current_instance = 0;
void
cib_ccm_msg_callback(
oc_ed_t event, void *cookie, size_t size, const void *data)
{
gboolean update_id = FALSE;
const oc_ev_membership_t *membership = data;
CRM_ASSERT(membership != NULL);
crm_info("Processing CCM event=%s (id=%d)",
ccm_event_name(event), membership->m_instance);
if(current_instance > membership->m_instance) {
crm_err("Membership instance ID went backwards! %d->%d",
current_instance, membership->m_instance);
CRM_ASSERT(current_instance <= membership->m_instance);
}
switch(event) {
case OC_EV_MS_NEW_MEMBERSHIP:
case OC_EV_MS_INVALID:
update_id = TRUE;
break;
case OC_EV_MS_PRIMARY_RESTORED:
update_id = TRUE;
break;
case OC_EV_MS_NOT_PRIMARY:
crm_debug_2("Ignoring transitional CCM event: %s",
ccm_event_name(event));
break;
case OC_EV_MS_EVICTED:
crm_err("Evicted from CCM: %s", ccm_event_name(event));
break;
default:
crm_err("Unknown CCM event: %d", event);
}
if(update_id) {
unsigned int lpc = 0;
CRM_CHECK(membership != NULL, return);
current_instance = membership->m_instance;
for(lpc=0; lpc < membership->m_n_out; lpc++) {
crm_update_ccm_node(
membership, lpc+membership->m_out_idx, CRM_NODE_LOST);
}
for(lpc=0; lpc < membership->m_n_member; lpc++) {
crm_update_ccm_node(
membership, lpc+membership->m_memb_idx,CRM_NODE_ACTIVE);
}
}
oc_ev_callback_done(cookie);
set_connected_peers(the_cib);
return;
}
#endif
gboolean
can_write(int flags)
{
return TRUE;
}
static gboolean
cib_force_exit(gpointer data)
{
crm_notice("Forcing exit!");
terminate_cib(__FUNCTION__);
return FALSE;
}
void
initiate_exit(void)
{
int active = 0;
HA_Message *leaving = NULL;
active = crm_active_peers(crm_proc_cib);
if(active < 2) {
terminate_cib(__FUNCTION__);
return;
}
crm_info("Sending disconnect notification to %d peers...", active);
leaving = ha_msg_new(3);
ha_msg_add(leaving, F_TYPE, "cib");
ha_msg_add(leaving, F_CIB_OPERATION, "cib_shutdown_req");
send_cluster_message(NULL, crm_msg_cib, leaving, TRUE);
crm_msg_del(leaving);
Gmain_timeout_add(crm_get_msec("5s"), cib_force_exit, NULL);
}
void
terminate_cib(const char *caller)
{
#if SUPPORT_AIS
if(is_openais_cluster()) {
cib_ha_connection_destroy(NULL);
return;
}
#endif
#if SUPPORT_HEARTBEAT
if(hb_conn != NULL) {
crm_info("%s: Disconnecting heartbeat", caller);
hb_conn->llc_ops->signoff(hb_conn, FALSE);
} else {
crm_err("%s: No heartbeat connection", caller);
}
#endif
uninitializeCib();
crm_info("Exiting...");
if (mainloop != NULL && g_main_is_running(mainloop)) {
g_main_quit(mainloop);
} else {
exit(LSB_EXIT_OK);
}
}
diff --git a/crm/admin/crm_uuid.c b/crm/admin/crm_uuid.c
index b9e68cd3f6..c25c60f246 100644
--- a/crm/admin/crm_uuid.c
+++ b/crm/admin/crm_uuid.c
@@ -1,190 +1,189 @@
/*
* 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 <config.h>
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <clplumbing/cl_log.h>
-#include <clplumbing/cl_malloc.h>
#include <clplumbing/cl_uuid.h>
#include <clplumbing/uids.h>
#include <clplumbing/lsb_exitcodes.h>
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
#define UUID_LEN 16
#define UUID_FILE HA_VARLIBDIR"/heartbeat/hb_uuid"
#define OPTARGS "?rw:"
int read_local_hb_uuid(void);
int write_local_hb_uuid(const char *buffer);
static void usage(int rc)
{
FILE *stream = rc != 0 ? stderr : stdout;
fprintf(stream, "crm_uuid [-r|-w new_ascii_value]\n");
exit(rc);
}
int
main(int argc, char **argv)
{
int flag;
int rc = 0;
cl_log_enable_stderr(TRUE);
if(argc == 1) {
/* no arguments specified, default to read */
rc = read_local_hb_uuid();
return rc;
}
cl_log_args(argc, argv);
while (1) {
flag = getopt(argc, argv, OPTARGS);
if (flag == -1) {
break;
}
switch(flag) {
case 'r':
rc = read_local_hb_uuid();
break;
case 'w':
rc = write_local_hb_uuid(optarg);
break;
case '?':
usage(LSB_EXIT_OK);
break;
default:
usage(LSB_EXIT_GENERIC);
break;
}
}
return rc;
}
int
read_local_hb_uuid(void)
{
int rc = 0;
cl_uuid_t uuid;
char *buffer = NULL;
long start = 0, read_len = 0;
FILE *input = fopen(UUID_FILE, "r");
if(input == NULL) {
cl_perror("Could not open UUID file %s\n", UUID_FILE);
return 1;
}
/* see how big the file is */
start = ftell(input);
fseek(input, 0L, SEEK_END);
if(UUID_LEN != ftell(input)) {
fprintf(stderr, "%s must contain exactly %d bytes\n",
UUID_FILE, UUID_LEN);
abort();
}
fseek(input, 0L, start);
if(start != ftell(input)) {
fprintf(stderr, "fseek not behaving: %ld vs. %ld\n",
start, ftell(input));
rc = 2;
goto bail;
}
/* fprintf(stderr, "Reading %d bytes from: %s\n", UUID_LEN, UUID_FILE); */
- buffer = cl_malloc(50);
+ buffer = malloc(50);
read_len = fread(uuid.uuid, 1, UUID_LEN, input);
if(read_len != UUID_LEN) {
fprintf(stderr, "Expected and read bytes differ: %d vs. %ld\n",
UUID_LEN, read_len);
rc = 3;
goto bail;
} else if(buffer != NULL) {
cl_uuid_unparse(&uuid, buffer);
fprintf(stdout, "%s\n", buffer);
} else {
fprintf(stderr, "No buffer to unparse\n");
rc = 4;
}
bail:
- cl_free(buffer);
+ free(buffer);
fclose(input);
return rc;
}
int
write_local_hb_uuid(const char *new_value)
{
int fd;
int rc = 0;
cl_uuid_t uuid;
char *buffer = strdup(new_value);
rc = cl_uuid_parse(buffer, &uuid);
if(rc != 0) {
fprintf(stderr, "Invalid ASCII UUID supplied: [%s]\n", new_value);
fprintf(stderr, "ASCII UUIDs must be of the form"
" XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
" and contain only letters and digits\n");
return 5;
}
if ((fd = open(UUID_FILE, O_WRONLY|O_SYNC|O_CREAT, 0644)) < 0) {
cl_perror("Could not open %s", UUID_FILE);
return 6;
}
if (write(fd, uuid.uuid, UUID_LEN) != UUID_LEN) {
cl_perror("Could not write UUID to %s", UUID_FILE);
rc=7;
}
if (close(fd) < 0) {
cl_perror("Could not close %s", UUID_FILE);
rc=8;
}
return rc;
}
diff --git a/crmd/lrm.c b/crmd/lrm.c
index bc0cd1fdff..6e61273a07 100644
--- a/crmd/lrm.c
+++ b/crmd/lrm.c
@@ -1,1905 +1,1906 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <crm/crm.h>
#include <crmd_fsa.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h> /* for access */
#include <heartbeat.h>
#include <clplumbing/cl_signal.h>
#include <errno.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crmd.h>
#include <crmd_messages.h>
#include <crmd_callbacks.h>
#include <crmd_lrm.h>
#include <lrm/raexec.h>
struct recurring_op_s
{
char *rsc_id;
char *op_key;
int call_id;
int interval;
gboolean remove;
gboolean cancelled;
};
char *make_stop_id(const char *rsc, int call_id);
gboolean build_operation_update(
crm_data_t *rsc_list, lrm_op_t *op, const char *src, int lpc);
gboolean build_active_RAs(crm_data_t *rsc_list);
gboolean is_rsc_active(const char *rsc_id);
void do_update_resource(lrm_op_t *op);
gboolean process_lrm_event(lrm_op_t *op);
void do_lrm_rsc_op(lrm_rsc_t *rsc, const char *operation,
crm_data_t *msg, HA_Message *request);
lrm_op_t *construct_op(
crm_data_t *rsc_op, const char *rsc_id, const char *operation);
void send_direct_ack(const char *to_host, const char *to_sys,
lrm_op_t* op, const char *rsc_id);
void free_recurring_op(gpointer value);
GHashTable *meta_hash = NULL;
GHashTable *resources = NULL;
GHashTable *pending_ops = NULL;
GCHSource *lrm_source = NULL;
int num_lrm_register_fails = 0;
int max_lrm_register_fails = 30;
/* A_LRM_CONNECT */
void
do_lrm_control(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
int ret = HA_OK;
if(action & A_LRM_DISCONNECT) {
if(verify_stopped(cur_state, LOG_INFO) == FALSE) {
crmd_fsa_stall(NULL);
return;
}
if(lrm_source) {
crm_debug("Removing LRM connection from MainLoop");
if(G_main_del_IPC_Channel(lrm_source) == FALSE) {
crm_err("Could not remove LRM connection"
" from MainLoop");
}
lrm_source = NULL;
}
if(fsa_lrm_conn) {
fsa_lrm_conn->lrm_ops->signoff(fsa_lrm_conn);
crm_info("Disconnected from the LRM");
clear_bit_inplace(fsa_input_register, R_LRM_CONNECTED);
}
/* TODO: Clean up the hashtable */
}
if(action & A_LRM_CONNECT) {
ret = HA_OK;
pending_ops = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, free_recurring_op);
resources = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
if(NULL == fsa_lrm_conn) {
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
ret = HA_FAIL;
}
if(ret == HA_OK) {
crm_debug("Connecting to the LRM");
ret = fsa_lrm_conn->lrm_ops->signon(
fsa_lrm_conn, CRM_SYSTEM_CRMD);
}
if(ret != HA_OK) {
if(++num_lrm_register_fails < max_lrm_register_fails) {
crm_warn("Failed to sign on to the LRM %d"
" (%d max) times",
num_lrm_register_fails,
max_lrm_register_fails);
crm_timer_start(wait_timer);
crmd_fsa_stall(NULL);
return;
}
}
if(ret == HA_OK) {
crm_debug_4("LRM: set_lrm_callback...");
ret = fsa_lrm_conn->lrm_ops->set_lrm_callback(
fsa_lrm_conn, lrm_op_callback);
if(ret != HA_OK) {
crm_err("Failed to set LRM callbacks");
}
}
if(ret != HA_OK) {
crm_err("Failed to sign on to the LRM %d"
" (max) times", num_lrm_register_fails);
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
return;
}
/* TODO: create a destroy handler that causes
* some recovery to happen
*/
lrm_source = G_main_add_IPC_Channel(
G_PRIORITY_LOW,
fsa_lrm_conn->lrm_ops->ipcchan(fsa_lrm_conn),
FALSE, lrm_dispatch, fsa_lrm_conn,
default_ipc_connection_destroy);
set_bit_inplace(fsa_input_register, R_LRM_CONNECTED);
crm_debug("LRM connection established");
}
if(action & ~(A_LRM_CONNECT|A_LRM_DISCONNECT)) {
crm_err("Unexpected action %s in %s",
fsa_action2string(action), __FUNCTION__);
}
}
static void
ghash_print_pending(gpointer key, gpointer value, gpointer user_data)
{
const char *stop_id = key;
int *log_level = user_data;
struct recurring_op_s *pending = value;
do_crm_log(*log_level, "Pending action: %s (%s)", stop_id, pending->op_key);
}
static void
ghash_print_pending_for_rsc(gpointer key, gpointer value, gpointer user_data)
{
const char *stop_id = key;
char *rsc = user_data;
struct recurring_op_s *pending = value;
if(safe_str_eq(rsc, pending->rsc_id)) {
do_crm_log(LOG_NOTICE, "%sction %s (%s) incomplete at shutdown",
pending->interval==0?"A":"Recurring a", stop_id, pending->op_key);
}
}
static void
ghash_count_pending(gpointer key, gpointer value, gpointer user_data)
{
int *counter = user_data;
struct recurring_op_s *pending = value;
if(pending->interval > 0) {
/* Ignore recurring actions in the shutdown calculations */
return;
}
(*counter)++;
}
gboolean
verify_stopped(enum crmd_fsa_state cur_state, int log_level)
{
int counter = 0;
gboolean rc = TRUE;
GListPtr lrm_list = NULL;
crm_debug("Checking for active resources before exit");
if(cur_state == S_TERMINATE) {
log_level = LOG_ERR;
}
g_hash_table_foreach(pending_ops, ghash_count_pending, &counter);
if(counter > 0) {
rc = FALSE;
do_crm_log(log_level,
"%d pending LRM operations at shutdown%s",
g_hash_table_size(pending_ops),
cur_state == S_TERMINATE?"":"... waiting");
if(cur_state == S_TERMINATE || !is_set(fsa_input_register, R_SENT_RSC_STOP)) {
g_hash_table_foreach(
pending_ops, ghash_print_pending, &log_level);
}
goto bail;
}
if(lrm_source != NULL && fsa_lrm_conn != NULL) {
lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn);
}
slist_iter(
rsc_id, char, lrm_list, lpc,
if(is_rsc_active(rsc_id) == FALSE) {
continue;
}
crm_err("Resource %s was active at shutdown."
" You may ignore this error if it is unmanaged.",
rsc_id);
g_hash_table_foreach(
pending_ops, ghash_print_pending_for_rsc, rsc_id);
);
bail:
set_bit_inplace(fsa_input_register, R_SENT_RSC_STOP);
if(cur_state == S_TERMINATE) {
rc = TRUE;
}
return rc;
}
static const char *
get_rsc_metadata(const char *type, const char *class, const char *provider)
{
int len = 0;
char *key = NULL;
char *metadata = NULL;
if(meta_hash == NULL) {
meta_hash = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
}
CRM_CHECK(type != NULL, return NULL);
CRM_CHECK(class != NULL, return NULL);
if(provider == NULL) {
provider = "heartbeat";
}
len = strlen(type) + strlen(class) + strlen(provider) + 4;
crm_malloc0(key, len);
sprintf(key, "%s::%s:%s", type, class, provider);
key[len-1] = 0;
metadata = g_hash_table_lookup(meta_hash, key);
if(metadata) {
crm_debug_2("Returning cached metadata for %s", key);
goto out;
}
crm_debug("Retreiving metadata for %s", key);
metadata = fsa_lrm_conn->lrm_ops->get_rsc_type_metadata(
fsa_lrm_conn, class, type, provider);
if(metadata) {
/* copy the metadata because the LRM likes using
* g_alloc instead of cl_malloc
+ * TODO: Is this still needed?
*/
char *m_copy = crm_strdup(metadata);
g_hash_table_insert(meta_hash, key, m_copy);
key = NULL; /* prevent it from being free'd */
g_free(metadata);
metadata = m_copy;
} else {
crm_warn("No metadata found for %s", key);
}
out:
crm_free(key);
return metadata;
}
static GListPtr
get_rsc_restart_list(lrm_rsc_t *rsc, lrm_op_t *op)
{
gboolean supported = FALSE;
GListPtr restart_list = NULL;
const char *value = NULL;
const char *metadata_str = get_rsc_metadata(
rsc->type, rsc->class, rsc->provider);
crm_data_t *params = NULL;
crm_data_t *actions = NULL;
crm_data_t *metadata = NULL;
if(metadata_str == NULL) {
return NULL;
}
metadata = string2xml(metadata_str);
if(metadata == NULL) {
crm_err("Metadata for %s::%s:%s is not valid XML",
rsc->provider, rsc->class, rsc->type);
return NULL;
}
actions = find_xml_node(metadata, "actions", TRUE);
xml_child_iter_filter(
actions, action, "action",
value = crm_element_value(action, "name");
if(safe_str_eq("reload", value)) {
supported = TRUE;
break;
}
);
if(supported == FALSE) {
goto cleanup;
}
params = find_xml_node(metadata, "parameters", TRUE);
xml_child_iter_filter(
params, param, "parameter",
value = crm_element_value(param, "unique");
if(crm_is_true(value)) {
value = crm_element_value(param, "name");
crm_debug("Attr %s is not reloadable", value);
restart_list = g_list_append(
restart_list, crm_strdup(value));
}
);
cleanup:
free_xml(metadata);
return restart_list;
}
static void
append_restart_list(crm_data_t *update, lrm_op_t *op, const char *version)
{
int len = 0;
char *list = NULL;
char *digest = NULL;
lrm_rsc_t *rsc = NULL;
const char *value = NULL;
gboolean non_empty = FALSE;
crm_data_t *restart = NULL;
GListPtr restart_list = NULL;
if(op->interval > 0) {
/* monitors are not reloadable */
return;
} else if(safe_str_neq(CRMD_ACTION_START, op->op_type)) {
/* only starts are potentially reloadable */
return;
} else if(compare_version("1.0.8", version) > 0) {
crm_debug("Caller version %s does not support reloads", version);
return;
}
rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, op->rsc_id);
if(rsc == NULL) {
crm_info("Resource %s no longer in the LRM", op->rsc_id);
return;
}
restart_list = get_rsc_restart_list(rsc, op);
if(restart_list == NULL) {
crm_debug("Resource %s does not support reloads", op->rsc_id);
return;
}
restart = create_xml_node(NULL, XML_TAG_PARAMS);
slist_iter(param, const char, restart_list, lpc,
int start = len;
value = g_hash_table_lookup(op->params, param);
if(value != NULL) {
non_empty = TRUE;
crm_xml_add(restart, param, value);
}
len += strlen(param) + 2;
crm_realloc(list, len+1);
sprintf(list+start, " %s ", param);
);
digest = calculate_xml_digest(restart, TRUE, FALSE);
crm_xml_add(update, XML_LRM_ATTR_OP_RESTART, list);
crm_xml_add(update, XML_LRM_ATTR_RESTART_DIGEST, digest);
crm_debug("%s : %s", digest, list);
if(non_empty) {
crm_log_xml_debug(restart, "restart digest source");
}
slist_destroy(char, child, restart_list,
crm_free(child));
free_xml(restart);
crm_free(digest);
crm_free(list);
}
gboolean
build_operation_update(
crm_data_t *xml_rsc, lrm_op_t *op, const char *src, int lpc)
{
char *magic = NULL;
const char *task = NULL;
crm_data_t *xml_op = NULL;
char *op_id = NULL;
char *local_user_data = NULL;
const char *caller_version = NULL;
char *digest = NULL;
crm_data_t *args_xml = NULL;
crm_data_t *args_parent = NULL;
CRM_DEV_ASSERT(op != NULL);
if(crm_assert_failed) {
return FALSE;
}
crm_debug_2("%s: Updating resouce %s after %s %s op",
src, op->rsc_id, op_status2text(op->op_status), op->op_type);
if(op->op_status == LRM_OP_CANCELLED) {
crm_debug_3("Ignoring cancelled op");
return TRUE;
}
if(AM_I_DC) {
caller_version = CRM_FEATURE_SET;
} else if(fsa_our_dc_version != NULL) {
caller_version = fsa_our_dc_version;
} else {
/* there is a small risk in formerly mixed clusters that
* it will be sub-optimal.
* however with our upgrade policy, the update we send
* should still be completely supported anyway
*/
caller_version = g_hash_table_lookup(
op->params, XML_ATTR_CRM_VERSION);
crm_warn("Falling back to operation originator version: %s",
caller_version);
}
crm_debug_3("DC version: %s", caller_version);
task = op->op_type;
/* remap the task name under various scenarios
* this makes life easier for the PE when its trying determin the current state
*/
if(crm_str_eq(task, "reload", TRUE)) {
if(op->op_status == LRM_OP_DONE) {
task = CRMD_ACTION_START;
} else {
task = CRMD_ACTION_STATUS;
}
} else if(crm_str_eq(task, CRMD_ACTION_MIGRATE, TRUE)) {
/* if the migrate_from fails it will have enough info to do the right thing */
if(op->op_status == LRM_OP_DONE) {
task = CRMD_ACTION_STOP;
} else {
task = CRMD_ACTION_STATUS;
}
} else if(op->op_status == LRM_OP_DONE
&& crm_str_eq(task, CRMD_ACTION_MIGRATED, TRUE)) {
task = CRMD_ACTION_START;
}
if(safe_str_eq(task, CRMD_ACTION_NOTIFY)) {
const char *n_type = g_hash_table_lookup(
op->params, crm_meta_name("notify_type"));
const char *n_task = g_hash_table_lookup(
op->params, crm_meta_name("notify_operation"));
#if CRM_DEPRECATED_SINCE_2_0_5
if(n_type == NULL) {
n_type = g_hash_table_lookup(op->params, "notify_type");
}
if(n_task == NULL) {
n_task = g_hash_table_lookup(op->params, "notify_operation");
}
#endif
CRM_DEV_ASSERT(n_type != NULL);
CRM_DEV_ASSERT(n_task != NULL);
op_id = generate_notify_key(op->rsc_id, n_type, n_task);
/* these are not yet allowed to fail */
op->op_status = LRM_OP_DONE;
op->rc = 0;
} else {
op_id = generate_op_key(op->rsc_id, task, op->interval);
}
xml_op = find_entity(xml_rsc, XML_LRM_TAG_RSC_OP, op_id);
if(xml_op != NULL) {
crm_log_xml(LOG_DEBUG, "Replacing existing entry", xml_op);
} else {
xml_op = create_xml_node(xml_rsc, XML_LRM_TAG_RSC_OP);
}
crm_xml_add(xml_op, XML_ATTR_ID, op_id);
crm_free(op_id);
crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
crm_xml_add(xml_op, XML_ATTR_ORIGIN, src);
if(op->user_data == NULL) {
char *id = crm_itoa(op->call_id);
crm_debug("Generating fake transition key for:"
" %s_%s_%d %d from %s",
op->rsc_id, op->op_type, op->interval, op->call_id,
op->app_name);
local_user_data = generate_transition_key(-1, 0, 0, id);
op->user_data = local_user_data;
crm_free(id);
}
if(compare_version("1.0.3", caller_version) > 0) {
magic = generate_transition_magic_v202(
op->user_data, op->op_status);
} else {
magic = generate_transition_magic(
op->user_data, op->op_status, op->rc);
}
crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
crm_free(magic);
switch(op->op_status) {
case LRM_OP_PENDING:
break;
case LRM_OP_CANCELLED:
crm_err("What to do here");
break;
case LRM_OP_ERROR:
case LRM_OP_TIMEOUT:
case LRM_OP_NOTSUPPORTED:
crm_debug_2("Resource action %s/%s %s: %d",
op->rsc_id, task,
op_status2text(op->op_status), op->rc);
break;
case LRM_OP_DONE:
break;
}
crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
/* set these on 'xml_rsc' too to make life easy for the PE */
crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
crm_xml_add_int(xml_op, XML_LRM_ATTR_RC, op->rc);
crm_xml_add_int(xml_op, XML_LRM_ATTR_OPSTATUS, op->op_status);
crm_xml_add_int(xml_op, XML_LRM_ATTR_INTERVAL, op->interval);
if(compare_version("2.1", caller_version) <= 0) {
if(op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
crm_debug("Timing data (%s_%s_%d): last=%lu change=%lu exec=%lu queue=%lu",
op->rsc_id, op->op_type, op->interval,
op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
crm_xml_add_int(xml_op, "last_run", op->t_run);
crm_xml_add_int(xml_op, "last_rc_change", op->t_rcchange);
crm_xml_add_int(xml_op, "exec_time", op->exec_time);
crm_xml_add_int(xml_op, "queue_time", op->queue_time);
}
}
/* this will enable us to later determin that the
* resource's parameters have changed and we should force
* a restart
*/
args_parent = NULL;
#if CRM_DEPRECATED_SINCE_2_0_4
if(compare_version("1.0.4", caller_version) > 0) {
args_parent = xml_op;
}
#endif
args_xml = create_xml_node(args_parent, XML_TAG_PARAMS);
g_hash_table_foreach(op->params, hash2field, args_xml);
filter_action_parameters(args_xml, caller_version);
digest = calculate_xml_digest(args_xml, TRUE, FALSE);
if(op->interval == 0 && safe_str_neq(task, CRMD_ACTION_STOP)) {
crm_debug("Calculated digest %s for %s (%s)\n",
digest, ID(xml_op),
crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
crm_log_xml(LOG_DEBUG, "digest:source", args_xml);
}
crm_xml_add(xml_op, XML_LRM_ATTR_OP_DIGEST, digest);
crm_free(digest);
if(args_parent == NULL) {
free_xml(args_xml);
}
append_restart_list(xml_op, op, caller_version);
if(op->op_status != LRM_OP_DONE
&& crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) {
const char *host = g_hash_table_lookup(
op->params, crm_meta_name("migrate_source_uuid"));
crm_xml_add(xml_op, CRMD_ACTION_MIGRATED, host);
}
if(local_user_data) {
crm_free(local_user_data);
op->user_data = NULL;
}
return TRUE;
}
gboolean
is_rsc_active(const char *rsc_id)
{
GList *op_list = NULL;
gboolean active = FALSE;
lrm_rsc_t *the_rsc = NULL;
state_flag_t cur_state = 0;
int max_call_id = -1;
if(fsa_lrm_conn == NULL) {
return FALSE;
}
the_rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rsc_id);
crm_debug_3("Processing lrm_rsc_t entry %s", rsc_id);
if(the_rsc == NULL) {
crm_err("NULL resource returned from the LRM");
return FALSE;
}
op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state);
crm_debug_3("\tcurrent state:%s",cur_state==LRM_RSC_IDLE?"Idle":"Busy");
slist_iter(
op, lrm_op_t, op_list, llpc,
crm_debug_2("Processing op %s_%d (%d) for %s (status=%d, rc=%d)",
op->op_type, op->interval, op->call_id, the_rsc->id,
op->op_status, op->rc);
CRM_ASSERT(max_call_id <= op->call_id);
if(op->rc == EXECRA_OK
&& safe_str_eq(op->op_type, CRMD_ACTION_STOP)) {
active = FALSE;
} else if(op->rc == EXECRA_OK
&& safe_str_eq(op->op_type, CRMD_ACTION_MIGRATE)) {
/* a stricter check is too complex...
* leave that to the PE
*/
active = FALSE;
} else if(op->rc == EXECRA_NOT_RUNNING) {
active = FALSE;
} else {
active = TRUE;
}
max_call_id = op->call_id;
lrm_free_op(op);
);
g_list_free(op_list);
lrm_free_rsc(the_rsc);
return active;
}
gboolean
build_active_RAs(crm_data_t *rsc_list)
{
GList *op_list = NULL;
GList *lrm_list = NULL;
gboolean found_op = FALSE;
state_flag_t cur_state = 0;
if(fsa_lrm_conn == NULL) {
return FALSE;
}
lrm_list = fsa_lrm_conn->lrm_ops->get_all_rscs(fsa_lrm_conn);
slist_iter(
rid, char, lrm_list, lpc,
lrm_rsc_t *the_rsc =
fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid);
crm_data_t *xml_rsc = create_xml_node(
rsc_list, XML_LRM_TAG_RESOURCE);
int max_call_id = -1;
crm_debug_2("Processing lrm_rsc_t entry %s", rid);
if(the_rsc == NULL) {
crm_err("NULL resource returned from the LRM");
continue;
}
crm_xml_add(xml_rsc, XML_ATTR_ID, the_rsc->id);
crm_xml_add(xml_rsc, XML_ATTR_TYPE, the_rsc->type);
crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, the_rsc->class);
crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER,the_rsc->provider);
op_list = the_rsc->ops->get_cur_state(the_rsc, &cur_state);
crm_debug_2("\tcurrent state:%s",
cur_state==LRM_RSC_IDLE?"Idle":"Busy");
slist_iter(
op, lrm_op_t, op_list, llpc,
crm_debug_2("Processing op %s for %s (status=%d, rc=%d)",
op->op_type, the_rsc->id, op->op_status, op->rc);
if(max_call_id < op->call_id) {
build_operation_update(
xml_rsc, op, __FUNCTION__, llpc);
} else if(max_call_id > op->call_id) {
crm_err("Bad call_id in list=%d. Previous call_id=%d",
op->call_id, max_call_id);
} else {
crm_warn("lrm->get_cur_state() returned"
" duplicate entries for call_id=%d",
op->call_id);
}
max_call_id = op->call_id;
found_op = TRUE;
lrm_free_op(op);
);
if(found_op == FALSE && g_list_length(op_list) != 0) {
crm_err("Could not properly determin last op"
" for %s from %d entries", the_rsc->id,
g_list_length(op_list));
}
g_list_free(op_list);
lrm_free_rsc(the_rsc);
);
g_list_free(lrm_list);
return TRUE;
}
crm_data_t*
do_lrm_query(gboolean is_replace)
{
gboolean shut_down = FALSE;
crm_data_t *xml_result= NULL;
crm_data_t *xml_state = NULL;
crm_data_t *xml_data = NULL;
crm_data_t *rsc_list = NULL;
const char *exp_state = CRMD_JOINSTATE_MEMBER;
if(is_set(fsa_input_register, R_SHUTDOWN)) {
exp_state = CRMD_STATE_INACTIVE;
shut_down = TRUE;
}
xml_state = create_node_state(
fsa_our_uname, ACTIVESTATUS, XML_BOOLEAN_TRUE,
ONLINESTATUS, CRMD_JOINSTATE_MEMBER, exp_state,
!shut_down, __FUNCTION__);
xml_data = create_xml_node(xml_state, XML_CIB_TAG_LRM);
crm_xml_add(xml_data, XML_ATTR_ID, fsa_our_uuid);
rsc_list = create_xml_node(xml_data, XML_LRM_TAG_RESOURCES);
/* Build a list of active (not always running) resources */
build_active_RAs(rsc_list);
if(is_replace) {
crm_xml_add(xml_state, XML_CIB_ATTR_REPLACE, XML_CIB_TAG_LRM);
}
xml_result = create_cib_fragment(xml_state, XML_CIB_TAG_STATUS);
free_xml(xml_state);
crm_log_xml_debug_3(xml_state, "Current state of the LRM");
return xml_result;
}
static void
delete_rsc_entry(const char *rsc_id)
{
crm_data_t *xml_top = NULL;
crm_data_t *xml_tmp = NULL;
/*
* Remove the rsc from the CIB
*
* Avoids refreshing the entire LRM section of this host
*/
CRM_CHECK(rsc_id != NULL, return);
xml_top = create_xml_node(NULL, XML_CIB_TAG_STATE);
crm_xml_add(xml_top, XML_ATTR_ID, fsa_our_uuid);
xml_tmp = create_xml_node(xml_top, XML_CIB_TAG_LRM);
crm_xml_add(xml_tmp, XML_ATTR_ID, fsa_our_uuid);
xml_tmp = create_xml_node(xml_tmp, XML_LRM_TAG_RESOURCES);
xml_tmp = create_xml_node(xml_tmp, XML_LRM_TAG_RESOURCE);
crm_xml_add(xml_tmp, XML_ATTR_ID, rsc_id);
crm_debug("sync: Sending delete op for %s", rsc_id);
fsa_cib_conn->cmds->delete_absolute(fsa_cib_conn, XML_CIB_TAG_STATUS, xml_top,
NULL, cib_quorum_override);
/* crm_log_xml_err(xml_top, "op:cancel"); */
free_xml(xml_top);
}
static void
delete_op_entry(lrm_op_t *op, const char *rsc_id, const char *key, int call_id)
{
crm_data_t *xml_top = NULL;
/*
* Remove the op from the CIB
*
* Avoids refreshing the entire LRM section of this host
*/
if(op != NULL) {
xml_top = create_xml_node(NULL, XML_LRM_TAG_RSC_OP);
crm_xml_add_int(xml_top, XML_LRM_ATTR_CALLID, op->call_id);
crm_xml_add(xml_top, XML_ATTR_TRANSITION_KEY, op->user_data);
crm_debug("async: Sending delete op for %s_%s_%d (call=%d)",
op->rsc_id, op->op_type, op->interval, op->call_id);
fsa_cib_conn->cmds->delete(fsa_cib_conn, XML_CIB_TAG_STATUS, xml_top,
NULL, cib_quorum_override);
} else if (rsc_id != NULL && key != NULL) {
crm_data_t *xml_tmp = NULL;
xml_top = create_xml_node(NULL, XML_CIB_TAG_STATE);
crm_xml_add(xml_top, XML_ATTR_ID, fsa_our_uuid);
xml_tmp = create_xml_node(xml_top, XML_CIB_TAG_LRM);
crm_xml_add(xml_tmp, XML_ATTR_ID, fsa_our_uuid);
xml_tmp = create_xml_node(xml_tmp, XML_LRM_TAG_RESOURCES);
xml_tmp = create_xml_node(xml_tmp, XML_LRM_TAG_RESOURCE);
crm_xml_add(xml_tmp, XML_ATTR_ID, rsc_id);
xml_tmp = create_xml_node(xml_tmp, XML_LRM_TAG_RSC_OP);
crm_xml_add(xml_tmp, XML_ATTR_ID, key);
if(call_id > 0) {
crm_xml_add_int(xml_tmp, XML_LRM_ATTR_CALLID, call_id);
}
crm_debug("sync: Sending delete op for %s (call=%d)", key, call_id);
fsa_cib_conn->cmds->delete_absolute(fsa_cib_conn, XML_CIB_TAG_STATUS, xml_top,
NULL, cib_quorum_override);
} else {
crm_err("Not enough information to delete op entry: rsc=%p key=%p", rsc_id, key);
return;
}
crm_log_xml_debug_2(xml_top, "op:cancel");
free_xml(xml_top);
}
static gboolean
cancel_op(lrm_rsc_t *rsc, const char *key, int op, gboolean remove)
{
int rc = HA_OK;
struct recurring_op_s *pending = NULL;
CRM_CHECK(op != 0, return FALSE);
CRM_CHECK(rsc != NULL, return FALSE);
if(key == NULL) {
key = make_stop_id(rsc->id, op);
}
pending = g_hash_table_lookup(pending_ops, key);
if(pending) {
if(remove && pending->remove == FALSE) {
pending->remove = TRUE;
crm_debug("Scheduling %s for removal", key);
}
if(pending->cancelled) {
crm_debug("Operation %s already cancelled", key);
return TRUE;
}
pending->cancelled = TRUE;
} else {
crm_info("No pending op found for %s", key);
}
crm_debug("Cancelling op %d for %s (%s)", op, rsc->id, key);
rc = rsc->ops->cancel_op(rsc, op);
if(rc != HA_OK) {
crm_debug("Op %d for %s (%s): Nothing to cancel", op, rsc->id, key);
/* The caller needs to make sure the entry is
* removed from the pending_ops list
*
* Usually by returning TRUE inside the worker function
* supplied to g_hash_table_foreach_remove()
*
* Not removing the entry from pending_ops will block
* the node from shutting down
*/
return FALSE;
}
return TRUE;
}
struct cancel_data
{
gboolean done;
gboolean remove;
const char *key;
lrm_rsc_t *rsc;
};
static gboolean
cancel_action_by_key(gpointer key, gpointer value, gpointer user_data)
{
struct cancel_data *data = user_data;
struct recurring_op_s *op = (struct recurring_op_s*)value;
if(safe_str_eq(op->op_key, data->key)) {
data->done = TRUE;
if (cancel_op(data->rsc, key, op->call_id, data->remove) == FALSE) {
return TRUE;
}
}
return FALSE;
}
static gboolean
cancel_op_key(lrm_rsc_t *rsc, const char *key, gboolean remove)
{
struct cancel_data data;
CRM_CHECK(rsc != NULL, return FALSE);
CRM_CHECK(key != NULL, return FALSE);
data.key = key;
data.rsc = rsc;
data.done = FALSE;
data.remove = remove;
g_hash_table_foreach_remove(pending_ops, cancel_action_by_key, &data);
return data.done;
}
static lrm_rsc_t *
get_lrm_resource(crm_data_t *resource, crm_data_t *op_msg, gboolean do_create)
{
char rid[64];
lrm_rsc_t *rsc = NULL;
const char *short_id = ID(resource);
const char *long_id = crm_element_value(resource, XML_ATTR_ID_LONG);
crm_debug_2("Retrieving %s from the LRM.", short_id);
CRM_CHECK(short_id != NULL, return NULL);
if(rsc == NULL) {
/* check if its already there (short name) */
strncpy(rid, short_id, 64);
rid[63] = 0;
rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid);
}
if(rsc == NULL && long_id != NULL) {
/* try the long name instead */
strncpy(rid, long_id, 64);
rid[63] = 0;
rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid);
}
if(rsc == NULL && do_create) {
/* add it to the LRM */
const char *type = crm_element_value(resource, XML_ATTR_TYPE);
const char *class = crm_element_value(resource, XML_AGENT_ATTR_CLASS);
const char *provider = crm_element_value(resource, XML_AGENT_ATTR_PROVIDER);
GHashTable *params = xml2list(op_msg);
CRM_CHECK(class != NULL, return NULL);
CRM_CHECK(type != NULL, return NULL);
crm_debug("Adding rsc %s before operation", short_id);
strncpy(rid, short_id, 64);
rid[63] = 0;
#if CRM_DEPRECATED_SINCE_2_0_3
if(op_msg != NULL) {
if(g_hash_table_lookup(
params, XML_ATTR_CRM_VERSION) == NULL) {
g_hash_table_destroy(params);
params = xml2list_202(op_msg);
}
}
#endif
if(g_hash_table_size(params) == 0) {
crm_log_xml_warn(op_msg, "EmptyParams");
}
fsa_lrm_conn->lrm_ops->add_rsc(
fsa_lrm_conn, rid, class, type, provider, params);
rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, rid);
g_hash_table_destroy(params);
if(rsc == NULL) {
fsa_data_t *msg_data = NULL;
crm_err("Could not add resource %s to LRM", rid);
register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
}
}
return rsc;
}
static gboolean lrm_remove_deleted_op(
gpointer key, gpointer value, gpointer user_data)
{
const char *rsc = user_data;
struct recurring_op_s *pending = value;
if(safe_str_eq(rsc, pending->rsc_id)) {
crm_info("Removing op %s:%d for deleted resource %s",
pending->op_key, pending->call_id, rsc);
return TRUE;
}
return FALSE;
}
/* A_LRM_INVOKE */
void
do_lrm_invoke(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input current_input,
fsa_data_t *msg_data)
{
gboolean done = FALSE;
gboolean create_rsc = TRUE;
const char *crm_op = NULL;
const char *from_sys = NULL;
const char *from_host = NULL;
const char *operation = NULL;
ha_msg_input_t *input = fsa_typed_data(fsa_dt_ha_msg);
crm_op = cl_get_string(input->msg, F_CRM_TASK);
from_sys = cl_get_string(input->msg, F_CRM_SYS_FROM);
if(safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) {
from_host = cl_get_string(input->msg, F_CRM_HOST_FROM);
}
crm_debug_2("LRM command from: %s", from_sys);
if(safe_str_eq(crm_op, CRM_OP_LRM_DELETE)) {
operation = CRMD_ACTION_DELETE;
} else if(safe_str_eq(operation, CRM_OP_LRM_REFRESH)) {
crm_op = CRM_OP_LRM_REFRESH;
} else if(safe_str_eq(crm_op, CRM_OP_LRM_FAIL)) {
crm_info("Failing resource...");
operation = "fail";
} else if(input->xml != NULL) {
operation = crm_element_value(input->xml, XML_LRM_ATTR_TASK);
}
if(safe_str_eq(crm_op, CRM_OP_LRM_REFRESH)) {
enum cib_errors rc = cib_ok;
crm_data_t *fragment = do_lrm_query(TRUE);
crm_info("Forcing a local LRM refresh");
fsa_cib_update(XML_CIB_TAG_STATUS, fragment,
cib_quorum_override, rc);
free_xml(fragment);
} else if(safe_str_eq(crm_op, CRM_OP_LRM_QUERY)) {
crm_data_t *data = do_lrm_query(FALSE);
HA_Message *reply = create_reply(input->msg, data);
if(relay_message(reply, TRUE) == FALSE) {
crm_err("Unable to route reply");
crm_log_message(LOG_ERR, reply);
crm_msg_del(reply);
}
free_xml(data);
} else if(safe_str_eq(operation, CRM_OP_PROBED)
|| safe_str_eq(crm_op, CRM_OP_REPROBE)) {
int cib_options = cib_inhibit_notify;
const char *probed = XML_BOOLEAN_TRUE;
if(safe_str_eq(crm_op, CRM_OP_REPROBE)) {
cib_options = cib_none;
probed = XML_BOOLEAN_FALSE;
}
update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_STATUS,
fsa_our_uuid, NULL, NULL, CRM_OP_PROBED, probed, FALSE);
} else if(operation != NULL) {
lrm_rsc_t *rsc = NULL;
crm_data_t *params = NULL;
crm_data_t *xml_rsc = find_xml_node(
input->xml, XML_CIB_TAG_RESOURCE, TRUE);
CRM_CHECK(xml_rsc != NULL, return);
/* only the first 16 chars are used by the LRM */
params = find_xml_node(input->xml, XML_TAG_ATTRS, TRUE);
if(safe_str_eq(operation, CRMD_ACTION_DELETE)) {
create_rsc = FALSE;
}
rsc = get_lrm_resource(xml_rsc, input->xml, create_rsc);
if(rsc == NULL && create_rsc) {
crm_err("Invalid resource definition");
crm_log_xml_warn(input->msg, "Bad command");
} else if(rsc == NULL) {
lrm_op_t* op = NULL;
crm_err("Not creating resource for a %s event: %s",
operation, ID(input->xml));
crm_log_xml_warn(input->msg, "Bad command");
op = construct_op(input->xml, ID(xml_rsc), operation);
op->op_status = LRM_OP_DONE;
op->rc = EXECRA_OK;
CRM_ASSERT(op != NULL);
send_direct_ack(from_host, from_sys, op, ID(xml_rsc));
free_lrm_op(op);
} else if(safe_str_eq(operation, CRMD_ACTION_CANCEL)) {
lrm_op_t* op = NULL;
char *op_key = NULL;
int call = 0;
const char *call_id = NULL;
const char *op_task = NULL;
const char *op_interval = NULL;
CRM_CHECK(params != NULL,
crm_log_xml_warn(input->xml, "Bad command");
return);
op_interval = crm_element_value(params, crm_meta_name("interval"));
op_task = crm_element_value(params, crm_meta_name(XML_LRM_ATTR_TASK));
call_id = crm_element_value(params, crm_meta_name(XML_LRM_ATTR_CALLID));
#if CRM_DEPRECATED_SINCE_2_0_5
if(op_interval == NULL) {
op_interval = crm_element_value(params, "interval");
}
if(op_task == NULL) {
op_task = crm_element_value(params, XML_LRM_ATTR_TASK);
if(op_task == NULL) {
op_task = crm_element_value(params, "task");
}
}
#endif
CRM_CHECK(op_task != NULL,
crm_log_xml_warn(input->xml, "Bad command");
return);
CRM_CHECK(op_interval != NULL,
crm_log_xml_warn(input->xml, "Bad command");
return);
op = construct_op(input->xml, rsc->id, op_task);
CRM_ASSERT(op != NULL);
op_key = generate_op_key(
rsc->id,op_task,crm_parse_int(op_interval,"0"));
crm_debug("PE requested op %s (call=%s) be cancelled",
op_key, call_id?call_id:"NA");
call = crm_parse_int(call_id, "0");
if(call == 0) {
/* the normal case when the PE cancels a recurring op */
done = cancel_op_key(rsc, op_key, TRUE);
} else {
/* the normal case when the PE cancels an orphan op */
done = cancel_op(rsc, NULL, call, TRUE);
}
if(done == FALSE) {
crm_debug("Nothing known about operation %d for %s", call, op_key);
delete_op_entry(NULL, rsc->id, op_key, call);
/* needed?? surely not otherwise the cancel_op_(_key) wouldn't
* have failed in the first place
*/
g_hash_table_remove(pending_ops, op_key);
}
op->rc = EXECRA_OK;
op->op_status = LRM_OP_DONE;
send_direct_ack(from_host, from_sys, op, rsc->id);
crm_free(op_key);
free_lrm_op(op);
} else if(safe_str_eq(operation, CRMD_ACTION_DELETE)) {
int rc = HA_OK;
lrm_op_t* op = NULL;
CRM_ASSERT(rsc != NULL);
op = construct_op(input->xml, rsc->id, operation);
CRM_ASSERT(op != NULL);
op->op_status = LRM_OP_DONE;
op->rc = EXECRA_OK;
crm_info("Removing resource %s from the LRM", rsc->id);
rc = fsa_lrm_conn->lrm_ops->delete_rsc(fsa_lrm_conn, rsc->id);
if(rc != HA_OK) {
crm_err("Failed to remove resource %s", rsc->id);
op->op_status = LRM_OP_ERROR;
op->rc = EXECRA_UNKNOWN_ERROR;
}
delete_rsc_entry(rsc->id);
send_direct_ack(from_host, from_sys, op, rsc->id);
free_lrm_op(op);
g_hash_table_foreach_remove(pending_ops, lrm_remove_deleted_op, rsc->id);
if(safe_str_neq(from_sys, CRM_SYSTEM_TENGINE)) {
/* this isn't expected - trigger a new transition */
time_t now = time(NULL);
char *now_s = crm_itoa(now);
crm_debug("Triggering a refresh after %s deleted %s from the LRM",
from_sys, rsc->id);
update_attr(fsa_cib_conn, cib_none, XML_CIB_TAG_CRMCONFIG,
NULL, NULL, NULL, "last-lrm-refresh", now_s, FALSE);
crm_free(now_s);
}
} else if(rsc != NULL) {
do_lrm_rsc_op(rsc, operation, input->xml, input->msg);
}
lrm_free_rsc(rsc);
} else {
crm_err("Operation was neither a lrm_query, nor a rsc op. %s",
crm_str(crm_op));
register_fsa_error(C_FSA_INTERNAL, I_ERROR, NULL);
}
}
lrm_op_t *
construct_op(crm_data_t *rsc_op, const char *rsc_id, const char *operation)
{
lrm_op_t *op = NULL;
const char *op_delay = NULL;
const char *op_timeout = NULL;
const char *op_interval = NULL;
const char *transition = NULL;
CRM_DEV_ASSERT(rsc_id != NULL);
crm_malloc0(op, sizeof(lrm_op_t));
op->op_type = crm_strdup(operation);
op->op_status = LRM_OP_PENDING;
op->rc = -1;
op->rsc_id = crm_strdup(rsc_id);
op->interval = 0;
op->timeout = 0;
op->start_delay = 0;
op->copyparams = 0;
op->app_name = crm_strdup(CRM_SYSTEM_CRMD);
if(rsc_op == NULL) {
CRM_DEV_ASSERT(safe_str_eq(CRMD_ACTION_STOP, operation));
op->user_data = NULL;
op->user_data_len = 0;
/* the stop_all_resources() case
* by definition there is no DC (or they'd be shutting
* us down).
* So we should put our version here.
*/
op->params = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
g_hash_table_insert(op->params,
crm_strdup(XML_ATTR_CRM_VERSION),
crm_strdup(CRM_FEATURE_SET));
crm_debug_2("Constructed %s op for %s", operation, rsc_id);
return op;
}
op->params = xml2list(rsc_op);
#if CRM_DEPRECATED_SINCE_2_0_3
if(g_hash_table_lookup(op->params, XML_ATTR_CRM_VERSION) == NULL) {
g_hash_table_destroy(op->params);
op->params = xml2list_202(rsc_op);
}
#endif
if(op->params == NULL) {
CRM_DEV_ASSERT(safe_str_eq(CRMD_ACTION_STOP, operation));
}
op_delay = g_hash_table_lookup(op->params, crm_meta_name("start_delay"));
op_timeout = g_hash_table_lookup(op->params, crm_meta_name("timeout"));
op_interval = g_hash_table_lookup(op->params, crm_meta_name("interval"));
#if CRM_DEPRECATED_SINCE_2_0_5
if(op_delay == NULL) {
op_delay = g_hash_table_lookup(op->params, "start_delay");
}
if(op_timeout == NULL) {
op_timeout = g_hash_table_lookup(op->params, "timeout");
}
if(op_interval == NULL) {
op_interval = g_hash_table_lookup(op->params, "interval");
}
#endif
op->interval = crm_parse_int(op_interval, "0");
op->timeout = crm_parse_int(op_timeout, "0");
op->start_delay = crm_parse_int(op_delay, "0");
/* sanity */
if(op->interval < 0) {
op->interval = 0;
}
if(op->timeout < 0) {
op->timeout = 0;
}
if(op->start_delay < 0) {
op->start_delay = 0;
}
transition = crm_element_value(rsc_op, XML_ATTR_TRANSITION_KEY);
CRM_CHECK(transition != NULL, return op);
op->user_data = crm_strdup(transition);
op->user_data_len = 1+strlen(op->user_data);
if(op->interval != 0) {
if(safe_str_eq(operation, CRMD_ACTION_START)
|| safe_str_eq(operation, CRMD_ACTION_STOP)) {
crm_err("Start and Stop actions cannot have an interval");
op->interval = 0;
}
}
/* reset the resource's parameters? */
if(op->interval == 0) {
if(safe_str_eq(CRMD_ACTION_START, operation)
|| safe_str_eq(CRMD_ACTION_STATUS, operation)) {
op->copyparams = 1;
}
}
crm_debug_2("Constructed %s op for %s: interval=%d",
operation, rsc_id, op->interval);
return op;
}
void
send_direct_ack(const char *to_host, const char *to_sys,
lrm_op_t* op, const char *rsc_id)
{
HA_Message *reply = NULL;
crm_data_t *update, *iter;
crm_data_t *fragment;
CRM_DEV_ASSERT(op != NULL);
if(crm_assert_failed) {
return;
}
if(op->rsc_id == NULL) {
CRM_DEV_ASSERT(rsc_id != NULL);
op->rsc_id = crm_strdup(rsc_id);
}
if(to_sys == NULL) {
to_sys = CRM_SYSTEM_TENGINE;
}
update = create_node_state(
fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__);
iter = create_xml_node(update, XML_CIB_TAG_LRM);
crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE);
crm_xml_add(iter, XML_ATTR_ID, op->rsc_id);
build_operation_update(iter, op, __FUNCTION__, 0);
fragment = create_cib_fragment(update, XML_CIB_TAG_STATUS);
reply = create_request(CRM_OP_INVOKE_LRM, fragment, to_host,
to_sys, CRM_SYSTEM_LRMD, NULL);
crm_log_xml_debug_2(update, "ACK Update");
crm_info("ACK'ing resource op %s_%s_%d from %s: %s",
op->rsc_id, op->op_type, op->interval, op->user_data,
cl_get_string(reply, XML_ATTR_REFERENCE));
if(relay_message(reply, TRUE) == FALSE) {
crm_log_message_adv(LOG_ERR, "Unable to route reply", reply);
crm_msg_del(reply);
}
free_xml(fragment);
free_xml(update);
}
static gboolean
stop_recurring_action_by_rsc(gpointer key, gpointer value, gpointer user_data)
{
lrm_rsc_t *rsc = user_data;
struct recurring_op_s *op = (struct recurring_op_s*)value;
if(op->interval != 0 && safe_str_eq(op->rsc_id, rsc->id)) {
if (cancel_op(rsc, key, op->call_id, FALSE) == FALSE) {
return TRUE;
}
}
return FALSE;
}
void
do_lrm_rsc_op(lrm_rsc_t *rsc, const char *operation,
crm_data_t *msg, HA_Message *request)
{
int call_id = 0;
char *op_id = NULL;
lrm_op_t* op = NULL;
fsa_data_t *msg_data = NULL;
const char *transition = NULL;
CRM_CHECK(rsc != NULL, return);
if(msg != NULL) {
transition = crm_element_value(msg, XML_ATTR_TRANSITION_KEY);
if(transition == NULL) {
crm_err("Missing transition");
crm_log_message(LOG_ERR, msg);
}
}
op = construct_op(msg, rsc->id, operation);
/* stop the monitor before stopping the resource */
if(crm_str_eq(operation, CRMD_ACTION_STOP, TRUE)
|| crm_str_eq(operation, CRMD_ACTION_DEMOTE, TRUE)
|| crm_str_eq(operation, CRMD_ACTION_PROMOTE, TRUE)
|| crm_str_eq(operation, CRMD_ACTION_MIGRATE, TRUE)) {
g_hash_table_foreach_remove(pending_ops, stop_recurring_action_by_rsc, rsc);
}
/* now do the op */
crm_info("Performing op=%s_%s_%d key=%s)",
rsc->id, operation, op->interval, transition);
if(fsa_state != S_NOT_DC && fsa_state != S_TRANSITION_ENGINE) {
if(safe_str_neq(operation, "fail")
&& safe_str_neq(operation, CRMD_ACTION_STOP)) {
crm_info("Discarding attempt to perform action %s on %s"
" in state %s", operation, rsc->id,
fsa_state2string(fsa_state));
op->rc = 99;
op->op_status = LRM_OP_ERROR;
send_direct_ack(NULL, NULL, op, rsc->id);
free_lrm_op(op);
crm_free(op_id);
return;
}
}
op_id = generate_op_key(rsc->id, op->op_type, op->interval);
if(op->interval > 0) {
/* cancel it so we can then restart it without conflict */
cancel_op_key(rsc, op_id, FALSE);
op->target_rc = CHANGED;
} else {
op->target_rc = EVERYTIME;
}
g_hash_table_replace(resources,crm_strdup(rsc->id), crm_strdup(op_id));
call_id = rsc->ops->perform_op(rsc, op);
if(call_id <= 0) {
crm_err("Operation %s on %s failed: %d",
operation, rsc->id, call_id);
register_fsa_error(C_FSA_INTERNAL, I_FAIL, NULL);
} else {
/* record all operations so we can wait
* for them to complete during shutdown
*/
char *call_id_s = make_stop_id(rsc->id, call_id);
struct recurring_op_s *pending = NULL;
crm_malloc0(pending, sizeof(struct recurring_op_s));
crm_debug("Recording pending op: %d - %s %s", call_id, op_id, call_id_s);
pending->call_id = call_id;
pending->interval = op->interval;
pending->op_key = crm_strdup(op_id);
pending->rsc_id = crm_strdup(rsc->id);
g_hash_table_replace(pending_ops, call_id_s, pending);
}
crm_free(op_id);
free_lrm_op(op);
return;
}
void
free_recurring_op(gpointer value)
{
struct recurring_op_s *op = (struct recurring_op_s*)value;
crm_free(op->rsc_id);
crm_free(op->op_key);
crm_free(op);
}
void
free_lrm_op(lrm_op_t *op)
{
g_hash_table_destroy(op->params);
crm_free(op->user_data);
crm_free(op->output);
crm_free(op->rsc_id);
crm_free(op->op_type);
crm_free(op->app_name);
crm_free(op);
}
static void dup_attr(gpointer key, gpointer value, gpointer user_data)
{
g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value));
}
lrm_op_t *
copy_lrm_op(const lrm_op_t *op)
{
lrm_op_t *op_copy = NULL;
CRM_DEV_ASSERT(op != NULL);
if(crm_assert_failed) {
return NULL;
}
CRM_ASSERT(op->rsc_id != NULL);
crm_malloc0(op_copy, sizeof(lrm_op_t));
op_copy->op_type = crm_strdup(op->op_type);
/* input fields */
op_copy->params = g_hash_table_new_full(
g_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
if(op->params != NULL) {
g_hash_table_foreach(op->params, dup_attr, op_copy->params);
}
op_copy->timeout = op->timeout;
op_copy->interval = op->interval;
op_copy->target_rc = op->target_rc;
/* in the CRM, this is always a string */
if(op->user_data != NULL) {
op_copy->user_data = crm_strdup(op->user_data);
}
/* output fields */
op_copy->op_status = op->op_status;
op_copy->rc = op->rc;
op_copy->call_id = op->call_id;
op_copy->output = NULL;
op_copy->rsc_id = crm_strdup(op->rsc_id);
if(op->app_name != NULL) {
op_copy->app_name = crm_strdup(op->app_name);
}
if(op->output != NULL) {
op_copy->output = crm_strdup(op->output);
}
return op_copy;
}
lrm_rsc_t *
copy_lrm_rsc(const lrm_rsc_t *rsc)
{
lrm_rsc_t *rsc_copy = NULL;
if(rsc == NULL) {
return NULL;
}
crm_malloc0(rsc_copy, sizeof(lrm_rsc_t));
rsc_copy->id = crm_strdup(rsc->id);
rsc_copy->type = crm_strdup(rsc->type);
rsc_copy->class = NULL;
rsc_copy->provider = NULL;
if(rsc->class != NULL) {
rsc_copy->class = crm_strdup(rsc->class);
}
if(rsc->provider != NULL) {
rsc_copy->provider = crm_strdup(rsc->provider);
}
/* GHashTable* params; */
rsc_copy->params = NULL;
rsc_copy->ops = NULL;
return rsc_copy;
}
static void
cib_rsc_callback(const HA_Message *msg, int call_id, int rc,
crm_data_t *output, void *user_data)
{
switch(rc) {
case cib_ok:
case cib_diff_failed:
case cib_diff_resync:
crm_debug("Resource update %d complete: rc=%d", call_id, rc);
break;
default:
crm_err("Resource update %d failed: (rc=%d) %s",
call_id, rc, cib_error2string(rc));
}
}
void
do_update_resource(lrm_op_t* op)
{
/*
<status>
<nodes_status id=uname>
<lrm>
<lrm_resources>
<lrm_resource id=...>
</...>
*/
int rc = cib_ok;
lrm_rsc_t *rsc = NULL;
crm_data_t *update, *iter;
CRM_CHECK(op != NULL, return);
update = create_node_state(
fsa_our_uname, NULL, NULL, NULL, NULL, NULL, FALSE, __FUNCTION__);
iter = create_xml_node(update, XML_CIB_TAG_LRM);
crm_xml_add(iter, XML_ATTR_ID, fsa_our_uuid);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCES);
iter = create_xml_node(iter, XML_LRM_TAG_RESOURCE);
crm_xml_add(iter, XML_ATTR_ID, op->rsc_id);
rsc = fsa_lrm_conn->lrm_ops->get_rsc(fsa_lrm_conn, op->rsc_id);
CRM_CHECK(rsc->type != NULL,
crm_err("Resource %s has no value for type", op->rsc_id));
CRM_CHECK(rsc->class != NULL,
crm_err("Resource %s has no value for class", op->rsc_id));
crm_xml_add(iter, XML_ATTR_TYPE, rsc->type);
crm_xml_add(iter, XML_AGENT_ATTR_CLASS, rsc->class);
crm_xml_add(iter, XML_AGENT_ATTR_PROVIDER,rsc->provider);
lrm_free_rsc(rsc);
build_operation_update(iter, op, __FUNCTION__, 0);
/* make it an asyncronous call and be done with it
*
* Best case:
* the resource state will be discovered during
* the next signup or election.
*
* Bad case:
* we are shutting down and there is no DC at the time,
* but then why were we shutting down then anyway?
* (probably because of an internal error)
*
* Worst case:
* we get shot for having resources "running" when the really weren't
*
* the alternative however means blocking here for too long, which
* isnt acceptable
*/
fsa_cib_update(XML_CIB_TAG_STATUS, update, cib_quorum_override, rc);
if(rc > 0) {
/* the return code is a call number, not an error code */
crm_debug("Sent resource state update message: %d", rc);
add_cib_op_callback(rc, FALSE, NULL, cib_rsc_callback);
} else {
crm_err("Resource state update failed: %s",
cib_error2string(rc));
}
free_xml(update);
}
void
do_lrm_event(long long action,
enum crmd_fsa_cause cause,
enum crmd_fsa_state cur_state,
enum crmd_fsa_input cur_input,
fsa_data_t *msg_data)
{
CRM_CHECK(FALSE, return);
}
gboolean
process_lrm_event(lrm_op_t *op)
{
char *op_id = NULL;
char *op_key = NULL;
int log_level = LOG_ERR;
struct recurring_op_s *pending = NULL;
CRM_CHECK(op != NULL, return FALSE);
CRM_CHECK(op->rsc_id != NULL, return FALSE);
op_key = generate_op_key(op->rsc_id, op->op_type, op->interval);
switch(op->op_status) {
case LRM_OP_ERROR:
case LRM_OP_PENDING:
case LRM_OP_NOTSUPPORTED:
break;
case LRM_OP_CANCELLED:
log_level = LOG_INFO;
break;
case LRM_OP_DONE:
log_level = LOG_INFO;
break;
case LRM_OP_TIMEOUT:
log_level = LOG_DEBUG_3;
crm_err("LRM operation %s (%d) %s (timeout=%dms)",
op_key, op->call_id,
op_status2text(op->op_status), op->timeout);
break;
default:
crm_err("Mapping unknown status (%d) to ERROR",
op->op_status);
op->op_status = LRM_OP_ERROR;
}
if(op->op_status == LRM_OP_ERROR
&& (op->rc == EXECRA_RUNNING_MASTER || op->rc == EXECRA_NOT_RUNNING)) {
/* Leave it up to the TE/PE to decide if this is an error */
op->op_status = LRM_OP_DONE;
log_level = LOG_INFO;
}
do_crm_log(log_level, "LRM operation %s (call=%d, rc=%d) %s %s",
op_key, op->call_id, op->rc, op_status2text(op->op_status),
op->op_status==LRM_OP_ERROR?execra_code2string(op->rc):"");
if(op->op_status == LRM_OP_ERROR && op->output != NULL) {
crm_info("Result: %s", op->output);
}
op_id = make_stop_id(op->rsc_id, op->call_id);
pending = g_hash_table_lookup(pending_ops, op_id);
if(op->op_status != LRM_OP_CANCELLED) {
do_update_resource(op);
if(op->interval != 0) {
goto out;
}
} else if(op->interval == 0) {
/* no known valid reason for this to happen */
crm_err("Op %s (call=%d): Cancelled", op_key, op->call_id);
} else if(pending == NULL) {
crm_err("Op %s (call=%d): No 'pending' entry",
op_key, op->call_id);
} else if(op->user_data == NULL) {
crm_err("Op %s (call=%d): No user data", op_key, op->call_id);
} else if(pending->remove) {
delete_op_entry(op, op->rsc_id, op_key, op->call_id);
} else {
crm_debug("Op %s (call=%d): no delete event required", op_key, op->call_id);
}
if(g_hash_table_remove(pending_ops, op_id)) {
crm_debug("Op %s (call=%d, stop_id=%s): Confirmed", op_key, op->call_id, op_id);
}
out:
crm_free(op_key);
crm_free(op_id);
return TRUE;
}
char *
make_stop_id(const char *rsc, int call_id)
{
char *op_id = NULL;
crm_malloc0(op_id, strlen(rsc) + 34);
if(op_id != NULL) {
snprintf(op_id, strlen(rsc) + 34, "%s:%d", rsc, call_id);
}
return op_id;
}
diff --git a/include/crm/crm.h b/include/crm/crm.h
index 4c8bb165e6..ac5f9dbf37 100644
--- a/include/crm/crm.h
+++ b/include/crm/crm.h
@@ -1,324 +1,316 @@
/*
* 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__H
#define CRM__H
#include <config.h>
#include <stdlib.h>
#include <glib.h>
#undef MIN
#undef MAX
#include <string.h>
#include <clplumbing/cl_log.h>
-#include <clplumbing/cl_malloc.h>
#ifdef MCHECK
#include <mcheck.h>
#endif
#define EOS '\0'
#define DIMOF(a) ((int) (sizeof(a)/sizeof(a[0])) )
#define HAURL(url) HA_URLBASE url
#ifndef CRM_DEV_BUILD
# define CRM_DEV_BUILD 0
#endif
#define CRM_DEPRECATED_SINCE_2_0_1 0
#define CRM_DEPRECATED_SINCE_2_0_2 0
#define CRM_DEPRECATED_SINCE_2_0_3 0
#define CRM_DEPRECATED_SINCE_2_0_4 0
#define CRM_DEPRECATED_SINCE_2_0_5 0
#define CRM_DEPRECATED_SINCE_2_0_6 1
#define CRM_DEPRECATED_SINCE_2_0_7 1
#define CRM_DEPRECATED_SINCE_2_0_8 1
#define CRM_DEPRECATED_SINCE_2_1_0 1
#define CRM_META "CRM_meta"
#define crm_meta_name(field) CRM_META"_"field
#define ipc_call_diff_max_ms 5000
#define action_diff_warn_ms 5000
#define action_diff_max_ms 20000
#define fsa_diff_warn_ms 10000
#define fsa_diff_max_ms 30000
#include <crm/common/util.h>
#define CRM_ASSERT(expr) if((expr) == FALSE) { \
crm_abort(__FILE__, __PRETTY_FUNCTION__, __LINE__, #expr, TRUE, FALSE); \
}
extern gboolean crm_assert_failed;
#define CRM_DEV_ASSERT(expr) \
crm_assert_failed = FALSE; \
if((expr) == FALSE) { \
crm_assert_failed = TRUE; \
crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \
}
#define CRM_CHECK(expr, failure_action) if((expr) == FALSE) { \
crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, FALSE, TRUE); \
failure_action; \
}
#define CRM_CHECK_AND_STORE(expr, failure_action) if((expr) == FALSE) { \
crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, #expr, TRUE, TRUE); \
failure_action; \
}
extern const char *crm_system_name;
/* Clean these up at some point, some probably should be runtime options */
#define WORKING_DIR HA_VARLIBDIR"/heartbeat/crm"
#define CRM_SOCK_DIR HA_VARRUNDIR"/heartbeat/crm"
#define BIN_DIR HA_LIBDIR"/heartbeat"
#define SOCKET_LEN 1024
#define APPNAME_LEN 256
#define MAX_IPC_FAIL 5
#define CIB_FILENAME WORKING_DIR"/cib.xml"
#define CIB_BACKUP WORKING_DIR"/cib_backup.xml"
#define CRM_FEATURE_SET "2.1"
#define MSG_LOG 1
#define DOT_FSA_ACTIONS 1
#define DOT_ALL_FSA_INPUTS 1
/* #define FSA_TRACE 1 */
#define INFINITY_S "INFINITY"
#define MINUS_INFINITY_S "-INFINITY"
#define INFINITY 1000000
/* Sub-systems */
#define CRM_SYSTEM_DC "dc"
#define CRM_SYSTEM_DCIB "dcib" /* The master CIB */
#define CRM_SYSTEM_CIB "cib"
#define CRM_SYSTEM_CRMD "crmd"
#define CRM_SYSTEM_LRMD "lrmd"
#define CRM_SYSTEM_PENGINE "pengine"
#define CRM_SYSTEM_TENGINE "tengine"
#define CRM_SYSTEM_STONITHD "stonithd"
/* Valid operations */
#define CRM_OP_NOOP "noop"
#define CRM_OP_JOIN_ANNOUNCE "join_announce"
#define CRM_OP_JOIN_OFFER "join_offer"
#define CRM_OP_JOIN_REQUEST "join_request"
#define CRM_OP_JOIN_ACKNAK "join_ack_nack"
#define CRM_OP_JOIN_CONFIRM "join_confirm"
#define CRM_OP_DIE "die_no_respawn"
#define CRM_OP_RETRIVE_CIB "retrieve_cib"
#define CRM_OP_PING "ping"
#define CRM_OP_VOTE "vote"
#define CRM_OP_NOVOTE "no-vote"
#define CRM_OP_HELLO "hello"
#define CRM_OP_HBEAT "dc_beat"
#define CRM_OP_PECALC "pe_calc"
#define CRM_OP_ABORT "abort"
#define CRM_OP_QUIT "quit"
#define CRM_OP_LOCAL_SHUTDOWN "start_shutdown"
#define CRM_OP_SHUTDOWN_REQ "req_shutdown"
#define CRM_OP_SHUTDOWN "do_shutdown"
#define CRM_OP_FENCE "stonith"
#define CRM_OP_EVENTCC "event_cc"
#define CRM_OP_TEABORT "te_abort"
#define CRM_OP_TEABORTED "te_abort_confirmed" /* we asked */
#define CRM_OP_TE_HALT "te_halt"
#define CRM_OP_TECOMPLETE "te_complete"
#define CRM_OP_TETIMEOUT "te_timeout"
#define CRM_OP_TRANSITION "transition"
#define CRM_OP_REGISTER "register"
#define CRM_OP_DEBUG_UP "debug_inc"
#define CRM_OP_DEBUG_DOWN "debug_dec"
#define CRM_OP_INVOKE_LRM "lrm_invoke"
#define CRM_OP_LRM_REFRESH "lrm_refresh"
#define CRM_OP_LRM_QUERY "lrm_query"
#define CRM_OP_LRM_DELETE "lrm_delete"
#define CRM_OP_LRM_FAIL "lrm_fail"
#define CRM_OP_PROBED "probe_complete"
#define CRM_OP_REPROBE "probe_again"
#define CRMD_STATE_ACTIVE "member"
#define CRMD_STATE_INACTIVE "down"
#define CRMD_JOINSTATE_DOWN CRMD_STATE_INACTIVE
#define CRMD_JOINSTATE_PENDING "pending"
#define CRMD_JOINSTATE_MEMBER CRMD_STATE_ACTIVE
#define CRMD_ACTION_DELETE "delete"
#define CRMD_ACTION_CANCEL "cancel"
#define CRMD_ACTION_MIGRATE "migrate_to"
#define CRMD_ACTION_MIGRATED "migrate_from"
#define CRMD_ACTION_START "start"
#define CRMD_ACTION_STARTED "running"
#define CRMD_ACTION_STOP "stop"
#define CRMD_ACTION_STOPPED "stopped"
#define CRMD_ACTION_PROMOTE "promote"
#define CRMD_ACTION_PROMOTED "promoted"
#define CRMD_ACTION_DEMOTE "demote"
#define CRMD_ACTION_DEMOTED "demoted"
#define CRMD_ACTION_NOTIFY "notify"
#define CRMD_ACTION_NOTIFIED "notified"
#define CRMD_ACTION_STATUS "monitor"
typedef GList* GListPtr;
#define slist_destroy(child_type, child, parent, a) \
{ \
GListPtr __crm_iter_head = parent; \
child_type *child = NULL; \
while(__crm_iter_head != NULL) { \
child = (child_type *) __crm_iter_head->data; \
__crm_iter_head = __crm_iter_head->next; \
{ a; } \
} \
g_list_free(parent); \
}
#define slist_iter(child, child_type, parent, counter, a) \
{ \
GListPtr __crm_iter_head = parent; \
child_type *child = NULL; \
int counter = 0; \
for(; __crm_iter_head != NULL; counter++) { \
child = (child_type *) __crm_iter_head->data; \
__crm_iter_head = __crm_iter_head->next; \
{ a; } \
} \
}
#define LOG_DEBUG_2 LOG_DEBUG+1
#define LOG_DEBUG_3 LOG_DEBUG+2
#define LOG_DEBUG_4 LOG_DEBUG+3
#define LOG_DEBUG_5 LOG_DEBUG+4
#define LOG_DEBUG_6 LOG_DEBUG+5
#define LOG_MSG LOG_DEBUG_3
/*
* Throughout the macros below, note the leading, pre-comma, space in the
* various ' , ##args' occurences to aid portability across versions of 'gcc'.
* http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html#Variadic-Macros
*/
#define do_crm_log(level, fmt, args...) do { \
if(crm_log_level < (level)) { \
continue; \
} else if((level) > LOG_DEBUG) { \
cl_log(LOG_DEBUG, "debug%d: %s: " fmt, \
level-LOG_INFO, __PRETTY_FUNCTION__ , ##args); \
} else { \
cl_log(level, "%s: " fmt, \
__PRETTY_FUNCTION__ , ##args); \
} \
} while(0)
#define crm_crit(fmt, args...) do_crm_log(LOG_CRIT, fmt , ##args)
#define crm_err(fmt, args...) do_crm_log(LOG_ERR, fmt , ##args)
#define crm_warn(fmt, args...) do_crm_log(LOG_WARNING, fmt , ##args)
#define crm_notice(fmt, args...) do_crm_log(LOG_NOTICE, fmt , ##args)
#define crm_info(fmt, args...) do_crm_log(LOG_INFO, fmt , ##args)
#define crm_debug(fmt, args...) do_crm_log(LOG_DEBUG, fmt , ##args)
#define crm_debug_2(fmt, args...) do_crm_log(LOG_DEBUG_2, fmt , ##args)
#define crm_debug_3(fmt, args...) do_crm_log(LOG_DEBUG_3, fmt , ##args)
#define crm_debug_4(fmt, args...) do_crm_log(LOG_DEBUG_4, fmt , ##args)
#define crm_debug_5(fmt, args...) do_crm_log(LOG_DEBUG_5, fmt , ##args)
#define crm_debug_6(fmt, args...) do_crm_log(LOG_DEBUG_6, fmt , ##args)
extern void crm_log_message_adv(
int level, const char *alt_debugfile, const HA_Message *msg);
#define crm_log_message(level, msg) if(crm_log_level >= (level)) { \
crm_log_message_adv(level, NULL, msg); \
}
#define crm_log_xml(level, text, xml) if(crm_log_level >= (level)) { \
print_xml_formatted(level, __PRETTY_FUNCTION__, xml, text); \
}
#define crm_log_xml_crit(xml, text) crm_log_xml(LOG_CRIT, text, xml)
#define crm_log_xml_err(xml, text) crm_log_xml(LOG_ERR, text, xml)
#define crm_log_xml_warn(xml, text) crm_log_xml(LOG_WARNING, text, xml)
#define crm_log_xml_notice(xml, text) crm_log_xml(LOG_NOTICE, text, xml)
#define crm_log_xml_info(xml, text) crm_log_xml(LOG_INFO, text, xml)
#define crm_log_xml_debug(xml, text) crm_log_xml(LOG_DEBUG, text, xml)
#define crm_log_xml_debug_2(xml, text) crm_log_xml(LOG_DEBUG_2, text, xml)
#define crm_log_xml_debug_3(xml, text) crm_log_xml(LOG_DEBUG_3, text, xml)
#define crm_log_xml_debug_4(xml, text) crm_log_xml(LOG_DEBUG_4, text, xml)
#define crm_log_xml_debug_5(xml, text) crm_log_xml(LOG_DEBUG_5, text, xml)
#define crm_str(x) (const char*)(x?x:"<null>")
#if CRM_DEV_BUILD
# define crm_malloc0(malloc_obj, length) do { \
if(malloc_obj) { \
crm_err("Potential memory leak:" \
" %s at %s:%d not NULL before alloc.", \
#malloc_obj, __FILE__, __LINE__); \
} \
- malloc_obj = cl_malloc(length); \
+ malloc_obj = malloc(length); \
if(malloc_obj == NULL) { \
crm_err("Failed allocation of %lu bytes", (unsigned long)length); \
CRM_ASSERT(malloc_obj != NULL); \
} \
memset(malloc_obj, 0, length); \
} while(0)
/* it's not a memory leak to already have an object to realloc, that's
* the usual case, however if it does have a value, it must have been
* allocated by the same allocator!
*/
# define crm_realloc(realloc_obj, length) do { \
- if (realloc_obj != NULL) { \
- CRM_ASSERT(cl_is_allocated(realloc_obj) == 1); \
- } \
- realloc_obj = cl_realloc(realloc_obj, length); \
+ realloc_obj = realloc(realloc_obj, length); \
CRM_ASSERT(realloc_obj != NULL); \
} while(0)
-# define crm_free(free_obj) if(free_obj) { \
- CRM_ASSERT(cl_is_allocated(free_obj) == 1); \
- cl_free(free_obj); \
- free_obj=NULL; \
- }
+# define crm_free(free_obj) free(free_obj); free_obj = NULL;
#else
# define crm_malloc0(malloc_obj, length) do { \
- malloc_obj = cl_malloc(length); \
+ malloc_obj = malloc(length); \
if(malloc_obj == NULL) { \
crm_err("Failed allocation of %lu bytes", (unsigned long)length); \
CRM_ASSERT(malloc_obj != NULL); \
} \
memset(malloc_obj, 0, length); \
} while(0)
# define crm_realloc(realloc_obj, length) do { \
- realloc_obj = cl_realloc(realloc_obj, length); \
+ realloc_obj = realloc(realloc_obj, length); \
CRM_ASSERT(realloc_obj != NULL); \
} while(0)
-# define crm_free(free_obj) if(free_obj) { cl_free(free_obj); free_obj=NULL; }
+# define crm_free(free_obj) free(free_obj); free_obj = NULL;
#endif
#define crm_msg_del(msg) if(msg != NULL) { ha_msg_del(msg); msg = NULL; }
#define crm_strdup(str) crm_strdup_fn(str, __FILE__, __PRETTY_FUNCTION__, __LINE__)
#endif
diff --git a/lib/crm/common/utils.c b/lib/crm/common/utils.c
index eb7fdd3bda..0f15ec1f42 100644
--- a/lib/crm/common/utils.c
+++ b/lib/crm/common/utils.c
@@ -1,1640 +1,1639 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <crm_internal.h>
#ifndef _GNU_SOURCE
# define _GNU_SOURCE
#endif
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ctype.h>
#include <pwd.h>
#include <grp.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 <clplumbing/lsb_exitcodes.h>
#include <clplumbing/cl_pidfile.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>
#ifndef MAXLINE
# define MAXLINE 512
#endif
static uint ref_counter = 0;
gboolean crm_assert_failed = FALSE;
unsigned int crm_log_level = LOG_INFO;
gboolean crm_config_error = FALSE;
gboolean crm_config_warning = FALSE;
const char *crm_system_name = "unknown";
void crm_set_env_options(void);
gboolean
check_time(const char *value)
{
if(crm_get_msec(value) < 5000) {
return FALSE;
}
return TRUE;
}
gboolean
check_timer(const char *value)
{
if(crm_get_msec(value) < 0) {
return FALSE;
}
return TRUE;
}
gboolean
check_boolean(const char *value)
{
int tmp = FALSE;
if(crm_str_to_boolean(value, &tmp) != 1) {
return FALSE;
}
return TRUE;
}
gboolean
check_number(const char *value)
{
errno = 0;
if(value == NULL) {
return FALSE;
} else if(safe_str_eq(value, MINUS_INFINITY_S)) {
} else if(safe_str_eq(value, INFINITY_S)) {
} else {
crm_int_helper(value, NULL);
}
if(errno != 0) {
return FALSE;
}
return TRUE;
}
int
char2score(const char *score)
{
int score_f = 0;
if(score == NULL) {
} else if(safe_str_eq(score, MINUS_INFINITY_S)) {
score_f = -INFINITY;
} else if(safe_str_eq(score, INFINITY_S)) {
score_f = INFINITY;
} else if(safe_str_eq(score, "+"INFINITY_S)) {
score_f = INFINITY;
} else {
score_f = crm_parse_int(score, NULL);
if(score_f > 0 && score_f > INFINITY) {
score_f = INFINITY;
} else if(score_f < 0 && score_f < -INFINITY) {
score_f = -INFINITY;
}
}
return score_f;
}
char *
score2char(int score)
{
if(score >= INFINITY) {
return crm_strdup("+"INFINITY_S);
} else if(score <= -INFINITY) {
return crm_strdup("-"INFINITY_S);
}
return crm_itoa(score);
}
const char *
cluster_option(GHashTable* options, gboolean(*validate)(const char*),
const char *name, const char *old_name, const char *def_value)
{
const char *value = NULL;
CRM_ASSERT(name != NULL);
if(options != NULL) {
value = g_hash_table_lookup(options, name);
}
if(value == NULL && old_name && options != NULL) {
value = g_hash_table_lookup(options, old_name);
if(value != NULL) {
crm_config_warn("Using deprecated name '%s' for"
" cluster option '%s'", old_name, name);
g_hash_table_insert(
options, crm_strdup(name), crm_strdup(value));
value = g_hash_table_lookup(options, old_name);
}
}
if(value == NULL) {
crm_debug_2("Using default value '%s' for cluster option '%s'",
def_value, name);
if(options == NULL) {
return def_value;
}
g_hash_table_insert(
options, crm_strdup(name), crm_strdup(def_value));
value = g_hash_table_lookup(options, name);
}
if(validate && validate(value) == FALSE) {
crm_config_err("Value '%s' for cluster option '%s' is invalid."
" Defaulting to %s", value, name, def_value);
g_hash_table_replace(options, crm_strdup(name),
crm_strdup(def_value));
value = g_hash_table_lookup(options, name);
}
return value;
}
const char *
get_cluster_pref(GHashTable *options, pe_cluster_option *option_list, int len, const char *name)
{
int lpc = 0;
const char *value = NULL;
gboolean found = FALSE;
for(lpc = 0; lpc < len; lpc++) {
if(safe_str_eq(name, option_list[lpc].name)) {
found = TRUE;
value = cluster_option(options,
option_list[lpc].is_valid,
option_list[lpc].name,
option_list[lpc].alt_name,
option_list[lpc].default_value);
}
}
CRM_CHECK(found, crm_err("No option named: %s", name));
CRM_ASSERT(value != NULL);
return value;
}
void
config_metadata(const char *name, const char *version, const char *desc_short, const char *desc_long,
pe_cluster_option *option_list, int len)
{
int lpc = 0;
fprintf(stdout, "<?xml version=\"1.0\"?>"
"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
"<resource-agent name=\"%s\">\n"
" <version>%s</version>\n"
" <longdesc lang=\"en\">%s</longdesc>\n"
" <shortdesc lang=\"en\">%s</shortdesc>\n"
" <parameters>\n", name, version, desc_long, desc_short);
for(lpc = 0; lpc < len; lpc++) {
if(option_list[lpc].description_long == NULL
&& option_list[lpc].description_short == NULL) {
continue;
}
fprintf(stdout, " <parameter name=\"%s\" unique=\"0\">\n"
" <shortdesc lang=\"en\">%s</shortdesc>\n"
" <content type=\"%s\" default=\"%s\"/>\n"
" <longdesc lang=\"en\">%s%s%s</longdesc>\n"
" </parameter>\n",
option_list[lpc].name,
option_list[lpc].description_short,
option_list[lpc].type,
option_list[lpc].default_value,
option_list[lpc].description_long?option_list[lpc].description_long:option_list[lpc].description_short,
option_list[lpc].values?" Allowed values: ":"",
option_list[lpc].values?option_list[lpc].values:"");
}
fprintf(stdout, " </parameters>\n</resource-agent>\n");
}
void
verify_all_options(GHashTable *options, pe_cluster_option *option_list, int len)
{
int lpc = 0;
for(lpc = 0; lpc < len; lpc++) {
cluster_option(options,
option_list[lpc].is_valid,
option_list[lpc].name,
option_list[lpc].alt_name,
option_list[lpc].default_value);
}
}
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);
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_ASSERT(name != NULL && value != NULL);
*name = NULL;
*value = NULL;
crm_debug_4("Attempting to decode: [%s]", srcstring);
if (srcstring != NULL) {
len = strlen(srcstring);
while(lpc <= len) {
if (srcstring[lpc] == separator) {
crm_malloc0(*name, 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, 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++;
}
}
if(*name != NULL) {
crm_free(*name);
}
*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, (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)
{
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)
{
char *hash_value = NULL;
if (src_node == NULL || src_subsys == NULL) {
return NULL;
}
if (strcasecmp(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 hash_value;
}
hash_value = crm_concat(src_node, src_subsys, '_');
crm_info("created hash value: (%s)", hash_value);
return hash_value;
}
char *
crm_itoa(int an_int)
{
int len = 32;
char *buffer = NULL;
crm_malloc0(buffer, (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, int level, gboolean coredir, gboolean to_stderr,
int argc, char **argv)
{
/* 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*/
crm_system_name = entity;
cl_log_set_entity(entity);
cl_log_set_facility(HA_LOG_FACILITY);
if(coredir) {
cl_set_corerootdir(HA_COREDIR);
cl_cdtocoredir();
}
set_crm_log_level(level);
crm_set_env_options();
cl_log_args(argc, argv);
cl_log_enable_stderr(to_stderr);
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 < 100 && crm_log_level < level) {
alter_debug(DEBUG_INC);
}
while(crm_log_level > 0 && 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, "#========= %s message start ==========#", prefix?prefix:"");
if(level > LOG_DEBUG) {
cl_log_message(LOG_DEBUG, msg);
} else {
cl_log_message(level, msg);
}
}
}
static int
crm_version_helper(const char *text, char **end_text)
{
int atoi_result = -1;
CRM_ASSERT(end_text != NULL);
errno = 0;
if(text != NULL && text[0] != 0) {
atoi_result = (int)strtol(text, end_text, 10);
if(errno == EINVAL) {
crm_err("Conversion of '%s' %c failed", text, text[0]);
atoi_result = -1;
}
}
return atoi_result;
}
/*
* version1 < version2 : -1
* version1 = version2 : 0
* version1 > version2 : 1
*/
int
compare_version(const char *version1, const char *version2)
{
int rc = 0;
int lpc = 0;
char *ver1_copy = NULL, *ver2_copy = 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;
}
ver1_copy = crm_strdup(version1);
ver2_copy = crm_strdup(version2);
rest1 = ver1_copy;
rest2 = ver2_copy;
while(1) {
int digit1 = 0;
int digit2 = 0;
lpc++;
if(rest1 == rest2) {
break;
}
if(rest1 != NULL) {
digit1 = crm_version_helper(rest1, &rest1);
}
if(rest2 != NULL) {
digit2 = crm_version_helper(rest2, &rest2);
}
if(digit1 < digit2){
rc = -1;
crm_debug_5("%d < %d", digit1, digit2);
break;
} else if (digit1 > digit2){
rc = 1;
crm_debug_5("%d > %d", digit1, digit2);
break;
}
if(rest1 != NULL && rest1[0] == '.') {
rest1++;
}
if(rest1 != NULL && rest1[0] == 0) {
rest1 = NULL;
}
if(rest2 != NULL && rest2[0] == '.') {
rest2++;
}
if(rest2 != NULL && rest2[0] == 0) {
rest2 = NULL;
}
}
crm_free(ver1_copy);
crm_free(ver2_copy);
if(rc == 0) {
crm_debug_3("%s == %s (%d)", version1, version2, lpc);
} else if(rc < 0) {
crm_debug_3("%s < %s (%d)", version1, version2, lpc);
} else if(rc > 0) {
crm_debug_3("%s > %s (%d)", version1, version2, lpc);
}
return rc;
}
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:
if (crm_log_level < 100) {
crm_log_level++;
}
break;
case DEBUG_DEC:
if (crm_log_level > 0) {
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);
}
#include <sys/types.h>
#include <stdlib.h>
#include <limits.h>
long
crm_int_helper(const char *text, char **end_text)
{
long atoi_result = -1;
char *local_end_text = NULL;
errno = 0;
if(text != NULL) {
if(end_text != NULL) {
atoi_result = strtoul(text, end_text, 10);
} else {
atoi_result = strtoul(text, &local_end_text, 10);
}
/* CRM_CHECK(errno != EINVAL); */
if(errno == EINVAL) {
crm_err("Conversion of %s failed", text);
atoi_result = -1;
} else {
if(errno == ERANGE) {
crm_err("Conversion of %s was clipped: %ld",
text, atoi_result);
}
if(end_text == NULL && local_end_text[0] != '\0') {
crm_err("Characters left over after parsing "
"\"%s\": \"%s\"", text, local_end_text);
}
}
}
return atoi_result;
}
int
crm_parse_int(const char *text, const char *default_text)
{
int atoi_result = -1;
if(text != NULL) {
atoi_result = crm_int_helper(text, NULL);
if(errno == 0) {
return atoi_result;
}
}
if(default_text != NULL) {
atoi_result = crm_int_helper(default_text, NULL);
if(errno == 0) {
return atoi_result;
}
} else {
crm_err("No default conversion value supplied");
}
return -1;
}
gboolean
crm_str_eq(const char *a, const char *b, gboolean use_case)
{
if(a == NULL || b == NULL) {
/* shouldn't be comparing NULLs */
CRM_CHECK(a != b, return TRUE);
return FALSE;
} else if(use_case && a[0] != b[0]) {
return FALSE;
} else if(a == b) {
return TRUE;
} else if(strcasecmp(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(strcasecmp(a, b) == 0) {
return FALSE;
}
return TRUE;
}
char *
crm_strdup_fn(const char *src, const char *file, const char *fn, int line)
{
char *dup = NULL;
CRM_CHECK(src != NULL, return NULL);
crm_malloc0(dup, strlen(src) + 1);
return strcpy(dup, src);
}
#define ENV_PREFIX "HA_"
void
crm_set_env_options(void)
{
cl_inherit_logging_environment(500);
cl_log_set_logd_channel_source(NULL, NULL);
if(debug_level > 0 && (debug_level+LOG_INFO) > (int)crm_log_level) {
set_crm_log_level(LOG_INFO + debug_level);
}
}
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 (strncasecmp(units, "h", 1) == 0
|| strncasecmp(units, "hr", 2) == 0) {
multiplier = 60*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);
}
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_CHECK(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_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
len += strlen(op_type);
len += strlen(rsc_id);
crm_malloc0(op_id, len);
CRM_CHECK(op_id != NULL, return NULL);
sprintf(op_id, "%s_%s_%d", rsc_id, op_type, interval);
return op_id;
}
gboolean
parse_op_key(const char *key, char **rsc_id, char **op_type, int *interval)
{
char *mutable_key = NULL;
char *mutable_key_ptr = NULL;
int len = 0, offset = 0, ch = 0;
CRM_CHECK(key != NULL, return FALSE);
*interval = 0;
len = strlen(key);
offset = len-1;
crm_debug_3("Source: %s", key);
while(offset > 0 && isdigit(key[offset])) {
int digits = len-offset;
ch = key[offset] - '0';
CRM_CHECK(ch < 10, return FALSE);
CRM_CHECK(ch >= 0, return FALSE);
while(digits > 1) {
digits--;
ch = ch * 10;
}
*interval += ch;
offset--;
}
crm_debug_3(" Interval: %d", *interval);
CRM_CHECK(key[offset] == '_', return FALSE);
mutable_key = crm_strdup(key);
mutable_key_ptr = mutable_key_ptr;
mutable_key[offset] = 0;
offset--;
while(offset > 0 && key[offset] != '_') {
offset--;
}
CRM_CHECK(key[offset] == '_',
crm_free(mutable_key); return FALSE);
mutable_key_ptr = mutable_key+offset+1;
crm_debug_3(" Action: %s", mutable_key_ptr);
*op_type = crm_strdup(mutable_key_ptr);
mutable_key[offset] = 0;
offset--;
CRM_CHECK(mutable_key != mutable_key_ptr,
crm_free(mutable_key); return FALSE);
crm_debug_3(" Resource: %s", mutable_key);
*rsc_id = crm_strdup(mutable_key);
crm_free(mutable_key);
return TRUE;
}
char *
generate_notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
{
int len = 12;
char *op_id = NULL;
CRM_CHECK(rsc_id != NULL, return NULL);
CRM_CHECK(op_type != NULL, return NULL);
CRM_CHECK(notify_type != NULL, return NULL);
len += strlen(op_type);
len += strlen(rsc_id);
len += strlen(notify_type);
crm_malloc0(op_id, 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_v202(const char *transition_key, int op_status)
{
int len = 80;
char *fail_state = NULL;
CRM_CHECK(transition_key != NULL, return NULL);
len += strlen(transition_key);
crm_malloc0(fail_state, len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%s", op_status,transition_key);
}
return fail_state;
}
char *
generate_transition_magic(const char *transition_key, int op_status, int op_rc)
{
int len = 80;
char *fail_state = NULL;
CRM_CHECK(transition_key != NULL, return NULL);
len += strlen(transition_key);
crm_malloc0(fail_state, len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%d;%s",
op_status, op_rc, transition_key);
}
return fail_state;
}
gboolean
decode_transition_magic(
const char *magic, char **uuid, int *transition_id, int *action_id,
int *op_status, int *op_rc, int *target_rc)
{
int res = 0;
char *key = NULL;
gboolean result = TRUE;
CRM_CHECK(magic != NULL, return FALSE);
CRM_CHECK(op_rc != NULL, return FALSE);
CRM_CHECK(op_status != NULL, return FALSE);
crm_malloc0(key, strlen(magic));
res = sscanf(magic, "%d:%d;%s", op_status, op_rc, key);
if(res != 3) {
crm_crit("Only found %d items in: %s", res, magic);
return FALSE;
}
CRM_CHECK(decode_transition_key(key, uuid, transition_id, action_id, target_rc),
result = FALSE;
goto bail;
);
bail:
crm_free(key);
return result;
}
char *
generate_transition_key(int transition_id, int action_id, int target_rc, const char *node)
{
int len = 40;
char *fail_state = NULL;
CRM_CHECK(node != NULL, return NULL);
len += strlen(node);
crm_malloc0(fail_state, len);
if(fail_state != NULL) {
snprintf(fail_state, len, "%d:%d:%d:%s",
action_id, transition_id, target_rc, node);
}
return fail_state;
}
gboolean
decode_transition_key(
const char *key, char **uuid, int *transition_id, int *action_id, int *target_rc)
{
int res = 0;
gboolean done = FALSE;
CRM_CHECK(uuid != NULL, return FALSE);
CRM_CHECK(target_rc != NULL, return FALSE);
CRM_CHECK(action_id != NULL, return FALSE);
CRM_CHECK(transition_id != NULL, return FALSE);
crm_malloc0(*uuid, strlen(key));
res = sscanf(key, "%d:%d:%d:%s", action_id, transition_id, target_rc, *uuid);
switch(res) {
case 4:
/* Post Pacemaker 0.6 */
done = TRUE;
break;
case 3:
case 2:
/* this can be tricky - the UUID might start with an integer */
/* Until Pacemaker 0.6 */
done = TRUE;
*target_rc = -1;
res = sscanf(key, "%d:%d:%s", action_id, transition_id, *uuid);
if(res == 2) {
*action_id = -1;
res = sscanf(key, "%d:%s", transition_id, *uuid);
CRM_CHECK(res == 2, done = FALSE);
} else if(res != 3) {
CRM_CHECK(res == 3, done = FALSE);
}
break;
case 1:
/* Prior to Heartbeat 2.0.8 */
done = TRUE;
*action_id = -1;
*target_rc = -1;
res = sscanf(key, "%d:%s", transition_id, *uuid);
CRM_CHECK(res == 2, done = FALSE);
break;
default:
crm_crit("Unhandled sscanf result (%d) for %s", res, key);
}
if(strlen(*uuid) != 36) {
crm_warn("Bad UUID (%s) in sscanf result (%d) for %s", *uuid, res, key);
}
if(done == FALSE) {
crm_err("Cannot decode '%s' rc=%d", key, res);
crm_free(*uuid);
*uuid = NULL;
*target_rc = -1;
*action_id = -1;
*transition_id = -1;
}
return done;
}
void
filter_action_parameters(crm_data_t *param_set, const char *version)
{
char *timeout = NULL;
char *interval = NULL;
#if CRM_DEPRECATED_SINCE_2_0_5
const char *filter_205[] = {
XML_ATTR_TE_TARGET_RC,
XML_ATTR_LRM_PROBE,
XML_RSC_ATTR_START,
XML_RSC_ATTR_NOTIFY,
XML_RSC_ATTR_UNIQUE,
XML_RSC_ATTR_MANAGED,
XML_RSC_ATTR_PRIORITY,
XML_RSC_ATTR_MULTIPLE,
XML_RSC_ATTR_STICKINESS,
XML_RSC_ATTR_FAIL_STICKINESS,
XML_RSC_ATTR_TARGET_ROLE,
/* ignore clone fields */
XML_RSC_ATTR_INCARNATION,
XML_RSC_ATTR_INCARNATION_MAX,
XML_RSC_ATTR_INCARNATION_NODEMAX,
XML_RSC_ATTR_MASTER_MAX,
XML_RSC_ATTR_MASTER_NODEMAX,
/* old field names */
"role",
"crm_role",
"te-target-rc",
/* ignore notify fields */
"notify_stop_resource",
"notify_stop_uname",
"notify_start_resource",
"notify_start_uname",
"notify_active_resource",
"notify_active_uname",
"notify_inactive_resource",
"notify_inactive_uname",
"notify_promote_resource",
"notify_promote_uname",
"notify_demote_resource",
"notify_demote_uname",
"notify_master_resource",
"notify_master_uname",
"notify_slave_resource",
"notify_slave_uname"
};
#endif
const char *attr_filter[] = {
XML_ATTR_ID,
XML_ATTR_CRM_VERSION,
XML_LRM_ATTR_OP_DIGEST,
};
gboolean do_delete = FALSE;
int lpc = 0;
static int meta_len = 0;
if(meta_len == 0) {
meta_len = strlen(CRM_META);
}
if(param_set == NULL) {
return;
}
#if CRM_DEPRECATED_SINCE_2_0_5
if(version == NULL || compare_version("1.0.5", version) > 0) {
for(lpc = 0; lpc < DIMOF(filter_205); lpc++) {
xml_remove_prop(param_set, filter_205[lpc]);
}
}
#endif
for(lpc = 0; lpc < DIMOF(attr_filter); lpc++) {
xml_remove_prop(param_set, attr_filter[lpc]);
}
timeout = crm_element_value_copy(param_set, CRM_META"_timeout");
interval = crm_element_value_copy(param_set, CRM_META"_interval");
xml_prop_iter(param_set, prop_name, prop_value,
do_delete = FALSE;
if(strncasecmp(prop_name, CRM_META, meta_len) == 0) {
do_delete = TRUE;
}
if(do_delete) {
/* remove it */
xml_remove_prop(param_set, prop_name);
/* unwind the counetr */
__counter--;
}
);
if(crm_get_msec(interval) && compare_version(version, "1.0.8") > 0) {
/* Re-instate the operation's timeout value */
if(timeout != NULL) {
crm_xml_add(param_set, CRM_META"_timeout", timeout);
}
}
crm_free(interval);
crm_free(timeout);
}
void
filter_reload_parameters(crm_data_t *param_set, const char *restart_string)
{
int len = 0;
char *name = NULL;
char *match = NULL;
if(param_set == NULL) {
return;
}
xml_prop_iter(param_set, prop_name, prop_value,
name = NULL;
len = strlen(prop_name) + 3;
crm_malloc0(name, len);
sprintf(name, " %s ", prop_name);
name[len-1] = 0;
match = strstr(restart_string, name);
if(match == NULL) {
/* remove it */
crm_debug_3("%s not found in %s",
prop_name, restart_string);
xml_remove_prop(param_set, prop_name);
/* unwind the counetr */
__counter--;
}
crm_free(name);
);
}
void
crm_abort(const char *file, const char *function, int line,
const char *assert_condition, gboolean do_core, gboolean do_fork)
{
int rc = 0;
int pid = 0;
int status = 0;
if(do_core == FALSE) {
do_crm_log(LOG_ERR, "%s: Triggered assert at %s:%d : %s",
function, file, line, assert_condition);
return;
} else if(do_fork) {
do_crm_log(LOG_ERR, "%s: Triggered non-fatal assert at %s:%d : %s",
function, file, line, assert_condition);
pid=fork();
} else {
do_crm_log(LOG_ERR, "%s: Triggered fatal assert at %s:%d : %s",
function, file, line, assert_condition);
}
switch(pid) {
case -1:
crm_err("Cannot fork!");
return;
default: /* Parent */
do_crm_log(LOG_ERR,
"%s: Forked child %d to record non-fatal assert at %s:%d : %s",
function, pid, file, line, assert_condition);
do {
rc = waitpid(pid, &status, 0);
if(rc < 0 && errno != EINTR) {
cl_perror("%s: Cannot wait on forked child %d", function, pid);
}
} while(rc < 0 && errno == EINTR);
return;
case 0: /* Child */
abort();
break;
}
}
char *
generate_series_filename(
const char *directory, const char *series, int sequence, gboolean bzip)
{
int len = 40;
char *filename = NULL;
const char *ext = "raw";
CRM_CHECK(directory != NULL, return NULL);
CRM_CHECK(series != NULL, return NULL);
len += strlen(directory);
len += strlen(series);
crm_malloc0(filename, len);
CRM_CHECK(filename != NULL, return NULL);
if(bzip) {
ext = "bz2";
}
sprintf(filename, "%s/%s-%d.%s", directory, series, sequence, ext);
return filename;
}
int
get_last_sequence(const char *directory, const char *series)
{
FILE *file_strm = NULL;
int start = 0, length = 0, read_len = 0;
char *series_file = NULL;
char *buffer = NULL;
int seq = 0;
int len = 36;
CRM_CHECK(directory != NULL, return 0);
CRM_CHECK(series != NULL, return 0);
len += strlen(directory);
len += strlen(series);
crm_malloc0(series_file, len);
CRM_CHECK(series_file != NULL, return 0);
sprintf(series_file, "%s/%s.last", directory, series);
file_strm = fopen(series_file, "r");
if(file_strm == NULL) {
crm_debug("Series file %s does not exist", series_file);
crm_free(series_file);
return 0;
}
/* see how big the file is */
start = ftell(file_strm);
fseek(file_strm, 0L, SEEK_END);
length = ftell(file_strm);
fseek(file_strm, 0L, start);
CRM_ASSERT(start == ftell(file_strm));
crm_debug_3("Reading %d bytes from file", length);
crm_malloc0(buffer, (length+1));
read_len = fread(buffer, 1, length, file_strm);
if(read_len != length) {
crm_err("Calculated and read bytes differ: %d vs. %d",
length, read_len);
crm_free(buffer);
buffer = NULL;
} else if(length <= 0) {
crm_info("%s was not valid", series_file);
crm_free(buffer);
buffer = NULL;
}
crm_free(series_file);
seq = crm_parse_int(buffer, "0");
crm_free(buffer);
fclose(file_strm);
return seq;
}
void
write_last_sequence(
const char *directory, const char *series, int sequence, int max)
{
int rc = 0;
int len = 36;
char *buffer = NULL;
FILE *file_strm = NULL;
char *series_file = NULL;
CRM_CHECK(directory != NULL, return);
CRM_CHECK(series != NULL, return);
if(max == 0) {
return;
}
while(max > 0 && sequence > max) {
sequence -= max;
}
buffer = crm_itoa(sequence);
len += strlen(directory);
len += strlen(series);
crm_malloc0(series_file, len);
sprintf(series_file, "%s/%s.last", directory, series);
file_strm = fopen(series_file, "w");
if(file_strm == NULL) {
crm_err("Cannout open series file %s for writing", series_file);
goto bail;
}
rc = fprintf(file_strm, "%s", buffer);
if(rc < 0) {
cl_perror("Cannot write to series file %s", series_file);
}
bail:
if(file_strm != NULL) {
fflush(file_strm);
fclose(file_strm);
}
crm_free(series_file);
crm_free(buffer);
}
void
crm_make_daemon(const char *name, gboolean daemonize, const char *pidfile)
{
long pid;
const char *devnull = "/dev/null";
if(daemonize == FALSE) {
return;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "%s: could not start daemon\n", name);
cl_perror("fork");
exit(LSB_EXIT_GENERIC);
} else if (pid > 0) {
exit(LSB_EXIT_OK);
}
if (cl_lock_pidfile(pidfile) < 0 ) {
pid = cl_read_pidfile_no_checking(pidfile);
crm_warn("%s: already running [pid %ld] (%s).\n",
name, pid, pidfile);
exit(LSB_EXIT_OK);
}
umask(022);
close(STDIN_FILENO);
(void)open(devnull, O_RDONLY); /* Stdin: fd 0 */
close(STDOUT_FILENO);
(void)open(devnull, O_WRONLY); /* Stdout: fd 1 */
close(STDERR_FILENO);
(void)open(devnull, O_WRONLY); /* Stderr: fd 2 */
}
gboolean
crm_is_writable(const char *dir, const char *file,
const char *user, const char *group, gboolean need_both)
{
int s_res = -1;
struct stat buf;
char *full_file = NULL;
const char *target = NULL;
gboolean pass = TRUE;
gboolean readwritable = FALSE;
CRM_ASSERT(dir != NULL);
if(file != NULL) {
full_file = crm_concat(dir, file, '/');
target = full_file;
s_res = stat(full_file, &buf);
if( s_res == 0 && S_ISREG(buf.st_mode) == FALSE ) {
crm_err("%s must be a regular file", target);
pass = FALSE;
goto out;
}
}
if (s_res != 0) {
target = dir;
s_res = stat(dir, &buf);
if(s_res != 0) {
crm_err("%s must exist and be a directory", dir);
pass = FALSE;
goto out;
} else if( S_ISDIR(buf.st_mode) == FALSE ) {
crm_err("%s must be a directory", dir);
pass = FALSE;
}
}
if(user) {
struct passwd *sys_user = NULL;
sys_user = getpwnam(user);
readwritable = (sys_user != NULL
&& buf.st_uid == sys_user->pw_uid
&& (buf.st_mode & (S_IRUSR|S_IWUSR)));
if(readwritable == FALSE) {
crm_err("%s must be owned and r/w by user %s",
target, user);
if(need_both) {
pass = FALSE;
}
}
}
if(group) {
struct group *sys_grp = getgrnam(group);
readwritable = (
sys_grp != NULL
&& buf.st_gid == sys_grp->gr_gid
&& (buf.st_mode & (S_IRGRP|S_IWGRP)));
if(readwritable == FALSE) {
if(need_both || user == NULL) {
pass = FALSE;
crm_err("%s must be owned and r/w by group %s",
target, group);
} else {
crm_warn("%s should be owned and r/w by group %s",
target, group);
}
}
}
out:
crm_free(full_file);
return pass;
}
static unsigned long long crm_bit_filter = 0; /* 0x00000002ULL; */
static unsigned int bit_log_level = LOG_DEBUG_5;
long long
crm_clear_bit(const char *function, long long word, long long bit)
{
unsigned int level = bit_log_level;
if(bit & crm_bit_filter) {
level = LOG_ERR;
}
do_crm_log(level, "Bit 0x%.16llx cleared by %s", bit, function);
word &= ~bit;
return word;
}
long long
crm_set_bit(const char *function, long long word, long long bit)
{
unsigned int level = bit_log_level;
if(bit & crm_bit_filter) {
level = LOG_ERR;
}
do_crm_log(level, "Bit 0x%.16llx set by %s", bit, function);
word |= bit;
return word;
}
gboolean
is_not_set(long long word, long long bit)
{
crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word);
return ((word & bit) == 0);
}
gboolean
is_set(long long word, long long bit)
{
crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word);
return ((word & bit) == bit);
}
gboolean
is_set_any(long long word, long long bit)
{
crm_debug_5("Checking bit\t%.16llx in %.16llx", bit, word);
return ((word & bit) != 0);
}
gboolean is_openais_cluster(void)
{
static const char *cluster_type = NULL;
if(cluster_type == NULL) {
cluster_type = getenv("HA_cluster_type");
}
if(safe_str_eq("openais", cluster_type)) {
#if SUPPORT_AIS
return TRUE;
#else
CRM_ASSERT(safe_str_eq("openais", cluster_type) == FALSE);
#endif
}
return FALSE;
}
gboolean is_heartbeat_cluster(void)
{
#if SUPPORT_HEARTBEAT
return !is_openais_cluster();
#else
CRM_ASSERT(is_openais_cluster());
return FALSE;
#endif
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Sep 22, 11:14 PM (13 h, 44 m ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2403417
Default Alt Text
(154 KB)

Event Timeline