Page MenuHomeClusterLabs Projects

No OneTemporary

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

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)

Event Timeline