Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F1842217
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
58 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
index 205bda8f24..a4977d5c68 100644
--- a/lib/fencing/st_client.c
+++ b/lib/fencing/st_client.c
@@ -1,1944 +1,1944 @@
/*
* 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include <crm_internal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glib.h>
#include <dirent.h>
#include <libgen.h> /* Add it for compiling on OSX */
#include <crm/crm.h>
#include <crm/stonith-ng.h>
#include <crm/stonith-ng-internal.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <stonith/stonith.h>
CRM_TRACE_INIT_DATA(stonith);
typedef struct stonith_private_s {
char *token;
IPC_Channel *command_channel;
IPC_Channel *callback_channel;
GCHSource *callback_source;
GHashTable *stonith_op_callback_table;
GList *notify_list;
void (*op_callback) (stonith_t * st, const xmlNode * msg, int call, int rc, xmlNode * output,
void *userdata);
} 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 (*notify) (stonith_t * st, const char *event, xmlNode * msg);
} stonith_notify_client_t;
typedef struct stonith_callback_client_s {
void (*callback) (stonith_t * st, const xmlNode * msg, int call, int rc, xmlNode * output,
void *userdata);
const char *id;
void *user_data;
gboolean only_success;
struct timer_rec_s *timer;
} stonith_callback_client_t;
struct notify_blob_s {
stonith_t *stonith;
xmlNode *xml;
};
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 **);
static const char META_TEMPLATE[] =
"<?xml version=\"1.0\"?>\n"
"<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
"<resource-agent name=\"%s\">\n"
" <version>1.0</version>\n"
" <longdesc lang=\"en\">\n"
"%s\n"
" </longdesc>\n"
" <shortdesc lang=\"en\">%s</shortdesc>\n"
"%s\n"
" <actions>\n"
- " <action name=\"start\" timeout=\"60\" />\n"
+ " <action name=\"start\" timeout=\"20\" />\n"
" <action name=\"stop\" timeout=\"15\" />\n"
- " <action name=\"status\" timeout=\"60\" />\n"
- " <action name=\"monitor\" timeout=\"60\" interval=\"3600\" start-delay=\"15\" />\n"
+ " <action name=\"status\" timeout=\"20\" />\n"
+ " <action name=\"monitor\" timeout=\"20\" interval=\"3600\" start-delay=\"15\" />\n"
" <action name=\"meta-data\" timeout=\"15\" />\n"
" </actions>\n"
" <special tag=\"heartbeat\">\n"
" <version>2.0</version>\n" " </special>\n" "</resource-agent>\n";
bool stonith_dispatch(stonith_t * st);
gboolean stonith_dispatch_internal(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, int timeout);
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;
stonith_private_t *native = NULL;
struct notify_blob_s blob;
blob.stonith = stonith;
blob.xml = create_xml_node(NULL, "notify");
native = stonith->private;
native->callback_source = NULL;
stonith->state = stonith_disconnected;
crm_xml_add(blob.xml, F_TYPE, T_STONITH_NOTIFY);
crm_xml_add(blob.xml, F_SUBTYPE, T_STONITH_NOTIFY_DISCONNECT);
g_list_foreach(native->notify_list, stonith_send_notification, &blob);
free_xml(blob.xml);
}
static int
stonith_api_register_device(stonith_t * stonith, int call_options,
const char *id, const char *namespace, const char *agent,
stonith_key_value_t * 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, "origin", __FUNCTION__);
crm_xml_add(data, "agent", agent);
crm_xml_add(data, "namespace", namespace);
for (; params; params = params->next) {
hash2field((gpointer) params->key, (gpointer) params->value, args);
}
rc = stonith_send_command(stonith, STONITH_OP_DEVICE_ADD, data, NULL, call_options, 0);
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, "origin", __FUNCTION__);
crm_xml_add(data, XML_ATTR_ID, name);
rc = stonith_send_command(stonith, STONITH_OP_DEVICE_DEL, data, NULL, call_options, 0);
free_xml(data);
return rc;
}
static void
append_arg(gpointer key, gpointer value, gpointer user_data)
{
int len = 3; /* =, \n, \0 */
int last = 0;
char **args = user_data;
CRM_CHECK(key != NULL, return);
CRM_CHECK(value != NULL, return);
if (strstr(key, "pcmk_")) {
return;
} else if (strstr(key, CRM_META)) {
return;
} else if (safe_str_eq(key, "crm_feature_set")) {
return;
}
len += strlen(key);
len += strlen(value);
if (*args != NULL) {
last = strlen(*args);
}
crm_realloc(*args, last + len);
crm_debug_2("Appending: %s=%s", (char *)key, (char *)value);
sprintf((*args) + last, "%s=%s\n", (char *)key, (char *)value);
}
static void
append_const_arg(const char *key, const char *value, char **arg_list)
{
char *glib_sucks_key = crm_strdup(key);
char *glib_sucks_value = crm_strdup(value);
append_arg(glib_sucks_key, glib_sucks_value, arg_list);
crm_free(glib_sucks_value);
crm_free(glib_sucks_key);
}
static void
append_host_specific_args(const char *victim, const char *map, GHashTable * params, char **arg_list)
{
char *name = NULL;
int last = 0, lpc = 0, max = 0;
if (map == NULL) {
/* The best default there is for now... */
crm_debug("Using default arg map: port=uname");
append_const_arg("port", victim, arg_list);
return;
}
max = strlen(map);
crm_debug("Processing arg map: %s", map);
for (; lpc < max + 1; lpc++) {
if (isalpha(map[lpc])) {
/* keep going */
} else if (map[lpc] == '=' || map[lpc] == ':') {
crm_free(name);
crm_malloc0(name, 1 + lpc - last);
strncpy(name, map + last, lpc - last);
crm_debug("Got name: %s", name);
last = lpc + 1;
} else if (map[lpc] == 0 || map[lpc] == ',' || isspace(map[lpc])) {
char *param = NULL;
const char *value = NULL;
crm_malloc0(param, 1 + lpc - last);
strncpy(param, map + last, lpc - last);
last = lpc + 1;
crm_debug("Got key: %s", param);
if (name == NULL) {
crm_err("Misparsed '%s', found '%s' without a name", map, param);
crm_free(param);
continue;
}
if (safe_str_eq(param, "uname")) {
value = victim;
} else {
char *key = crm_meta_name(param);
value = g_hash_table_lookup(params, key);
crm_free(key);
}
if (value) {
crm_debug("Setting '%s'='%s' (%s) for %s", name, value, param, victim);
append_const_arg(name, value, arg_list);
} else {
crm_err("No node attribute '%s' for '%s'", name, victim);
}
crm_free(name);
name = NULL;
crm_free(param);
if (map[lpc] == 0) {
break;
}
} else if (isspace(map[lpc])) {
last = lpc;
}
}
crm_free(name);
}
static char *
make_args(const char *action, const char *victim, GHashTable * device_args, GHashTable * port_map)
{
char buffer[512];
char *arg_list = NULL;
const char *value = NULL;
CRM_CHECK(action != NULL, return NULL);
if (device_args) {
g_hash_table_foreach(device_args, append_arg, &arg_list);
}
buffer[511] = 0;
snprintf(buffer, 511, "pcmk_%s_action", action);
if (device_args) {
value = g_hash_table_lookup(device_args, buffer);
}
if (value == NULL && device_args) {
/* Legacy support for early 1.1 releases - Remove for 1.2 */
snprintf(buffer, 511, "pcmk_%s_cmd", action);
value = g_hash_table_lookup(device_args, buffer);
}
if (value) {
crm_info("Substituting action '%s' for requested operation '%s'", value, action);
action = value;
}
append_const_arg(STONITH_ATTR_ACTION_OP, action, &arg_list);
if (victim && device_args) {
const char *alias = victim;
const char *param = g_hash_table_lookup(device_args, STONITH_ATTR_HOSTARG);
if (port_map && g_hash_table_lookup(port_map, victim)) {
alias = g_hash_table_lookup(port_map, victim);
}
/* Always supply the node's name too:
* https://fedorahosted.org/cluster/wiki/FenceAgentAPI
*/
append_const_arg("nodename", victim, &arg_list);
/* Check if we need to supply the victim in any other form */
if (param == NULL) {
const char *map = g_hash_table_lookup(device_args, STONITH_ATTR_ARGMAP);
if (map == NULL) {
param = "port";
value = g_hash_table_lookup(device_args, param);
} else {
/* Legacy handling */
append_host_specific_args(alias, map, device_args, &arg_list);
value = map; /* Nothing more to do */
}
} else if (safe_str_eq(param, "none")) {
value = param; /* Nothing more to do */
} else {
value = g_hash_table_lookup(device_args, param);
}
/* Don't overwrite explictly set values for $param */
if (value == NULL || safe_str_eq(value, "dynamic")) {
crm_info("%s-ing node '%s' as '%s=%s'", action, victim, param, alias);
append_const_arg(param, alias, &arg_list);
}
}
crm_debug_3("Calculated: %s", arg_list);
return arg_list;
}
/* Borrowed from libfence and extended */
int
run_stonith_agent(const char *agent, const char *action, const char *victim,
GHashTable * device_args, GHashTable * port_map, int *agent_result, char **output,
async_command_t * track)
{
char *args = make_args(action, victim, device_args, port_map);
int pid, status, len, rc = st_err_internal;
int p_read_fd, p_write_fd; /* parent read/write file descriptors */
int c_read_fd, c_write_fd; /* child read/write file descriptors */
int fd1[2];
int fd2[2];
c_read_fd = c_write_fd = p_read_fd = p_write_fd = -1;
if (args == NULL || agent == NULL)
goto fail;
len = strlen(args);
if (pipe(fd1))
goto fail;
p_read_fd = fd1[0];
c_write_fd = fd1[1];
if (pipe(fd2))
goto fail;
c_read_fd = fd2[0];
p_write_fd = fd2[1];
crm_debug("forking");
pid = fork();
if (pid < 0) {
rc = st_err_agent_fork;
goto fail;
}
if (pid) {
/* parent */
int ret;
int total = 0;
fcntl(p_read_fd, F_SETFL, fcntl(p_read_fd, F_GETFL, 0) | O_NONBLOCK);
do {
crm_debug("sending args");
ret = write(p_write_fd, args + total, len - total);
if (ret > 0) {
total += ret;
}
} while (errno == EINTR && total < len);
if (total != len) {
crm_perror(LOG_ERR, "Sent %d not %d bytes", total, len);
if (ret >= 0) {
rc = st_err_agent_args;
}
goto fail;
}
close(p_write_fd);
if (track) {
track->stdout = p_read_fd;
NewTrackedProc(pid, 0, PT_LOGNORMAL, track, track->pt_ops);
crm_trace("Op: %s on %s, timeout: %d", action, agent, track->timeout);
if (track->timeout) {
track->killseq[0].mstimeout = track->timeout; /* after timeout send TERM */
track->killseq[0].signalno = SIGTERM;
track->killseq[1].mstimeout = 5000; /* after another 5s remove it */
track->killseq[1].signalno = SIGKILL;
track->killseq[2].mstimeout = 5000; /* if it's still there after another 5s, complain */
track->killseq[2].signalno = 0;
SetTrackedProcTimeouts(pid, track->killseq);
} else {
crm_err("No timeout set for stonith operation %s with device %s", action, agent);
}
close(c_write_fd);
close(c_read_fd);
crm_free(args);
return pid;
} else {
waitpid(pid, &status, 0);
if (output != NULL) {
len = 0;
do {
char buf[500];
ret = read(p_read_fd, buf, 500);
if (ret > 0) {
buf[ret] = 0;
crm_realloc(*output, len + ret + 1);
sprintf((*output) + len, "%s", buf);
crm_debug("%d: %s", ret, (*output) + len);
len += ret;
}
} while (ret == 500 || (ret < 0 && errno == EINTR));
}
rc = st_err_agent;
*agent_result = st_err_agent;
if (WIFEXITED(status)) {
crm_debug("result = %d", WEXITSTATUS(status));
*agent_result = -WEXITSTATUS(status);
rc = 0;
}
}
} else {
/* child */
close(1);
if (dup(c_write_fd) < 0)
goto fail;
close(2);
if (dup(c_write_fd) < 0)
goto fail;
close(0);
if (dup(c_read_fd) < 0)
goto fail;
/* keep c_write_fd open so parent can report all errors. */
close(c_read_fd);
close(p_read_fd);
close(p_write_fd);
execlp(agent, agent, NULL);
exit(EXIT_FAILURE);
}
fail:
crm_free(args);
if (p_read_fd >= 0) {
close(p_read_fd);
}
if (p_write_fd >= 0) {
close(p_write_fd);
}
if (c_read_fd >= 0) {
close(c_read_fd);
}
if (c_write_fd >= 0) {
close(c_write_fd);
}
return rc;
}
static int
stonith_api_device_list(stonith_t * stonith, int call_options, const char *namespace,
stonith_key_value_t ** devices, int timeout)
{
int count = 0;
if (devices == NULL) {
crm_err("Parameter error: stonith_api_device_list");
return -2;
}
/* Include Heartbeat agents */
if (namespace == NULL || safe_str_eq("heartbeat", namespace)) {
char **entry = NULL;
char **type_list = stonith_types();
for (entry = type_list; entry != NULL && *entry; ++entry) {
crm_trace("Added: %s", *entry);
*devices = stonith_key_value_add(*devices, NULL, *entry);
count++;
}
if (type_list) {
stonith_free_hostlist(type_list);
}
}
/* Include Red Hat agents, basically: ls -1 @sbin_dir@/fence_* */
if (namespace == NULL || safe_str_eq("redhat", namespace)) {
struct dirent **namelist;
int file_num = scandir(RH_STONITH_DIR, &namelist, 0, alphasort);
if (file_num > 0) {
struct stat prop;
char buffer[FILENAME_MAX + 1];
while (file_num--) {
if ('.' == namelist[file_num]->d_name[0]) {
free(namelist[file_num]);
continue;
} else if (0 != strncmp(RH_STONITH_PREFIX,
namelist[file_num]->d_name, strlen(RH_STONITH_PREFIX))) {
free(namelist[file_num]);
continue;
}
snprintf(buffer, FILENAME_MAX, "%s/%s", RH_STONITH_DIR, namelist[file_num]->d_name);
if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
*devices = stonith_key_value_add(*devices, NULL, namelist[file_num]->d_name);
count++;
}
free(namelist[file_num]);
}
free(namelist);
}
}
return count;
}
static int
stonith_api_device_metadata(stonith_t * stonith, int call_options, const char *agent,
const char *namespace, char **output, int timeout)
{
int rc = 0;
int bufferlen = 0;
char *buffer = NULL;
char *xml_meta_longdesc = NULL;
char *xml_meta_shortdesc = NULL;
char *meta_param = NULL;
char *meta_longdesc = NULL;
char *meta_shortdesc = NULL;
const char *provider = get_stonith_provider(agent, namespace);
Stonith *stonith_obj = NULL;
static const char *no_parameter_info = "<!-- no value -->";
crm_info("looking up %s/%s metadata", agent, provider);
/* By having this in a library, we can access it from stonith_admin
* when neither lrmd or stonith-ng are running
* Important for the crm shell's validations...
*/
if (safe_str_eq(provider, "redhat")) {
int exec_rc = run_stonith_agent(agent, "metadata", NULL, NULL, NULL, &rc, &buffer, NULL);
if (exec_rc < 0 || rc != 0 || buffer == NULL) {
/* failed */
crm_debug("Query failed: %d %d: %s", exec_rc, rc, crm_str(buffer));
/* provide a fake metadata entry */
meta_longdesc = crm_strdup(no_parameter_info);
meta_shortdesc = crm_strdup(no_parameter_info);
meta_param = crm_strdup(" <parameters>\n"
" <parameter name=\"action\">\n"
" <getopt mixed=\"-o\" />\n"
" <content type=\"string\" default=\"reboot\" />\n"
" <shortdesc lang=\"en\">Fencing action (null, off, on, [reboot], status, hostlist, devstatus)</shortdesc>\n"
" </parameter>\n" " </parameters>");
goto build;
}
} else {
stonith_obj = stonith_new(agent);
meta_longdesc = crm_strdup(stonith_get_info(stonith_obj, ST_DEVICEDESCR));
if (meta_longdesc == NULL) {
crm_warn("no long description in %s's metadata.", agent);
meta_longdesc = crm_strdup(no_parameter_info);
}
meta_shortdesc = crm_strdup(stonith_get_info(stonith_obj, ST_DEVICEID));
if (meta_shortdesc == NULL) {
crm_warn("no short description in %s's metadata.", agent);
meta_shortdesc = crm_strdup(no_parameter_info);
}
meta_param = crm_strdup(stonith_get_info(stonith_obj, ST_CONF_XML));
if (meta_param == NULL) {
crm_warn("no list of parameters in %s's metadata.", agent);
meta_param = crm_strdup(no_parameter_info);
}
build:
xml_meta_longdesc =
(char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_longdesc);
xml_meta_shortdesc =
(char *)xmlEncodeEntitiesReentrant(NULL, (const unsigned char *)meta_shortdesc);
bufferlen = strlen(META_TEMPLATE) + strlen(agent)
+ strlen(xml_meta_longdesc) + strlen(xml_meta_shortdesc)
+ strlen(meta_param) + 1;
crm_malloc0(buffer, bufferlen);
snprintf(buffer, bufferlen - 1, META_TEMPLATE,
agent, xml_meta_longdesc, xml_meta_shortdesc, meta_param);
xmlFree(xml_meta_longdesc);
xmlFree(xml_meta_shortdesc);
if (stonith_obj) {
stonith_delete(stonith_obj);
}
crm_free(meta_shortdesc);
crm_free(meta_longdesc);
crm_free(meta_param);
}
if (output) {
*output = buffer;
} else {
crm_free(buffer);
}
return rc;
}
static int
stonith_api_query(stonith_t * stonith, int call_options, const char *target,
stonith_key_value_t ** devices, int timeout)
{
int rc = 0, lpc = 0, max = 0;
xmlNode *data = NULL;
xmlNode *output = NULL;
xmlXPathObjectPtr xpathObj = NULL;
CRM_CHECK(devices != NULL, return st_err_missing);
data = create_xml_node(NULL, F_STONITH_DEVICE);
crm_xml_add(data, "origin", __FUNCTION__);
crm_xml_add(data, F_STONITH_TARGET, target);
rc = stonith_send_command(stonith, STONITH_OP_QUERY, data, &output, call_options, timeout);
if (rc < 0) {
return rc;
}
xpathObj = xpath_search(output, "//@agent");
if (xpathObj) {
max = xpathObj->nodesetval->nodeNr;
for (lpc = 0; lpc < max; lpc++) {
xmlNode *match = getXpathResult(xpathObj, lpc);
CRM_CHECK(match != NULL, continue);
crm_info("%s[%d] = %s", "//@agent", lpc, xmlGetNodePath(match));
*devices = stonith_key_value_add(*devices, NULL, crm_element_value(match, XML_ATTR_ID));
}
}
free_xml(output);
free_xml(data);
return max;
}
static int
stonith_api_call(stonith_t * stonith, int call_options, const char *id, const char *action,
const char *victim, int timeout)
{
int rc = 0;
xmlNode *data = NULL;
data = create_xml_node(NULL, F_STONITH_DEVICE);
crm_xml_add(data, "origin", __FUNCTION__);
crm_xml_add(data, F_STONITH_DEVICE, id);
crm_xml_add(data, F_STONITH_ACTION, action);
crm_xml_add(data, F_STONITH_TARGET, victim);
rc = stonith_send_command(stonith, STONITH_OP_EXEC, data, NULL, call_options, timeout);
free_xml(data);
return rc;
}
static int
stonith_api_fence(stonith_t * stonith, int call_options, const char *node, const char *action,
int timeout)
{
int rc = 0;
xmlNode *data = NULL;
data = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(data, F_STONITH_TARGET, node);
crm_xml_add(data, F_STONITH_ACTION, action);
crm_xml_add_int(data, F_STONITH_TIMEOUT, timeout);
rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout);
free_xml(data);
return rc;
}
static int
stonith_api_confirm(stonith_t * stonith, int call_options, const char *target)
{
return stonith_api_fence(stonith, call_options|st_opt_manual_ack, target, "off", 0);
}
static int
stonith_api_history(stonith_t * stonith, int call_options, const char *node,
stonith_history_t ** history, int timeout)
{
int rc = 0;
xmlNode *data = NULL;
xmlNode *output = NULL;
stonith_history_t *last = NULL;
*history = NULL;
if (node) {
data = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(data, F_STONITH_TARGET, node);
}
rc = stonith_send_command(stonith, STONITH_OP_FENCE_HISTORY, data, &output,
call_options | st_opt_sync_call, timeout);
free_xml(data);
if (rc == 0) {
xmlNode *op = NULL;
xmlNode *reply = get_xpath_object("//" F_STONITH_HISTORY_LIST, output, LOG_ERR);
for (op = __xml_first_child(reply); op != NULL; op = __xml_next(op)) {
stonith_history_t *kvp;
crm_malloc0(kvp, sizeof(stonith_history_t));
kvp->target = crm_element_value_copy(op, F_STONITH_TARGET);
kvp->action = crm_element_value_copy(op, F_STONITH_ACTION);
kvp->origin = crm_element_value_copy(op, F_STONITH_ORIGIN);
kvp->delegate = crm_element_value_copy(op, F_STONITH_DELEGATE);
crm_element_value_int(op, F_STONITH_DATE, &kvp->completed);
crm_element_value_int(op, F_STONITH_STATE, &kvp->state);
if (last) {
last->next = kvp;
} else {
*history = kvp;
}
last = kvp;
}
}
return rc;
}
const char *
stonith_error2string(enum stonith_errors return_code)
{
const char *error_msg = NULL;
switch (return_code) {
case stonith_ok:
error_msg = "OK";
break;
case st_err_not_supported:
error_msg = "Not supported";
break;
case st_err_authentication:
error_msg = "Not authenticated";
break;
case st_err_generic:
error_msg = "Generic error";
break;
case st_err_internal:
error_msg = "Internal error";
break;
case st_err_unknown_device:
error_msg = "Unknown device";
break;
case st_err_unknown_operation:
error_msg = "Unknown operation";
break;
case st_err_unknown_port:
error_msg = "Unknown victim";
break;
case st_err_none_available:
error_msg = "No available fencing devices";
break;
case st_err_connection:
error_msg = "Not connected";
break;
case st_err_missing:
error_msg = "Missing input";
break;
case st_err_exists:
error_msg = "Device exists";
break;
case st_err_timeout:
error_msg = "Operation timed out";
break;
case st_err_signal:
error_msg = "Killed by signal";
break;
case st_err_ipc:
error_msg = "IPC connection failed";
break;
case st_err_peer:
error_msg = "Error from peer";
break;
case stonith_pending:
error_msg = "Stonith operation is in progress";
break;
case st_err_agent_fork:
error_msg = "Call to fork() failed";
break;
case st_err_agent_args:
error_msg = "Could not send arguments to the stonith device";
break;
case st_err_agent:
error_msg = "Execution of the stonith agent failed";
break;
}
if (error_msg == NULL) {
crm_err("Unknown Stonith error code: %d", return_code);
error_msg = "<unknown error>";
}
return error_msg;
}
gboolean
is_redhat_agent(const char *agent)
{
int rc = 0;
struct stat prop;
char buffer[FILENAME_MAX + 1];
snprintf(buffer, FILENAME_MAX, "%s/%s", RH_STONITH_DIR, agent);
rc = stat(buffer, &prop);
if (rc >= 0 && S_ISREG(prop.st_mode)) {
return TRUE;
}
return FALSE;
}
const char *
get_stonith_provider(const char *agent, const char *provider)
{
/* This function sucks */
if (is_redhat_agent(agent)) {
return "redhat";
} else {
Stonith *stonith_obj = stonith_new(agent);
if (stonith_obj) {
stonith_delete(stonith_obj);
return "heartbeat";
}
}
crm_err("No such device: %s", agent);
return NULL;
}
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->notify == NULL || b_client->notify == NULL) {
return 0;
} else if (a_client->notify == b_client->notify) {
return 0;
} else if (((long)a_client->notify) < ((long)b_client->notify)) {
crm_err("callbacks for %s are not equal: %p vs. %p",
a_client->event, a_client->notify, b_client->notify);
return -1;
}
crm_err("callbacks for %s are not equal: %p vs. %p",
a_client->event, a_client->notify, b_client->notify);
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 st_err_missing);
CRM_CHECK(token != NULL, return st_err_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 st_err_connection;
} else if (reg_msg == NULL) {
crm_err("No reply message - empty");
return st_err_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 = st_err_internal;
} else if (tmp_ticket == NULL) {
crm_err("No registration token provided");
crm_log_xml_warn(reg_msg, "Bad reply");
rc = st_err_internal;
} 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_NG);
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, "BadOp", 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 *stonith_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 = st_err_connection;
} else if (native->command_channel->ch_status != IPC_CONNECT) {
crm_err("Connection may have succeeded," " but authentication to command channel failed");
rc = st_err_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 = st_err_connection;
} else if (native->callback_channel->ch_status != IPC_CONNECT) {
crm_err("Connection may have succeeded," " but authentication to command channel failed");
rc = st_err_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 = st_err_internal;
}
free_xml(hello);
}
if (rc == stonith_ok) {
if (stonith_fd != NULL) {
*stonith_fd =
native->callback_channel->ops->get_recv_select_fd(native->callback_channel);
} else { /* 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_internal, stonith,
default_ipc_connection_destroy);
if (native->callback_source == NULL) {
crm_err("Callback source not recorded");
rc = st_err_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) (stonith_t * stonith, const char *event,
xmlNode * msg))
{
GList *list_item = NULL;
stonith_notify_client_t *new_client = NULL;
stonith_private_t *private = NULL;
private = stonith->private;
crm_debug_2("Adding callback for %s events (%d)", event, g_list_length(private->notify_list));
crm_malloc0(new_client, sizeof(stonith_notify_client_t));
new_client->event = event;
new_client->notify = callback;
list_item = g_list_find_custom(private->notify_list, new_client, stonithlib_GCompareFunc);
if (list_item != NULL) {
crm_warn("Callback already present");
crm_free(new_client);
return st_err_exists;
} else {
private->notify_list = g_list_append(private->notify_list, new_client);
stonith_set_notification(stonith, event, 1);
crm_debug_3("Callback added (%d)", g_list_length(private->notify_list));
}
return stonith_ok;
}
static int
stonith_api_del_notification(stonith_t * stonith, const char *event)
{
GList *list_item = NULL;
stonith_notify_client_t *new_client = NULL;
stonith_private_t *private = NULL;
crm_debug("Removing callback for %s events", event);
private = stonith->private;
crm_malloc0(new_client, sizeof(stonith_notify_client_t));
new_client->event = event;
new_client->notify = NULL;
list_item = g_list_find_custom(private->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;
private->notify_list = g_list_remove(private->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 %dms", timer->call_id, timer->timeout);
stonith_perform_callback(timer->stonith, NULL, timer->call_id, st_err_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, bool only_success,
void *user_data, const char *callback_name,
void (*callback) (stonith_t * st, const xmlNode * msg, int call, int rc,
xmlNode * output, void *userdata))
{
stonith_callback_client_t *blob = NULL;
stonith_private_t *private = NULL;
CRM_CHECK(stonith != NULL, return st_err_missing);
CRM_CHECK(stonith->private != NULL, return st_err_missing);
private = stonith->private;
if (call_id == 0) {
private->op_callback = callback;
} else if (call_id < 0) {
if (only_success == FALSE) {
callback(stonith, 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 * 1100;
async_timer->ref =
g_timeout_add(async_timer->timeout, stonith_async_timeout_handler, async_timer);
}
g_hash_table_insert(private->stonith_op_callback_table, GINT_TO_POINTER(call_id), blob);
return TRUE;
}
static int
stonith_api_del_callback(stonith_t * stonith, int call_id, bool all_callbacks)
{
stonith_private_t *private = stonith->private;
if (all_callbacks) {
private->op_callback = NULL;
g_hash_table_destroy(private->stonith_op_callback_table);
private->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(private->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(stonith_t * stonith)
{
stonith_private_t *private = stonith->private;
if (private->stonith_op_callback_table == NULL) {
return;
}
return g_hash_table_foreach(private->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);
}
CRM_CHECK(call_id > 0, crm_warn("Strange or missing call-id"));
blob = g_hash_table_lookup(private->stonith_op_callback_table, GINT_TO_POINTER(call_id));
if (blob != NULL) {
local_blob = *blob;
blob = NULL;
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(stonith, 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(stonith, msg, call_id, rc, output, NULL);
}
crm_debug_4("OP callback activated.");
}
static void
stonith_send_notification(gpointer data, gpointer user_data)
{
struct notify_blob_s *blob = user_data;
stonith_notify_client_t *entry = data;
const char *event = NULL;
if (blob->xml == NULL) {
crm_warn("Skipping callback - NULL message");
return;
}
event = crm_element_value(blob->xml, F_SUBTYPE);
if (entry == NULL) {
crm_warn("Skipping callback - NULL callback client");
return;
} else if (entry->notify == 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->notify(blob->stonith, event, blob->xml);
crm_debug_4("Callback invoked...");
}
int
stonith_send_command(stonith_t * stonith, const char *op, xmlNode * data, xmlNode ** output_data,
int call_options, int timeout)
{
int rc = HA_OK;
xmlNode *op_msg = NULL;
xmlNode *op_reply = NULL;
stonith_private_t *native = stonith->private;
if (stonith->state == stonith_disconnected) {
return st_err_connection;
}
if (output_data != NULL) {
*output_data = NULL;
}
if (op == NULL) {
crm_err("No operation specified");
return st_err_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 st_err_missing;
}
crm_xml_add_int(op_msg, F_STONITH_TIMEOUT, timeout);
crm_debug_3("Sending %s message to STONITH service, Timeout: %d", op, timeout);
if (send_ipc_message(native->command_channel, op_msg) == FALSE) {
crm_err("Sending message to STONITH service FAILED");
free_xml(op_msg);
return st_err_ipc;
} else {
crm_debug_3("Message sent");
}
free_xml(op_msg);
if ((call_options & st_opt_discard_reply)) {
crm_debug_3("Discarding reply");
return stonith_ok;
} else if (!(call_options & st_opt_sync_call)) {
crm_debug_3("Async call, returning");
CRM_CHECK(stonith->call_id != 0, return st_err_ipc);
return stonith->call_id;
}
rc = IPC_OK;
crm_debug_3("Waiting for a syncronous reply");
rc = stonith_ok;
while (IPC_ISRCONN(native->command_channel)) {
int reply_id = -1;
int msg_id = stonith->call_id;
op_reply = xmlfromIPC(native->command_channel, timeout);
if (op_reply == NULL) {
rc = st_err_peer;
break;
}
crm_element_value_int(op_reply, F_STONITH_CALLID, &reply_id);
if (reply_id <= 0) {
rc = st_err_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 = st_err_peer;
}
if (output_data != NULL && is_not_set(call_options, st_opt_discard_reply)) {
*output_data = op_reply;
op_reply = NULL;
}
goto done;
} 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 (op_reply == NULL && stonith->state == stonith_disconnected) {
rc = st_err_connection;
} else if (rc == stonith_ok && op_reply == NULL) {
rc = st_err_peer;
}
done:
if (IPC_ISRCONN(native->command_channel) == FALSE) {
crm_err("STONITH disconnected: %d", native->command_channel->ch_status);
stonith->state = stonith_disconnected;
}
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;
stonith_private_t *private = NULL;
struct notify_blob_s blob;
if (stonith == NULL) {
crm_err("No STONITH!");
return FALSE;
}
blob.stonith = stonith;
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 */
blob.xml = xmlfromIPC(private->callback_channel, MAX_IPC_DELAY);
if (blob.xml == NULL) {
crm_warn("Received a NULL msg from STONITH service.");
return 0;
}
/* do callbacks */
type = crm_element_value(blob.xml, F_TYPE);
crm_debug_4("Activating %s callbacks...", type);
if (safe_str_eq(type, T_STONITH_NG)) {
stonith_perform_callback(stonith, blob.xml, 0, 0);
} else if (safe_str_eq(type, T_STONITH_NOTIFY)) {
g_list_foreach(private->notify_list, stonith_send_notification, &blob);
} else {
crm_err("Unknown message type: %s", type);
crm_log_xml_warn(blob.xml, "BadReply");
}
free_xml(blob.xml);
return 1;
}
bool
stonith_dispatch(stonith_t * st)
{
stonith_private_t *private = NULL;
bool stay_connected = TRUE;
CRM_CHECK(st != NULL, return FALSE);
private = st->private;
stay_connected = stonith_dispatch_internal(private->callback_channel, (gpointer *) st);
return stay_connected;
}
gboolean
stonith_dispatch_internal(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;
g_hash_table_destroy(private->stonith_op_callback_table);
crm_free(private->token);
crm_free(stonith->private);
crm_free(stonith->cmds);
crm_free(stonith);
}
return rc;
}
void
stonith_api_delete(stonith_t * stonith)
{
stonith_private_t *private = stonith->private;
GList *list = private->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);
}
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;
private->stonith_op_callback_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, stonith_destroy_op_callback);
private->notify_list = NULL;
new_stonith->call_id = 1;
new_stonith->state = stonith_disconnected;
crm_malloc0(new_stonith->cmds, sizeof(stonith_api_operations_t));
/* *INDENT-OFF* */
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->confirm = stonith_api_confirm;
new_stonith->cmds->history = stonith_api_history;
new_stonith->cmds->list = stonith_api_device_list;
new_stonith->cmds->metadata = stonith_api_device_metadata;
new_stonith->cmds->query = stonith_api_query;
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;
/* *INDENT-ON* */
return new_stonith;
}
stonith_key_value_t *
stonith_key_value_add(stonith_key_value_t * kvp, const char *key, const char *value)
{
stonith_key_value_t *p;
crm_malloc(p, sizeof(stonith_key_value_t));
p->next = kvp;
p->key = crm_strdup(key);
p->value = crm_strdup(value);
return (p);
}
void
stonith_key_value_freeall(stonith_key_value_t * kvp, int keys, int values)
{
stonith_key_value_t *p;
while (kvp) {
p = kvp->next;
if (keys) {
free(kvp->key);
}
if (values) {
free(kvp->value);
}
free(kvp);
kvp = p;
}
}
int
stonith_api_cs_kick(int nodeid, int timeout, bool off)
{
int rc = stonith_ok;
stonith_t *st = NULL;
enum stonith_call_options opts = st_opt_sync_call|st_opt_allow_suicide|st_opt_cs_nodeid;
crm_log_init("st-client", LOG_INFO, FALSE, FALSE, 0, NULL);
st = stonith_api_new();
if(st) {
rc = st->cmds->connect(st, crm_system_name, NULL);
}
if(st && rc == stonith_ok) {
char *name = crm_itoa(nodeid);
crm_info("Requesting that node %d be terminated", nodeid);
rc = st->cmds->fence(st, opts, name, "off", 120);
crm_free(name);
}
if(st) {
st->cmds->disconnect(st);
stonith_api_delete(st);
}
if(rc < stonith_ok) {
crm_err("Could not terminate node %d: %s", nodeid, stonith_error2string(rc));
rc = 1;
} else {
rc = 0;
}
return rc;
}
time_t
stonith_api_cs_time(int nodeid, bool in_progress)
{
int rc = 0;
time_t when = 0;
time_t progress = 0;
stonith_t *st = NULL;
stonith_history_t *history, *hp = NULL;
crm_log_init("st-client", LOG_INFO, FALSE, FALSE, 0, NULL);
st = stonith_api_new();
if(st) {
rc = st->cmds->connect(st, crm_system_name, NULL);
}
if(st && rc == stonith_ok) {
char *name = NULL;
if(nodeid > 0) {
name = crm_itoa(nodeid);
}
st->cmds->history(st, st_opt_sync_call|st_opt_cs_nodeid, name, &history, 120);
crm_free(name);
for(hp = history; hp; hp = hp->next) {
if(in_progress) {
if(hp->state != st_done && hp->state != st_failed) {
progress = time(NULL);
}
} else if(hp->state == st_done) {
when = hp->completed;
}
}
}
if(progress) {
crm_debug("Node %d is in the process of being shot", nodeid);
when = progress;
} else if(when != 0) {
crm_debug("Node %d was last shot at: %s", nodeid, ctime(&when));
} else {
crm_debug("It does not appear node %d has been shot", nodeid);
}
if(st) {
st->cmds->disconnect(st);
stonith_api_delete(st);
}
return when;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 2:25 PM (1 d, 46 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018780
Default Alt Text
(58 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment