Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F1841314
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
18 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/crm/cib/io.c b/crm/cib/io.c
index 99e0e96ac9..72295919c8 100644
--- a/crm/cib/io.c
+++ b/crm/cib/io.c
@@ -1,770 +1,775 @@
/* $Id: io.c,v 1.81 2006/07/18 06:15:54 andrew Exp $ */
/*
* 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 <portability.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 <heartbeat.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 <clplumbing/cl_misc.h>
#include <clplumbing/lsb_exitcodes.h>
#include <cibprimatives.h>
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;
crm_data_t *the_cib = NULL;
crm_data_t *node_search = NULL;
crm_data_t *resource_search = NULL;
crm_data_t *constraint_search = NULL;
crm_data_t *status_search = NULL;
extern gboolean cib_writes_enabled;
extern char *ccm_transition_id;
extern gboolean cib_have_quorum;
extern GHashTable *peer_hash;
extern GHashTable *ccm_membership;
extern GTRIGSource *cib_writer;
extern enum cib_errors cib_status;
int set_connected_peers(crm_data_t *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(crm_data_t *local_cib)
{
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;
s_res = stat(CIB_FILENAME ".sig", &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);
}
expected_strm = fopen(CIB_FILENAME ".sig", "r");
start = ftell(expected_strm);
fseek(expected_strm, 0L, SEEK_END);
length = ftell(expected_strm);
fseek(expected_strm, 0L, start);
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);
if(expected == NULL) {
crm_err("On-disk digest is empty");
} else if(safe_str_eq(expected, digest)) {
crm_debug("Digest comparision passed: %s", digest);
passed = TRUE;
} else {
crm_err("Digest comparision failed: %s vs. %s",
expected, digest);
}
crm_free(digest);
crm_free(expected);
return passed;
}
static int
write_cib_digest(crm_data_t *local_cib, char *digest)
{
int rc = 0;
FILE *digest_strm = fopen(CIB_FILENAME ".sig", "w");
char *local_digest = NULL;
CRM_ASSERT(digest_strm != NULL);
if(digest == NULL) {
local_digest = calculate_xml_digest(local_cib, FALSE);
CRM_ASSERT(digest != NULL);
digest = local_digest;
}
rc = fprintf(digest_strm, "%s", digest);
if(rc < 0) {
cl_perror("Cannot write output to %s.sig", CIB_FILENAME);
}
fflush(digest_strm);
fclose(digest_strm);
crm_free(local_digest);
return rc;
}
static gboolean
validate_on_disk_cib(const char *filename, crm_data_t **on_disk_cib)
{
int s_res = -1;
struct stat buf;
FILE *cib_file = NULL;
gboolean passed = TRUE;
crm_data_t *root = NULL;
if(filename != NULL) {
s_res = stat(filename, &buf);
}
if (s_res == 0) {
cib_file = fopen(filename, "r");
crm_debug_2("Reading cluster configuration from: %s", filename);
root = file2xml(cib_file, FALSE);
fclose(cib_file);
if(validate_cib_digest(root) == FALSE) {
passed = FALSE;
}
}
if(on_disk_cib != NULL) {
*on_disk_cib = root;
} else {
free_xml(root);
}
return passed;
}
/*
* It is the callers responsibility to free the output of this function
*/
crm_data_t*
readCibXmlFile(const char *dir, const char *file, gboolean discard_status)
{
struct stat buf;
FILE *cib_file = NULL;
gboolean dtd_ok = TRUE;
char *filename = NULL;
const char *name = NULL;
const char *value = NULL;
const char *ignore_dtd = NULL;
crm_data_t *root = NULL;
crm_data_t *status = NULL;
if(!crm_is_writable(dir, file, HA_CCMUSER, NULL, FALSE)) {
cib_status = cib_bad_permissions;
return NULL;
}
filename = crm_concat(dir, file, '/');
if(stat(filename, &buf) != 0) {
crm_warn("Cluster configuration not found: %s."
" Creating an empty one.", filename);
} else {
crm_info("Reading cluster configuration from: %s", filename);
cib_file = fopen(filename, "r");
- root = file2xml(cib_file, FALSE);
- fclose(cib_file);
+ if(cib_file == NULL) {
+ cl_perror("could not open: %s", filename);
+ } else {
+ root = file2xml(cib_file, FALSE);
+ fclose(cib_file);
+ }
+
if(root == NULL) {
crm_err("%s exists but does NOT contain valid XML. ",
filename);
crm_err("Continuing with an empty configuration."
" %s will NOT be overwritten.", filename);
cib_writes_enabled = FALSE;
} else if(validate_cib_digest(root) == FALSE) {
crm_err("%s has been manually changed! If this was"
" intended, remove the digest in %s.sig",
filename, filename);
cib_status = cib_bad_digest;
}
}
#ifdef WITH_VALGRIND
cib_writes_enabled = FALSE;
crm_err("*********************************************************");
crm_err("*** Disabling disk writes to avoid confusing Valgrind ***");
crm_err("*********************************************************");
#endif
if(root == NULL) {
root = createEmptyCib();
} else {
crm_xml_add(root, "generated", XML_BOOLEAN_FALSE);
}
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;
}
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_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 */
update_counters(__FILE__, __PRETTY_FUNCTION__, root);
xml_remove_prop(root, XML_ATTR_DC_UUID);
if(discard_status) {
crm_log_xml_info(root, "[on-disk]");
}
ignore_dtd = crm_element_value(root, "ignore_dtd");
dtd_ok = validate_with_dtd(root, TRUE, HA_LIBDIR"/heartbeat/crm.dtd");
if(dtd_ok == FALSE) {
if(ignore_dtd == NULL
&& crm_is_true(ignore_dtd) == FALSE) {
cib_status = cib_dtd_validation;
}
} else if(ignore_dtd == NULL) {
crm_notice("Enabling DTD validation on"
" the existing (sane) configuration");
crm_xml_add(root, "ignore_dtd", XML_BOOLEAN_FALSE);
}
if(do_id_check(root, NULL, TRUE, FALSE)) {
crm_err("%s does not contain a vaild configuration:"
" ID check failed",
filename);
cib_status = cib_id_check;
}
if (verifyCibXml(root) == FALSE) {
crm_err("%s does not contain a vaild configuration:"
" structure test failed",
filename);
cib_status = cib_bad_config;
}
crm_free(filename);
return root;
}
/*
* The caller should never free the return value
*/
crm_data_t*
get_the_CIB(void)
{
return the_cib;
}
gboolean
uninitializeCib(void)
{
crm_data_t *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_info("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(crm_data_t *new_cib)
{
gboolean is_valid = TRUE;
crm_data_t *tmp_node = NULL;
if(new_cib == NULL) {
return FALSE;
}
xml_validate(new_cib);
tmp_node = get_object_root(XML_CIB_TAG_NODES, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_RESOURCES, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_CONSTRAINTS, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_CRMCONFIG, new_cib);
if (tmp_node == NULL) { is_valid = FALSE; }
tmp_node = get_object_root(XML_CIB_TAG_STATUS, new_cib);
if (is_valid && tmp_node == NULL) {
create_xml_node(new_cib, XML_CIB_TAG_STATUS);
}
if(is_valid == FALSE) {
crm_warn("CIB Verification failed");
return FALSE;
}
update_counters(__FILE__, __PRETTY_FUNCTION__, new_cib);
the_cib = new_cib;
initialized = TRUE;
return TRUE;
}
static int
archive_file(const char *oldname, const char *newname, const char *ext)
{
/* move 'oldname' to 'newname' by creating a hard link to it
* and then removing the original hard link
*/
int rc = 0;
int res = 0;
struct stat tmp;
int s_res = 0;
char *backup_file = NULL;
static const char *back_ext = "bak";
/* calculate the backup name if required */
if(newname != NULL) {
backup_file = crm_strdup(newname);
} else {
int max_name_len = 1024;
crm_malloc0(backup_file, max_name_len);
if (ext == NULL) {
ext = back_ext;
}
snprintf(backup_file, max_name_len - 1, "%s.%s", oldname, ext);
}
if(backup_file == NULL || strlen(backup_file) == 0) {
crm_err("%s backup filename was %s",
newname == NULL?"calculated":"supplied",
backup_file == NULL?"null":"empty");
rc = -4;
}
s_res = stat(backup_file, &tmp);
/* unlink the old backup */
if (rc == 0 && s_res >= 0) {
res = unlink(backup_file);
if (res < 0) {
cl_perror("Could not unlink %s", backup_file);
rc = -1;
}
}
s_res = stat(oldname, &tmp);
/* copy */
if (rc == 0 && s_res >= 0) {
res = link(oldname, backup_file);
if (res < 0) {
cl_perror("Could not create backup %s from %s",
backup_file, oldname);
rc = -2;
}
}
/* unlink the original */
if (rc == 0 && s_res >= 0) {
res = unlink(oldname);
if (res < 0) {
cl_perror("Could not unlink %s", oldname);
rc = -3;
}
}
crm_free(backup_file);
return rc;
}
/*
* This method will free the old CIB pointer on success and the new one
* on failure.
*/
int
activateCibXml(crm_data_t *new_cib, const char *ignored)
{
int error_code = cib_ok;
crm_data_t *saved_cib = the_cib;
const char *ignore_dtd = NULL;
crm_log_xml_debug_4(new_cib, "Attempting to activate CIB");
CRM_ASSERT(new_cib != saved_cib);
if(saved_cib != NULL) {
crm_validate_data(saved_cib);
}
ignore_dtd = crm_element_value(new_cib, "ignore_dtd");
if(
#if CRM_DEPRECATED_SINCE_2_0_4
ignore_dtd != NULL &&
#endif
crm_is_true(ignore_dtd) == FALSE
&& validate_with_dtd(
new_cib, TRUE, HA_LIBDIR"/heartbeat/crm.dtd") == FALSE) {
error_code = cib_dtd_validation;
crm_err("Ignoring invalid CIB");
}
if(error_code == cib_ok && initializeCib(new_cib) == FALSE) {
error_code = cib_ACTIVATION;
crm_err("Ignoring invalid or NULL CIB");
}
if(error_code != cib_ok) {
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");
}
} else if(per_action_cib && cib_writes_enabled && cib_status == cib_ok) {
crm_err("Per-action CIB");
write_cib_contents(the_cib);
} else if(cib_writes_enabled && cib_status == cib_ok) {
crm_debug_2("Triggering CIB write");
G_main_set_trigger(cib_writer);
}
#if CIB_MEM_STATS
/* this chews through a bunch of CPU */
if(the_cib == new_cib) {
long new_bytes, new_allocs, new_frees;
long old_bytes, old_allocs, old_frees;
crm_xml_nbytes(new_cib, &new_bytes, &new_allocs, &new_frees);
crm_xml_nbytes(saved_cib, &old_bytes, &old_allocs, &old_frees);
if(new_bytes != old_bytes) {
crm_info("CIB size is %ld bytes (was %ld)", new_bytes, old_bytes);
crm_adjust_mem_stats(NULL, new_bytes - old_bytes,
new_allocs - old_allocs, new_frees - old_frees);
if(crm_running_stats != NULL) {
crm_adjust_mem_stats(
crm_running_stats, new_bytes - old_bytes,
new_allocs - old_allocs, new_frees - old_frees);
}
}
}
#endif
if(the_cib != saved_cib && the_cib != new_cib) {
CRM_DEV_ASSERT(error_code != cib_ok);
CRM_DEV_ASSERT(the_cib == NULL);
}
if(the_cib != new_cib) {
free_xml(new_cib);
CRM_DEV_ASSERT(error_code != cib_ok);
}
if(the_cib != saved_cib) {
free_xml(saved_cib);
}
return error_code;
}
int
write_cib_contents(gpointer p)
{
int rc = 0;
int exit_rc = LSB_EXIT_OK;
char *digest = NULL;
crm_data_t *cib_status_root = NULL;
const char *digest_filename = CIB_FILENAME ".sig";
/* 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 *updates = crm_element_value(the_cib, XML_ATTR_NUMUPDATES);
const char *admin_epoch = crm_element_value(
the_cib, XML_ATTR_GENERATION_ADMIN);
/* check the admin didnt modify it underneath us */
if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) {
crm_err("%s was manually modified while Heartbeat was active!",
CIB_FILENAME);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
rc = archive_file(CIB_FILENAME, NULL, "last");
if(rc != 0) {
crm_err("Could not make backup of the existing CIB: %d", rc);
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
rc = archive_file(digest_filename, NULL, "last");
if(rc != 0) {
crm_warn("Could not make backup of the existing CIB digest: %d",
rc);
}
/* 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
*
* Although, it does help me once in a while
*
* So delete the status section before we write it out
*/
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);
}
}
rc = write_xml_file(the_cib, CIB_FILENAME, FALSE);
if(rc <= 0) {
crm_err("Changes couldn't be written to disk");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
digest = calculate_xml_digest(the_cib, FALSE);
crm_info("Wrote version %s.%s.%s of the CIB to disk (digest: %s)",
admin_epoch?admin_epoch:"0",
epoch?epoch:"0", updates?updates:"0", digest);
rc = write_cib_digest(the_cib, digest);
if(rc <= 0) {
crm_err("Digest couldn't be written to disk");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
#if 0
if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) {
crm_err("wrote incorrect digest");
exit_rc = LSB_EXIT_GENERIC;
goto cleanup;
}
#endif
cleanup:
crm_free(digest);
if(p == NULL) {
/* fork-and-write mode */
uninitializeCib();
cib_cleanup();
exit(exit_rc);
}
/* stand-alone mode */
return exit_rc;
}
gboolean
set_transition(crm_data_t *xml_obj)
{
const char *current = NULL;
if(xml_obj == NULL) {
return FALSE;
}
current = crm_element_value(xml_obj, XML_ATTR_CCM_TRANSITION);
if(safe_str_neq(current, ccm_transition_id)) {
crm_debug("CCM transition: old=%s, new=%s",
current, ccm_transition_id);
crm_xml_add(xml_obj, XML_ATTR_CCM_TRANSITION,ccm_transition_id);
return TRUE;
}
return FALSE;
}
gboolean
set_connected_peers(crm_data_t *xml_obj)
{
int active = 0;
int current = 0;
char *peers_s = NULL;
const char *current_s = NULL;
if(xml_obj == NULL) {
return FALSE;
}
current_s = crm_element_value(xml_obj, XML_ATTR_NUMPEERS);
g_hash_table_foreach(peer_hash, GHFunc_count_peers, &active);
current = crm_parse_int(current_s, "0");
if(current != active) {
peers_s = crm_itoa(active);
crm_xml_add(xml_obj, XML_ATTR_NUMPEERS, peers_s);
crm_debug("We now have %s active peers", peers_s);
crm_free(peers_s);
return TRUE;
}
return FALSE;
}
gboolean
update_quorum(crm_data_t *xml_obj)
{
const char *quorum_value = XML_BOOLEAN_FALSE;
const char *current = NULL;
if(xml_obj == NULL) {
return FALSE;
}
current = crm_element_value(xml_obj, XML_ATTR_HAVE_QUORUM);
if(cib_have_quorum) {
quorum_value = XML_BOOLEAN_TRUE;
}
if(safe_str_neq(current, quorum_value)) {
crm_debug("CCM quorum: old=%s, new=%s",
current, quorum_value);
crm_xml_add(xml_obj, XML_ATTR_HAVE_QUORUM, quorum_value);
return TRUE;
}
return FALSE;
}
gboolean
update_counters(const char *file, const char *fn, crm_data_t *xml_obj)
{
gboolean did_update = FALSE;
did_update = did_update || update_quorum(xml_obj);
did_update = did_update || set_transition(xml_obj);
did_update = did_update || set_connected_peers(xml_obj);
if(did_update) {
do_crm_log(LOG_DEBUG, "Counters updated by %s", fn);
}
return did_update;
}
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)++;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 23, 3:12 AM (29 m, 28 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1018089
Default Alt Text
(18 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment