Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F1841595
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
36 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/include/crm/stonith-ng.h b/include/crm/stonith-ng.h
index 59067e3c22..a0a35df0a9 100644
--- a/include/crm/stonith-ng.h
+++ b/include/crm/stonith-ng.h
@@ -1,146 +1,153 @@
/*
* 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 STONITH_NG__H
#define STONITH_NG__H
#include <crm/common/ipc.h>
#include <crm/common/xml.h>
enum stonith_state {
stonith_connected_command,
stonith_connected_query,
stonith_disconnected
};
enum stonith_call_options {
stonith_none = 0x00000000,
stonith_verbose = 0x00000001,
stonith_discard_reply = 0x00000010,
stonith_scope_local = 0x00000100,
stonith_sync_call = 0x00001000,
};
#define stonith_default_options = stonith_none
enum stonith_errors {
stonith_ok = 0,
stonith_not_supported = -1,
stonith_connection = -2,
stonith_authentication = -3,
stonith_callback_register = -4,
stonith_missing = -5,
stonith_exists = -6,
stonith_timeout = -7,
stonith_ipc = -8,
stonith_peer = -9,
};
#define F_STONITH_CLIENTID "st_clientid"
#define F_STONITH_CALLOPTS "st_callopt"
#define F_STONITH_CALLID "st_callid"
#define F_STONITH_CALLDATA "st_calldata"
#define F_STONITH_OPERATION "st_op"
#define F_STONITH_HOST "st_host"
#define F_STONITH_RC "st_rc"
#define F_STONITH_DELEGATED "st_delegated_from"
#define F_STONITH_TIMEOUT "st_timeout"
#define F_STONITH_CALLBACK_TOKEN "st_async_id"
#define F_STONITH_CLIENTNAME "st_clientname"
#define F_STONITH_NOTIFY_TYPE "st_notify_type"
#define F_STONITH_NOTIFY_ACTIVATE "st_notify_activate"
+#define F_STONITH_NOTIFY_DEACTIVATE "st_notify_deactivate"
#define T_STONITH_NG "stonith-ng"
#define F_STONITH_DEVICE "st_device_id"
#define F_STONITH_PORT "st_device_port"
#define F_STONITH_ACTION "st_device_action"
-#define T_STONITH_NOTIFY "st_notify"
+#define T_STONITH_NOTIFY "st_notify"
+#define T_STONITH_NOTIFY_FENCE "st_notify_fence"
+#define T_STONITH_NOTIFY_UNFENCE "st_notify_unfence"
+#define T_STONITH_NOTIFY_DEVICE_ADD "st_notify_device_add"
+#define T_STONITH_NOTIFY_DEVICE_DEL "st_notify_device_del"
+#define T_STONITH_NOTIFY_DISCONNECT "st_notify_disconnect"
#define STONITH_OP_EXEC "st_execute"
#define STONITH_OP_QUERY "st_query"
#define STONITH_OP_FENCE "st_fence"
#define STONITH_OP_UNFENCE "st_unfence"
#define STONITH_OP_DEVICE_ADD "st_device_register"
#define STONITH_OP_DEVICE_DEL "st_device_remove"
#define stonith_channel "st_command"
#define stonith_channel_callback "st_callback"
typedef struct stonith_s stonith_t;
typedef struct stonith_api_operations_s
{
int (*free) (stonith_t *st);
int (*connect) (stonith_t *st, const char *name, int *async_fd, int *sync_fd);
int (*disconnect)(stonith_t *st);
int (*remove_device)(
stonith_t *st, int options, const char *name);
int (*register_device)(
stonith_t *st, int options, const char *id,
const char *namespace, const char *agent, GHashTable *parameters);
int (*call)(stonith_t *st, int options, const char *id,
const char *action, const char *port, int timeout);
int (*query)(stonith_t *st, int options, const char *node, int timeout);
int (*fence)(stonith_t *st, int options, const char *node, int timeout);
int (*unfence)(stonith_t *st, int options, const char *node, int timeout);
int (*register_notification)(
stonith_t *st, const char *event, void (*callback)(
const char *event, xmlNode *msg));
int (*remove_notification)(
stonith_t *st, const char *event, void (*callback)(
const char *event, xmlNode *msg));
- int (*remove_callback)(int call_id, gboolean all_callbacks);
+ int (*remove_callback)(
+ stonith_t *st, int call_id, gboolean all_callbacks);
int (*register_callback)(
stonith_t *st, int call_id, int timeout, gboolean only_success, void *user_data,
const char *callback_name, void (*callback)(const xmlNode*, int, int, xmlNode*,void*));
} stonith_api_operations_t;
struct stonith_s
{
enum stonith_state state;
int call_id;
int call_timeout;
void *private;
GList *notify_list;
stonith_api_operations_t *cmds;
};
/* Core functions */
extern stonith_t *stonith_api_new(void);
extern void stonith_api_delete(stonith_t *st);
extern const char *stonith_error2string(enum stonith_errors return_code);
extern void stonith_dump_pending_callbacks(void);
extern int num_stonith_op_callbacks(void);
extern void remove_stonith_op_callback(int call_id, gboolean all_callbacks);
#define add_stonith_op_callback(cib, id, flag, data, fn) cib->cmds->register_callback(cib, id, 120, flag, data, #fn, fn)
#endif
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
index 7663133c3a..ee84f8221e 100644
--- a/lib/fencing/st_client.c
+++ b/lib/fencing/st_client.c
@@ -1,1097 +1,1111 @@
/*
* Copyright (c) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/stonith-ng.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
GHashTable *stonith_op_callback_table = NULL;
typedef struct stonith_private_s
{
char *token;
IPC_Channel *command_channel;
IPC_Channel *callback_channel;
GCHSource *callback_source;
void (*op_callback)(const xmlNode *msg, int call_id,
int rc, xmlNode *output, void *data);
} stonith_private_t;
typedef struct stonith_notify_client_s
{
const char *event;
const char *obj_id; /* implement one day */
const char *obj_type; /* implement one day */
void (*callback)(const char *event, xmlNode *msg);
} stonith_notify_client_t;
typedef struct stonith_callback_client_s
{
void (*callback)(const xmlNode*, int, int, xmlNode*, void*);
const char *id;
void *user_data;
gboolean only_success;
struct timer_rec_s *timer;
} stonith_callback_client_t;
struct timer_rec_s
{
int call_id;
int timeout;
guint ref;
stonith_t *stonith;
};
typedef enum stonith_errors (*stonith_op_t)(
const char *, int, const char *, xmlNode *,
xmlNode*, xmlNode*, xmlNode**, xmlNode**);
gboolean stonith_dispatch(IPC_Channel *channel, gpointer user_data);
void stonith_perform_callback(stonith_t *stonith, xmlNode *msg, int call_id, int rc);
xmlNode *stonith_create_op(
int call_id, const char *token, const char *op, xmlNode *data, int call_options);
int stonith_send_command(
stonith_t *stonith, const char *op, xmlNode *data, xmlNode **output_data, int call_options);
+static void stonith_connection_destroy(gpointer user_data);
+static void stonith_send_notification(gpointer data, gpointer user_data);
+
static void stonith_connection_destroy(gpointer user_data)
{
stonith_t *stonith = user_data;
+ xmlNode *notify = create_xml_node(NULL, "notify");;
+
stonith->state = stonith_disconnected;
+ crm_xml_add(notify, F_TYPE, T_STONITH_NOTIFY);
+ crm_xml_add(notify, F_SUBTYPE, T_STONITH_NOTIFY_DISCONNECT);
- /* TODO: Provide a notification */
- /* g_list_foreach(stonith->notify_list, stonith_send_notification, msg); */
+ g_list_foreach(stonith->notify_list, stonith_send_notification, notify);
+ free_xml(notify);
}
static int stonith_api_register_device(
stonith_t *stonith, int call_options,
const char *id, const char *namespace, const char *agent, GHashTable *params)
{
int rc = 0;
xmlNode *data = create_xml_node(NULL, F_STONITH_DEVICE);
xmlNode *args = create_xml_node(data, XML_TAG_ATTRS);
crm_xml_add(data, XML_ATTR_ID, id);
crm_xml_add(data, "agent", agent);
crm_xml_add(data, "namespace", namespace);
g_hash_table_foreach(params, hash2field, args);
rc = stonith_send_command(stonith, STONITH_OP_DEVICE_ADD, data, NULL, call_options);
free_xml(data);
return rc;
}
static int stonith_api_remove_device(
stonith_t *stonith, int call_options, const char *name)
{
int rc = 0;
xmlNode *data = NULL;
data = create_xml_node(NULL, F_STONITH_DEVICE);
crm_xml_add(data, XML_ATTR_ID, name);
rc = stonith_send_command(stonith, STONITH_OP_DEVICE_DEL, data, NULL, call_options);
free_xml(data);
return rc;
}
static int stonith_api_call(
stonith_t *stonith, int call_options, const char *id, const char *action, const char *port, int timeout)
{
int rc = 0;
xmlNode *data = NULL;
data = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(data, F_STONITH_DEVICE, id);
crm_xml_add(data, F_STONITH_ACTION, action);
crm_xml_add(data, F_STONITH_PORT, port);
crm_xml_add_int(data, "timeout", timeout);
rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, NULL, call_options);
free_xml(data);
return rc;
}
static int stonith_api_fence(
stonith_t *stonith, int call_options, const char *node, int timeout)
{
int rc = 0;
xmlNode *data = NULL;
data = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(data, "target", node);
crm_xml_add_int(data, "timeout", timeout);
rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options);
free_xml(data);
return rc;
}
static int stonith_api_unfence(
stonith_t *stonith, int call_options, const char *node, int timeout)
{
int rc = 0;
xmlNode *data = NULL;
data = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(data, "target", node);
crm_xml_add_int(data, "timeout", timeout);
rc = stonith_send_command(stonith, STONITH_OP_UNFENCE, data, NULL, call_options);
free_xml(data);
return rc;
}
const char *
stonith_error2string(enum stonith_errors return_code)
{
const char *error_msg = NULL;
switch(return_code) {
case stonith_ok:
error_msg = "";
break;
case stonith_not_supported:
error_msg = "";
break;
case stonith_connection:
error_msg = "";
break;
case stonith_authentication:
error_msg = "";
break;
case stonith_callback_register:
error_msg = "";
break;
case stonith_missing:
error_msg = "";
break;
case stonith_exists:
error_msg = "";
break;
case stonith_timeout:
error_msg = "";
break;
case stonith_ipc:
error_msg = "";
break;
case stonith_peer:
error_msg = "";
break;
}
if(error_msg == NULL) {
crm_err("Unknown Stonith error code: %d", return_code);
error_msg = "<unknown error>";
}
return error_msg;
}
static gint stonithlib_GCompareFunc(gconstpointer a, gconstpointer b)
{
int rc = 0;
const stonith_notify_client_t *a_client = a;
const stonith_notify_client_t *b_client = b;
CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
rc = strcmp(a_client->event, b_client->event);
if(rc == 0) {
if(a_client->callback == b_client->callback) {
return 0;
} else if(((long)a_client->callback) < ((long)b_client->callback)) {
crm_err("callbacks for %s are not equal: %p vs. %p",
a_client->event, a_client->callback, b_client->callback);
return -1;
}
crm_err("callbacks for %s are not equal: %p vs. %p",
a_client->event, a_client->callback, b_client->callback);
return 1;
}
return rc;
}
static int get_stonith_token(IPC_Channel *ch, char **token)
{
int rc = stonith_ok;
xmlNode *reg_msg = NULL;
const char *msg_type = NULL;
const char *tmp_ticket = NULL;
CRM_CHECK(ch != NULL, return stonith_missing);
CRM_CHECK(token != NULL, return stonith_missing);
crm_debug_4("Waiting for msg on command channel");
reg_msg = xmlfromIPC(ch, MAX_IPC_DELAY);
if(ch->ops->get_chan_status(ch) != IPC_CONNECT) {
crm_err("No reply message - disconnected");
free_xml(reg_msg);
return stonith_connection;
} else if(reg_msg == NULL) {
crm_err("No reply message - empty");
return stonith_ipc;
}
msg_type = crm_element_value(reg_msg, F_STONITH_OPERATION);
tmp_ticket = crm_element_value(reg_msg, F_STONITH_CLIENTID);
if(safe_str_neq(msg_type, CRM_OP_REGISTER) ) {
crm_err("Invalid registration message: %s", msg_type);
rc = stonith_callback_register;
} else if(tmp_ticket == NULL) {
crm_err("No registration token provided");
crm_log_xml_warn(reg_msg, "Bad reply")
rc = stonith_peer;
} else {
crm_debug("Obtained registration token: %s", tmp_ticket);
*token = crm_strdup(tmp_ticket);
}
free_xml(reg_msg);
return rc;
}
xmlNode *stonith_create_op(
int call_id, const char *token, const char *op, xmlNode *data, int call_options)
{
int rc = HA_OK;
xmlNode *op_msg = create_xml_node(NULL, "stonith_command");
CRM_CHECK(op_msg != NULL, return NULL);
CRM_CHECK(token != NULL, return NULL);
crm_xml_add(op_msg, F_XML_TAGNAME, "stonith_command");
crm_xml_add(op_msg, F_TYPE, T_STONITH);
crm_xml_add(op_msg, F_STONITH_CALLBACK_TOKEN, token);
crm_xml_add(op_msg, F_STONITH_OPERATION, op);
crm_xml_add_int(op_msg, F_STONITH_CALLID, call_id);
crm_debug_4("Sending call options: %.8lx, %d",
(long)call_options, call_options);
crm_xml_add_int(op_msg, F_STONITH_CALLOPTS, call_options);
if(data != NULL) {
add_message_xml(op_msg, F_STONITH_CALLDATA, data);
}
if (rc != HA_OK) {
crm_err("Failed to create STONITH operation message");
crm_log_xml(LOG_ERR, "op", op_msg);
free_xml(op_msg);
return NULL;
}
return op_msg;
}
static void stonith_destroy_op_callback(gpointer data)
{
stonith_callback_client_t *blob = data;
if(blob->timer && blob->timer->ref > 0) {
g_source_remove(blob->timer->ref);
}
crm_free(blob->timer);
crm_free(blob);
}
static int stonith_api_signoff(stonith_t* stonith)
{
stonith_private_t *native = stonith->private;
crm_debug("Signing out of the STONITH Service");
/* close channels */
if (native->command_channel != NULL) {
native->command_channel->ops->destroy(
native->command_channel);
native->command_channel = NULL;
}
if (native->callback_source != NULL) {
G_main_del_IPC_Channel(native->callback_source);
native->callback_source = NULL;
}
if (native->callback_channel != NULL) {
#ifdef BUG
native->callback_channel->ops->destroy(
native->callback_channel);
#endif
native->callback_channel = NULL;
}
stonith->state = stonith_disconnected;
return stonith_ok;
}
static int stonith_api_signon(
stonith_t* stonith, const char *name, int *async_fd, int *sync_fd)
{
int rc = stonith_ok;
xmlNode *hello = NULL;
char *uuid_ticket = NULL;
stonith_private_t *native = stonith->private;
crm_debug_4("Connecting command channel");
stonith->state = stonith_connected_command;
native->command_channel = init_client_ipc_comms_nodispatch(stonith_channel);
if(native->command_channel == NULL) {
crm_debug("Connection to command channel failed");
rc = stonith_connection;
} else if(native->command_channel->ch_status != IPC_CONNECT) {
crm_err("Connection may have succeeded,"
" but authentication to command channel failed");
rc = stonith_authentication;
}
if(rc == stonith_ok) {
rc = get_stonith_token(native->command_channel, &uuid_ticket);
if(rc == stonith_ok) {
native->token = uuid_ticket;
uuid_ticket = NULL;
} else {
stonith->state = stonith_disconnected;
native->command_channel->ops->disconnect(native->command_channel);
return rc;
}
}
native->callback_channel = init_client_ipc_comms_nodispatch(
stonith_channel_callback);
if(native->callback_channel == NULL) {
crm_debug("Connection to callback channel failed");
rc = stonith_connection;
} else if(native->callback_channel->ch_status != IPC_CONNECT) {
crm_err("Connection may have succeeded,"
" but authentication to command channel failed");
rc = stonith_authentication;
}
if(rc == stonith_ok) {
native->callback_channel->send_queue->max_qlen = 500;
rc = get_stonith_token(native->callback_channel, &uuid_ticket);
if(rc == stonith_ok) {
crm_free(native->token);
native->token = uuid_ticket;
}
}
if(rc == stonith_ok) {
CRM_CHECK(native->token != NULL, ;);
hello = stonith_create_op(0, native->token, CRM_OP_REGISTER, NULL, 0);
crm_xml_add(hello, F_STONITH_CLIENTNAME, name);
if(send_ipc_message(native->command_channel, hello) == FALSE) {
rc = stonith_callback_register;
}
free_xml(hello);
}
if(rc == stonith_ok) {
gboolean do_mainloop = TRUE;
if(async_fd != NULL) {
do_mainloop = FALSE;
*async_fd = native->callback_channel->ops->get_recv_select_fd(native->callback_channel);
}
if(sync_fd != NULL) {
do_mainloop = FALSE;
*sync_fd = native->callback_channel->ops->get_send_select_fd(native->callback_channel);
}
if(do_mainloop) {
crm_debug_4("Connecting callback channel");
native->callback_source = G_main_add_IPC_Channel(
G_PRIORITY_HIGH, native->callback_channel, FALSE, stonith_dispatch,
stonith, default_ipc_connection_destroy);
if(native->callback_source == NULL) {
crm_err("Callback source not recorded");
rc = stonith_connection;
} else {
set_IPC_Channel_dnotify(
native->callback_source, stonith_connection_destroy);
}
}
}
if(rc == stonith_ok) {
#if HAVE_MSGFROMIPC_TIMEOUT
stonith->call_timeout = MAX_IPC_DELAY;
#endif
crm_debug("Connection to STONITH successful");
return stonith_ok;
}
crm_debug("Connection to STONITH failed: %s", stonith_error2string(rc));
stonith->cmds->disconnect(stonith);
return rc;
}
static int stonith_set_notification(stonith_t* stonith, const char *callback, int enabled)
{
xmlNode *notify_msg = create_xml_node(NULL, __FUNCTION__);
stonith_private_t *native = stonith->private;
if(stonith->state != stonith_disconnected) {
crm_xml_add(notify_msg, F_STONITH_OPERATION, T_STONITH_NOTIFY);
if(enabled) {
crm_xml_add(notify_msg, F_STONITH_NOTIFY_ACTIVATE, callback);
} else {
crm_xml_add(notify_msg, F_STONITH_NOTIFY_DEACTIVATE, callback);
}
send_ipc_message(native->callback_channel, notify_msg);
}
free_xml(notify_msg);
return stonith_ok;
}
static int stonith_api_add_notification(
stonith_t *stonith, const char *event, void (*callback)(
const char *event, xmlNode *msg))
{
GList *list_item = NULL;
stonith_notify_client_t *new_client = NULL;
crm_debug_2("Adding callback for %s events (%d)",
event, g_list_length(stonith->notify_list));
crm_malloc0(new_client, sizeof(stonith_notify_client_t));
new_client->event = event;
new_client->callback = callback;
list_item = g_list_find_custom(
stonith->notify_list, new_client, stonithlib_GCompareFunc);
if(list_item != NULL) {
crm_warn("Callback already present");
crm_free(new_client);
return stonith_exists;
} else {
stonith->notify_list = g_list_append(
stonith->notify_list, new_client);
stonith_set_notification(stonith, event, 1);
crm_debug_3("Callback added (%d)", g_list_length(stonith->notify_list));
}
return stonith_ok;
}
static int stonith_api_del_notification(
stonith_t *stonith, const char *event, void (*callback)(
const char *event, xmlNode *msg))
{
GList *list_item = NULL;
stonith_notify_client_t *new_client = NULL;
crm_debug("Removing callback for %s events", event);
crm_malloc0(new_client, sizeof(stonith_notify_client_t));
new_client->event = event;
new_client->callback = callback;
list_item = g_list_find_custom(
stonith->notify_list, new_client, stonithlib_GCompareFunc);
stonith_set_notification(stonith, event, 0);
if(list_item != NULL) {
stonith_notify_client_t *list_client = list_item->data;
stonith->notify_list =
g_list_remove(stonith->notify_list, list_client);
crm_free(list_client);
crm_debug_3("Removed callback");
} else {
crm_debug_3("Callback not present");
}
crm_free(new_client);
return stonith_ok;
}
static gboolean stonith_async_timeout_handler(gpointer data)
{
struct timer_rec_s *timer = data;
crm_debug("Async call %d timed out after %ds", timer->call_id, timer->timeout);
stonith_perform_callback(timer->stonith, NULL, timer->call_id, stonith_timeout);
/* Always return TRUE, never remove the handler
* We do that in stonith_del_callback()
*/
return TRUE;
}
static int stonith_api_add_callback(
stonith_t *stonith, int call_id, int timeout, gboolean only_success, void *user_data,
const char *callback_name, void (*callback)(const xmlNode*, int, int, xmlNode*,void*))
{
stonith_callback_client_t *blob = NULL;
CRM_CHECK(stonith != NULL, return stonith_missing);
CRM_CHECK(stonith->private != NULL, return stonith_missing);
if(call_id == 0) {
stonith_private_t *private = stonith->private;
private->op_callback = callback;
} else if(call_id < 0) {
if(only_success == FALSE) {
callback(NULL, call_id, call_id, NULL, user_data);
} else {
crm_warn("STONITH call failed: %s", stonith_error2string(call_id));
}
return FALSE;
}
crm_malloc0(blob, sizeof(stonith_callback_client_t));
blob->id = callback_name;
blob->only_success = only_success;
blob->user_data = user_data;
blob->callback = callback;
if(timeout > 0) {
struct timer_rec_s *async_timer = NULL;
crm_malloc0(async_timer, sizeof(struct timer_rec_s));
blob->timer = async_timer;
async_timer->stonith = stonith;
async_timer->call_id = call_id;
async_timer->timeout = timeout*1000;
async_timer->ref = g_timeout_add(
async_timer->timeout, stonith_async_timeout_handler, async_timer);
}
g_hash_table_insert(stonith_op_callback_table, GINT_TO_POINTER(call_id), blob);
return TRUE;
}
-static int stonith_api_del_callback(int call_id, gboolean all_callbacks)
+static int stonith_api_del_callback(stonith_t *stonith, int call_id, gboolean all_callbacks)
{
+ stonith_private_t *private = stonith->private;
+
if(all_callbacks) {
+ private->op_callback = NULL;
if(stonith_op_callback_table != NULL) {
g_hash_table_destroy(stonith_op_callback_table);
}
stonith_op_callback_table = g_hash_table_new_full(
g_direct_hash, g_direct_equal,
NULL, stonith_destroy_op_callback);
+ } else if(call_id == 0) {
+ private->op_callback = NULL;
+
} else {
g_hash_table_remove(stonith_op_callback_table, GINT_TO_POINTER(call_id));
}
return stonith_ok;
}
static void stonith_dump_pending_op(
gpointer key, gpointer value, gpointer user_data)
{
int call = GPOINTER_TO_INT(key);
stonith_callback_client_t *blob = value;
crm_debug("Call %d (%s): pending", call, crm_str(blob->id));
}
void stonith_dump_pending_callbacks(void)
{
if(stonith_op_callback_table == NULL) {
return;
}
return g_hash_table_foreach(
stonith_op_callback_table, stonith_dump_pending_op, NULL);
}
void stonith_perform_callback(stonith_t *stonith, xmlNode *msg, int call_id, int rc)
{
xmlNode *output = NULL;
stonith_private_t *private = NULL;
stonith_callback_client_t *blob = NULL;
stonith_callback_client_t local_blob;
CRM_CHECK(stonith != NULL, return);
CRM_CHECK(stonith->private != NULL, return);
private = stonith->private;
local_blob.id = NULL;
local_blob.callback = NULL;
local_blob.user_data = NULL;
local_blob.only_success = FALSE;
if(msg != NULL) {
crm_element_value_int(msg, F_STONITH_RC, &rc);
crm_element_value_int(msg, F_STONITH_CALLID, &call_id);
output = get_message_xml(msg, F_STONITH_CALLDATA);
}
blob = g_hash_table_lookup(
stonith_op_callback_table, GINT_TO_POINTER(call_id));
if(blob != NULL) {
local_blob = *blob;
blob = NULL;
- stonith_api_del_callback(call_id, FALSE);
+ stonith_api_del_callback(stonith, call_id, FALSE);
} else {
crm_debug_2("No callback found for call %d", call_id);
local_blob.callback = NULL;
}
if(stonith == NULL) {
crm_debug("No stonith object supplied");
}
if(local_blob.callback != NULL
&& (rc == stonith_ok || local_blob.only_success == FALSE)) {
crm_debug_2("Invoking callback %s for call %d", crm_str(local_blob.id), call_id);
local_blob.callback(msg, call_id, rc, output, local_blob.user_data);
} else if(private->op_callback == NULL && rc != stonith_ok) {
crm_warn("STONITH command failed: %s", stonith_error2string(rc));
crm_log_xml(LOG_DEBUG, "Failed STONITH Update", msg);
}
if(private->op_callback != NULL) {
crm_debug_2("Invoking global callback for call %d", call_id);
private->op_callback(msg, call_id, rc, output, stonith);
}
crm_debug_4("OP callback activated.");
}
static void stonith_send_notification(gpointer data, gpointer user_data)
{
xmlNode *msg = user_data;
stonith_notify_client_t *entry = data;
const char *event = NULL;
if(msg == NULL) {
crm_warn("Skipping callback - NULL message");
return;
}
event = crm_element_value(msg, F_SUBTYPE);
if(entry == NULL) {
crm_warn("Skipping callback - NULL callback client");
return;
} else if(entry->callback == NULL) {
crm_warn("Skipping callback - NULL callback");
return;
} else if(safe_str_neq(entry->event, event)) {
crm_debug_4("Skipping callback - event mismatch %p/%s vs. %s",
entry, entry->event, event);
return;
}
crm_debug_4("Invoking callback for %p/%s event...", entry, event);
entry->callback(event, msg);
crm_debug_4("Callback invoked...");
}
+
static gboolean timer_expired = FALSE;
int stonith_send_command(
stonith_t *stonith, const char *op, xmlNode *data, xmlNode **output_data, int call_options)
{
int rc = HA_OK;
xmlNode *op_msg = NULL;
xmlNode *op_reply = NULL;
stonith_private_t *native = stonith->private;
if(stonith->state == stonith_disconnected) {
return stonith_connection;
}
if(output_data != NULL) {
*output_data = NULL;
}
if(op == NULL) {
crm_err("No operation specified");
return stonith_missing;
}
stonith->call_id++;
/* prevent call_id from being negative (or zero) and conflicting
* with the stonith_errors enum
* use 2 because we use it as (stonith->call_id - 1) below
*/
if(stonith->call_id < 1) {
stonith->call_id = 1;
}
CRM_CHECK(native->token != NULL, ;);
op_msg = stonith_create_op(stonith->call_id, native->token, op, data, call_options);
if(op_msg == NULL) {
return stonith_missing;
}
crm_debug_3("Sending %s message to STONITH service", op);
if(send_ipc_message(native->command_channel, op_msg) == FALSE) {
crm_err("Sending message to STONITH service FAILED");
free_xml(op_msg);
return stonith_ipc;
} else {
crm_debug_3("Message sent");
}
free_xml(op_msg);
if((call_options & stonith_discard_reply)) {
crm_debug_3("Discarding reply");
return stonith_ok;
} else if(!(call_options & stonith_sync_call)) {
crm_debug_3("Async call, returning");
CRM_CHECK(stonith->call_id != 0, return stonith_ipc);
return stonith->call_id;
}
rc = IPC_OK;
crm_debug_3("Waiting for a syncronous reply");
#ifndef HAVE_MSGFROMIPC_TIMEOUT
sync_timer.ref = 0;
if(stonith->call_timeout > 0) {
timer_expired = FALSE;
sync_timer.call_id = stonith->call_id;
sync_timer.timeout = stonith->call_timeout*1000;
sync_timer.ref = g_timeout_add(
sync_timer.timeout, stonith_timeout_handler, &sync_timer);
}
#endif
rc = stonith_ok;
while(timer_expired == FALSE && IPC_ISRCONN(native->command_channel)) {
int reply_id = -1;
int msg_id = stonith->call_id;
op_reply = xmlfromIPC(native->command_channel, stonith->call_timeout);
if(op_reply == NULL) {
rc = stonith_peer;
break;
}
crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id);
if(reply_id <= 0) {
rc = stonith_peer;
break;
} else if(reply_id == msg_id) {
crm_debug_3("Syncronous reply received");
crm_log_xml(LOG_MSG, "Reply", op_reply);
if(crm_element_value_int(op_reply, F_STONITH_RC, &rc) != 0) {
rc = stonith_peer;
}
if(output_data != NULL && is_not_set(call_options, stonith_discard_reply)) {
xmlNode *tmp = get_message_xml(op_reply, F_STONITH_CALLDATA);
if(tmp != NULL) {
*output_data = copy_xml(tmp);
}
}
break;
} else if(reply_id < msg_id) {
crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id);
crm_log_xml(LOG_MSG, "Old reply", op_reply);
} else if((reply_id - 10000) > msg_id) {
/* wrap-around case */
crm_debug("Recieved old reply: %d (wanted %d)", reply_id, msg_id);
crm_log_xml(LOG_MSG, "Old reply", op_reply);
} else {
crm_err("Received a __future__ reply:"
" %d (wanted %d)", reply_id, msg_id);
}
free_xml(op_reply);
op_reply = NULL;
}
if(IPC_ISRCONN(native->command_channel) == FALSE) {
crm_err("STONITH disconnected: %d", native->command_channel->ch_status);
stonith->state = stonith_disconnected;
}
if(op_reply == NULL && stonith->state == stonith_disconnected) {
rc = stonith_connection;
} else if(rc == stonith_ok && op_reply == NULL) {
rc = stonith_peer;
}
#ifndef HAVE_MSGFROMIPC_TIMEOUT
if(sync_timer.ref > 0) {
g_source_remove(sync_timer.ref);
sync_timer.ref = 0;
}
#endif
free_xml(op_reply);
return rc;
}
static gboolean stonith_msgready(stonith_t* stonith)
{
stonith_private_t *private = NULL;
if (stonith == NULL) {
crm_err("No STONITH!");
return FALSE;
}
private = stonith->private;
if(private->command_channel != NULL) {
/* drain the channel */
IPC_Channel *cmd_ch = private->command_channel;
xmlNode *cmd_msg = NULL;
while(cmd_ch->ch_status != IPC_DISCONNECT
&& cmd_ch->ops->is_message_pending(cmd_ch)) {
/* this will happen when the STONITH exited from beneath us */
cmd_msg = xmlfromIPC(cmd_ch, MAX_IPC_DELAY);
free_xml(cmd_msg);
}
} else {
crm_err("No command channel");
}
if(private->callback_channel == NULL) {
crm_err("No callback channel");
return FALSE;
} else if(private->callback_channel->ch_status == IPC_DISCONNECT) {
crm_info("Lost connection to the STONITH service [%d].",
private->callback_channel->farside_pid);
return FALSE;
} else if(private->callback_channel->ops->is_message_pending(
private->callback_channel)) {
crm_debug_4("Message pending on command channel [%d]",
private->callback_channel->farside_pid);
return TRUE;
}
crm_debug_3("No message pending");
return FALSE;
}
static int stonith_rcvmsg(stonith_t* stonith)
{
const char *type = NULL;
xmlNode* msg = NULL;
stonith_private_t *private = NULL;
if (stonith == NULL) {
crm_err("No STONITH!");
return FALSE;
}
private = stonith->private;
/* if it is not blocking mode and no message in the channel, return */
if (stonith_msgready(stonith) == FALSE) {
crm_debug_3("No message ready and non-blocking...");
return 0;
}
/* IPC_INTR is not a factor here */
msg = xmlfromIPC(private->callback_channel, MAX_IPC_DELAY);
if (msg == NULL) {
crm_warn("Received a NULL msg from STONITH service.");
return 0;
}
/* do callbacks */
type = crm_element_value(msg, F_TYPE);
crm_debug_4("Activating %s callbacks...", type);
if(safe_str_eq(type, T_STONITH_NG)) {
stonith_perform_callback(stonith, msg, 0, 0);
} else if(safe_str_eq(type, T_STONITH_NOTIFY)) {
g_list_foreach(stonith->notify_list, stonith_send_notification, msg);
} else {
crm_err("Unknown message type: %s", type);
}
free_xml(msg);
return 1;
}
gboolean stonith_dispatch(IPC_Channel *channel, gpointer user_data)
{
stonith_t *stonith = user_data;
stonith_private_t *private = NULL;
gboolean stay_connected = TRUE;
CRM_CHECK(stonith != NULL, return FALSE);
private = stonith->private;
CRM_CHECK(private->callback_channel == channel, return FALSE);
while(stonith_msgready(stonith)) {
/* invoke the callbacks but dont block */
int rc = stonith_rcvmsg(stonith);
if( rc < 0) {
crm_err("Message acquisition failed: %d", rc);
break;
} else if(rc == 0) {
break;
}
}
if(private->callback_channel
&& private->callback_channel->ch_status != IPC_CONNECT) {
crm_crit("Lost connection to the STONITH service [%d/callback].",
channel->farside_pid);
private->callback_source = NULL;
stay_connected = FALSE;
}
if(private->command_channel
&& private->command_channel->ch_status != IPC_CONNECT) {
crm_crit("Lost connection to the STONITH service [%d/command].",
channel->farside_pid);
private->callback_source = NULL;
stay_connected = FALSE;
}
return stay_connected;
}
static int stonith_api_free (stonith_t* stonith)
{
int rc = stonith_ok;
if(stonith->state != stonith_disconnected) {
rc = stonith->cmds->disconnect(stonith);
}
if(stonith->state == stonith_disconnected) {
stonith_private_t *private = stonith->private;
crm_free(private->token);
crm_free(stonith->private);
crm_free(stonith->cmds);
crm_free(stonith);
}
return rc;
}
void stonith_api_delete(stonith_t *stonith)
{
GList *list = stonith->notify_list;
while(list != NULL) {
stonith_notify_client_t *client = g_list_nth_data(list, 0);
list = g_list_remove(list, client);
crm_free(client);
}
g_hash_table_destroy(stonith_op_callback_table);
stonith->cmds->free(stonith);
stonith = NULL;
}
stonith_t *stonith_api_new(void)
{
stonith_t* new_stonith = NULL;
stonith_private_t* private = NULL;
crm_malloc0(new_stonith, sizeof(stonith_t));
crm_malloc0(private, sizeof(stonith_private_t));
new_stonith->private = private;
if(stonith_op_callback_table != NULL) {
g_hash_table_destroy(stonith_op_callback_table);
stonith_op_callback_table = NULL;
}
if(stonith_op_callback_table == NULL) {
stonith_op_callback_table = g_hash_table_new_full(
g_direct_hash, g_direct_equal,
NULL, stonith_destroy_op_callback);
}
new_stonith->call_id = 1;
new_stonith->notify_list = NULL;
new_stonith->state = stonith_disconnected;
crm_malloc0(new_stonith->cmds, sizeof(stonith_api_operations_t));
new_stonith->cmds->free = stonith_api_free;
new_stonith->cmds->connect = stonith_api_signon;
new_stonith->cmds->disconnect = stonith_api_signoff;
new_stonith->cmds->call = stonith_api_call;
new_stonith->cmds->fence = stonith_api_fence;
new_stonith->cmds->unfence = stonith_api_unfence;
new_stonith->cmds->remove_device = stonith_api_remove_device;
new_stonith->cmds->register_device = stonith_api_register_device;
new_stonith->cmds->remove_callback = stonith_api_del_callback;
new_stonith->cmds->register_callback = stonith_api_add_callback;
new_stonith->cmds->remove_notification = stonith_api_del_notification;
new_stonith->cmds->register_notification = stonith_api_add_notification;
return new_stonith;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 6:30 AM (16 h, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018294
Default Alt Text
(36 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment