diff --git a/crm/cib/Makefile.am b/crm/cib/Makefile.am index 05e05bf718..6adf338551 100644 --- a/crm/cib/Makefile.am +++ b/crm/cib/Makefile.am @@ -1,75 +1,75 @@ # # Copyright (C) 2004 Andrew Beekhof # # 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 # of the License, or (at your option) any later version. # # This program 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl \ -I$(top_builddir)/linux-ha -I$(top_srcdir)/linux-ha \ -I$(top_builddir) -I$(top_srcdir) hadir = $(sysconfdir)/ha.d halibdir = $(libdir)/@HB_PKG@ commmoddir = $(halibdir)/modules/comm havarlibdir = $(localstatedir)/lib/@HB_PKG@ # sockets with path crmdir = $(havarlibdir)/crm apigid = @HA_APIGID@ crmuid = @HA_CCMUID@ crmreqsocket = $(havarlibdir)/api/crm.req crmressocket = $(havarlibdir)/api/crm.rsp COMMONLIBS = $(CRM_DEBUG_LIBS) \ $(top_builddir)/lib/clplumbing/libplumb.la \ $(top_builddir)/lib/pils/libpils.la \ $(top_builddir)/lib/crm/common/libcrmcommon.la \ $(top_builddir)/lib/apphb/libapphb.la \ $(top_builddir)/lib/hbclient/libhbclient.la \ $(top_builddir)/lib/crm/cib/libcib.la \ $(GLIBLIB) \ $(LIBRT) LIBRT = @LIBRT@ AM_CFLAGS = @CFLAGS@ $(CRM_DEBUG_FLAGS) ## binary progs halib_PROGRAMS = cib cibmon ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = cibio.h cibmessages.h cibprimatives.h notify.h \ callbacks.h cib_SOURCES = io.c primatives.c messages.c cib.c notify.c \ callbacks.c main.c cib_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' -cib_LDADD = $(COMMONLIBS) \ +cib_LDADD = $(COMMONLIBS) $(CRYPTOLIB) \ $(top_builddir)/membership/ccm/libccmclient.la cibmon_SOURCES = cibmon.c cibmon_CFLAGS = -DHA_VARLIBDIR='"@HA_VARLIBDIR@"' cibmon_LDADD = $(COMMONLIBS) clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: diff --git a/crm/cib/cibio.h b/crm/cib/cibio.h index 81e6a252e3..a11a7dffe9 100644 --- a/crm/cib/cibio.h +++ b/crm/cib/cibio.h @@ -1,66 +1,63 @@ -/* $Id: cibio.h,v 1.15 2006/02/15 13:19:14 andrew Exp $ */ +/* $Id: cibio.h,v 1.16 2006/03/08 22:24:29 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef CIB_IO__H #define CIB_IO__H #include #include #include #include #include #include #include #include #include #include extern gboolean initialized; extern crm_data_t *the_cib; extern crm_data_t *node_search; extern crm_data_t *resource_search; extern crm_data_t *constraint_search; extern crm_data_t *status_search; extern unsigned int cib_diff_loglevel; extern crm_data_t *get_the_CIB(void); extern int initializeCib(crm_data_t *cib); extern gboolean uninitializeCib(void); extern crm_data_t *createEmptyCib(void); extern gboolean verifyCibXml(crm_data_t *cib); extern crm_data_t *readCibXml(char *buffer); extern crm_data_t *readCibXmlFile(const char *filename); extern int activateCibBuffer(char *buffer, const char *filename); extern int activateCibXml(crm_data_t *doc, const char *filename); -extern int moveFile(const char *oldname, const char *newname, - gboolean backup, char *ext); - extern gboolean update_quorum(crm_data_t *xml_obj); extern gboolean set_transition(crm_data_t *xml_obj); extern gboolean set_connected_peers(crm_data_t *xml_obj); extern gboolean update_counters( const char *file, const char *fn, crm_data_t *xml_obj); /* extern crm_data_t *server_get_cib_copy(void); */ #endif diff --git a/crm/cib/io.c b/crm/cib/io.c index 986f7c3ee3..348621f5de 100644 --- a/crm/cib/io.c +++ b/crm/cib/io.c @@ -1,559 +1,721 @@ -/* $Id: io.c,v 1.48 2006/02/22 13:54:12 andrew Exp $ */ +/* $Id: io.c,v 1.49 2006/03/08 22:24:29 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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; 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); -/* - * It is the callers responsibility to free the output of this function - */ -crm_data_t* -readCibXml(char *buffer) +#include +#include +#include + +#if HAVE_OPENSSL_EVP_H +#include +#endif + +static char * +calculate_cib_digest(crm_data_t *local_cib) { - crm_data_t *root = NULL; - if(buffer != NULL) { - root = string2xml(buffer); +#if HAVE_OPENSSL_EVP_H + EVP_MD_CTX mdctx; + static const EVP_MD *md = NULL; + unsigned char md_value[EVP_MAX_MD_SIZE]; + unsigned int md_len = 0; + int digest_len = 0; + int i; + + char *digest = NULL; + char *buffer = dump_xml_formatted(local_cib); + + CRM_DEV_ASSERT(buffer != NULL && strlen(buffer) > 0); + + if(md == NULL) { + OpenSSL_add_all_digests(); + + md = EVP_get_digestbyname("md5"); + CRM_ASSERT(md != NULL); + } + + EVP_MD_CTX_init(&mdctx); + EVP_DigestInit_ex(&mdctx, md, NULL); + EVP_DigestUpdate(&mdctx, buffer, strlen(buffer)); + EVP_DigestFinal_ex(&mdctx, md_value, &md_len); + EVP_MD_CTX_cleanup(&mdctx); + + digest_len = 2 * md_len + 1; + crm_malloc0(digest, sizeof(char) * digest_len); + for(i = 0; i < md_len; i++) { + sprintf(digest+(2*i), "%02x", md_value[i]); } + crm_debug_2("Digest is: %s\n", digest); + crm_free(buffer); + return digest; +#else + crm_err("Could not create a digest of the on-disk CIB"); + return crm_strdup("Unknown"); +#endif +} - do_id_check(root, NULL); +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; + + if(local_cib != NULL) { + digest = calculate_cib_digest(local_cib); + } - if (verifyCibXml(root) == FALSE) { - free_xml(root); - root = createEmptyCib(); - crm_xml_add(root, XML_ATTR_GENERATION_ADMIN, "0"); - crm_xml_add(root, XML_ATTR_GENERATION, "0"); - crm_xml_add(root, XML_ATTR_NUMUPDATES, "0"); + s_res = stat(CIB_FILENAME ".sig", &buf); + + if (s_res != 0) { + crm_warn("No on-disk digest present"); + return TRUE; } - return root; + + 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, sizeof(char) * (length+1)); + read_len = fread(expected, sizeof(char), length, expected_strm); + CRM_ASSERT(read_len == length); + + 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; } -#include -#include -#include +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) { + digest = calculate_cib_digest(local_cib); + CRM_ASSERT(digest != NULL); + local_digest = 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); + 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 *filename) { int s_res = -1; struct stat buf; + gboolean valid = FALSE; const char *name = NULL; const char *value = NULL; - + crm_data_t *root = NULL; crm_data_t *status = NULL; + + struct passwd *cib_user = NULL; + gboolean user_readwritable = FALSE; if(filename != NULL) { s_res = stat(filename, &buf); } - if (s_res == 0) { - FILE *cib_file = NULL; - struct passwd *cib_user = getpwnam(HA_CCMUSER); - gboolean user_readwritable = ( - cib_user != NULL - && buf.st_uid == cib_user->pw_uid - && (buf.st_mode & (S_IRUSR|S_IWUSR))); - - if( S_ISREG(buf.st_mode) == FALSE ) { - crm_err("%s must be a regular file", filename); - sleep(5); + if (s_res != 0) { + return NULL; + } + + cib_user = getpwnam(HA_CCMUSER); + user_readwritable = (cib_user != NULL + && buf.st_uid == cib_user->pw_uid + && (buf.st_mode & (S_IRUSR|S_IWUSR))); + + if( S_ISREG(buf.st_mode) == FALSE ) { + crm_err("%s must be a regular file", filename); + sleep(5); + exit(100); + + } else if( user_readwritable == FALSE ) { + struct group *cib_grp = getgrnam(HA_APIGROUP); + gboolean group_readwritable = ( + cib_grp != NULL + && buf.st_gid == cib_grp->gr_gid + && (buf.st_mode & (S_IRGRP|S_IWGRP))); + + if( group_readwritable == FALSE ) { + crm_err("%s must be owned and read/writeable by user %s," + " or owned and read/writable by group %s", + filename, HA_CCMUSER, HA_APIGROUP); + cl_flush_logs(); exit(100); - - } else if( user_readwritable == FALSE ) { - struct group *cib_grp = getgrnam(HA_APIGROUP); - gboolean group_readwritable = ( - cib_grp != NULL - && buf.st_gid == cib_grp->gr_gid - && (buf.st_mode & (S_IRGRP|S_IWGRP))); - - if( group_readwritable == FALSE ) { - crm_err("%s must be owned and read/writeable by user %s," - " or owned and read/writable by group %s", - filename, HA_CCMUSER, HA_APIGROUP); - sleep(5); - exit(100); - } - crm_warn("%s should be owned and read/writeable by user %s", - filename, HA_CCMUSER); } - - cib_file = fopen(filename, "r"); - crm_info("Reading cluster configuration from: %s", filename); - root = file2xml(cib_file); - crm_xml_add(root, "generated", XML_BOOLEAN_FALSE); - fclose(cib_file); + crm_warn("%s should be owned and read/writeable by user %s", + filename, HA_CCMUSER); } + + crm_info("Reading cluster configuration from: %s", filename); + valid = validate_on_disk_cib(filename, &root); + crm_log_xml_info(root, "[on-disk]"); - if(root == NULL && s_res == 0) { + if(root == NULL) { crm_crit("Parse ERROR reading %s.", filename); crm_crit("Inhibiting respawn by Heartbeat to avoid loss" " of configuration data."); - sleep(3); /* give the messages a little time to be logged */ + cl_flush_logs(); exit(100); - } else if(root == NULL) { - crm_warn("Cluster configuration not found: %s." - " Creating an empty one.", filename); - return NULL; + } else if(valid == FALSE) { + crm_err("%s has been manually changed" + " - please update the md5 digest in %s.sig", + filename, filename); + cl_flush_logs(); + exit(100); } - crm_log_xml_info(root, "[on-disk]"); + crm_xml_add(root, "generated", XML_BOOLEAN_FALSE); /* strip out the status section if there is one */ status = find_xml_node(root, XML_CIB_TAG_STATUS, TRUE); if(status != NULL) { free_xml_from_parent(root, status); } create_xml_node(root, XML_CIB_TAG_STATUS); /* fill in some defaults */ name = XML_ATTR_GENERATION_ADMIN; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } name = XML_ATTR_GENERATION; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } name = XML_ATTR_NUMUPDATES; value = crm_element_value(root, name); if(value == NULL) { crm_xml_add(root, name, "0"); } do_id_check(root, NULL); if (verifyCibXml(root) == FALSE) { - free_xml(root); - root = NULL; + crm_crit("%s does not contain a vaild configuration.", + filename); + crm_crit("Inhibiting respawn by Heartbeat to avoid loss" + " of configuration data."); + cl_flush_logs(); + exit(100); } 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_err("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_err("Deallocating the CIB."); free_xml(tmp_cib); crm_err("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__, __FUNCTION__, new_cib); the_cib = new_cib; initialized = TRUE; return TRUE; } -int -moveFile(const char *oldname, - const char *newname, - gboolean backup, - char *ext) +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 = stat(newname, &tmp); - - if (s_res >= 0) - { - if (backup == TRUE) { - char backname[1024]; - static const char *back_ext = "bak"; - if (ext != NULL) { back_ext = (char*)ext; } - - snprintf(backname, sizeof(backname)-1, - "%s.%s", newname, back_ext); - moveFile(newname, backname, FALSE, NULL); - } else { - res = unlink(newname); - if (res < 0) { - perror("Could not remove the current backup of Cib"); - return -1; - } + 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 { + crm_malloc0(backup_file, 1024); + if (ext == NULL) { + ext = back_ext; + } + snprintf(backup_file, strlen(backup_file)-1, + "%s.%s", oldname, ext); + } + + s_res = stat(backup_file, &tmp); + + /* unlink the old backup */ + if (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); - if (s_res >= 0) { - res = link(oldname, newname); + /* copy */ + if (rc == 0 && s_res >= 0) { + res = link(oldname, backup_file); if (res < 0) { - perror("Could not create backup of current Cib"); - return -2; + 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) { - perror("Could not unlink the current Cib"); - return -3; + cl_perror("Could not unlink %s", oldname); + rc = -3; } } - - return 0; + + 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 *filename) +activateCibXml(crm_data_t *new_cib, const char *ignored) { int error_code = cib_ok; crm_data_t *saved_cib = get_the_CIB(); - const char *filename_bak = CIB_BACKUP; /* calculate */ 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); } if (initializeCib(new_cib) == FALSE) { - crm_warn("Ignoring invalid or NULL CIB"); + crm_err("Ignoring invalid or NULL CIB"); error_code = -1; - - } else if(cib_writes_enabled) { - int local_rc = moveFile(filename, filename_bak, FALSE, NULL); - if(local_rc != 0) { - crm_err("Could not make backup of the current CIB..." - " disabling writes."); - cib_writes_enabled = FALSE; - } - - if(cib_writes_enabled) { - crm_debug_2("Triggering CIB write"); - G_main_set_trigger(cib_writer); - } - } - - if(error_code != cib_ok && saved_cib != NULL) { - int local_rc = 0; - crm_crit("Reverting to last known CIB (%d)...", error_code); - CRM_DEV_ASSERT(initializeCib(saved_cib)); - if (crm_assert_failed) { - /* oh we are so dead */ - crm_crit("Could not re-initialize with the old CIB."); - local_rc = -3; - } - - if(local_rc == 0 && cib_writes_enabled) { - local_rc = moveFile(filename_bak, filename,FALSE,NULL); - CRM_DEV_ASSERT(local_rc >= 0); - if (crm_assert_failed){ - /* At least if we stay up the config isnt lost - */ - crm_crit("Could not restore the backup of the " - " current Cib... disabling writes"); - cib_writes_enabled = FALSE; + if(saved_cib != NULL) { + crm_warn("Reverting to last known CIB"); + if (initializeCib(saved_cib)) { + /* oh we are so dead */ + crm_crit("Couldn't re-initialize the old CIB!"); + cl_flush_logs(); + exit(1); } + + } else if(error_code != cib_ok) { + crm_crit("Could not write out new CIB and no saved" + " version to revert to"); } - - } else if(error_code != cib_ok) { - crm_crit("Could not write out new CIB and no saved" - " version to revert to"); + + } else if(cib_writes_enabled) { + crm_debug_2("Triggering CIB write"); + G_main_set_trigger(cib_writer); } 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 +write_cib_contents(gpointer p) { int rc = 0; + 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); - - crm_info("Writing version %s.%s.%s of the CIB to disk", - admin_epoch?admin_epoch:"0", - epoch?epoch:"0", updates?updates:"0"); - + + /* 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(LSB_EXIT_GENERIC); + } + + rc = archive_file(CIB_FILENAME, NULL, "last"); + if(rc != 0) { + crm_err("Could not make backup of the existing CIB: %d", rc); + exit(LSB_EXIT_GENERIC); + } + + 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 */ 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); + if(rc <= 0) { + crm_err("Changes couldn't be written to disk"); + exit(LSB_EXIT_GENERIC); + } + + digest = calculate_cib_digest(the_cib); + 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(LSB_EXIT_GENERIC); + } - CRM_DEV_ASSERT(rc != -1 && rc != 0); - if(crm_assert_failed) { - crm_err("Changes activated but couldn't be written to disk"); + if(validate_on_disk_cib(CIB_FILENAME, NULL) == FALSE) { + crm_err("wrote incorrect digest"); exit(LSB_EXIT_GENERIC); } + exit(LSB_EXIT_OK); return HA_OK; } gboolean set_transition(crm_data_t *xml_obj) { const char *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 = 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 = 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, file, fn, "Counters updated"); } 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)++; } } diff --git a/crm/cib/main.c b/crm/cib/main.c index 4d6732fec2..d7e959f928 100644 --- a/crm/cib/main.c +++ b/crm/cib/main.c @@ -1,492 +1,508 @@ -/* $Id: main.c,v 1.39 2006/02/20 11:39:46 andrew Exp $ */ +/* $Id: main.c,v 1.40 2006/03/08 22:24:29 andrew Exp $ */ /* * Copyright (C) 2004 Andrew Beekhof * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include */ #include /* #include */ #include #include #include #include #include #include #include #include #include #include gboolean cib_shutdown_flag = FALSE; unsigned int cib_diff_loglevel = LOG_INFO; extern void oc_ev_special(const oc_ev_t *, oc_ev_class_t , int ); GMainLoop* mainloop = NULL; const char* crm_system_name = CRM_SYSTEM_CIB; char *cib_our_uname = NULL; oc_ev_t *cib_ev_token; gboolean cib_writes_enabled = TRUE; void usage(const char* cmd, int exit_status); int init_start(void); gboolean cib_register_ha(ll_cluster_t *hb_cluster, const char *client_name); gboolean cib_shutdown(int nsig, gpointer unused); void cib_ha_connection_destroy(gpointer user_data); gboolean startCib(const char *filename); extern gboolean cib_msg_timeout(gpointer data); extern int write_cib_contents(gpointer p); ll_cluster_t *hb_conn = NULL; GTRIGSource *cib_writer = NULL; #define OPTARGS "hV" +static void +cib_diskwrite_complete(gpointer userdata, int status, int signo, int exitcode) +{ + if(exitcode != LSB_EXIT_OK || signo != 0 || status != 0) { + crm_err("Disk write failed: status=%d, signo=%d, exitcode=%d", + status, signo, exitcode); + + if(cib_writes_enabled) { + crm_err("Disabling disk writes after write failure"); + cib_writes_enabled = FALSE; + } + + } else { + crm_debug_2("Disk write passed"); + } +} + int main(int argc, char ** argv) { int flag; int argerr = 0; char *param_val = NULL; const char *param_name = NULL; crm_log_init(crm_system_name); G_main_add_SignalHandler( G_PRIORITY_HIGH, SIGTERM, cib_shutdown, NULL, NULL); cib_writer = G_main_add_tempproc_trigger( G_PRIORITY_LOW, write_cib_contents, "write_cib_contents", - NULL, NULL, NULL); + NULL, NULL, NULL, cib_diskwrite_complete); EnableProcLogging(); set_sigchld_proctrack(G_PRIORITY_HIGH); client_list = g_hash_table_new(g_str_hash, g_str_equal); peer_hash = g_hash_table_new(g_str_hash, g_str_equal); while ((flag = getopt(argc, argv, OPTARGS)) != EOF) { switch(flag) { case 'V': cl_log_enable_stderr(1); alter_debug(DEBUG_INC); break; case 'h': /* Help message */ usage(crm_system_name, LSB_EXIT_OK); break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { usage(crm_system_name,LSB_EXIT_GENERIC); } /* apparently we're not allowed to free the result of getenv */ param_name = ENV_PREFIX "" KEY_CONFIG_WRITES_ENABLED; param_val = getenv(param_name); crm_debug("%s = %s", param_name, param_val); if(param_val != NULL) { cl_str_to_boolean(param_val, &cib_writes_enabled); param_val = NULL; } crm_info("Disk writes to %s: %s", CIB_FILENAME, cib_writes_enabled?"enabled":"DISABLED"); param_name = ENV_PREFIX "" KEY_LOG_CONFIG_CHANGES; param_val = getenv(param_name); crm_debug("%s = %s", param_name, param_val); cib_diff_loglevel = crm_log_level; if(param_val != NULL) { int do_log = 0; cl_str_to_boolean(param_val, &do_log); if(do_log == FALSE) { cib_diff_loglevel = crm_log_level + 1; } param_val = NULL; } crm_info("Configuration changes logged at: debug %d", cib_diff_loglevel - LOG_INFO); /* read local config file */ return init_start(); } unsigned long cib_num_ops = 0; const char *cib_stat_interval = "10min"; unsigned long cib_num_local = 0, cib_num_updates = 0, cib_num_fail = 0; unsigned long cib_bad_connects = 0, cib_num_timeouts = 0; longclock_t cib_call_time = 0; gboolean cib_stats(gpointer data); gboolean cib_stats(gpointer data) { int local_log_level = LOG_DEBUG; static unsigned long last_stat = 0; unsigned int cib_calls_ms = 0; static unsigned long cib_stat_interval_ms = 0; if(cib_stat_interval_ms == 0) { cib_stat_interval_ms = crm_get_msec(cib_stat_interval); } cib_calls_ms = longclockto_ms(cib_call_time); if((cib_num_ops - last_stat) > 0) { unsigned long calls_diff = cib_num_ops - last_stat; double stat_1 = (1000*cib_calls_ms)/calls_diff; local_log_level = LOG_INFO; crm_log_maybe(local_log_level, "Processed %lu operations" " (%.2fus average, %lu%% utilization) in the last %s", calls_diff, stat_1, (100*cib_calls_ms)/cib_stat_interval_ms, cib_stat_interval); } crm_log_maybe(local_log_level+1, "\tDetail: %lu operations (%ums total)" " (%lu local, %lu updates, %lu failures," " %lu timeouts, %lu bad connects)", cib_num_ops, cib_calls_ms, cib_num_local, cib_num_updates, cib_num_fail, cib_bad_connects, cib_num_timeouts); last_stat = cib_num_ops; cib_call_time = 0; return TRUE; } int init_start(void) { gboolean was_error = FALSE; hb_conn = ll_cluster_new("heartbeat"); if(cib_register_ha(hb_conn, CRM_SYSTEM_CIB) == FALSE) { crm_crit("Cannot sign in to heartbeat... terminating"); fprintf(stderr, "Cannot sign in to heartbeat... terminating"); exit(1); } if(startCib(CIB_FILENAME) == FALSE){ crm_crit("Cannot start CIB... terminating"); exit(1); } was_error = init_server_ipc_comms( crm_strdup(cib_channel_callback), cib_client_connect_null, default_ipc_connection_destroy); was_error = was_error || init_server_ipc_comms( crm_strdup(cib_channel_ro), cib_client_connect_rw_ro, default_ipc_connection_destroy); was_error = was_error || init_server_ipc_comms( crm_strdup(cib_channel_rw), cib_client_connect_rw_ro, default_ipc_connection_destroy); was_error = was_error || init_server_ipc_comms( crm_strdup(cib_channel_rw_synchronous), cib_client_connect_rw_synch, default_ipc_connection_destroy); was_error = was_error || init_server_ipc_comms( crm_strdup(cib_channel_ro_synchronous), cib_client_connect_ro_synch, default_ipc_connection_destroy); if(was_error == FALSE) { crm_debug_3("Be informed of CRM Client Status changes"); if (HA_OK != hb_conn->llc_ops->set_cstatus_callback( hb_conn, cib_client_status_callback, hb_conn)) { crm_err("Cannot set cstatus callback: %s", hb_conn->llc_ops->errmsg(hb_conn)); was_error = TRUE; } else { crm_debug_3("Client Status callback set"); } } if(was_error == FALSE) { gboolean did_fail = TRUE; int num_ccm_fails = 0; int max_ccm_fails = 30; int ret; int cib_ev_fd; while(did_fail && was_error == FALSE) { did_fail = FALSE; crm_debug_3("Registering with CCM"); ret = oc_ev_register(&cib_ev_token); if (ret != 0) { crm_warn("CCM registration failed"); did_fail = TRUE; } if(did_fail == FALSE) { crm_debug_3("Setting up CCM callbacks"); ret = oc_ev_set_callback( cib_ev_token, OC_EV_MEMB_CLASS, cib_ccm_msg_callback, NULL); if (ret != 0) { crm_warn("CCM callback not set"); did_fail = TRUE; } } if(did_fail == FALSE) { oc_ev_special(cib_ev_token, OC_EV_MEMB_CLASS, 0); crm_debug_3("Activating CCM token"); ret = oc_ev_activate(cib_ev_token, &cib_ev_fd); if (ret != 0){ crm_warn("CCM Activation failed"); did_fail = TRUE; } } if(did_fail) { num_ccm_fails++; oc_ev_unregister(cib_ev_token); if(num_ccm_fails < max_ccm_fails){ crm_warn("CCM Connection failed" " %d times (%d max)", num_ccm_fails, max_ccm_fails); sleep(1); } else { crm_err("CCM Activation failed" " %d (max) times", num_ccm_fails); was_error = TRUE; } } } crm_debug_3("CCM Activation passed... all set to go!"); G_main_add_fd(G_PRIORITY_HIGH, cib_ev_fd, FALSE, cib_ccm_dispatch, cib_ev_token, default_ipc_connection_destroy); } if(was_error == FALSE) { /* Async get client status information in the cluster */ crm_debug_3("Requesting an initial dump of CIB client_status"); hb_conn->llc_ops->client_status( hb_conn, NULL, CRM_SYSTEM_CIB, -1); /* Create the mainloop and run it... */ mainloop = g_main_new(FALSE); crm_info("Starting %s mainloop", crm_system_name); Gmain_timeout_add(crm_get_msec("10s"), cib_msg_timeout, NULL); Gmain_timeout_add( crm_get_msec(cib_stat_interval), cib_stats, NULL); g_main_run(mainloop); return_to_orig_privs(); } else { crm_err("Couldnt start all communication channels, exiting."); } return 0; } void usage(const char* cmd, int exit_status) { FILE* stream; stream = exit_status ? stderr : stdout; fprintf(stream, "usage: %s [-srkh]" "[-c configure file]\n", cmd); /* fprintf(stream, "\t-d\tsets debug level\n"); */ /* fprintf(stream, "\t-s\tgets daemon status\n"); */ /* fprintf(stream, "\t-r\trestarts daemon\n"); */ /* fprintf(stream, "\t-k\tstops daemon\n"); */ /* fprintf(stream, "\t-h\thelp message\n"); */ fflush(stream); exit(exit_status); } gboolean cib_register_ha(ll_cluster_t *hb_cluster, const char *client_name) { const char *uname = NULL; crm_info("Signing in with Heartbeat"); if (hb_cluster->llc_ops->signon(hb_cluster, client_name)!= HA_OK) { crm_err("Cannot sign on with heartbeat: %s", hb_cluster->llc_ops->errmsg(hb_cluster)); return FALSE; } crm_set_ha_options(hb_cluster); crm_debug_3("Be informed of CIB messages"); if (HA_OK != hb_cluster->llc_ops->set_msg_callback( hb_cluster, T_CIB, cib_peer_callback, hb_cluster)){ crm_err("Cannot set msg callback: %s", hb_cluster->llc_ops->errmsg(hb_cluster)); return FALSE; } crm_debug_3("Finding our node name"); if ((uname = hb_cluster->llc_ops->get_mynodeid(hb_cluster)) == NULL) { crm_err("get_mynodeid() failed"); return FALSE; } cib_our_uname = crm_strdup(uname); crm_info("FSA Hostname: %s", cib_our_uname); crm_debug_3("Adding channel to mainloop"); G_main_add_IPC_Channel( G_PRIORITY_DEFAULT, hb_cluster->llc_ops->ipcchan(hb_cluster), FALSE, cib_ha_dispatch, hb_cluster /* userdata */, cib_ha_connection_destroy); return TRUE; - } void cib_ha_connection_destroy(gpointer user_data) { if(cib_shutdown_flag) { crm_info("Heartbeat disconnection complete... exiting"); } else { crm_err("Heartbeat connection lost! Exiting."); } if (mainloop != NULL && g_main_is_running(mainloop)) { g_main_quit(mainloop); } else { exit(LSB_EXIT_OK); } } static void disconnect_cib_client(gpointer key, gpointer value, gpointer user_data) { cib_client_t *a_client = value; crm_debug_2("Processing client %s/%s... send=%d, recv=%d", a_client->name, a_client->channel_name, (int)a_client->channel->send_queue->current_qlen, (int)a_client->channel->recv_queue->current_qlen); if(a_client->channel->ch_status == IPC_CONNECT) { a_client->channel->ops->resume_io(a_client->channel); if(a_client->channel->send_queue->current_qlen != 0 || a_client->channel->recv_queue->current_qlen != 0) { crm_info("Flushed messages to/from %s/%s... send=%d, recv=%d", a_client->name, a_client->channel_name, (int)a_client->channel->send_queue->current_qlen, (int)a_client->channel->recv_queue->current_qlen); } } if(a_client->channel->ch_status == IPC_CONNECT) { crm_warn("Disconnecting %s/%s...", a_client->name, a_client->channel_name); a_client->channel->ops->disconnect(a_client->channel); } } extern gboolean cib_process_disconnect( IPC_Channel *channel, cib_client_t *cib_client); gboolean cib_shutdown(int nsig, gpointer unused) { if(cib_shutdown_flag == FALSE) { cib_shutdown_flag = TRUE; crm_debug("Disconnecting %d clients", g_hash_table_size(client_list)); g_hash_table_foreach(client_list, disconnect_cib_client, NULL); crm_info("Disconnected %d clients", g_hash_table_size(client_list)); cib_process_disconnect(NULL, NULL); } else { crm_info("Waiting for %d clients to disconnect...", g_hash_table_size(client_list)); } return TRUE; } gboolean startCib(const char *filename) { crm_data_t *cib = readCibXmlFile(filename); if(cib == NULL) { - crm_warn("CIB Initialization failed, " - "starting with an empty default."); + crm_warn("Cluster configuration not found: %s." + " Creating an empty one.", filename); cib = createEmptyCib(); crm_xml_add(cib, XML_ATTR_GENERATION_ADMIN, "0"); crm_xml_add(cib, XML_ATTR_GENERATION, "0"); crm_xml_add(cib, XML_ATTR_NUMUPDATES, "0"); } if(activateCibXml(cib, filename) != 0) { return FALSE; } crm_info("CIB Initialization completed successfully"); return TRUE; }