Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F5197296
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
117 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/cib/io.c b/cib/io.c
index 268d9c8a9c..9a008c21f0 100644
--- a/cib/io.c
+++ b/cib/io.c
@@ -1,658 +1,659 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <crm_internal.h>
#include <sys/param.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <dirent.h>
#include <crm/crm.h>
#include <cibio.h>
#include <crm/cib.h>
#include <crm/common/util.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/common/cluster.h>
#define CIB_SERIES "cib"
extern const char *cib_root;
static int cib_wrap=100;
#define CIB_WRITE_PARANOIA 0
const char * local_resource_path[] =
{
XML_CIB_TAG_STATUS,
};
const char * resource_path[] =
{
XML_CIB_TAG_RESOURCES,
};
const char * node_path[] =
{
XML_CIB_TAG_NODES,
};
const char * constraint_path[] =
{
XML_CIB_TAG_CONSTRAINTS,
};
gboolean initialized = FALSE;
xmlNode *node_search = NULL;
xmlNode *resource_search = NULL;
xmlNode *constraint_search = NULL;
xmlNode *status_search = NULL;
extern gboolean cib_writes_enabled;
extern GTRIGSource *cib_writer;
extern enum cib_errors cib_status;
int set_connected_peers(xmlNode *xml_obj);
void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data);
int write_cib_contents(gpointer p);
extern void cib_cleanup(void);
static gboolean
validate_cib_digest(xmlNode *local_cib, const char *sigfile)
{
int s_res = -1;
struct stat buf;
char *digest = NULL;
char *expected = NULL;
gboolean passed = FALSE;
FILE *expected_strm = NULL;
int start = 0, length = 0, read_len = 0;
CRM_ASSERT(sigfile != NULL);
s_res = stat(sigfile, &buf);
if (s_res != 0) {
crm_warn("No on-disk digest present");
return TRUE;
}
if(local_cib != NULL) {
digest = calculate_xml_digest(local_cib, FALSE, FALSE);
}
expected_strm = fopen(sigfile, "r");
if(expected_strm == NULL) {
crm_perror(LOG_ERR,"Could not open signature file %s for reading", sigfile);
goto bail;
}
start = ftell(expected_strm);
fseek(expected_strm, 0L, SEEK_END);
length = ftell(expected_strm);
fseek(expected_strm, 0L, start);
+ CRM_ASSERT(length >= 0);
CRM_ASSERT(start == ftell(expected_strm));
crm_debug_3("Reading %d bytes from file", length);
crm_malloc0(expected, (length+1));
read_len = fread(expected, 1, length, expected_strm);
CRM_ASSERT(read_len == length);
fclose(expected_strm);
bail:
if(expected == NULL) {
crm_err("On-disk digest is empty");
} else if(safe_str_eq(expected, digest)) {
crm_debug_2("Digest comparision passed: %s", digest);
passed = TRUE;
} else {
crm_err("Digest comparision failed: expected %s (%s), calculated %s",
expected, sigfile, digest);
}
crm_free(digest);
crm_free(expected);
return passed;
}
static int
write_cib_digest(xmlNode *local_cib, const char *digest_file, char *digest)
{
int rc = 0;
char *local_digest = NULL;
FILE *digest_strm = fopen(digest_file, "w");
if(digest_strm == NULL) {
crm_perror(LOG_ERR,"Cannot open signature file %s for writing", digest_file);
return -1;
}
if(digest == NULL) {
local_digest = calculate_xml_digest(local_cib, FALSE, FALSE);
CRM_ASSERT(digest != NULL);
digest = local_digest;
}
rc = fprintf(digest_strm, "%s", digest);
if(rc < 0) {
crm_perror(LOG_ERR,"Cannot write to signature file %s", digest_file);
}
CRM_ASSERT(digest_strm != NULL);
if(fflush(digest_strm) != 0) {
crm_perror(LOG_ERR,"Couldnt flush the contents of %s", digest_file);
rc = -1;
}
if(fsync(fileno(digest_strm)) < 0) {
crm_perror(LOG_ERR,"Couldnt sync the contents of %s", digest_file);
rc = -1;
}
fclose(digest_strm);
crm_free(local_digest);
return rc;
}
static gboolean
validate_on_disk_cib(const char *filename, xmlNode **on_disk_cib)
{
int s_res = -1;
struct stat buf;
gboolean passed = TRUE;
xmlNode *root = NULL;
CRM_ASSERT(filename != NULL);
s_res = stat(filename, &buf);
if (s_res == 0) {
char *sigfile = NULL;
size_t fnsize;
crm_debug_2("Reading cluster configuration from: %s", filename);
root = filename2xml(filename);
fnsize = strlen(filename) + 5;
crm_malloc0(sigfile, fnsize);
snprintf(sigfile, fnsize, "%s.sig", filename);
if(validate_cib_digest(root, sigfile) == FALSE) {
passed = FALSE;
}
crm_free(sigfile);
}
if(on_disk_cib != NULL) {
*on_disk_cib = root;
} else {
free_xml(root);
}
return passed;
}
static int
cib_rename(const char *old, const char *new)
{
int rc = 0;
char *automatic = NULL;
if(new == NULL) {
automatic = crm_concat(cib_root, "cib.auto.XXXXXX", '/');
automatic = mktemp(automatic);
new = automatic;
crm_err("Archiving corrupt or unusable file %s as %s", old, automatic);
}
rc = rename(old, new);
if(rc < 0) {
crm_perror(LOG_ERR, "Couldn't rename %s as %s - Disabling disk writes and continuing", old, new);
cib_writes_enabled = FALSE;
}
crm_free(automatic);
return rc;
}
/*
* It is the callers responsibility to free the output of this function
*/
static xmlNode*
retrieveCib(const char *filename, const char *sigfile, gboolean archive_invalid)
{
struct stat buf;
xmlNode *root = NULL;
crm_info("Reading cluster configuration from: %s (digest: %s)",
filename, sigfile);
if(stat(filename, &buf) != 0) {
crm_warn("Cluster configuration not found: %s", filename);
return NULL;
}
root = filename2xml(filename);
if(root == NULL) {
crm_err("%s exists but does NOT contain valid XML. ", filename);
crm_warn("Continuing but %s will NOT used.", filename);
} else if(validate_cib_digest(root, sigfile) == FALSE) {
crm_err("Checksum of %s failed! Configuration contents ignored!", filename);
crm_err("Usually this is caused by manual changes, "
"please refer to http://clusterlabs.org/wiki/FAQ#cib_changes_detected");
crm_warn("Continuing but %s will NOT used.", filename);
free_xml(root);
root = NULL;
if(archive_invalid) {
/* Archive the original files so the contents are not lost */
cib_rename(filename, NULL);
cib_rename(sigfile, NULL);
}
}
return root;
}
xmlNode*
readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
{
int seq = 0;
char *filename = NULL, *sigfile = NULL;
const char *name = NULL;
const char *value = NULL;
const char *validation = NULL;
const char *use_valgrind = getenv("HA_VALGRIND_ENABLED");
xmlNode *root = NULL;
xmlNode *status = NULL;
if(!crm_is_writable(dir, file, CRM_DAEMON_USER, NULL, FALSE)) {
cib_status = cib_bad_permissions;
return NULL;
}
filename = crm_concat(dir, file, '/');
sigfile = crm_concat(filename, "sig", '.');
cib_status = cib_ok;
root = retrieveCib(filename, sigfile, TRUE);
if(root == NULL) {
crm_warn("Primary configuration corrupt or unusable, trying backup...");
seq = get_last_sequence(cib_root, CIB_SERIES);
}
while(root == NULL) {
struct stat buf;
char *backup_file = NULL;
crm_free(sigfile);
if(seq == 0) {
seq += cib_wrap; /* unwrap */
}
backup_file = generate_series_filename(cib_root, CIB_SERIES, seq-1, FALSE);
sigfile = crm_concat(filename, "sig", '.');
if(stat(backup_file, &buf) != 0) {
crm_debug("Backup file %s not found", backup_file);
break;
}
crm_warn("Attempting to load: %s", backup_file);
root = retrieveCib(backup_file, sigfile, FALSE);
seq--;
}
if(root == NULL) {
root = createEmptyCib();
crm_xml_add(root, XML_ATTR_GENERATION, "0");
crm_xml_add(root, XML_ATTR_NUMUPDATES, "0");
crm_xml_add(root, XML_ATTR_GENERATION_ADMIN, "0");
crm_xml_add(root, XML_ATTR_VALIDATION, LATEST_SCHEMA_VERSION);
crm_warn("Continuing with an empty configuration.");
}
if(cib_writes_enabled && use_valgrind) {
if(crm_is_true(use_valgrind) || strstr(use_valgrind, "cib")) {
cib_writes_enabled = FALSE;
crm_err("*********************************************************");
crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
crm_err("*********************************************************");
}
}
status = find_xml_node(root, XML_CIB_TAG_STATUS, FALSE);
if(discard_status && status != NULL) {
/* strip out the status section if there is one */
free_xml_from_parent(root, status);
status = NULL;
}
if(status == NULL) {
create_xml_node(root, XML_CIB_TAG_STATUS);
}
/* Do this before DTD validation happens */
/* fill in some defaults */
name = XML_ATTR_GENERATION_ADMIN;
value = crm_element_value(root, name);
if(value == NULL) {
crm_warn("No value for %s was specified in the configuration.",
name);
crm_warn("The reccomended course of action is to shutdown,"
" run crm_verify and fix any errors it reports.");
crm_warn("We will default to zero and continue but may get"
" confused about which configuration to use if"
" multiple nodes are powered up at the same time.");
crm_xml_add_int(root, name, 0);
}
name = XML_ATTR_GENERATION;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add_int(root, name, 0);
}
name = XML_ATTR_NUMUPDATES;
value = crm_element_value(root, name);
if(value == NULL) {
crm_xml_add_int(root, name, 0);
}
/* unset these and require the DC/CCM to update as needed */
xml_remove_prop(root, XML_ATTR_DC_UUID);
if(discard_status) {
crm_log_xml_debug(root, "[on-disk]");
}
validation = crm_element_value(root, XML_ATTR_VALIDATION);
if(validate_xml(root, NULL, TRUE) == FALSE) {
crm_err("CIB does not validate with %s", crm_str(validation));
cib_status = cib_dtd_validation;
} else if(validation == NULL) {
int version = 0;
update_validation(&root, &version, FALSE, FALSE);
if(version > 0) {
crm_notice("Enabling %s validation on"
" the existing (sane) configuration",
get_schema_name(version));
} else {
crm_err("CIB does not validate with any known DTD or schema");
cib_status = cib_dtd_validation;
}
}
crm_free(filename);
crm_free(sigfile);
return root;
}
/*
* The caller should never free the return value
*/
xmlNode*
get_the_CIB(void)
{
return the_cib;
}
gboolean
uninitializeCib(void)
{
xmlNode *tmp_cib = the_cib;
if(tmp_cib == NULL) {
crm_debug("The CIB has already been deallocated.");
return FALSE;
}
initialized = FALSE;
the_cib = NULL;
node_search = NULL;
resource_search = NULL;
constraint_search = NULL;
status_search = NULL;
crm_debug("Deallocating the CIB.");
free_xml(tmp_cib);
crm_debug("The CIB has been deallocated.");
return TRUE;
}
/*
* This method will not free the old CIB pointer or the new one.
* We rely on the caller to have saved a pointer to the old CIB
* and to free the old/bad one depending on what is appropriate.
*/
gboolean
initializeCib(xmlNode *new_cib)
{
if(new_cib == NULL) {
return FALSE;
}
the_cib = new_cib;
initialized = TRUE;
return TRUE;
}
static void
sync_directory(const char *name)
{
int fd = 0;
DIR *directory = NULL;
directory = opendir(name);
if(directory == NULL) {
crm_perror(LOG_ERR, "Could not open %s for syncing", name);
return;
}
fd = dirfd(directory);
if(fd < 0) {
crm_perror(LOG_ERR,"Could not obtain file descriptor for %s", name);
} else if(fsync(fd) < 0) {
crm_perror(LOG_ERR,"Could not sync %s", name);
}
if(closedir(directory) < 0) {
crm_perror(LOG_ERR,"Could not close %s after fsync", name);
}
}
/*
* This method will free the old CIB pointer on success and the new one
* on failure.
*/
int
activateCibXml(xmlNode *new_cib, gboolean to_disk, const char *op)
{
xmlNode *saved_cib = the_cib;
CRM_ASSERT(new_cib != saved_cib);
if(initializeCib(new_cib) == FALSE) {
free_xml(new_cib);
crm_err("Ignoring invalid or NULL CIB");
if(saved_cib != NULL) {
crm_warn("Reverting to last known CIB");
if (initializeCib(saved_cib) == FALSE) {
/* oh we are so dead */
crm_crit("Couldn't re-initialize the old CIB!");
cl_flush_logs();
exit(1);
}
} else {
crm_crit("Could not write out new CIB and no saved"
" version to revert to");
}
return cib_ACTIVATION;
}
free_xml(saved_cib);
if(cib_writes_enabled && cib_status == cib_ok && to_disk) {
crm_debug("Triggering CIB write for %s op", op);
G_main_set_trigger(cib_writer);
}
return cib_ok;
}
int
write_cib_contents(gpointer p)
{
gboolean need_archive = FALSE;
struct stat buf;
char *digest = NULL;
int exit_rc = LSB_EXIT_OK;
xmlNode *cib_status_root = NULL;
/* we can scribble on "the_cib" here and not affect the parent */
const char *epoch = crm_element_value(the_cib, XML_ATTR_GENERATION);
const char *admin_epoch = crm_element_value(the_cib, XML_ATTR_GENERATION_ADMIN);
char *tmp1 = crm_concat(cib_root, "cib.XXXXXX", '/');
char *tmp2 = crm_concat(cib_root, "cib.XXXXXX", '/');
char *primary_file = crm_concat(cib_root, "cib.xml", '/');
char *digest_file = crm_concat(primary_file, "sig", '.');
/* Always write out with num_updates=0 */
crm_xml_add(the_cib, XML_ATTR_NUMUPDATES, "0");
if(crm_log_level > LOG_INFO) {
crm_log_level--;
}
need_archive = (stat(primary_file, &buf) == 0);
if (need_archive) {
char *backup_file = NULL;
char *backup_digest = NULL;
int seq = get_last_sequence(cib_root, CIB_SERIES);
/* check the admin didnt modify it underneath us */
if(validate_on_disk_cib(primary_file, NULL) == FALSE) {
crm_err("%s was manually modified while the cluster was active!", primary_file);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
backup_file = generate_series_filename(cib_root, CIB_SERIES, seq, FALSE);
backup_digest = crm_concat(backup_file, "sig", '.');
link(primary_file, backup_file);
link(digest_file, backup_digest);
write_last_sequence(cib_root, CIB_SERIES, seq+1, cib_wrap);
sync_directory(cib_root);
crm_info("Archived previous version as %s", backup_file);
crm_free(backup_digest);
crm_free(backup_file);
}
/* Given that we discard the status section on startup
* there is no point writing it out in the first place
* since users just get confused by it
*
* So delete the status section before we write it out
*/
crm_debug("Writing CIB to disk");
if(p == NULL) {
cib_status_root = find_xml_node(the_cib, XML_CIB_TAG_STATUS, TRUE);
CRM_DEV_ASSERT(cib_status_root != NULL);
if(cib_status_root != NULL) {
free_xml_from_parent(the_cib, cib_status_root);
}
}
tmp1 = mktemp(tmp1); /* cib */
tmp2 = mktemp(tmp2); /* digest */
if(write_xml_file(the_cib, tmp1, FALSE) <= 0) {
crm_err("Changes couldn't be written to %s", tmp1);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
/* Must calculate the digest after writing as write_xml_file() updates the last-written field */
digest = calculate_xml_digest(the_cib, FALSE, FALSE);
crm_info("Wrote version %s.%s.0 of the CIB to disk (digest: %s)",
admin_epoch?admin_epoch:"0",
epoch?epoch:"0", digest);
if(write_cib_digest(the_cib, tmp2, digest) <= 0) {
crm_err("Digest couldn't be written to %s", tmp2);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
crm_debug("Wrote digest %s to disk", digest);
CRM_ASSERT(retrieveCib(tmp1, tmp2, FALSE) != NULL);
sync_directory(cib_root);
crm_debug("Activating %s", tmp1);
cib_rename(tmp1, primary_file);
cib_rename(tmp2, digest_file);
sync_directory(cib_root);
cleanup:
crm_free(primary_file);
crm_free(digest_file);
crm_free(digest);
crm_free(tmp2);
crm_free(tmp1);
if(p == NULL) {
/* fork-and-write mode */
exit(exit_rc);
}
/* stand-alone mode */
return exit_rc;
}
void GHFunc_count_peers(gpointer key, gpointer value, gpointer user_data)
{
int *active = user_data;
if(safe_str_eq(value, ONLINESTATUS)) {
(*active)++;
} else if(safe_str_eq(value, JOINSTATUS)) {
(*active)++;
}
}
diff --git a/lib/common/utils.c b/lib/common/utils.c
index 9bc9d2c691..186c526f02 100644
--- a/lib/common/utils.c
+++ b/lib/common/utils.c
@@ -1,2362 +1,2363 @@
/*
* 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 <time.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/common/ipc.h>
#include <crm/common/iso8601.h>
#if HAVE_HB_CONFIG_H
#include <heartbeat/hb_config.h> /* for HB_COREDIR */
#endif
#if HAVE_GLUE_CONFIG_H
#include <glue_config.h> /* for HB_COREDIR */
#endif
#ifndef MAXLINE
# define MAXLINE 512
#endif
#ifdef HAVE_GETOPT_H
# include <getopt.h>
#endif
static uint ref_counter = 0;
unsigned int crm_log_level = LOG_INFO;
gboolean crm_config_error = FALSE;
gboolean crm_config_warning = FALSE;
const char *crm_system_name = "unknown";
int node_score_red = 0;
int node_score_green = 0;
int node_score_yellow = 0;
int node_score_infinity = INFINITY;
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 = -node_score_infinity;
} else if(safe_str_eq(score, INFINITY_S)) {
score_f = node_score_infinity;
} else if(safe_str_eq(score, "+"INFINITY_S)) {
score_f = node_score_infinity;
} else if(safe_str_eq(score, "red")) {
score_f = node_score_red;
} else if(safe_str_eq(score, "yellow")) {
score_f = node_score_yellow;
} else if(safe_str_eq(score, "green")) {
score_f = node_score_green;
} else {
score_f = crm_parse_int(score, NULL);
if(score_f > 0 && score_f > node_score_infinity) {
score_f = node_score_infinity;
} else if(score_f < 0 && score_f < -node_score_infinity) {
score_f = -node_score_infinity;
}
}
return score_f;
}
char *
score2char(int score)
{
if(score >= node_score_infinity) {
return crm_strdup(INFINITY_S);
} else if(score <= -node_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);
CRM_ASSERT(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);
#ifdef HAVE_G_LOG_SET_DEFAULT_HANDLER
GLogFunc glib_log_default;
static void
crm_glib_handler(const gchar *log_domain, GLogLevelFlags flags, const gchar *message, gpointer user_data)
{
int log_level = LOG_WARNING;
GLogLevelFlags msg_level = (flags & G_LOG_LEVEL_MASK);
switch(msg_level) {
case G_LOG_LEVEL_CRITICAL:
/* log and record how we got here */
crm_abort(__FILE__,__PRETTY_FUNCTION__,__LINE__, message, TRUE, TRUE);
return;
case G_LOG_LEVEL_ERROR: log_level = LOG_ERR; break;
case G_LOG_LEVEL_MESSAGE: log_level = LOG_NOTICE; break;
case G_LOG_LEVEL_INFO: log_level = LOG_INFO; break;
case G_LOG_LEVEL_DEBUG: log_level = LOG_DEBUG; break;
case G_LOG_LEVEL_WARNING:
case G_LOG_FLAG_RECURSION:
case G_LOG_FLAG_FATAL:
case G_LOG_LEVEL_MASK:
log_level = LOG_WARNING;
break;
}
do_crm_log(log_level, "%s: %s", log_domain, message);
}
#endif
void crm_log_deinit(void) {
#ifdef HAVE_G_LOG_SET_DEFAULT_HANDLER
g_log_set_default_handler(glib_log_default, NULL);
#endif
}
gboolean
crm_log_init(
const char *entity, int level, gboolean coredir, gboolean to_stderr,
int argc, char **argv)
{
/* Redirect messages from glib functions to our handler */
/* cl_malloc_forced_for_glib(); */
#ifdef HAVE_G_LOG_SET_DEFAULT_HANDLER
glib_log_default = g_log_set_default_handler(crm_glib_handler, NULL);
#endif
/* 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;
setenv("PCMK_service", crm_system_name, 1);
cl_log_set_entity(entity);
if(argc == 0) {
/* Nuke any syslog activity */
unsetenv("HA_logfacility");
} else if(getenv("HA_logfacility") == NULL) {
/* Set a default */
cl_log_set_facility(HA_LOG_FACILITY);
} /* else: picked up by crm_set_env_options() */
if(coredir) {
const char *user = getenv("USER");
if(safe_str_neq(user, "root") && safe_str_neq(user, CRM_DAEMON_USER)) {
crm_info("Not switching to corefile directory");
coredir = FALSE;
}
}
if(coredir) {
int user = getuid();
struct passwd *pwent = NULL;
const char *base = HA_COREDIR;
pwent = getpwuid(user);
if (pwent == NULL) {
crm_perror(LOG_ERR, "Cannot get name for uid: %d", user);
} else if(safe_str_neq(pwent->pw_name, "root")
&& safe_str_neq(pwent->pw_name, "nobody")
&& safe_str_neq(pwent->pw_name, CRM_DAEMON_USER)) {
crm_debug("Don't change active directory for regular user: %s", pwent->pw_name);
} else if (chdir(base) < 0) {
crm_perror(LOG_ERR, "Cannot change active directory to %s", base);
} else if (chdir(pwent->pw_name) < 0) {
crm_perror(LOG_ERR, "Cannot change active directory to %s/%s", base, pwent->pw_name);
} else {
crm_info("Changed active directory to %s/%s", base, pwent->pw_name);
}
}
set_crm_log_level(level);
crm_set_env_options();
cl_log_args(argc, argv);
cl_log_enable_stderr(to_stderr);
crm_signal(DEBUG_INC, alter_debug);
crm_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;
}
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)
{
crm_signal(DEBUG_INC, alter_debug);
crm_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 long
crm_int_helper(const char *text, char **end_text)
{
long long result = -1;
char *local_end_text = NULL;
int saved_errno = 0;
errno = 0;
if(text != NULL) {
#ifdef ANSI_ONLY
if(end_text != NULL) {
result = strtol(text, end_text, 10);
} else {
result = strtol(text, &local_end_text, 10);
}
#else
if(end_text != NULL) {
result = strtoll(text, end_text, 10);
} else {
result = strtoll(text, &local_end_text, 10);
}
#endif
saved_errno = errno;
/* CRM_CHECK(errno != EINVAL); */
if(errno == EINVAL) {
crm_err("Conversion of %s failed", text);
result = -1;
} else if(errno == ERANGE) {
crm_err("Conversion of %s was clipped: %lld", text, result);
} else if(errno != 0) {
crm_perror(LOG_ERR,"Conversion of %s failed:", text);
}
if(local_end_text != NULL && local_end_text[0] != '\0') {
crm_err("Characters left over after parsing '%s': '%s'", text, local_end_text);
}
errno = saved_errno;
}
return 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
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,
crm_err("Could not perform copy at %s:%d (%s)", file, line, fn);
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) {
crm_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
unsigned long long
crm_get_interval(const char * input)
{
ha_time_t *interval = NULL;
char *input_copy = crm_strdup(input);
char *input_copy_mutable = input_copy;
unsigned long long msec = 0;
if(input == NULL) {
return 0;
} else if(input[0] != 'P') {
crm_free(input_copy);
return crm_get_msec(input);
}
interval = parse_time_duration(&input_copy_mutable);
msec = date_in_seconds(interval);
free_ha_date(interval);
crm_free(input_copy);
return msec * 1000;
}
long long
crm_get_msec(const char * input)
{
const char *cp = input;
const char *units;
long long multiplier = 1000;
long long divisor = 1;
long long msec = -1;
char *end_text = NULL;
/* double dret; */
if(input == NULL) {
return msec;
}
cp += strspn(cp, WHITESPACE);
units = cp + strspn(cp, NUMCHARS);
units += strspn(units, WHITESPACE);
if (strchr(NUMCHARS, *cp) == NULL) {
return msec;
}
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 msec;
}
msec = crm_int_helper(cp, &end_text);
msec *= multiplier;
msec /= divisor;
/* dret += 0.5; */
/* msec = (long long)dret; */
return msec;
}
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_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);
result = FALSE;
goto bail;
}
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(xmlNode *param_set, const char *version)
{
char *key = NULL;
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]);
}
key = crm_meta_name(XML_LRM_ATTR_INTERVAL);
interval = crm_element_value_copy(param_set, key);
crm_free(key);
key = crm_meta_name(XML_ATTR_TIMEOUT);
timeout = crm_element_value_copy(param_set, key);
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) {
xml_remove_prop(param_set, prop_name);
}
);
if(crm_get_msec(interval) > 0 && compare_version(version, "1.0.8") > 0) {
/* Re-instate the operation's timeout value */
if(timeout != NULL) {
crm_xml_add(param_set, key, timeout);
}
}
crm_free(interval);
crm_free(timeout);
crm_free(key);
}
void
filter_reload_parameters(xmlNode *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) {
crm_debug_3("%s not found in %s",
prop_name, restart_string);
xml_remove_prop(param_set, prop_name);
}
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) {
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:
do_crm_log(LOG_CRIT, "%s: Cannot create core for non-fatal assert at %s:%d : %s",
function, file, line, assert_condition);
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) {
crm_perror(LOG_ERR,"%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(length >= 0);
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;
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;
}
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, "%d", sequence);
if(rc < 0) {
crm_perror(LOG_ERR,"Cannot write to series file %s", series_file);
}
bail:
if(file_strm != NULL) {
fflush(file_strm);
fclose(file_strm);
}
crm_free(series_file);
}
#define LOCKSTRLEN 11
int crm_pid_active(long pid)
{
int rc = 0;
int running = 0;
char proc_path[PATH_MAX], exe_path[PATH_MAX], myexe_path[PATH_MAX];
if(pid <= 0) {
return -1;
} else if (kill(pid, 0) < 0 && errno == ESRCH) {
return 0;
}
#ifndef HAVE_PROC_PID
return 1;
#endif
/* check to make sure pid hasn't been reused by another process */
snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", pid);
rc = readlink(proc_path, exe_path, PATH_MAX-1);
if(rc < 0) {
crm_perror(LOG_ERR, "Could not read from %s", proc_path);
goto bail;
}
exe_path[rc] = 0;
snprintf(proc_path, sizeof(proc_path), "/proc/%lu/exe", (long unsigned int)getpid());
rc = readlink(proc_path, myexe_path, PATH_MAX-1);
if(rc < 0) {
crm_perror(LOG_ERR, "Could not read from %s", proc_path);
goto bail;
}
myexe_path[rc] = 0;
if(strcmp(exe_path, myexe_path) == 0) {
running = 1;
}
bail:
return running;
}
int
crm_read_pidfile(const char *filename)
{
int fd;
long pid = -1;
char buf[LOCKSTRLEN+1];
if ((fd = open(filename, O_RDONLY)) < 0) {
goto bail;
}
if (read(fd, buf, sizeof(buf)) < 1) {
goto bail;
}
if (sscanf(buf, "%lu", &pid) > 0) {
if (pid <= 0){
pid = -LSB_STATUS_STOPPED;
}
}
bail:
- close(fd);
+ if(fd >= 0) { close(fd); }
return pid;
}
int
crm_lock_pidfile(const char *filename)
{
struct stat sbuf;
int fd = 0, rc = 0;
long pid = 0, mypid = 0;
char lf_name[256], tf_name[256], buf[LOCKSTRLEN+1];
mypid = (unsigned long) getpid();
snprintf(lf_name, sizeof(lf_name), "%s",filename);
snprintf(tf_name, sizeof(tf_name), "%s.%lu", filename, mypid);
if ((fd = open(lf_name, O_RDONLY)) >= 0) {
if (fstat(fd, &sbuf) >= 0 && sbuf.st_size < LOCKSTRLEN) {
sleep(1); /* if someone was about to create one,
* give'm a sec to do so
* Though if they follow our protocol,
* this won't happen. They should really
* put the pid in, then link, not the
* other way around.
*/
}
if (read(fd, buf, sizeof(buf)) > 0) {
if (sscanf(buf, "%lu", &pid) > 0) {
if (pid > 1 && pid != getpid() && crm_pid_active(pid)) {
/* locked by existing process - give up */
close(fd);
return -1;
}
}
}
unlink(lf_name);
close(fd);
}
if ((fd = open(tf_name, O_CREAT | O_WRONLY | O_EXCL, 0644)) < 0) {
/* Hmmh, why did we fail? Anyway, nothing we can do about it */
return -3;
}
/* Slight overkill with the %*d format ;-) */
snprintf(buf, sizeof(buf), "%*lu\n", LOCKSTRLEN-1, mypid);
if (write(fd, buf, LOCKSTRLEN) != LOCKSTRLEN) {
/* Again, nothing we can do about this */
rc = -3;
close(fd);
goto out;
}
close(fd);
switch (link(tf_name, lf_name)) {
case 0:
if (stat(tf_name, &sbuf) < 0) {
/* something weird happened */
rc = -3;
} else if (sbuf.st_nlink < 2) {
/* somehow, it didn't get through - NFS trouble? */
rc = -2;
} else {
rc = 0;
}
break;
case EEXIST:
rc = -1;
break;
default:
rc = -3;
}
out:
unlink(tf_name);
return rc;
}
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);
crm_perror(LOG_ERR,"fork");
exit(LSB_EXIT_GENERIC);
} else if (pid > 0) {
exit(LSB_EXIT_OK);
}
if (crm_lock_pidfile(pidfile) < 0 ) {
pid = crm_read_pidfile(pidfile);
if(crm_pid_active(pid) > 0) {
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_unlikely(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_unlikely(level, "Bit 0x%.16llx set by %s", bit, function);
word |= bit;
return word;
}
static const char *cluster_type = NULL;
gboolean is_openais_cluster(void)
{
if(cluster_type == NULL) {
cluster_type = getenv("HA_cluster_type");
if(cluster_type == NULL) {
cluster_type = "Heartbeat";
}
}
if(safe_str_eq("openais", cluster_type)) {
#if SUPPORT_AIS
return TRUE;
#else
crm_crit("The installation of Pacemaker only supports Heartbeat"
" but you're trying to run it on %s. Terminating.",
cluster_type);
exit(100);
#endif
}
return FALSE;
}
gboolean is_heartbeat_cluster(void)
{
#if SUPPORT_HEARTBEAT
return !is_openais_cluster();
#else
if(is_openais_cluster() == FALSE) {
crm_crit("The installation of Pacemaker only supports OpenAIS"
" but you're trying to run it on %s. Terminating.",
cluster_type);
exit(100);
}
return FALSE;
#endif
}
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
{
if(a == b) {
return TRUE;
} else if(a == NULL || b == NULL) {
/* shouldn't be comparing NULLs */
return FALSE;
} else if(use_case && a[0] != b[0]) {
return FALSE;
} else if(strcasecmp(a, b) == 0) {
return TRUE;
}
return FALSE;
}
char *crm_meta_name(const char *field)
{
int lpc = 0;
int max = 0;
char *crm_name = NULL;
CRM_CHECK(field != NULL, return NULL);
crm_name = crm_concat(CRM_META, field, '_');
/* Massage the names so they can be used as shell variables */
max = strlen(crm_name);
for(; lpc < max; lpc++) {
switch(crm_name[lpc]) {
case '-':
crm_name[lpc] = '_';
break;
}
}
return crm_name;
}
const char *crm_meta_value(GHashTable *hash, const char *field)
{
char *key = NULL;
const char *value = NULL;
key = crm_meta_name(field);
if(key) {
value = g_hash_table_lookup(hash, key);
crm_free(key);
}
return value;
}
static struct crm_option *crm_long_options = NULL;
static const char *crm_app_description = NULL;
static const char *crm_short_options = NULL;
static const char *crm_app_usage = NULL;
static struct option *crm_create_long_opts(struct crm_option *long_options)
{
struct option *long_opts = NULL;
#ifdef HAVE_GETOPT_H
int index = 0, lpc = 0;
/*
* A previous, possibly poor, choice of '?' as the short form of --help
* means that getopt_long() returns '?' for both --help and for "unknown option"
*
* This dummy entry allows us to differentiate between the two in crm_get_option()
* and exit with the correct error code
*/
crm_realloc(long_opts, (index+1) * sizeof(struct option));
long_opts[index].name = "__dummmy__";
long_opts[index].has_arg = 0;
long_opts[index].flag = 0;
long_opts[index].val = '_';
index++;
for(lpc = 0; long_options[lpc].name != NULL; lpc++) {
if(long_options[lpc].name[0] == '-') {
continue;
}
crm_realloc(long_opts, (index+1) * sizeof(struct option));
/*fprintf(stderr, "Creating %d %s = %c\n", index,
* long_options[lpc].name, long_options[lpc].val); */
long_opts[index].name = long_options[lpc].name;
long_opts[index].has_arg = long_options[lpc].has_arg;
long_opts[index].flag = long_options[lpc].flag;
long_opts[index].val = long_options[lpc].val;
index++;
}
/* Now create the list terminator */
crm_realloc(long_opts, (index+1) * sizeof(struct option));
long_opts[index].name = NULL;
long_opts[index].has_arg = 0;
long_opts[index].flag = 0;
long_opts[index].val = 0;
#endif
return long_opts;
}
void crm_set_options(const char *short_options, const char *app_usage, struct crm_option *long_options, const char *app_desc)
{
if(short_options) {
crm_short_options = short_options;
}
if(long_options) {
crm_long_options = long_options;
}
if(app_desc) {
crm_app_description = app_desc;
}
if(app_usage) {
crm_app_usage = app_usage;
}
}
int crm_get_option(int argc, char **argv, int *index)
{
#ifdef HAVE_GETOPT_H
static struct option *long_opts = NULL;
if(long_opts == NULL && crm_long_options) {
long_opts = crm_create_long_opts(crm_long_options);
}
if(long_opts) {
int flag = getopt_long(argc, argv, crm_short_options, long_opts, index);
switch(flag) {
case 0: return long_opts[*index].val;
case -1: /* End of option processing */ break;
case ':': crm_debug_2("Missing argument"); crm_help('?', 1); break;
case '?': crm_help('?', *index?0:1); break;
}
return flag;
}
#endif
if(crm_short_options) {
return getopt(argc, argv, crm_short_options);
}
return -1;
}
void crm_help(char cmd, int exit_code)
{
int i = 0;
FILE *stream = (exit_code ? stderr : stdout);
if(cmd == 'v' || cmd == '$') {
fprintf(stream, "%s %s for %s (Build: %s)\n",
crm_system_name, VERSION,
#if !SUPPORT_HEARTBEAT
"OpenAIS",
#elif !SUPPORT_AIS
"Heartbeat",
#else
"OpenAIS and Heartbeat",
#endif
BUILD_VERSION);
fprintf(stream, "\nWritten by Andrew Beekhof\n");
goto out;
}
fprintf(stream, "%s - %s\n", crm_system_name, crm_app_description);
if(crm_app_usage) {
fprintf(stream, "Usage: %s %s\n", crm_system_name, crm_app_usage);
}
if(crm_long_options) {
fprintf(stream, "Options:\n");
for(i = 0; crm_long_options[i].name != NULL; i++) {
if(crm_long_options[i].flags & pcmk_option_hidden) {
} else if(crm_long_options[i].flags & pcmk_option_paragraph) {
fprintf(stream, "%s\n\n", crm_long_options[i].desc);
} else if(crm_long_options[i].flags & pcmk_option_example) {
fprintf(stream, "\t#%s\n\n", crm_long_options[i].desc);
} else if(crm_long_options[i].val == '-' && crm_long_options[i].desc) {
fprintf(stream, "%s\n", crm_long_options[i].desc);
} else {
fprintf(stream, " -%c, --%s%c%s\t%s\n", crm_long_options[i].val, crm_long_options[i].name,
crm_long_options[i].has_arg?'=':' ',crm_long_options[i].has_arg?"value":"",
crm_long_options[i].desc?crm_long_options[i].desc:"");
}
}
} else if(crm_short_options) {
fprintf(stream, "Usage: %s - %s\n", crm_system_name, crm_app_description);
for(i = 0; crm_short_options[i] != 0; i++) {
int has_arg = FALSE;
if(crm_short_options[i+1] == ':') {
has_arg = TRUE;
}
fprintf(stream, " -%c %s\n", crm_short_options[i], has_arg?"{value}":"");
if(has_arg) {
i++;
}
}
}
fprintf(stream, "\nReport bugs to %s\n", PACKAGE_BUGREPORT);
out:
if(exit_code >= 0) {
exit(exit_code);
}
}
#include <../../tools/attrd.h>
gboolean attrd_update(IPC_Channel *cluster, char command, const char *host, const char *name, const char *value, const char *section, const char *set, const char *dampen)
{
gboolean success = FALSE;
const char *reason = "Cluster connection failed";
/* remap common aliases */
if(safe_str_eq(section, "reboot")) {
section = XML_CIB_TAG_STATUS;
} else if(safe_str_eq(section, "forever")) {
section = XML_CIB_TAG_NODES;
}
if(cluster == NULL) {
reason = "No connection to the cluster";
} else {
xmlNode *update = create_xml_node(NULL, __FUNCTION__);
crm_xml_add(update, F_TYPE, T_ATTRD);
crm_xml_add(update, F_ORIG, crm_system_name);
if(name == NULL && command == 'U') {
command = 'R';
}
switch(command) {
case 'D':
case 'U':
case 'v':
crm_xml_add(update, F_ATTRD_TASK, "update");
crm_xml_add(update, F_ATTRD_ATTRIBUTE, name);
break;
case 'R':
crm_xml_add(update, F_ATTRD_TASK, "refresh");
break;
case 'q':
crm_xml_add(update, F_ATTRD_TASK, "query");
break;
}
crm_xml_add(update, F_ATTRD_VALUE, value);
crm_xml_add(update, F_ATTRD_DAMPEN, dampen);
crm_xml_add(update, F_ATTRD_SECTION, section);
crm_xml_add(update, F_ATTRD_HOST, host);
crm_xml_add(update, F_ATTRD_SET, set);
success = send_ipc_message(cluster, update);
free_xml(update);
}
if(success) {
crm_debug("Sent update: %s=%s for %s", name, value, host?host:"localhost");
return TRUE;
}
crm_info("Could not send update: %s=%s for %s", name, value, host?host:"localhost");
return FALSE;
}
gboolean attrd_lazy_update(char command, const char *host, const char *name, const char *value, const char *section, const char *set, const char *dampen)
{
int max = 5;
gboolean updated = FALSE;
static IPC_Channel *cluster = NULL;
while(updated == 0 && max > 0) {
if(cluster == NULL) {
crm_info("Connecting to cluster... %d retries remaining", max);
cluster = init_client_ipc_comms_nodispatch(T_ATTRD);
}
if(cluster != NULL) {
updated = attrd_update(cluster, command, host, name, value, section, set, dampen);
}
if(updated == 0) {
cluster = NULL;
sleep(2);
max--;
}
}
return updated;
}
gboolean attrd_update_no_mainloop(int *connection, char command, const char *host, const char *name, const char *value, const char *section, const char *set, const char *dampen)
{
int max = 5;
gboolean updated = FALSE;
static IPC_Channel *cluster = NULL;
if(connection && *connection == 0 && cluster) {
crm_info("Forcing a new connection to the cluster");
cluster = NULL;
}
while(updated == 0 && max > 0) {
if(cluster == NULL) {
crm_info("Connecting to cluster... %d retries remaining", max);
cluster = init_client_ipc_comms_nodispatch(T_ATTRD);
}
if(connection) {
if(cluster != NULL) {
*connection = cluster->ops->get_recv_select_fd(cluster);
} else {
*connection = 0;
}
}
if(cluster != NULL) {
updated = attrd_update(cluster, command, host, name, value, section, set, dampen);
}
if(updated == 0) {
cluster = NULL;
sleep(2);
max--;
}
}
return updated;
}
#define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
static void
append_digest(lrm_op_t *op, xmlNode *update, const char *version, const char *magic, int level)
{
/* this will enable us to later determine that the
* resource's parameters have changed and we should force
* a restart
*/
char *digest = NULL;
xmlNode *args_xml = NULL;
if(op->params == NULL) {
return;
}
args_xml = create_xml_node(NULL, XML_TAG_PARAMS);
g_hash_table_foreach(op->params, hash2field, args_xml);
filter_action_parameters(args_xml, version);
digest = calculate_xml_digest(args_xml, TRUE, FALSE);
#if 0
if(level < crm_log_level
&& op->interval == 0
&& crm_str_eq(op->op_type, CRMD_ACTION_START, TRUE)) {
char *digest_source = dump_xml_unformatted(args_xml);
do_crm_log(level, "Calculated digest %s for %s (%s). Source: %s\n",
digest, ID(update), magic, digest_source);
crm_free(digest_source);
}
#endif
crm_xml_add(update, XML_LRM_ATTR_OP_DIGEST, digest);
free_xml(args_xml);
crm_free(digest);
}
xmlNode *
create_operation_update(
xmlNode *parent, lrm_op_t *op, const char *caller_version, int target_rc, const char *origin, int level)
{
char *magic = NULL;
const char *task = NULL;
xmlNode *xml_op = NULL;
char *op_id = NULL;
char *local_user_data = NULL;
CRM_CHECK(op != NULL, return NULL);
do_crm_log(level, "%s: Updating resouce %s after %s %s op (interval=%d)",
origin, op->rsc_id, op_status2text(op->op_status), op->op_type, op->interval);
if(op->op_status == LRM_OP_CANCELLED) {
crm_debug_3("Ignoring cancelled op");
return NULL;
}
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;
} else if(crm_str_eq(task, CRMD_ACTION_NOTIFY, TRUE)) {
const char *n_type = crm_meta_value(op->params, "notify_type");
const char *n_task = crm_meta_value(op->params, "notify_operation");
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;
}
if (op_id == NULL) {
op_id = generate_op_key(op->rsc_id, task, op->interval);
}
xml_op = find_entity(parent, 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(parent, XML_LRM_TAG_RSC_OP);
}
if(op->user_data == NULL) {
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, op->call_id, target_rc, FAKE_TE_ID);
op->user_data = local_user_data;
}
magic = generate_transition_magic(op->user_data, op->op_status, op->rc);
crm_xml_add(xml_op, XML_ATTR_ID, op_id);
crm_xml_add(xml_op, XML_LRM_ATTR_TASK, task);
crm_xml_add(xml_op, XML_ATTR_ORIGIN, origin);
crm_xml_add(xml_op, XML_ATTR_CRM_VERSION, caller_version);
crm_xml_add(xml_op, XML_ATTR_TRANSITION_KEY, op->user_data);
crm_xml_add(xml_op, XML_ATTR_TRANSITION_MAGIC, magic);
crm_xml_add_int(xml_op, XML_LRM_ATTR_CALLID, op->call_id);
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_2("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);
}
}
append_digest(op, xml_op, caller_version, magic, LOG_DEBUG);
if(op->op_status != LRM_OP_DONE
&& crm_str_eq(op->op_type, CRMD_ACTION_MIGRATED, TRUE)) {
const char *host = crm_meta_value(op->params, "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;
}
crm_free(magic);
crm_free(op_id);
return xml_op;
}
diff --git a/lib/fencing/st_client.c b/lib/fencing/st_client.c
index 1d26f61bc5..977e3fd185 100644
--- a/lib/fencing/st_client.c
+++ b/lib/fencing/st_client.c
@@ -1,1593 +1,1594 @@
/*
* 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 <ctype.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glib.h>
#include <crm/crm.h>
#include <crm/stonith-ng.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <stonith/stonith.h>
#define FE_AGENT_FORK -2
#define FE_AGENT_ERROR -3
typedef struct stonith_private_s
{
char *token;
IPC_Channel *command_channel;
IPC_Channel *callback_channel;
GCHSource *callback_source;
GHashTable *stonith_op_callback_table;
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=\"15\" />\n"
" <action name=\"stop\" timeout=\"15\" />\n"
" <action name=\"status\" timeout=\"15\" />\n"
" <action name=\"monitor\" timeout=\"15\" interval=\"15\" 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";
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, 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(stonith->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, 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, "origin", __FUNCTION__);
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, 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_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;
}
}
}
static char *make_args(GHashTable *dev_hash, GHashTable *node_hash, const char *action, const char *victim)
{
char *arg_list = NULL;
const char *map = NULL;
CRM_CHECK(action != NULL, return NULL);
if(dev_hash) {
map = g_hash_table_lookup(dev_hash, STONITH_ATTR_ARGMAP);
g_hash_table_foreach(dev_hash, append_arg, &arg_list);
}
append_const_arg(STONITH_ATTR_ACTION_OP, action, &arg_list);
if(victim) {
append_const_arg("nodename", victim, &arg_list);
append_host_specific_args(victim, map, node_hash, &arg_list);
}
crm_debug_3("Calculated: %s", arg_list);
return arg_list;
}
/* Borrowed from libfence and extended */
int run_stonith_agent(
const char *agent, GHashTable *dev_hash, GHashTable *node_hash, const char *action, const char *victim,
int *agent_result, char **output, async_command_t *track)
{
char *args = make_args(dev_hash, node_hash, action, victim);
int pid, status, len, rc = -1;
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) {
*agent_result = FE_AGENT_FORK;
goto fail;
}
if (pid) {
/* parent */
int ret;
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, len);
} while (ret < 0 && errno == EINTR);
if (ret != len) {
if(ret >= 0) {
rc = st_err_generic;
}
goto fail;
}
close(p_write_fd);
if(track) {
NewTrackedProc(pid, 0, PT_LOGNORMAL, track, track->pt_ops);
#if 0
ProcTrackKillInfo *info = NULL;
crm_malloc0(info, sizeof(ProcTrackKillInfo) * 3);
killseq[0].mstimeout = timeout; /* after timeout send TERM */
killseq[0].signalno = SIGTERM;
killseq[1].mstimeout = 5000; /* after 5 secs remove it */
killseq[1].signalno = SIGKILL;
killseq[2].mstimeout = 5000; /* if it's still there after 5, complain */
killseq[2].signalno = 0;
SetTrackedProcTimeouts(pid,killseq);
#endif
track->stdout = p_read_fd;
crm_free(args);
close(c_write_fd);
close(c_read_fd);
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));
}
*agent_result = FE_AGENT_ERROR;
if (WIFEXITED(status)) {
crm_debug("result = %d", WEXITSTATUS(status));
*agent_result = -WEXITSTATUS(status);
rc = 0;
}
if(node_hash) {
g_hash_table_destroy(node_hash);
}
}
} 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);
- close(p_read_fd);
- close(p_write_fd);
+ if(p_read_fd >= 0) { close(p_read_fd); }
+ if(p_write_fd >= 0) { close(p_write_fd); }
- close(c_read_fd);
- close(c_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_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, NULL, NULL, "metadata", 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_DEVICENAME));
crm_info("short description: %s", meta_shortdesc);
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, GListPtr *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 = g_list_append(*devices, crm_element_value_copy(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);
crm_xml_add_int(data, "timeout", timeout);
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, GHashTable *parameters,
const char *action, int timeout)
{
int rc = 0;
xmlNode *data = NULL;
xmlNode *params = 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, "timeout", timeout);
params = create_xml_node(data, XML_TAG_ATTRS);
g_hash_table_foreach(parameters, hash2nvpair, params);
rc = stonith_send_command(stonith, STONITH_OP_FENCE, data, NULL, call_options, timeout);
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 = "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;
}
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 *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 = 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) {
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 = 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;
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->notify = 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 st_err_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)
{
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->notify = NULL;
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 %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, gboolean 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, gboolean 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_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 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, stonith->call_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;
}
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 = st_err_connection;
} else if(rc == stonith_ok && op_reply == NULL) {
rc = st_err_peer;
}
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(stonith->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;
}
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;
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)
{
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);
}
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);
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->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;
return new_stonith;
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Aug 14, 12:03 PM (9 h, 41 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2207558
Default Alt Text
(117 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment