summaryrefslogtreecommitdiffstats
path: root/lib/common
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/common/Makefile.am33
-rw-r--r--lib/common/acl.c41
-rw-r--r--lib/common/actions.c (renamed from lib/common/operations.c)28
-rw-r--r--lib/common/alerts.c87
-rw-r--r--lib/common/cib.c23
-rw-r--r--lib/common/crmcommon_private.h63
-rw-r--r--lib/common/digest.c4
-rw-r--r--lib/common/io.c8
-rw-r--r--lib/common/ipc_attrd.c37
-rw-r--r--lib/common/ipc_client.c461
-rw-r--r--lib/common/ipc_common.c2
-rw-r--r--lib/common/ipc_controld.c61
-rw-r--r--lib/common/ipc_pacemakerd.c4
-rw-r--r--lib/common/ipc_schedulerd.c4
-rw-r--r--lib/common/ipc_server.c48
-rw-r--r--lib/common/iso8601.c3
-rw-r--r--lib/common/logging.c151
-rw-r--r--lib/common/mainloop.c42
-rw-r--r--lib/common/mock.c26
-rw-r--r--lib/common/mock_private.h6
-rw-r--r--lib/common/nvpair.c92
-rw-r--r--lib/common/options.c19
-rw-r--r--lib/common/output_html.c4
-rw-r--r--lib/common/output_log.c130
-rw-r--r--lib/common/output_xml.c20
-rw-r--r--lib/common/patchset.c121
-rw-r--r--lib/common/patchset_display.c26
-rw-r--r--lib/common/remote.c39
-rw-r--r--lib/common/results.c133
-rw-r--r--lib/common/scheduler.c14
-rw-r--r--lib/common/schemas.c149
-rw-r--r--lib/common/strings.c16
-rw-r--r--lib/common/tests/Makefile.am4
-rw-r--r--lib/common/tests/acl/Makefile.am11
-rw-r--r--lib/common/tests/actions/Makefile.am (renamed from lib/common/tests/operations/Makefile.am)16
-rw-r--r--lib/common/tests/actions/copy_in_properties_test.c (renamed from lib/common/tests/operations/copy_in_properties_test.c)0
-rw-r--r--lib/common/tests/actions/expand_plus_plus_test.c (renamed from lib/common/tests/operations/expand_plus_plus_test.c)0
-rw-r--r--lib/common/tests/actions/fix_plus_plus_recursive_test.c (renamed from lib/common/tests/operations/fix_plus_plus_recursive_test.c)0
-rw-r--r--lib/common/tests/actions/parse_op_key_test.c (renamed from lib/common/tests/operations/parse_op_key_test.c)0
-rw-r--r--lib/common/tests/actions/pcmk_is_probe_test.c (renamed from lib/common/tests/operations/pcmk_is_probe_test.c)0
-rw-r--r--lib/common/tests/actions/pcmk_xe_is_probe_test.c (renamed from lib/common/tests/operations/pcmk_xe_is_probe_test.c)0
-rw-r--r--lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c (renamed from lib/common/tests/operations/pcmk_xe_mask_probe_failure_test.c)0
-rw-r--r--lib/common/tests/agents/Makefile.am12
-rw-r--r--lib/common/tests/agents/crm_parse_agent_spec_test.c18
-rw-r--r--lib/common/tests/cmdline/Makefile.am5
-rw-r--r--lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c13
-rw-r--r--lib/common/tests/cmdline/pcmk__new_common_args_test.c62
-rw-r--r--lib/common/tests/flags/Makefile.am11
-rw-r--r--lib/common/tests/io/Makefile.am7
-rw-r--r--lib/common/tests/lists/Makefile.am9
-rw-r--r--lib/common/tests/nvpair/Makefile.am8
-rw-r--r--lib/common/tests/options/Makefile.am9
-rw-r--r--lib/common/tests/options/pcmk__set_env_option_test.c57
-rw-r--r--lib/common/tests/output/Makefile.am20
-rw-r--r--lib/common/tests/output/pcmk__output_new_test.c8
-rw-r--r--lib/common/tests/results/Makefile.am4
-rw-r--r--lib/common/tests/results/pcmk__results_test.c8
-rw-r--r--lib/common/tests/scores/Makefile.am9
-rw-r--r--lib/common/tests/scores/pcmk__add_scores_test.c4
-rw-r--r--lib/common/tests/strings/Makefile.am54
-rw-r--r--lib/common/tests/strings/pcmk__compress_test.c2
-rw-r--r--lib/common/tests/strings/pcmk__guint_from_hash_test.c4
-rw-r--r--lib/common/tests/strings/pcmk__scan_ll_test.c64
-rw-r--r--lib/common/tests/utils/Makefile.am22
-rw-r--r--lib/common/tests/utils/pcmk__fail_attr_name_test.c36
-rw-r--r--lib/common/tests/utils/pcmk__failcount_name_test.c35
-rw-r--r--lib/common/tests/utils/pcmk__lastfailure_name_test.c35
-rw-r--r--lib/common/tests/xml/Makefile.am6
-rw-r--r--lib/common/tests/xml/pcmk__xe_foreach_child_test.c13
-rw-r--r--lib/common/tests/xpath/Makefile.am4
-rw-r--r--lib/common/watchdog.c13
-rw-r--r--lib/common/xml.c527
-rw-r--r--lib/common/xml_attr.c84
-rw-r--r--lib/common/xml_display.c18
-rw-r--r--lib/common/xpath.c13
75 files changed, 1858 insertions, 1262 deletions
diff --git a/lib/common/Makefile.am b/lib/common/Makefile.am
index ef729d4..f9c43b9 100644
--- a/lib/common/Makefile.am
+++ b/lib/common/Makefile.am
@@ -8,7 +8,8 @@
#
include $(top_srcdir)/mk/common.mk
-AM_CPPFLAGS += -I$(top_builddir)/lib/gnu -I$(top_srcdir)/lib/gnu
+AM_CPPFLAGS += -I$(top_builddir)/lib/gnu \
+ -I$(top_srcdir)/lib/gnu
## libraries
lib_LTLIBRARIES = libcrmcommon.la
@@ -29,14 +30,16 @@ CFLAGS = $(CFLAGS_COPY:-Wcast-qual=) -fPIC
# changes the order so the subdirectories are processed afterwards.
SUBDIRS = . tests
-noinst_HEADERS = crmcommon_private.h mock_private.h
+noinst_HEADERS = crmcommon_private.h \
+ mock_private.h
-libcrmcommon_la_LDFLAGS = -version-info 45:0:11
+libcrmcommon_la_LDFLAGS = -version-info 46:0:12
libcrmcommon_la_CFLAGS = $(CFLAGS_HARDENED_LIB)
libcrmcommon_la_LDFLAGS += $(LDFLAGS_HARDENED_LIB)
-libcrmcommon_la_LIBADD = @LIBADD_DL@ $(top_builddir)/lib/gnu/libgnu.la
+libcrmcommon_la_LIBADD = @LIBADD_DL@ \
+ $(top_builddir)/lib/gnu/libgnu.la
# If configured with --with-profiling or --with-coverage, BUILD_PROFILING will
# be set and -fno-builtin will be added to the CFLAGS. However, libcrmcommon
@@ -47,9 +50,10 @@ if BUILD_PROFILING
libcrmcommon_la_LIBADD += -lm
endif
-# Use += rather than backlashed continuation lines for parsing by bumplibs
+## Library sources (*must* use += format for bumplibs)
libcrmcommon_la_SOURCES =
libcrmcommon_la_SOURCES += acl.c
+libcrmcommon_la_SOURCES += actions.c
libcrmcommon_la_SOURCES += agents.c
libcrmcommon_la_SOURCES += alerts.c
libcrmcommon_la_SOURCES += attrs.c
@@ -75,7 +79,6 @@ libcrmcommon_la_SOURCES += mainloop.c
libcrmcommon_la_SOURCES += messages.c
libcrmcommon_la_SOURCES += nodes.c
libcrmcommon_la_SOURCES += nvpair.c
-libcrmcommon_la_SOURCES += operations.c
libcrmcommon_la_SOURCES += options.c
libcrmcommon_la_SOURCES += output.c
libcrmcommon_la_SOURCES += output_html.c
@@ -89,12 +92,14 @@ libcrmcommon_la_SOURCES += pid.c
libcrmcommon_la_SOURCES += procfs.c
libcrmcommon_la_SOURCES += remote.c
libcrmcommon_la_SOURCES += results.c
+libcrmcommon_la_SOURCES += scheduler.c
libcrmcommon_la_SOURCES += schemas.c
libcrmcommon_la_SOURCES += scores.c
libcrmcommon_la_SOURCES += strings.c
libcrmcommon_la_SOURCES += utils.c
libcrmcommon_la_SOURCES += watchdog.c
libcrmcommon_la_SOURCES += xml.c
+libcrmcommon_la_SOURCES += xml_attr.c
libcrmcommon_la_SOURCES += xml_display.c
libcrmcommon_la_SOURCES += xpath.c
@@ -107,18 +112,22 @@ include $(top_srcdir)/mk/tap.mk
libcrmcommon_test_la_SOURCES = $(libcrmcommon_la_SOURCES)
libcrmcommon_test_la_SOURCES += mock.c
-libcrmcommon_test_la_LDFLAGS = $(libcrmcommon_la_LDFLAGS) -rpath $(libdir) $(LDFLAGS_WRAP)
+libcrmcommon_test_la_LDFLAGS = $(libcrmcommon_la_LDFLAGS) \
+ -rpath $(libdir) \
+ $(LDFLAGS_WRAP)
# If GCC emits a builtin function in place of something we've mocked up, that will
# get used instead of the mocked version which leads to unexpected test results. So
# disable all builtins. Older versions of GCC (at least, on RHEL7) will still emit
# replacement code for strdup (and possibly other functions) unless -fno-inline is
# also added.
-libcrmcommon_test_la_CFLAGS = $(libcrmcommon_la_CFLAGS) -DPCMK__UNIT_TESTING -fno-builtin -fno-inline
+libcrmcommon_test_la_CFLAGS = $(libcrmcommon_la_CFLAGS) \
+ -DPCMK__UNIT_TESTING \
+ -fno-builtin \
+ -fno-inline
# If -fno-builtin is used, -lm also needs to be added. See the comment at
# BUILD_PROFILING above.
-libcrmcommon_test_la_LIBADD = $(libcrmcommon_la_LIBADD) -lcmocka -lm
+libcrmcommon_test_la_LIBADD = $(libcrmcommon_la_LIBADD) \
+ -lcmocka \
+ -lm
nodist_libcrmcommon_test_la_SOURCES = $(nodist_libcrmcommon_la_SOURCES)
-
-clean-generic:
- rm -f *.log *.debug *.xml *~
diff --git a/lib/common/acl.c b/lib/common/acl.c
index 33a4e00..1ebd765 100644
--- a/lib/common/acl.c
+++ b/lib/common/acl.c
@@ -26,7 +26,7 @@
typedef struct xml_acl_s {
enum xml_private_flags mode;
- char *xpath;
+ gchar *xpath;
} xml_acl_t;
static void
@@ -35,7 +35,7 @@ free_acl(void *data)
if (data) {
xml_acl_t *acl = data;
- free(acl->xpath);
+ g_free(acl->xpath);
free(acl);
}
}
@@ -68,7 +68,7 @@ create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode)
if ((tag == NULL) && (ref == NULL) && (xpath == NULL)) {
// Schema should prevent this, but to be safe ...
crm_trace("Ignoring ACL <%s> element without selection criteria",
- crm_element_name(xml));
+ xml->name);
return NULL;
}
@@ -77,10 +77,9 @@ create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode)
acl->mode = mode;
if (xpath) {
- acl->xpath = strdup(xpath);
- CRM_ASSERT(acl->xpath != NULL);
+ acl->xpath = g_strdup(xpath);
crm_trace("Unpacked ACL <%s> element using xpath: %s",
- crm_element_name(xml), acl->xpath);
+ xml->name, acl->xpath);
} else {
GString *buf = g_string_sized_new(128);
@@ -101,12 +100,11 @@ create_acl(const xmlNode *xml, GList *acls, enum xml_private_flags mode)
pcmk__g_strcat(buf, "//", pcmk__s(tag, "*"), NULL);
}
- acl->xpath = strdup((const char *) buf->str);
- CRM_ASSERT(acl->xpath != NULL);
+ acl->xpath = buf->str;
- g_string_free(buf, TRUE);
+ g_string_free(buf, FALSE);
crm_trace("Unpacked ACL <%s> element as xpath: %s",
- crm_element_name(xml), acl->xpath);
+ xml->name, acl->xpath);
}
return g_list_append(acls, acl);
@@ -131,10 +129,10 @@ parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls)
for (child = pcmk__xe_first_child(acl_entry); child;
child = pcmk__xe_next(child)) {
- const char *tag = crm_element_name(child);
+ const char *tag = (const char *) child->name;
const char *kind = crm_element_value(child, XML_ACL_ATTR_KIND);
- if (strcmp(XML_ACL_TAG_PERMISSION, tag) == 0){
+ if (pcmk__xe_is(child, XML_ACL_TAG_PERMISSION)) {
CRM_ASSERT(kind != NULL);
crm_trace("Unpacking ACL <%s> element of kind '%s'", tag, kind);
tag = kind;
@@ -157,7 +155,7 @@ parse_acl_entry(const xmlNode *acl_top, const xmlNode *acl_entry, GList *acls)
if (role_id && strcmp(ref_role, role_id) == 0) {
crm_trace("Unpacking referenced role '%s' in ACL <%s> element",
- role_id, crm_element_name(acl_entry));
+ role_id, acl_entry->name);
acls = parse_acl_entry(acl_top, role, acls);
break;
}
@@ -304,10 +302,9 @@ pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
for (child = pcmk__xe_first_child(acls); child;
child = pcmk__xe_next(child)) {
- const char *tag = crm_element_name(child);
- if (!strcmp(tag, XML_ACL_TAG_USER)
- || !strcmp(tag, XML_ACL_TAG_USERv1)) {
+ if (pcmk__xe_is(child, XML_ACL_TAG_USER)
+ || pcmk__xe_is(child, XML_ACL_TAG_USERv1)) {
const char *id = crm_element_value(child, XML_ATTR_NAME);
if (id == NULL) {
@@ -318,7 +315,7 @@ pcmk__unpack_acl(xmlNode *source, xmlNode *target, const char *user)
crm_debug("Unpacking ACLs for user '%s'", id);
docpriv->acls = parse_acl_entry(acls, child, docpriv->acls);
}
- } else if (!strcmp(tag, XML_ACL_TAG_GROUP)) {
+ } else if (pcmk__xe_is(child, XML_ACL_TAG_GROUP)) {
const char *id = crm_element_value(child, XML_ATTR_NAME);
if (id == NULL) {
@@ -392,7 +389,7 @@ purge_xml_attributes(xmlNode *xml)
if (test_acl_mode(nodepriv->flags, pcmk__xf_acl_read)) {
crm_trace("%s[@" XML_ATTR_ID "=%s] is readable",
- crm_element_name(xml), ID(xml));
+ xml->name, ID(xml));
return true;
}
@@ -571,22 +568,22 @@ pcmk__apply_creation_acl(xmlNode *xml, bool check_top)
if (implicitly_allowed(xml)) {
crm_trace("Creation of <%s> scaffolding with id=\"%s\""
" is implicitly allowed",
- crm_element_name(xml), display_id(xml));
+ xml->name, display_id(xml));
} else if (pcmk__check_acl(xml, NULL, pcmk__xf_acl_write)) {
crm_trace("ACLs allow creation of <%s> with id=\"%s\"",
- crm_element_name(xml), display_id(xml));
+ xml->name, display_id(xml));
} else if (check_top) {
crm_trace("ACLs disallow creation of <%s> with id=\"%s\"",
- crm_element_name(xml), display_id(xml));
+ xml->name, display_id(xml));
pcmk_free_xml_subtree(xml);
return;
} else {
crm_notice("ACLs would disallow creation of %s<%s> with id=\"%s\"",
((xml == xmlDocGetRootElement(xml->doc))? "root element " : ""),
- crm_element_name(xml), display_id(xml));
+ xml->name, display_id(xml));
}
}
diff --git a/lib/common/operations.c b/lib/common/actions.c
index 3db96cd..e710615 100644
--- a/lib/common/operations.c
+++ b/lib/common/actions.c
@@ -107,15 +107,15 @@ parse_op_key(const char *key, char **rsc_id, char **op_type, guint *interval_ms)
* contain underbars. Here, list action names and name prefixes that can.
*/
const char *actions_with_underbars[] = {
- CRMD_ACTION_MIGRATED,
- CRMD_ACTION_MIGRATE,
+ PCMK_ACTION_MIGRATE_FROM,
+ PCMK_ACTION_MIGRATE_TO,
NULL
};
const char *action_prefixes_with_underbars[] = {
- "pre_" CRMD_ACTION_NOTIFY,
- "post_" CRMD_ACTION_NOTIFY,
- "confirmed-pre_" CRMD_ACTION_NOTIFY,
- "confirmed-post_" CRMD_ACTION_NOTIFY,
+ "pre_" PCMK_ACTION_NOTIFY,
+ "post_" PCMK_ACTION_NOTIFY,
+ "confirmed-pre_" PCMK_ACTION_NOTIFY,
+ "confirmed-post_" PCMK_ACTION_NOTIFY,
NULL,
};
@@ -470,11 +470,11 @@ crm_op_needs_metadata(const char *rsc_class, const char *op)
}
// Metadata is needed only for these actions
- return pcmk__str_any_of(op, CRMD_ACTION_START, CRMD_ACTION_STATUS,
- CRMD_ACTION_PROMOTE, CRMD_ACTION_DEMOTE,
- CRMD_ACTION_RELOAD, CRMD_ACTION_RELOAD_AGENT,
- CRMD_ACTION_MIGRATE, CRMD_ACTION_MIGRATED,
- CRMD_ACTION_NOTIFY, NULL);
+ return pcmk__str_any_of(op, PCMK_ACTION_START, PCMK_ACTION_MONITOR,
+ PCMK_ACTION_PROMOTE, PCMK_ACTION_DEMOTE,
+ PCMK_ACTION_RELOAD, PCMK_ACTION_RELOAD_AGENT,
+ PCMK_ACTION_MIGRATE_TO, PCMK_ACTION_MIGRATE_FROM,
+ PCMK_ACTION_NOTIFY, NULL);
}
/*!
@@ -488,7 +488,8 @@ crm_op_needs_metadata(const char *rsc_class, const char *op)
bool
pcmk__is_fencing_action(const char *action)
{
- return pcmk__str_any_of(action, "off", "reboot", "poweroff", NULL);
+ return pcmk__str_any_of(action, PCMK_ACTION_OFF, PCMK_ACTION_REBOOT,
+ "poweroff", NULL);
}
bool
@@ -498,7 +499,8 @@ pcmk_is_probe(const char *task, guint interval)
return false;
}
- return (interval == 0) && pcmk__str_eq(task, CRMD_ACTION_STATUS, pcmk__str_none);
+ return (interval == 0)
+ && pcmk__str_eq(task, PCMK_ACTION_MONITOR, pcmk__str_none);
}
bool
diff --git a/lib/common/alerts.c b/lib/common/alerts.c
index abdadef..98b1e3f 100644
--- a/lib/common/alerts.c
+++ b/lib/common/alerts.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2022 the Pacemaker project contributors
+ * Copyright 2015-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -12,8 +12,8 @@
#include <crm/lrmd.h>
#include <crm/msg_xml.h>
#include <crm/common/alerts_internal.h>
+#include <crm/common/cib_internal.h>
#include <crm/common/xml_internal.h>
-#include <crm/cib/internal.h> /* for F_CIB_UPDATE_RESULT */
/*
* to allow script compatibility we can have more than one
@@ -168,86 +168,3 @@ pcmk__add_alert_key_int(GHashTable *table, enum pcmk__alert_keys_e name,
g_hash_table_insert(table, strdup(*key), pcmk__itoa(value));
}
}
-
-#define XPATH_PATCHSET1_DIFF "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED
-
-#define XPATH_PATCHSET1_CRMCONFIG XPATH_PATCHSET1_DIFF "//" XML_CIB_TAG_CRMCONFIG
-#define XPATH_PATCHSET1_ALERTS XPATH_PATCHSET1_DIFF "//" XML_CIB_TAG_ALERTS
-
-#define XPATH_PATCHSET1_EITHER \
- XPATH_PATCHSET1_CRMCONFIG " | " XPATH_PATCHSET1_ALERTS
-
-#define XPATH_CONFIG "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION
-
-#define XPATH_CRMCONFIG XPATH_CONFIG "/" XML_CIB_TAG_CRMCONFIG "/"
-#define XPATH_ALERTS XPATH_CONFIG "/" XML_CIB_TAG_ALERTS
-
-/*!
- * \internal
- * \brief Check whether a CIB update affects alerts
- *
- * \param[in] msg XML containing CIB update
- * \param[in] config Whether to check for crmconfig change as well
- *
- * \return TRUE if update affects alerts, FALSE otherwise
- */
-bool
-pcmk__alert_in_patchset(xmlNode *msg, bool config)
-{
- int rc = -1;
- int format= 1;
- xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
- xmlNode *change = NULL;
- xmlXPathObject *xpathObj = NULL;
-
- CRM_CHECK(msg != NULL, return FALSE);
-
- crm_element_value_int(msg, F_CIB_RC, &rc);
- if (rc < pcmk_ok) {
- crm_trace("Ignore failed CIB update: %s (%d)", pcmk_strerror(rc), rc);
- return FALSE;
- }
-
- crm_element_value_int(patchset, "format", &format);
- if (format == 1) {
- const char *diff = (config? XPATH_PATCHSET1_EITHER : XPATH_PATCHSET1_ALERTS);
-
- if ((xpathObj = xpath_search(msg, diff)) != NULL) {
- freeXpathObject(xpathObj);
- return TRUE;
- }
- } else if (format == 2) {
- for (change = pcmk__xml_first_child(patchset); change != NULL;
- change = pcmk__xml_next(change)) {
- const char *xpath = crm_element_value(change, XML_DIFF_PATH);
-
- if (xpath == NULL) {
- continue;
- }
-
- if ((!config || !strstr(xpath, XPATH_CRMCONFIG))
- && !strstr(xpath, XPATH_ALERTS)) {
-
- /* this is not a change to an existing section ... */
-
- xmlNode *section = NULL;
- const char *name = NULL;
-
- if ((strcmp(xpath, XPATH_CONFIG) != 0) ||
- ((section = pcmk__xml_first_child(change)) == NULL) ||
- ((name = crm_element_name(section)) == NULL) ||
- (strcmp(name, XML_CIB_TAG_ALERTS) != 0)) {
-
- /* ... nor is it a newly added alerts section */
- continue;
- }
- }
-
- return TRUE;
- }
-
- } else {
- crm_warn("Unknown patch format: %d", format);
- }
- return FALSE;
-}
diff --git a/lib/common/cib.c b/lib/common/cib.c
index b84c5e8..fee7881 100644
--- a/lib/common/cib.c
+++ b/lib/common/cib.c
@@ -1,6 +1,6 @@
/*
* Original copyright 2004 International Business Machines
- * Later changes copyright 2008-2021 the Pacemaker project contributors
+ * Later changes copyright 2008-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,6 +14,8 @@
#include <libxml/tree.h> // xmlNode
#include <crm/msg_xml.h>
+#include <crm/common/cib.h>
+#include <crm/common/cib_internal.h>
/*
* Functions to help find particular sections of the CIB
@@ -99,7 +101,7 @@ static struct {
};
/*!
- * \brief Get the XPath needed to find a specified CIB element name
+ * \brief Get the relative XPath needed to find a specified CIB element name
*
* \param[in] element_name Name of CIB element
*
@@ -120,6 +122,23 @@ pcmk_cib_xpath_for(const char *element_name)
}
/*!
+ * \internal
+ * \brief Get the absolute XPath needed to find a specified CIB element name
+ *
+ * \param[in] element Name of CIB element
+ *
+ * \return XPath for finding \p element in CIB XML (or \c NULL if unknown)
+ */
+const char *
+pcmk__cib_abs_xpath_for(const char *element)
+{
+ const char *xpath = pcmk_cib_xpath_for(element);
+
+ // XPaths returned by pcmk_cib_xpath_for() are relative (starting with "//")
+ return ((xpath != NULL)? (xpath + 1) : NULL);
+}
+
+/*!
* \brief Get the parent element name of a given CIB element name
*
* \param[in] element_name Name of CIB element
diff --git a/lib/common/crmcommon_private.h b/lib/common/crmcommon_private.h
index 7faccb6..121d663 100644
--- a/lib/common/crmcommon_private.h
+++ b/lib/common/crmcommon_private.h
@@ -63,7 +63,7 @@ typedef struct xml_doc_private_s {
} while (0)
G_GNUC_INTERNAL
-void pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer,
+void pcmk__xml2text(const xmlNode *data, uint32_t options, GString *buffer,
int depth);
G_GNUC_INTERNAL
@@ -116,12 +116,14 @@ G_GNUC_INTERNAL
void pcmk__log_xmllib_err(void *ctx, const char *fmt, ...)
G_GNUC_PRINTF(2, 3);
-static inline const char *
-pcmk__xml_attr_value(const xmlAttr *attr)
-{
- return ((attr == NULL) || (attr->children == NULL))? NULL
- : (const char *) attr->children->content;
-}
+G_GNUC_INTERNAL
+void pcmk__mark_xml_node_dirty(xmlNode *xml);
+
+G_GNUC_INTERNAL
+bool pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data);
+
+G_GNUC_INTERNAL
+void pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer);
/*
* IPC
@@ -173,11 +175,11 @@ typedef struct pcmk__ipc_methods_s {
* \brief Check whether an IPC request results in a reply
*
* \param[in,out] api IPC API connection
- * \param[in,out] request IPC request XML
+ * \param[in] request IPC request XML
*
* \return true if request would result in an IPC reply, false otherwise
*/
- bool (*reply_expected)(pcmk_ipc_api_t *api, xmlNode *request);
+ bool (*reply_expected)(pcmk_ipc_api_t *api, const xmlNode *request);
/*!
* \internal
@@ -222,7 +224,7 @@ typedef struct pcmk__ipc_header_s {
} pcmk__ipc_header_t;
G_GNUC_INTERNAL
-int pcmk__send_ipc_request(pcmk_ipc_api_t *api, xmlNode *request);
+int pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request);
G_GNUC_INTERNAL
void pcmk__call_ipc_callback(pcmk_ipc_api_t *api,
@@ -264,47 +266,6 @@ pcmk__ipc_methods_t *pcmk__schedulerd_api_methods(void);
//! XML has been moved
#define PCMK__XML_PREFIX_MOVED "+~"
-/*!
- * \brief Check the authenticity of the IPC socket peer process
- *
- * If everything goes well, peer's authenticity is verified by the means
- * of comparing against provided referential UID and GID (either satisfies),
- * and the result of this check can be deduced from the return value.
- * As an exception, detected UID of 0 ("root") satisfies arbitrary
- * provided referential daemon's credentials.
- *
- * \param[in] qb_ipc libqb client connection if available
- * \param[in] sock IPC related, connected Unix socket to check peer of
- * \param[in] refuid referential UID to check against
- * \param[in] refgid referential GID to check against
- * \param[out] gotpid to optionally store obtained PID of the peer
- * (not available on FreeBSD, special value of 1
- * used instead, and the caller is required to
- * special case this value respectively)
- * \param[out] gotuid to optionally store obtained UID of the peer
- * \param[out] gotgid to optionally store obtained GID of the peer
- *
- * \return Standard Pacemaker return code
- * ie: 0 if it the connection is authentic
- * pcmk_rc_ipc_unauthorized if the connection is not authentic,
- * standard errors.
- *
- * \note While this function is tolerant on what constitutes authorized
- * IPC daemon process (its effective user matches UID=0 or \p refuid,
- * or at least its group matches \p refgid), either or both (in case
- * of UID=0) mismatches on the expected credentials of such peer
- * process \e shall be investigated at the caller when value of 1
- * gets returned there, since higher-than-expected privileges in
- * respect to the expected/intended credentials possibly violate
- * the least privilege principle and may pose an additional risk
- * (i.e. such accidental inconsistency shall be eventually fixed).
- */
-int pcmk__crm_ipc_is_authentic_process(qb_ipcc_connection_t *qb_ipc, int sock,
- uid_t refuid, gid_t refgid,
- pid_t *gotpid, uid_t *gotuid,
- gid_t *gotgid);
-
-
/*
* Output
*/
diff --git a/lib/common/digest.c b/lib/common/digest.c
index 3bf04bf..4de6f97 100644
--- a/lib/common/digest.c
+++ b/lib/common/digest.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2022 the Pacemaker project contributors
+ * Copyright 2015-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -89,7 +89,7 @@ calculate_xml_digest_v1(xmlNode *input, gboolean sort, gboolean ignored)
* \return Newly allocated string containing digest
*/
static char *
-calculate_xml_digest_v2(xmlNode *source, gboolean do_filter)
+calculate_xml_digest_v2(const xmlNode *source, gboolean do_filter)
{
char *digest = NULL;
GString *buffer = g_string_sized_new(1024);
diff --git a/lib/common/io.c b/lib/common/io.c
index 2264e16..35efbe9 100644
--- a/lib/common/io.c
+++ b/lib/common/io.c
@@ -460,11 +460,17 @@ pcmk__file_contents(const char *filename, char **contents)
goto bail;
}
rewind(fp);
- read_len = fread(*contents, 1, length, fp); /* Coverity: False positive */
+
+ read_len = fread(*contents, 1, length, fp);
if (read_len != length) {
free(*contents);
*contents = NULL;
rc = EIO;
+ } else {
+ /* Coverity thinks *contents isn't null-terminated. It doesn't
+ * understand calloc().
+ */
+ (*contents)[length] = '\0';
}
}
diff --git a/lib/common/ipc_attrd.c b/lib/common/ipc_attrd.c
index 7c40aa7..9caaabe 100644
--- a/lib/common/ipc_attrd.c
+++ b/lib/common/ipc_attrd.c
@@ -44,7 +44,7 @@ set_pairs_data(pcmk__attrd_api_reply_t *data, xmlNode *msg_data)
}
static bool
-reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
+reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
{
const char *command = crm_element_value(request, PCMK__XA_TASK);
@@ -169,32 +169,29 @@ destroy_api(pcmk_ipc_api_t *api)
}
static int
-connect_and_send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
+connect_and_send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
{
int rc = pcmk_rc_ok;
- int max = 5;
-
- while (max > 0) {
- crm_info("Connecting to cluster... %d retries remaining", max);
- rc = pcmk_connect_ipc(api, pcmk_ipc_dispatch_sync);
-
- if (rc == pcmk_rc_ok) {
- rc = pcmk__send_ipc_request(api, request);
- break;
- } else if (rc == EAGAIN || rc == EALREADY) {
- sleep(5 - max);
- max--;
- } else {
- crm_err("Could not connect to attrd: %s", pcmk_rc_str(rc));
- break;
- }
+
+ rc = pcmk__connect_ipc(api, pcmk_ipc_dispatch_sync, 5);
+ if (rc != pcmk_rc_ok) {
+ crm_err("Could not connect to %s: %s",
+ pcmk_ipc_name(api, true), pcmk_rc_str(rc));
+ return rc;
}
- return rc;
+ rc = pcmk__send_ipc_request(api, request);
+ if (rc != pcmk_rc_ok) {
+ crm_err("Could not send request to %s: %s",
+ pcmk_ipc_name(api, true), pcmk_rc_str(rc));
+ return rc;
+ }
+
+ return pcmk_rc_ok;
}
static int
-send_attrd_request(pcmk_ipc_api_t *api, xmlNode *request)
+send_attrd_request(pcmk_ipc_api_t *api, const xmlNode *request)
{
return pcmk__send_ipc_request(api, request);
}
diff --git a/lib/common/ipc_client.c b/lib/common/ipc_client.c
index c6d1645..0d38650 100644
--- a/lib/common/ipc_client.c
+++ b/lib/common/ipc_client.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2022 the Pacemaker project contributors
+ * Copyright 2004-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -31,6 +31,10 @@
#include <crm/common/ipc_internal.h>
#include "crmcommon_private.h"
+static int is_ipc_provider_expected(qb_ipcc_connection_t *qb_ipc, int sock,
+ uid_t refuid, gid_t refgid, pid_t *gotpid,
+ uid_t *gotuid, gid_t *gotgid);
+
/*!
* \brief Create a new object for using Pacemaker daemon IPC
*
@@ -164,7 +168,7 @@ ipc_post_disconnect(gpointer user_data)
{
pcmk_ipc_api_t *api = user_data;
- crm_info("Disconnected from %s IPC API", pcmk_ipc_name(api, true));
+ crm_info("Disconnected from %s", pcmk_ipc_name(api, true));
// Perform any daemon-specific handling needed
if ((api->cmds != NULL) && (api->cmds->post_disconnect != NULL)) {
@@ -389,7 +393,7 @@ dispatch_ipc_source_data(const char *buffer, ssize_t length, gpointer user_data)
* meaning no data is available; all other values indicate errors.
* \todo This does not allow the caller to poll multiple file descriptors at
* once. If there is demand for that, we could add a wrapper for
- * crm_ipc_get_fd(api->ipc), so the caller can call poll() themselves.
+ * pcmk__ipc_fd(api->ipc), so the caller can call poll() themselves.
*/
int
pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms)
@@ -400,7 +404,14 @@ pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms)
if ((api == NULL) || (api->dispatch_type != pcmk_ipc_dispatch_poll)) {
return EINVAL;
}
- pollfd.fd = crm_ipc_get_fd(api->ipc);
+
+ rc = pcmk__ipc_fd(api->ipc, &(pollfd.fd));
+ if (rc != pcmk_rc_ok) {
+ crm_debug("Could not obtain file descriptor for %s IPC: %s",
+ pcmk_ipc_name(api, true), pcmk_rc_str(rc));
+ return rc;
+ }
+
pollfd.events = POLLIN;
rc = poll(&pollfd, 1, timeout_ms);
if (rc < 0) {
@@ -465,54 +476,54 @@ connect_with_main_loop(pcmk_ipc_api_t *api)
static int
connect_without_main_loop(pcmk_ipc_api_t *api)
{
- int rc;
+ int rc = pcmk__connect_generic_ipc(api->ipc);
- if (!crm_ipc_connect(api->ipc)) {
- rc = errno;
+ if (rc != pcmk_rc_ok) {
crm_ipc_close(api->ipc);
- return rc;
+ } else {
+ crm_debug("Connected to %s IPC (without main loop)",
+ pcmk_ipc_name(api, true));
}
- crm_debug("Connected to %s IPC (without main loop)",
- pcmk_ipc_name(api, true));
- return pcmk_rc_ok;
+ return rc;
}
/*!
- * \brief Connect to a Pacemaker daemon via IPC
+ * \internal
+ * \brief Connect to a Pacemaker daemon via IPC (retrying after soft errors)
*
* \param[in,out] api IPC API instance
* \param[in] dispatch_type How IPC replies should be dispatched
+ * \param[in] attempts How many times to try (in case of soft error)
*
* \return Standard Pacemaker return code
*/
int
-pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
+pcmk__connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type,
+ int attempts)
{
- const int n_attempts = 2;
int rc = pcmk_rc_ok;
- if (api == NULL) {
- crm_err("Cannot connect to uninitialized API object");
+ if ((api == NULL) || (attempts < 1)) {
return EINVAL;
}
if (api->ipc == NULL) {
- api->ipc = crm_ipc_new(pcmk_ipc_name(api, false),
- api->ipc_size_max);
+ api->ipc = crm_ipc_new(pcmk_ipc_name(api, false), api->ipc_size_max);
if (api->ipc == NULL) {
- crm_err("Failed to re-create IPC API");
return ENOMEM;
}
}
if (crm_ipc_connected(api->ipc)) {
- crm_trace("Already connected to %s IPC API", pcmk_ipc_name(api, true));
+ crm_trace("Already connected to %s", pcmk_ipc_name(api, true));
return pcmk_rc_ok;
}
api->dispatch_type = dispatch_type;
- for (int i = 0; i < n_attempts; i++) {
+ crm_debug("Attempting connection to %s (up to %d time%s)",
+ pcmk_ipc_name(api, true), attempts, pcmk__plural_s(attempts));
+ for (int remaining = attempts - 1; remaining >= 0; --remaining) {
switch (dispatch_type) {
case pcmk_ipc_dispatch_main:
rc = connect_with_main_loop(api);
@@ -524,17 +535,15 @@ pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
break;
}
- if (rc != EAGAIN) {
- break;
+ if ((remaining == 0) || ((rc != EAGAIN) && (rc != EALREADY))) {
+ break; // Result is final
}
- /* EAGAIN may occur due to interruption by a signal or due to some
- * transient issue. Try one more time to be more resilient.
- */
- if (i < (n_attempts - 1)) {
- crm_trace("Connection to %s IPC API failed with EAGAIN, retrying",
- pcmk_ipc_name(api, true));
- }
+ // Retry after soft error (interrupted by signal, etc.)
+ pcmk__sleep_ms((attempts - remaining) * 500);
+ crm_debug("Re-attempting connection to %s (%d attempt%s remaining)",
+ pcmk_ipc_name(api, true), remaining,
+ pcmk__plural_s(remaining));
}
if (rc != pcmk_rc_ok) {
@@ -551,6 +560,26 @@ pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
}
/*!
+ * \brief Connect to a Pacemaker daemon via IPC
+ *
+ * \param[in,out] api IPC API instance
+ * \param[in] dispatch_type How IPC replies should be dispatched
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
+{
+ int rc = pcmk__connect_ipc(api, dispatch_type, 2);
+
+ if (rc != pcmk_rc_ok) {
+ crm_err("Connection to %s failed: %s",
+ pcmk_ipc_name(api, true), pcmk_rc_str(rc));
+ }
+ return rc;
+}
+
+/*!
* \brief Disconnect an IPC API instance
*
* \param[in,out] api IPC API connection
@@ -628,7 +657,7 @@ pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb,
* \brief Send an XML request across an IPC API connection
*
* \param[in,out] api IPC API connection
- * \param[in,out] request XML request to send
+ * \param[in] request XML request to send
*
* \return Standard Pacemaker return code
*
@@ -636,7 +665,7 @@ pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb,
* requests, because it handles different dispatch types appropriately.
*/
int
-pcmk__send_ipc_request(pcmk_ipc_api_t *api, xmlNode *request)
+pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request)
{
int rc;
xmlNode *reply = NULL;
@@ -855,6 +884,77 @@ crm_ipc_new(const char *name, size_t max_size)
}
/*!
+ * \internal
+ * \brief Connect a generic (not daemon-specific) IPC object
+ *
+ * \param[in,out] ipc Generic IPC object to connect
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk__connect_generic_ipc(crm_ipc_t *ipc)
+{
+ uid_t cl_uid = 0;
+ gid_t cl_gid = 0;
+ pid_t found_pid = 0;
+ uid_t found_uid = 0;
+ gid_t found_gid = 0;
+ int rc = pcmk_rc_ok;
+
+ if (ipc == NULL) {
+ return EINVAL;
+ }
+
+ ipc->need_reply = FALSE;
+ ipc->ipc = qb_ipcc_connect(ipc->server_name, ipc->buf_size);
+ if (ipc->ipc == NULL) {
+ return errno;
+ }
+
+ rc = qb_ipcc_fd_get(ipc->ipc, &ipc->pfd.fd);
+ if (rc < 0) { // -errno
+ crm_ipc_close(ipc);
+ return -rc;
+ }
+
+ rc = pcmk_daemon_user(&cl_uid, &cl_gid);
+ rc = pcmk_legacy2rc(rc);
+ if (rc != pcmk_rc_ok) {
+ crm_ipc_close(ipc);
+ return rc;
+ }
+
+ rc = is_ipc_provider_expected(ipc->ipc, ipc->pfd.fd, cl_uid, cl_gid,
+ &found_pid, &found_uid, &found_gid);
+ if (rc != pcmk_rc_ok) {
+ if (rc == pcmk_rc_ipc_unauthorized) {
+ crm_info("%s IPC provider authentication failed: process %lld has "
+ "uid %lld (expected %lld) and gid %lld (expected %lld)",
+ ipc->server_name,
+ (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
+ (long long) found_uid, (long long) cl_uid,
+ (long long) found_gid, (long long) cl_gid);
+ }
+ crm_ipc_close(ipc);
+ return rc;
+ }
+
+ ipc->max_buf_size = qb_ipcc_get_buffer_size(ipc->ipc);
+ if (ipc->max_buf_size > ipc->buf_size) {
+ free(ipc->buffer);
+ ipc->buffer = calloc(ipc->max_buf_size, sizeof(char));
+ if (ipc->buffer == NULL) {
+ rc = errno;
+ crm_ipc_close(ipc);
+ return rc;
+ }
+ ipc->buf_size = ipc->max_buf_size;
+ }
+
+ return pcmk_rc_ok;
+}
+
+/*!
* \brief Establish an IPC connection to a Pacemaker component
*
* \param[in,out] client Connection instance obtained from crm_ipc_new()
@@ -866,76 +966,26 @@ crm_ipc_new(const char *name, size_t max_size)
bool
crm_ipc_connect(crm_ipc_t *client)
{
- uid_t cl_uid = 0;
- gid_t cl_gid = 0;
- pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
- int rv;
+ int rc = pcmk__connect_generic_ipc(client);
- if (client == NULL) {
- errno = EINVAL;
- return false;
+ if (rc == pcmk_rc_ok) {
+ return true;
}
-
- client->need_reply = FALSE;
- client->ipc = qb_ipcc_connect(client->server_name, client->buf_size);
-
- if (client->ipc == NULL) {
+ if ((client != NULL) && (client->ipc == NULL)) {
+ errno = (rc > 0)? rc : ENOTCONN;
crm_debug("Could not establish %s IPC connection: %s (%d)",
client->server_name, pcmk_rc_str(errno), errno);
- return false;
- }
-
- client->pfd.fd = crm_ipc_get_fd(client);
- if (client->pfd.fd < 0) {
- rv = errno;
- /* message already omitted */
- crm_ipc_close(client);
- errno = rv;
- return false;
- }
-
- rv = pcmk_daemon_user(&cl_uid, &cl_gid);
- if (rv < 0) {
- /* message already omitted */
- crm_ipc_close(client);
- errno = -rv;
- return false;
- }
-
- if ((rv = pcmk__crm_ipc_is_authentic_process(client->ipc, client->pfd.fd, cl_uid, cl_gid,
- &found_pid, &found_uid,
- &found_gid)) == pcmk_rc_ipc_unauthorized) {
- crm_err("%s IPC provider authentication failed: process %lld has "
- "uid %lld (expected %lld) and gid %lld (expected %lld)",
- client->server_name,
- (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
- (long long) found_uid, (long long) cl_uid,
- (long long) found_gid, (long long) cl_gid);
- crm_ipc_close(client);
+ } else if (rc == pcmk_rc_ipc_unauthorized) {
+ crm_err("%s IPC provider authentication failed",
+ (client == NULL)? "Pacemaker" : client->server_name);
errno = ECONNABORTED;
- return false;
-
- } else if (rv != pcmk_rc_ok) {
- crm_perror(LOG_ERR, "Could not verify authenticity of %s IPC provider",
- client->server_name);
- crm_ipc_close(client);
- if (rv > 0) {
- errno = rv;
- } else {
- errno = ENOTCONN;
- }
- return false;
- }
-
- qb_ipcc_context_set(client->ipc, client);
-
- client->max_buf_size = qb_ipcc_get_buffer_size(client->ipc);
- if (client->max_buf_size > client->buf_size) {
- free(client->buffer);
- client->buffer = calloc(1, client->max_buf_size);
- client->buf_size = client->max_buf_size;
+ } else {
+ crm_perror(LOG_ERR,
+ "Could not verify authenticity of %s IPC provider",
+ (client == NULL)? "Pacemaker" : client->server_name);
+ errno = ENOTCONN;
}
- return true;
+ return false;
}
void
@@ -977,18 +1027,40 @@ crm_ipc_destroy(crm_ipc_t * client)
}
}
+/*!
+ * \internal
+ * \brief Get the file descriptor for a generic IPC object
+ *
+ * \param[in,out] ipc Generic IPC object to get file descriptor for
+ * \param[out] fd Where to store file descriptor
+ *
+ * \return Standard Pacemaker return code
+ */
+int
+pcmk__ipc_fd(crm_ipc_t *ipc, int *fd)
+{
+ if ((ipc == NULL) || (fd == NULL)) {
+ return EINVAL;
+ }
+ if ((ipc->ipc == NULL) || (ipc->pfd.fd < 0)) {
+ return ENOTCONN;
+ }
+ *fd = ipc->pfd.fd;
+ return pcmk_rc_ok;
+}
+
int
crm_ipc_get_fd(crm_ipc_t * client)
{
- int fd = 0;
+ int fd = -1;
- if (client && client->ipc && (qb_ipcc_fd_get(client->ipc, &fd) == 0)) {
- return fd;
+ if (pcmk__ipc_fd(client, &fd) != pcmk_rc_ok) {
+ crm_err("Could not obtain file descriptor for %s IPC",
+ ((client == NULL)? "unspecified" : client->server_name));
+ errno = EINVAL;
+ return -EINVAL;
}
- errno = EINVAL;
- crm_perror(LOG_ERR, "Could not obtain file descriptor for %s IPC",
- (client? client->server_name : "unspecified"));
- return -errno;
+ return fd;
}
bool
@@ -1057,12 +1129,13 @@ crm_ipc_decompress(crm_ipc_t * client)
rc = BZ2_bzBuffToBuffDecompress(uncompressed + sizeof(pcmk__ipc_header_t), &size_u,
client->buffer + sizeof(pcmk__ipc_header_t), header->size_compressed, 1, 0);
+ rc = pcmk__bzlib2rc(rc);
- if (rc != BZ_OK) {
- crm_err("Decompression failed: %s " CRM_XS " bzerror=%d",
- bz2_strerror(rc), rc);
+ if (rc != pcmk_rc_ok) {
+ crm_err("Decompression failed: %s " CRM_XS " rc=%d",
+ pcmk_rc_str(rc), rc);
free(uncompressed);
- return EILSEQ;
+ return rc;
}
/*
@@ -1221,7 +1294,7 @@ internal_ipc_get_reply(crm_ipc_t *client, int request_id, int ms_timeout,
* \brief Send an IPC XML message
*
* \param[in,out] client Connection to IPC server
- * \param[in,out] message XML message to send
+ * \param[in] message XML message to send
* \param[in] flags Bitmask of crm_ipc_flags
* \param[in] ms_timeout Give up if not sent within this much time
* (5 seconds if 0, or no timeout if negative)
@@ -1231,8 +1304,8 @@ internal_ipc_get_reply(crm_ipc_t *client, int request_id, int ms_timeout,
* if reply was needed, otherwise number of bytes sent
*/
int
-crm_ipc_send(crm_ipc_t * client, xmlNode * message, enum crm_ipc_flags flags, int32_t ms_timeout,
- xmlNode ** reply)
+crm_ipc_send(crm_ipc_t *client, const xmlNode *message,
+ enum crm_ipc_flags flags, int32_t ms_timeout, xmlNode **reply)
{
int rc = 0;
ssize_t qb_rc = 0;
@@ -1385,89 +1458,129 @@ crm_ipc_send(crm_ipc_t * client, xmlNode * message, enum crm_ipc_flags flags, in
return rc;
}
-int
-pcmk__crm_ipc_is_authentic_process(qb_ipcc_connection_t *qb_ipc, int sock, uid_t refuid, gid_t refgid,
- pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
+/*!
+ * \brief Ensure an IPC provider has expected user or group
+ *
+ * \param[in] qb_ipc libqb client connection if available
+ * \param[in] sock Connected Unix socket for IPC
+ * \param[in] refuid Expected user ID
+ * \param[in] refgid Expected group ID
+ * \param[out] gotpid If not NULL, where to store provider's actual process ID
+ * (or 1 on platforms where ID is not available)
+ * \param[out] gotuid If not NULL, where to store provider's actual user ID
+ * \param[out] gotgid If not NULL, where to store provider's actual group ID
+ *
+ * \return Standard Pacemaker return code
+ * \note An actual user ID of 0 (root) will always be considered authorized,
+ * regardless of the expected values provided. The caller can use the
+ * output arguments to be stricter than this function.
+ */
+static int
+is_ipc_provider_expected(qb_ipcc_connection_t *qb_ipc, int sock,
+ uid_t refuid, gid_t refgid,
+ pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
{
- int ret = 0;
- pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
-#if defined(HAVE_UCRED)
- struct ucred ucred;
- socklen_t ucred_len = sizeof(ucred);
-#endif
+ int rc = EOPNOTSUPP;
+ pid_t found_pid = 0;
+ uid_t found_uid = 0;
+ gid_t found_gid = 0;
#ifdef HAVE_QB_IPCC_AUTH_GET
- if (qb_ipc && !qb_ipcc_auth_get(qb_ipc, &found_pid, &found_uid, &found_gid)) {
- goto do_checks;
+ if (qb_ipc != NULL) {
+ rc = qb_ipcc_auth_get(qb_ipc, &found_pid, &found_uid, &found_gid);
+ rc = -rc; // libqb returns 0 or -errno
+ if (rc == pcmk_rc_ok) {
+ goto found;
+ }
}
#endif
-#if defined(HAVE_UCRED)
- if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
- &ucred, &ucred_len)
- && ucred_len == sizeof(ucred)) {
- found_pid = ucred.pid; found_uid = ucred.uid; found_gid = ucred.gid;
+#ifdef HAVE_UCRED
+ {
+ struct ucred ucred;
+ socklen_t ucred_len = sizeof(ucred);
-#elif defined(HAVE_SOCKPEERCRED)
- struct sockpeercred sockpeercred;
- socklen_t sockpeercred_len = sizeof(sockpeercred);
-
- if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
- &sockpeercred, &sockpeercred_len)
- && sockpeercred_len == sizeof(sockpeercred_len)) {
- found_pid = sockpeercred.pid;
- found_uid = sockpeercred.uid; found_gid = sockpeercred.gid;
+ if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &ucred_len) < 0) {
+ rc = errno;
+ } else if (ucred_len != sizeof(ucred)) {
+ rc = EOPNOTSUPP;
+ } else {
+ found_pid = ucred.pid;
+ found_uid = ucred.uid;
+ found_gid = ucred.gid;
+ goto found;
+ }
+ }
+#endif
-#elif defined(HAVE_GETPEEREID)
- if (!getpeereid(sock, &found_uid, &found_gid)) {
- found_pid = PCMK__SPECIAL_PID; /* cannot obtain PID (FreeBSD) */
+#ifdef HAVE_SOCKPEERCRED
+ {
+ struct sockpeercred sockpeercred;
+ socklen_t sockpeercred_len = sizeof(sockpeercred);
-#elif defined(HAVE_GETPEERUCRED)
- ucred_t *ucred;
- if (!getpeerucred(sock, &ucred)) {
- errno = 0;
- found_pid = ucred_getpid(ucred);
- found_uid = ucred_geteuid(ucred); found_gid = ucred_getegid(ucred);
- ret = -errno;
- ucred_free(ucred);
- if (ret) {
- return (ret < 0) ? ret : -pcmk_err_generic;
+ if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
+ &sockpeercred, &sockpeercred_len) < 0) {
+ rc = errno;
+ } else if (sockpeercred_len != sizeof(sockpeercred)) {
+ rc = EOPNOTSUPP;
+ } else {
+ found_pid = sockpeercred.pid;
+ found_uid = sockpeercred.uid;
+ found_gid = sockpeercred.gid;
+ goto found;
}
-
-#else
-# error "No way to authenticate a Unix socket peer"
- errno = 0;
- if (0) {
+ }
#endif
-#ifdef HAVE_QB_IPCC_AUTH_GET
- do_checks:
+
+#ifdef HAVE_GETPEEREID // For example, FreeBSD
+ if (getpeereid(sock, &found_uid, &found_gid) < 0) {
+ rc = errno;
+ } else {
+ found_pid = PCMK__SPECIAL_PID;
+ goto found;
+ }
#endif
- if (gotpid != NULL) {
- *gotpid = found_pid;
- }
- if (gotuid != NULL) {
- *gotuid = found_uid;
- }
- if (gotgid != NULL) {
- *gotgid = found_gid;
- }
- if (found_uid == 0 || found_uid == refuid || found_gid == refgid) {
- ret = 0;
+
+#ifdef HAVE_GETPEERUCRED
+ {
+ ucred_t *ucred = NULL;
+
+ if (getpeerucred(sock, &ucred) < 0) {
+ rc = errno;
} else {
- ret = pcmk_rc_ipc_unauthorized;
+ found_pid = ucred_getpid(ucred);
+ found_uid = ucred_geteuid(ucred);
+ found_gid = ucred_getegid(ucred);
+ ucred_free(ucred);
+ goto found;
}
- } else {
- ret = (errno > 0) ? errno : pcmk_rc_error;
}
- return ret;
+#endif
+
+ return rc; // If we get here, nothing succeeded
+
+found:
+ if (gotpid != NULL) {
+ *gotpid = found_pid;
+ }
+ if (gotuid != NULL) {
+ *gotuid = found_uid;
+ }
+ if (gotgid != NULL) {
+ *gotgid = found_gid;
+ }
+ if ((found_uid != 0) && (found_uid != refuid) && (found_gid != refgid)) {
+ return pcmk_rc_ipc_unauthorized;
+ }
+ return pcmk_rc_ok;
}
int
crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid,
pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
{
- int ret = pcmk__crm_ipc_is_authentic_process(NULL, sock, refuid, refgid,
- gotpid, gotuid, gotgid);
+ int ret = is_ipc_provider_expected(NULL, sock, refuid, refgid,
+ gotpid, gotuid, gotgid);
/* The old function had some very odd return codes*/
if (ret == 0) {
@@ -1528,8 +1641,8 @@ pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid,
goto bail;
}
- auth_rc = pcmk__crm_ipc_is_authentic_process(c, fd, refuid, refgid, &found_pid,
- &found_uid, &found_gid);
+ auth_rc = is_ipc_provider_expected(c, fd, refuid, refgid,
+ &found_pid, &found_uid, &found_gid);
if (auth_rc == pcmk_rc_ipc_unauthorized) {
crm_err("Daemon (IPC %s) effectively blocked with unauthorized"
" process %lld (uid: %lld, gid: %lld)",
diff --git a/lib/common/ipc_common.c b/lib/common/ipc_common.c
index d0c0636..a48b0e9 100644
--- a/lib/common/ipc_common.c
+++ b/lib/common/ipc_common.c
@@ -35,7 +35,7 @@ pcmk__ipc_buffer_size(unsigned int max)
if (global_max == 0) {
long long global_ll;
- if ((pcmk__scan_ll(getenv("PCMK_ipc_buffer"), &global_ll,
+ if ((pcmk__scan_ll(pcmk__env_option(PCMK__ENV_IPC_BUFFER), &global_ll,
0LL) != pcmk_rc_ok)
|| (global_ll <= 0)) {
global_max = MAX_MSG_SIZE; // Default for unset or invalid
diff --git a/lib/common/ipc_controld.c b/lib/common/ipc_controld.c
index 9303afd..8e2016e 100644
--- a/lib/common/ipc_controld.c
+++ b/lib/common/ipc_controld.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the Pacemaker project contributors
+ * Copyright 2020-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -135,7 +135,7 @@ set_node_info_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
data->data.node_info.uuid = crm_element_value(msg_data, XML_ATTR_ID);
data->data.node_info.uname = crm_element_value(msg_data, XML_ATTR_UNAME);
- data->data.node_info.state = crm_element_value(msg_data, XML_NODE_IS_PEER);
+ data->data.node_info.state = crm_element_value(msg_data, PCMK__XA_CRMD);
}
static void
@@ -169,26 +169,24 @@ set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
node_info->id = id_ll;
}
node_info->uname = crm_element_value(node, XML_ATTR_UNAME);
- node_info->state = crm_element_value(node, XML_NODE_IN_CLUSTER);
+ node_info->state = crm_element_value(node, PCMK__XA_IN_CCM);
data->data.nodes = g_list_prepend(data->data.nodes, node_info);
}
}
static bool
-reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
+reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
{
- const char *command = crm_element_value(request, F_CRM_TASK);
-
- if (command == NULL) {
- return false;
- }
-
- // We only need to handle commands that functions in this file can send
- return !strcmp(command, CRM_OP_REPROBE)
- || !strcmp(command, CRM_OP_NODE_INFO)
- || !strcmp(command, CRM_OP_PING)
- || !strcmp(command, CRM_OP_LRM_FAIL)
- || !strcmp(command, CRM_OP_LRM_DELETE);
+ // We only need to handle commands that API functions can send
+ return pcmk__str_any_of(crm_element_value(request, F_CRM_TASK),
+ PCMK__CONTROLD_CMD_NODES,
+ CRM_OP_LRM_DELETE,
+ CRM_OP_LRM_FAIL,
+ CRM_OP_NODE_INFO,
+ CRM_OP_PING,
+ CRM_OP_REPROBE,
+ CRM_OP_RM_NODE_CACHE,
+ NULL);
}
static bool
@@ -202,22 +200,12 @@ dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
pcmk_controld_reply_unknown, NULL, NULL,
};
- /* If we got an ACK, return true so the caller knows to expect more responses
- * from the IPC server. We do this before decrementing replies_expected because
- * ACKs are not going to be included in that value.
- *
- * Note that we cannot do the same kind of status checking here that we do in
- * ipc_pacemakerd.c. The ACK message we receive does not necessarily contain
- * a status attribute. That is, we may receive this:
- *
- * <ack function="crmd_remote_proxy_cb" line="556"/>
- *
- * Instead of this:
- *
- * <ack function="dispatch_controller_ipc" line="391" status="112"/>
- */
- if (pcmk__str_eq(crm_element_name(reply), "ack", pcmk__str_none)) {
- return true; // More replies needed
+ if (pcmk__xe_is(reply, "ack")) {
+ /* ACKs are trivial responses that do not count toward expected replies,
+ * and do not have all the fields that validation requires, so skip that
+ * processing.
+ */
+ return private->replies_expected > 0;
}
if (private->replies_expected > 0) {
@@ -341,21 +329,18 @@ create_controller_request(const pcmk_ipc_api_t *api, const char *op,
// \return Standard Pacemaker return code
static int
-send_controller_request(pcmk_ipc_api_t *api, xmlNode *request,
+send_controller_request(pcmk_ipc_api_t *api, const xmlNode *request,
bool reply_is_expected)
{
- int rc;
-
if (crm_element_value(request, XML_ATTR_REFERENCE) == NULL) {
return EINVAL;
}
- rc = pcmk__send_ipc_request(api, request);
- if ((rc == pcmk_rc_ok) && reply_is_expected) {
+ if (reply_is_expected) {
struct controld_api_private_s *private = api->api_data;
private->replies_expected++;
}
- return rc;
+ return pcmk__send_ipc_request(api, request);
}
static xmlNode *
diff --git a/lib/common/ipc_pacemakerd.c b/lib/common/ipc_pacemakerd.c
index 91a3143..2f03709 100644
--- a/lib/common/ipc_pacemakerd.c
+++ b/lib/common/ipc_pacemakerd.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the Pacemaker project contributors
+ * Copyright 2020-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -178,7 +178,7 @@ post_disconnect(pcmk_ipc_api_t *api)
}
static bool
-reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
+reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
{
const char *command = crm_element_value(request, F_CRM_TASK);
diff --git a/lib/common/ipc_schedulerd.c b/lib/common/ipc_schedulerd.c
index c1b81a4..cf788e5 100644
--- a/lib/common/ipc_schedulerd.c
+++ b/lib/common/ipc_schedulerd.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2021-2022 the Pacemaker project contributors
+ * Copyright 2021-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -62,7 +62,7 @@ post_connect(pcmk_ipc_api_t *api)
}
static bool
-reply_expected(pcmk_ipc_api_t *api, xmlNode *request)
+reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
{
const char *command = crm_element_value(request, F_CRM_TASK);
diff --git a/lib/common/ipc_server.c b/lib/common/ipc_server.c
index 60f20fb..5cd7e70 100644
--- a/lib/common/ipc_server.c
+++ b/lib/common/ipc_server.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2022 the Pacemaker project contributors
+ * Copyright 2004-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -421,9 +421,11 @@ pcmk__client_data2xml(pcmk__client_t *c, void *data, uint32_t *id,
rc = BZ2_bzBuffToBuffDecompress(uncompressed, &size_u, text, header->size_compressed, 1, 0);
text = uncompressed;
- if (rc != BZ_OK) {
- crm_err("Decompression failed: %s " CRM_XS " bzerror=%d",
- bz2_strerror(rc), rc);
+ rc = pcmk__bzlib2rc(rc);
+
+ if (rc != pcmk_rc_ok) {
+ crm_err("Decompression failed: %s " CRM_XS " rc=%d",
+ pcmk_rc_str(rc), rc);
free(uncompressed);
return NULL;
}
@@ -568,16 +570,16 @@ crm_ipcs_flush_events(pcmk__client_t *c)
* \internal
* \brief Create an I/O vector for sending an IPC XML message
*
- * \param[in] request Identifier for libqb response header
- * \param[in,out] message XML message to send
- * \param[in] max_send_size If 0, default IPC buffer size is used
- * \param[out] result Where to store prepared I/O vector
- * \param[out] bytes Size of prepared data in bytes
+ * \param[in] request Identifier for libqb response header
+ * \param[in] message XML message to send
+ * \param[in] max_send_size If 0, default IPC buffer size is used
+ * \param[out] result Where to store prepared I/O vector
+ * \param[out] bytes Size of prepared data in bytes
*
* \return Standard Pacemaker return code
*/
int
-pcmk__ipc_prepare_iov(uint32_t request, xmlNode *message,
+pcmk__ipc_prepare_iov(uint32_t request, const xmlNode *message,
uint32_t max_send_size, struct iovec **result,
ssize_t *bytes)
{
@@ -741,7 +743,7 @@ pcmk__ipc_send_iov(pcmk__client_t *c, struct iovec *iov, uint32_t flags)
}
int
-pcmk__ipc_send_xml(pcmk__client_t *c, uint32_t request, xmlNode *message,
+pcmk__ipc_send_xml(pcmk__client_t *c, uint32_t request, const xmlNode *message,
uint32_t flags)
{
struct iovec *iov = NULL;
@@ -819,6 +821,7 @@ pcmk__ipc_send_ack_as(const char *function, int line, pcmk__client_t *c,
if (ack != NULL) {
crm_trace("Ack'ing IPC message from client %s as <%s status=%d>",
pcmk__client_name(c), tag, status);
+ crm_log_xml_trace(ack, "sent-ack");
c->request_id = 0;
rc = pcmk__ipc_send_xml(c, request, ack, flags);
free_xml(ack);
@@ -995,14 +998,17 @@ pcmk__serve_schedulerd_ipc(struct qb_ipcs_service_handlers *cb)
bool
crm_is_daemon_name(const char *name)
{
- name = pcmk__message_name(name);
- return (!strcmp(name, CRM_SYSTEM_CRMD)
- || !strcmp(name, CRM_SYSTEM_STONITHD)
- || !strcmp(name, "stonith-ng")
- || !strcmp(name, "attrd")
- || !strcmp(name, CRM_SYSTEM_CIB)
- || !strcmp(name, CRM_SYSTEM_MCP)
- || !strcmp(name, CRM_SYSTEM_DC)
- || !strcmp(name, CRM_SYSTEM_TENGINE)
- || !strcmp(name, CRM_SYSTEM_LRMD));
+ return pcmk__str_any_of(pcmk__message_name(name),
+ "attrd",
+ CRM_SYSTEM_CIB,
+ CRM_SYSTEM_CRMD,
+ CRM_SYSTEM_DC,
+ CRM_SYSTEM_LRMD,
+ CRM_SYSTEM_MCP,
+ CRM_SYSTEM_PENGINE,
+ CRM_SYSTEM_STONITHD,
+ CRM_SYSTEM_TENGINE,
+ "pacemaker-remoted",
+ "stonith-ng",
+ NULL);
}
diff --git a/lib/common/iso8601.c b/lib/common/iso8601.c
index 3e000e1..9de018f 100644
--- a/lib/common/iso8601.c
+++ b/lib/common/iso8601.c
@@ -1930,9 +1930,10 @@ pcmk__readable_interval(guint interval_ms)
#define MS_IN_H (MS_IN_M * 60)
#define MS_IN_D (MS_IN_H * 24)
#define MAXSTR sizeof("..d..h..m..s...ms")
- static char str[MAXSTR] = { '\0', };
+ static char str[MAXSTR];
int offset = 0;
+ str[0] = '\0';
if (interval_ms > MS_IN_D) {
offset += snprintf(str + offset, MAXSTR - offset, "%ud",
interval_ms / MS_IN_D);
diff --git a/lib/common/logging.c b/lib/common/logging.c
index dded873..7768c35 100644
--- a/lib/common/logging.c
+++ b/lib/common/logging.c
@@ -51,6 +51,11 @@ static unsigned int crm_log_priority = LOG_NOTICE;
static GLogFunc glib_log_default = NULL;
static pcmk__output_t *logger_out = NULL;
+pcmk__config_error_func pcmk__config_error_handler = NULL;
+pcmk__config_warning_func pcmk__config_warning_handler = NULL;
+void *pcmk__config_error_context = NULL;
+void *pcmk__config_warning_context = NULL;
+
static gboolean crm_tracing_enabled(void);
static void
@@ -237,7 +242,7 @@ chown_logfile(const char *filename, int logfd)
static void
chmod_logfile(const char *filename, int logfd)
{
- const char *modestr = getenv("PCMK_logfile_mode");
+ const char *modestr = pcmk__env_option(PCMK__ENV_LOGFILE_MODE);
mode_t filemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP;
if (modestr != NULL) {
@@ -297,7 +302,7 @@ setenv_logfile(const char *filename)
{
// Some resource agents will log only if environment variable is set
if (pcmk__env_option(PCMK__ENV_LOGFILE) == NULL) {
- pcmk__set_env_option(PCMK__ENV_LOGFILE, filename);
+ pcmk__set_env_option(PCMK__ENV_LOGFILE, filename, true);
}
}
@@ -609,6 +614,20 @@ crm_log_filter_source(int source, const char *trace_files, const char *trace_fns
}
}
+#ifndef HAVE_STRCHRNUL
+/* strchrnul() is a GNU extension. If not present, use our own definition.
+ * The GNU version returns char*, but we only need it to be const char*.
+ */
+static const char *
+strchrnul(const char *s, int c)
+{
+ while ((*s != c) && (*s != '\0')) {
+ ++s;
+ }
+ return s;
+}
+#endif
+
static void
crm_log_filter(struct qb_log_callsite *cs)
{
@@ -622,11 +641,11 @@ crm_log_filter(struct qb_log_callsite *cs)
if (need_init) {
need_init = 0;
- trace_fns = getenv("PCMK_trace_functions");
- trace_fmts = getenv("PCMK_trace_formats");
- trace_tags = getenv("PCMK_trace_tags");
- trace_files = getenv("PCMK_trace_files");
- trace_blackbox = getenv("PCMK_trace_blackbox");
+ trace_fns = pcmk__env_option(PCMK__ENV_TRACE_FUNCTIONS);
+ trace_fmts = pcmk__env_option(PCMK__ENV_TRACE_FORMATS);
+ trace_tags = pcmk__env_option(PCMK__ENV_TRACE_TAGS);
+ trace_files = pcmk__env_option(PCMK__ENV_TRACE_FILES);
+ trace_blackbox = pcmk__env_option(PCMK__ENV_TRACE_BLACKBOX);
if (trace_tags != NULL) {
uint32_t tag;
@@ -695,8 +714,10 @@ crm_update_callsites(void)
log = FALSE;
crm_debug
("Enabling callsites based on priority=%d, files=%s, functions=%s, formats=%s, tags=%s",
- crm_log_level, getenv("PCMK_trace_files"), getenv("PCMK_trace_functions"),
- getenv("PCMK_trace_formats"), getenv("PCMK_trace_tags"));
+ crm_log_level, pcmk__env_option(PCMK__ENV_TRACE_FILES),
+ pcmk__env_option(PCMK__ENV_TRACE_FUNCTIONS),
+ pcmk__env_option(PCMK__ENV_TRACE_FORMATS),
+ pcmk__env_option(PCMK__ENV_TRACE_TAGS));
}
qb_log_filter_fn_set(crm_log_filter);
}
@@ -704,13 +725,11 @@ crm_update_callsites(void)
static gboolean
crm_tracing_enabled(void)
{
- if (crm_log_level == LOG_TRACE) {
- return TRUE;
- } else if (getenv("PCMK_trace_files") || getenv("PCMK_trace_functions")
- || getenv("PCMK_trace_formats") || getenv("PCMK_trace_tags")) {
- return TRUE;
- }
- return FALSE;
+ return (crm_log_level == LOG_TRACE)
+ || (pcmk__env_option(PCMK__ENV_TRACE_FILES) != NULL)
+ || (pcmk__env_option(PCMK__ENV_TRACE_FUNCTIONS) != NULL)
+ || (pcmk__env_option(PCMK__ENV_TRACE_FORMATS) != NULL)
+ || (pcmk__env_option(PCMK__ENV_TRACE_TAGS) != NULL);
}
static int
@@ -784,7 +803,8 @@ set_identity(const char *entity, int argc, char *const *argv)
CRM_ASSERT(crm_system_name != NULL);
- setenv("PCMK_service", crm_system_name, 1);
+ // Used by fencing.py.py (in fence-agents)
+ pcmk__set_env_option(PCMK__ENV_SERVICE, crm_system_name, false);
}
void
@@ -897,7 +917,7 @@ crm_log_init(const char *entity, uint8_t level, gboolean daemon, gboolean to_std
} else {
facility = PCMK__VALUE_NONE;
}
- pcmk__set_env_option(PCMK__ENV_LOGFACILITY, facility);
+ pcmk__set_env_option(PCMK__ENV_LOGFACILITY, facility, true);
}
if (pcmk__str_eq(facility, PCMK__VALUE_NONE, pcmk__str_casei)) {
@@ -1127,16 +1147,21 @@ pcmk__cli_init_logging(const char *name, unsigned int verbosity)
/*!
* \brief Log XML line-by-line in a formatted fashion
*
- * \param[in] level Priority at which to log the messages
- * \param[in] text Prefix for each line
- * \param[in] xml XML to log
+ * \param[in] file File name to use for log filtering
+ * \param[in] function Function name to use for log filtering
+ * \param[in] line Line number to use for log filtering
+ * \param[in] tags Logging tags to use for log filtering
+ * \param[in] level Priority at which to log the messages
+ * \param[in] text Prefix for each line
+ * \param[in] xml XML to log
*
* \note This does nothing when \p level is \p LOG_STDOUT.
* \note Do not call this function directly. It should be called only from the
* \p do_crm_log_xml() macro.
*/
void
-pcmk_log_xml_impl(uint8_t level, const char *text, const xmlNode *xml)
+pcmk_log_xml_as(const char *file, const char *function, uint32_t line,
+ uint32_t tags, uint8_t level, const char *text, const xmlNode *xml)
{
if (xml == NULL) {
do_crm_log(level, "%s%sNo data to dump as XML",
@@ -1148,12 +1173,76 @@ pcmk_log_xml_impl(uint8_t level, const char *text, const xmlNode *xml)
}
pcmk__output_set_log_level(logger_out, level);
+ pcmk__output_set_log_filter(logger_out, file, function, line, tags);
pcmk__xml_show(logger_out, text, xml, 1,
pcmk__xml_fmt_pretty
|pcmk__xml_fmt_open
|pcmk__xml_fmt_children
|pcmk__xml_fmt_close);
+ pcmk__output_set_log_filter(logger_out, NULL, NULL, 0U, 0U);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Log XML changes line-by-line in a formatted fashion
+ *
+ * \param[in] file File name to use for log filtering
+ * \param[in] function Function name to use for log filtering
+ * \param[in] line Line number to use for log filtering
+ * \param[in] tags Logging tags to use for log filtering
+ * \param[in] level Priority at which to log the messages
+ * \param[in] xml XML whose changes to log
+ *
+ * \note This does nothing when \p level is \c LOG_STDOUT.
+ */
+void
+pcmk__log_xml_changes_as(const char *file, const char *function, uint32_t line,
+ uint32_t tags, uint8_t level, const xmlNode *xml)
+{
+ if (xml == NULL) {
+ do_crm_log(level, "No XML to dump");
+ return;
+ }
+
+ if (logger_out == NULL) {
+ CRM_CHECK(pcmk__log_output_new(&logger_out) == pcmk_rc_ok, return);
}
+ pcmk__output_set_log_level(logger_out, level);
+ pcmk__output_set_log_filter(logger_out, file, function, line, tags);
+ pcmk__xml_show_changes(logger_out, xml);
+ pcmk__output_set_log_filter(logger_out, NULL, NULL, 0U, 0U);
+}
+
+/*!
+ * \internal
+ * \brief Log an XML patchset line-by-line in a formatted fashion
+ *
+ * \param[in] file File name to use for log filtering
+ * \param[in] function Function name to use for log filtering
+ * \param[in] line Line number to use for log filtering
+ * \param[in] tags Logging tags to use for log filtering
+ * \param[in] level Priority at which to log the messages
+ * \param[in] patchset XML patchset to log
+ *
+ * \note This does nothing when \p level is \c LOG_STDOUT.
+ */
+void
+pcmk__log_xml_patchset_as(const char *file, const char *function, uint32_t line,
+ uint32_t tags, uint8_t level, const xmlNode *patchset)
+{
+ if (patchset == NULL) {
+ do_crm_log(level, "No patchset to dump");
+ return;
+ }
+
+ if (logger_out == NULL) {
+ CRM_CHECK(pcmk__log_output_new(&logger_out) == pcmk_rc_ok, return);
+ }
+ pcmk__output_set_log_level(logger_out, level);
+ pcmk__output_set_log_filter(logger_out, file, function, line, tags);
+ logger_out->message(logger_out, "xml-patchset", patchset);
+ pcmk__output_set_log_filter(logger_out, NULL, NULL, 0U, 0U);
}
/*!
@@ -1188,5 +1277,23 @@ crm_add_logfile(const char *filename)
return pcmk__add_logfile(filename) == pcmk_rc_ok;
}
+void
+pcmk_log_xml_impl(uint8_t level, const char *text, const xmlNode *xml)
+{
+ pcmk_log_xml_as(__FILE__, __func__, __LINE__, 0, level, text, xml);
+}
+
// LCOV_EXCL_STOP
// End deprecated API
+
+void pcmk__set_config_error_handler(pcmk__config_error_func error_handler, void *error_context)
+{
+ pcmk__config_error_handler = error_handler;
+ pcmk__config_error_context = error_context;
+}
+
+void pcmk__set_config_warning_handler(pcmk__config_warning_func warning_handler, void *warning_context)
+{
+ pcmk__config_warning_handler = warning_handler;
+ pcmk__config_warning_context = warning_context;
+} \ No newline at end of file
diff --git a/lib/common/mainloop.c b/lib/common/mainloop.c
index 3124e43..f971713 100644
--- a/lib/common/mainloop.c
+++ b/lib/common/mainloop.c
@@ -393,16 +393,6 @@ mainloop_add_signal(int sig, void (*dispatch) (int sig))
mainloop_destroy_signal_entry(sig);
return FALSE;
}
-#if 0
- /* If we want signals to interrupt mainloop's poll(), instead of waiting for
- * the timeout, then we should call siginterrupt() below
- *
- * For now, just enforce a low timeout
- */
- if (siginterrupt(sig, 1) < 0) {
- crm_perror(LOG_INFO, "Could not enable system call interruptions for signal %d", sig);
- }
-#endif
return TRUE;
}
@@ -624,7 +614,7 @@ struct qb_ipcs_poll_handlers gio_poll_funcs = {
static enum qb_ipc_type
pick_ipc_type(enum qb_ipc_type requested)
{
- const char *env = getenv("PCMK_ipc_type");
+ const char *env = pcmk__env_option(PCMK__ENV_IPC_TYPE);
if (env && strcmp("shared-mem", env) == 0) {
return QB_IPC_SHM;
@@ -668,7 +658,8 @@ mainloop_add_ipc_server_with_prio(const char *name, enum qb_ipc_type type,
server = qb_ipcs_create(name, 0, pick_ipc_type(type), callbacks);
if (server == NULL) {
- crm_err("Could not create %s IPC server: %s (%d)", name, pcmk_strerror(rc), rc);
+ crm_err("Could not create %s IPC server: %s (%d)",
+ name, pcmk_rc_str(errno), errno);
return NULL;
}
@@ -874,21 +865,34 @@ pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata,
const struct ipc_client_callbacks *callbacks,
mainloop_io_t **source)
{
+ int rc = pcmk_rc_ok;
+ int fd = -1;
+ const char *ipc_name = NULL;
+
CRM_CHECK((ipc != NULL) && (callbacks != NULL), return EINVAL);
- if (!crm_ipc_connect(ipc)) {
- int rc = errno;
- crm_debug("Connection to %s failed: %d", crm_ipc_name(ipc), errno);
+ ipc_name = pcmk__s(crm_ipc_name(ipc), "Pacemaker");
+ rc = pcmk__connect_generic_ipc(ipc);
+ if (rc != pcmk_rc_ok) {
+ crm_debug("Connection to %s failed: %s", ipc_name, pcmk_rc_str(rc));
return rc;
}
- *source = mainloop_add_fd(crm_ipc_name(ipc), priority, crm_ipc_get_fd(ipc),
- userdata, NULL);
- if (*source == NULL) {
- int rc = errno;
+ rc = pcmk__ipc_fd(ipc, &fd);
+ if (rc != pcmk_rc_ok) {
+ crm_debug("Could not obtain file descriptor for %s IPC: %s",
+ ipc_name, pcmk_rc_str(rc));
crm_ipc_close(ipc);
return rc;
}
+
+ *source = mainloop_add_fd(ipc_name, priority, fd, userdata, NULL);
+ if (*source == NULL) {
+ rc = errno;
+ crm_ipc_close(ipc);
+ return rc;
+ }
+
(*source)->ipc = ipc;
(*source)->destroy_fn = callbacks->destroy;
(*source)->dispatch_fn_ipc = callbacks->dispatch;
diff --git a/lib/common/mock.c b/lib/common/mock.c
index 2bd8334..6f837ad 100644
--- a/lib/common/mock.c
+++ b/lib/common/mock.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2021-2022 the Pacemaker project contributors
+ * Copyright 2021-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -7,6 +7,8 @@
* version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
*/
+#include <crm_internal.h>
+
#include <errno.h>
#include <pwd.h>
#include <stdarg.h>
@@ -262,6 +264,8 @@ __wrap_endgrent(void) {
* will_return(__wrap_fopen, errno_to_set);
*
* expect_* functions: https://api.cmocka.org/group__cmocka__param.html
+ *
+ * This has two mocked functions, since fopen() is sometimes actually fopen64().
*/
bool pcmk__mock_fopen = false;
@@ -285,6 +289,26 @@ __wrap_fopen(const char *pathname, const char *mode)
}
}
+#ifdef HAVE_FOPEN64
+FILE *
+__wrap_fopen64(const char *pathname, const char *mode)
+{
+ if (pcmk__mock_fopen) {
+ check_expected_ptr(pathname);
+ check_expected_ptr(mode);
+ errno = mock_type(int);
+
+ if (errno != 0) {
+ return NULL;
+ } else {
+ return __real_fopen64(pathname, mode);
+ }
+
+ } else {
+ return __real_fopen64(pathname, mode);
+ }
+}
+#endif
/* getpwnam_r()
*
diff --git a/lib/common/mock_private.h b/lib/common/mock_private.h
index 45207c4..b0e0ed2 100644
--- a/lib/common/mock_private.h
+++ b/lib/common/mock_private.h
@@ -1,5 +1,5 @@
/*
- * Copyright 2021-2022 the Pacemaker project contributors
+ * Copyright 2021-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -29,6 +29,10 @@ void *__wrap_calloc(size_t nmemb, size_t size);
extern bool pcmk__mock_fopen;
FILE *__real_fopen(const char *pathname, const char *mode);
FILE *__wrap_fopen(const char *pathname, const char *mode);
+#ifdef HAVE_FOPEN64
+FILE *__real_fopen64(const char *pathname, const char *mode);
+FILE *__wrap_fopen64(const char *pathname, const char *mode);
+#endif
extern bool pcmk__mock_getenv;
char *__real_getenv(const char *name);
diff --git a/lib/common/nvpair.c b/lib/common/nvpair.c
index 3766c45..dbb9c99 100644
--- a/lib/common/nvpair.c
+++ b/lib/common/nvpair.c
@@ -334,55 +334,6 @@ crm_xml_add(xmlNode *node, const char *name, const char *value)
}
/*!
- * \brief Replace an XML attribute with specified name and (possibly NULL) value
- *
- * \param[in,out] node XML node to modify
- * \param[in] name Attribute name to set
- * \param[in] value Attribute value to set
- *
- * \return New value on success, \c NULL otherwise
- * \note This does nothing if node or name is \c NULL or empty.
- */
-const char *
-crm_xml_replace(xmlNode *node, const char *name, const char *value)
-{
- bool dirty = FALSE;
- xmlAttr *attr = NULL;
- const char *old_value = NULL;
-
- CRM_CHECK(node != NULL, return NULL);
- CRM_CHECK(name != NULL && name[0] != 0, return NULL);
-
- old_value = crm_element_value(node, name);
-
- /* Could be re-setting the same value */
- CRM_CHECK(old_value != value, return value);
-
- if (pcmk__check_acl(node, name, pcmk__xf_acl_write) == FALSE) {
- /* Create a fake object linked to doc->_private instead? */
- crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
- return NULL;
-
- } else if (old_value && !value) {
- xml_remove_prop(node, name);
- return NULL;
- }
-
- if (pcmk__tracking_xml_changes(node, FALSE)) {
- if (!old_value || !value || !strcmp(old_value, value)) {
- dirty = TRUE;
- }
- }
-
- attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
- if (dirty) {
- pcmk__mark_xml_attr_dirty(attr);
- }
- CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
- return (char *) attr->children->content;
-}
-
-/*!
* \brief Create an XML attribute with specified name and integer value
*
* This is like \c crm_xml_add() but taking an integer value.
@@ -503,7 +454,7 @@ crm_element_value(const xmlNode *data, const char *name)
return NULL;
} else if (name == NULL) {
- crm_err("Couldn't find NULL in %s", crm_element_name(data));
+ crm_err("Couldn't find NULL in %s", data->name);
return NULL;
}
@@ -883,7 +834,7 @@ xml2list(const xmlNode *parent)
nvpair_list = find_xml_node(parent, XML_TAG_ATTRS, FALSE);
if (nvpair_list == NULL) {
- crm_trace("No attributes in %s", crm_element_name(parent));
+ crm_trace("No attributes in %s", parent->name);
crm_log_xml_trace(parent, "No attributes for resource op");
}
@@ -988,5 +939,44 @@ pcmk_format_named_time(const char *name, time_t epoch_time)
return result;
}
+const char *
+crm_xml_replace(xmlNode *node, const char *name, const char *value)
+{
+ bool dirty = FALSE;
+ xmlAttr *attr = NULL;
+ const char *old_value = NULL;
+
+ CRM_CHECK(node != NULL, return NULL);
+ CRM_CHECK(name != NULL && name[0] != 0, return NULL);
+
+ old_value = crm_element_value(node, name);
+
+ /* Could be re-setting the same value */
+ CRM_CHECK(old_value != value, return value);
+
+ if (pcmk__check_acl(node, name, pcmk__xf_acl_write) == FALSE) {
+ /* Create a fake object linked to doc->_private instead? */
+ crm_trace("Cannot replace %s=%s to %s", name, value, node->name);
+ return NULL;
+
+ } else if (old_value && !value) {
+ xml_remove_prop(node, name);
+ return NULL;
+ }
+
+ if (pcmk__tracking_xml_changes(node, FALSE)) {
+ if (!old_value || !value || !strcmp(old_value, value)) {
+ dirty = TRUE;
+ }
+ }
+
+ attr = xmlSetProp(node, (pcmkXmlStr) name, (pcmkXmlStr) value);
+ if (dirty) {
+ pcmk__mark_xml_attr_dirty(attr);
+ }
+ CRM_CHECK(attr && attr->children && attr->children->content, return NULL);
+ return (char *) attr->children->content;
+}
+
// LCOV_EXCL_STOP
// End deprecated API
diff --git a/lib/common/options.c b/lib/common/options.c
index cb32b3f..2d86ebc 100644
--- a/lib/common/options.c
+++ b/lib/common/options.c
@@ -91,15 +91,23 @@ pcmk__env_option(const char *option)
/*!
* \brief Set or unset a Pacemaker environment variable option
*
- * Set an environment variable option with both a PCMK_ and (for
- * backward compatibility) HA_ prefix.
+ * Set an environment variable option with a \c "PCMK_" prefix and optionally
+ * an \c "HA_" prefix for backward compatibility.
*
* \param[in] option Environment variable name (without prefix)
* \param[in] value New value (or NULL to unset)
+ * \param[in] compat If false and \p value is not \c NULL, set only
+ * \c "PCMK_<option>"; otherwise, set (or unset) both
+ * \c "PCMK_<option>" and \c "HA_<option>"
+ *
+ * \note \p compat is ignored when \p value is \c NULL. A \c NULL \p value
+ * means we're unsetting \p option. \c pcmk__get_env_option() checks for
+ * both prefixes, so we want to clear them both.
*/
void
-pcmk__set_env_option(const char *option, const char *value)
+pcmk__set_env_option(const char *option, const char *value, bool compat)
{
+ // @COMPAT Drop support for "HA_" options eventually
const char *const prefixes[] = {"PCMK_", "HA_"};
char env_name[NAME_MAX];
@@ -132,6 +140,11 @@ pcmk__set_env_option(const char *option, const char *value)
crm_err("Failed to %sset %s: %s", (value != NULL)? "" : "un",
env_name, strerror(errno));
}
+
+ if (!compat && (value != NULL)) {
+ // For set, don't proceed to HA_<option> unless compat is enabled
+ break;
+ }
}
}
diff --git a/lib/common/output_html.c b/lib/common/output_html.c
index 47b14c1..92e9010 100644
--- a/lib/common/output_html.c
+++ b/lib/common/output_html.c
@@ -152,7 +152,7 @@ html_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy
* anything else that the user could add, and we want it done last to pick up
* any options that may have been given.
*/
- head_node = xmlNewNode(NULL, (pcmkXmlStr) "head");
+ head_node = xmlNewDocRawNode(NULL, NULL, (pcmkXmlStr) "head", NULL);
if (title != NULL ) {
pcmk_create_xml_text_node(head_node, "title", title);
@@ -458,7 +458,7 @@ pcmk__html_add_header(const char *name, ...) {
va_start(ap, name);
- header_node = xmlNewNode(NULL, (pcmkXmlStr) name);
+ header_node = xmlNewDocRawNode(NULL, NULL, (pcmkXmlStr) name, NULL);
while (1) {
char *key = va_arg(ap, char *);
char *value;
diff --git a/lib/common/output_log.c b/lib/common/output_log.c
index aca168d..54fa37e 100644
--- a/lib/common/output_log.c
+++ b/lib/common/output_log.c
@@ -12,6 +12,7 @@
#include <ctype.h>
#include <stdarg.h>
+#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
@@ -23,8 +24,43 @@ typedef struct private_data_s {
/* gathered in log_begin_list */
GQueue/*<char*>*/ *prefixes;
uint8_t log_level;
+ const char *function;
+ const char *file;
+ uint32_t line;
+ uint32_t tags;
} private_data_t;
+/*!
+ * \internal
+ * \brief Log a message using output object's log level and filters
+ *
+ * \param[in] priv Output object's private_data_t
+ * \param[in] fmt printf(3)-style format string
+ * \param[in] args... Format string arguments
+ */
+#define logger(priv, fmt, args...) do { \
+ qb_log_from_external_source(pcmk__s((priv)->function, __func__), \
+ pcmk__s((priv)->file, __FILE__), fmt, (priv)->log_level, \
+ (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags, \
+ ##args); \
+ } while (0);
+
+/*!
+ * \internal
+ * \brief Log a message using an explicit log level and output object's filters
+ *
+ * \param[in] priv Output object's private_data_t
+ * \param[in] level Log level
+ * \param[in] fmt printf(3)-style format string
+ * \param[in] ap Variadic arguments
+ */
+#define logger_va(priv, level, fmt, ap) do { \
+ qb_log_from_external_source_va(pcmk__s((priv)->function, __func__), \
+ pcmk__s((priv)->file, __FILE__), fmt, level, \
+ (((priv)->line == 0)? __LINE__ : (priv)->line), (priv)->tags, \
+ ap); \
+ } while (0);
+
static void
log_subprocess_output(pcmk__output_t *out, int exit_status,
const char *proc_stdout, const char *proc_stderr) {
@@ -94,35 +130,31 @@ log_version(pcmk__output_t *out, bool extended) {
priv = out->priv;
if (extended) {
- do_crm_log(priv->log_level, "Pacemaker %s (Build: %s): %s",
- PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
+ logger(priv, "Pacemaker %s (Build: %s): %s",
+ PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
} else {
- do_crm_log(priv->log_level, "Pacemaker %s", PACEMAKER_VERSION);
- do_crm_log(priv->log_level, "Written by Andrew Beekhof and"
- "the Pacemaker project contributors");
+ logger(priv, "Pacemaker " PACEMAKER_VERSION);
+ logger(priv, "Written by Andrew Beekhof and "
+ "the Pacemaker project contributors");
}
}
G_GNUC_PRINTF(2, 3)
static void
-log_err(pcmk__output_t *out, const char *format, ...) {
+log_err(pcmk__output_t *out, const char *format, ...)
+{
va_list ap;
- char* buffer = NULL;
- int len = 0;
+ private_data_t *priv = NULL;
- CRM_ASSERT(out != NULL);
+ CRM_ASSERT((out != NULL) && (out->priv != NULL));
+ priv = out->priv;
- va_start(ap, format);
- /* Informational output does not get indented, to separate it from other
+ /* Error output does not get indented, to separate it from other
* potentially indented list output.
*/
- len = vasprintf(&buffer, format, ap);
- CRM_ASSERT(len >= 0);
+ va_start(ap, format);
+ logger_va(priv, LOG_ERR, format, ap);
va_end(ap);
-
- crm_err("%s", buffer);
-
- free(buffer);
}
static void
@@ -195,15 +227,15 @@ log_list_item(pcmk__output_t *out, const char *name, const char *format, ...) {
if (strcmp(buffer, "") != 0) { /* We don't want empty messages */
if ((name != NULL) && (strcmp(name, "") != 0)) {
if (strcmp(prefix, "") != 0) {
- do_crm_log(priv->log_level, "%s: %s: %s", prefix, name, buffer);
+ logger(priv, "%s: %s: %s", prefix, name, buffer);
} else {
- do_crm_log(priv->log_level, "%s: %s", name, buffer);
+ logger(priv, "%s: %s", name, buffer);
}
} else {
if (strcmp(prefix, "") != 0) {
- do_crm_log(priv->log_level, "%s: %s", prefix, buffer);
+ logger(priv, "%s: %s", prefix, buffer);
} else {
- do_crm_log(priv->log_level, "%s", buffer);
+ logger(priv, "%s", buffer);
}
}
}
@@ -228,23 +260,21 @@ log_end_list(pcmk__output_t *out) {
G_GNUC_PRINTF(2, 3)
static int
-log_info(pcmk__output_t *out, const char *format, ...) {
- private_data_t *priv = NULL;
- int len = 0;
+log_info(pcmk__output_t *out, const char *format, ...)
+{
va_list ap;
- char* buffer = NULL;
+ private_data_t *priv = NULL;
CRM_ASSERT(out != NULL && out->priv != NULL);
priv = out->priv;
+ /* Informational output does not get indented, to separate it from other
+ * potentially indented list output.
+ */
va_start(ap, format);
- len = vasprintf(&buffer, format, ap);
- CRM_ASSERT(len >= 0);
+ logger_va(priv, priv->log_level, format, ap);
va_end(ap);
- do_crm_log(priv->log_level, "%s", buffer);
-
- free(buffer);
return pcmk_rc_ok;
}
@@ -252,22 +282,16 @@ G_GNUC_PRINTF(2, 3)
static int
log_transient(pcmk__output_t *out, const char *format, ...)
{
- private_data_t *priv = NULL;
- int len = 0;
va_list ap;
- char *buffer = NULL;
+ private_data_t *priv = NULL;
CRM_ASSERT(out != NULL && out->priv != NULL);
priv = out->priv;
va_start(ap, format);
- len = vasprintf(&buffer, format, ap);
- CRM_ASSERT(len >= 0);
+ logger_va(priv, QB_MAX(priv->log_level, LOG_DEBUG), format, ap);
va_end(ap);
- do_crm_log(QB_MAX(priv->log_level, LOG_DEBUG), "%s", buffer);
-
- free(buffer);
return pcmk_rc_ok;
}
@@ -351,3 +375,33 @@ pcmk__output_set_log_level(pcmk__output_t *out, uint8_t log_level) {
priv = out->priv;
priv->log_level = log_level;
}
+
+/*!
+ * \internal
+ * \brief Set the file, function, line, and tags used to filter log output
+ *
+ * \param[in,out] out Logger output object
+ * \param[in] file File name to filter with (or NULL for default)
+ * \param[in] function Function name to filter with (or NULL for default)
+ * \param[in] line Line number to filter with (or 0 for default)
+ * \param[in] tags Tags to filter with (or 0 for none)
+ *
+ * \note Custom filters should generally be used only in short areas of a single
+ * function. When done, callers should call this function again with
+ * NULL/0 arguments to reset the filters.
+ */
+void
+pcmk__output_set_log_filter(pcmk__output_t *out, const char *file,
+ const char *function, uint32_t line, uint32_t tags)
+{
+ private_data_t *priv = NULL;
+
+ CRM_ASSERT((out != NULL) && (out->priv != NULL));
+ CRM_CHECK(pcmk__str_eq(out->fmt_name, "log", pcmk__str_none), return);
+
+ priv = out->priv;
+ priv->file = file;
+ priv->function = function;
+ priv->line = line;
+ priv->tags = tags;
+}
diff --git a/lib/common/output_xml.c b/lib/common/output_xml.c
index 0972638..ba61145 100644
--- a/lib/common/output_xml.c
+++ b/lib/common/output_xml.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2019-2022 the Pacemaker project contributors
+ * Copyright 2019-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -13,6 +13,10 @@
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
+#include <crm/crm.h>
+#include <crm/common/output.h>
+#include <crm/common/xml.h>
+#include <crm/common/xml_internal.h> /* pcmk__xml2fd */
#include <glib.h>
#include <crm/common/cmdline_internal.h>
@@ -43,8 +47,8 @@ typedef struct subst_s {
static subst_t substitutions[] = {
{ "Active Resources", "resources" },
- { "Allocation Scores", "allocations" },
- { "Allocation Scores and Utilization Information", "allocations_utilizations" },
+ { "Assignment Scores", "allocations" },
+ { "Assignment Scores and Utilization Information", "allocations_utilizations" },
{ "Cluster Summary", "summary" },
{ "Current cluster status", "cluster_status" },
{ "Executing Cluster Transition", "transition" },
@@ -190,10 +194,7 @@ xml_finish(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_
}
if (print) {
- char *buf = dump_xml_formatted_with_text(priv->root);
- fprintf(out->dest, "%s", buf);
- fflush(out->dest);
- free(buf);
+ pcmk__xml2fd(fileno(out->dest), priv->root);
}
if (copy_dest != NULL) {
@@ -286,7 +287,10 @@ xml_output_xml(pcmk__output_t *out, const char *name, const char *buf) {
CRM_ASSERT(out != NULL);
parent = pcmk__output_create_xml_node(out, name, NULL);
- cdata_node = xmlNewCDataBlock(getDocPtr(parent), (pcmkXmlStr) buf, strlen(buf));
+ if (parent == NULL) {
+ return;
+ }
+ cdata_node = xmlNewCDataBlock(parent->doc, (pcmkXmlStr) buf, strlen(buf));
xmlAddChild(parent, cdata_node);
}
diff --git a/lib/common/patchset.c b/lib/common/patchset.c
index 8c1362d..34e27fb 100644
--- a/lib/common/patchset.c
+++ b/lib/common/patchset.c
@@ -41,6 +41,14 @@ add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
xml_node_private_t *nodepriv = xml->_private;
const char *value = NULL;
+ if (nodepriv == NULL) {
+ /* Elements that shouldn't occur in a CIB don't have _private set. They
+ * should be stripped out, ignored, or have an error thrown by any code
+ * that processes their parent, so we ignore any changes to them.
+ */
+ return;
+ }
+
// If this XML node is new, just report that
if (patchset && pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
GString *xpath = pcmk__element_xpath(xml->parent);
@@ -93,7 +101,7 @@ add_xml_changes_to_patchset(xmlNode *xml, xmlNode *patchset)
} else {
crm_xml_add(attr, XML_DIFF_OP, "set");
- value = crm_element_value(xml, (const char *) pIter->name);
+ value = pcmk__xml_attr_value(pIter);
crm_xml_add(attr, XML_NVPAIR_ATTR_VALUE, value);
}
}
@@ -189,7 +197,7 @@ xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
return;
}
- tag = "diff-removed";
+ tag = XML_TAG_DIFF_REMOVED;
diff_child = find_xml_node(local_diff, tag, FALSE);
if (diff_child == NULL) {
diff_child = create_xml_node(local_diff, tag);
@@ -210,7 +218,7 @@ xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
}
}
- tag = "diff-added";
+ tag = XML_TAG_DIFF_ADDED;
diff_child = find_xml_node(local_diff, tag, FALSE);
if (diff_child == NULL) {
diff_child = create_xml_node(local_diff, tag);
@@ -229,7 +237,8 @@ xml_repair_v1_diff(xmlNode *last, xmlNode *next, xmlNode *local_diff,
}
for (xmlAttrPtr a = pcmk__xe_first_attr(next); a != NULL; a = a->next) {
- const char *p_value = crm_element_value(next, (const char *) a->name);
+
+ const char *p_value = pcmk__xml_attr_value(a);
xmlSetProp(cib, a->name, (pcmkXmlStr) p_value);
}
@@ -246,7 +255,7 @@ xml_create_patchset_v1(xmlNode *source, xmlNode *target, bool config,
if (patchset) {
CRM_LOG_ASSERT(xml_document_dirty(target));
xml_repair_v1_diff(source, target, patchset, config);
- crm_xml_add(patchset, "format", "1");
+ crm_xml_add(patchset, PCMK_XA_FORMAT, "1");
}
return patchset;
}
@@ -276,7 +285,7 @@ xml_create_patchset_v2(xmlNode *source, xmlNode *target)
docpriv = target->doc->_private;
patchset = create_xml_node(NULL, XML_TAG_DIFF);
- crm_xml_add_int(patchset, "format", 2);
+ crm_xml_add_int(patchset, PCMK_XA_FORMAT, 2);
version = create_xml_node(patchset, XML_DIFF_VERSION);
@@ -389,7 +398,7 @@ patchset_process_digest(xmlNode *patch, xmlNode *source, xmlNode *target,
*/
CRM_LOG_ASSERT(!xml_document_dirty(target));
- crm_element_value_int(patch, "format", &format);
+ crm_element_value_int(patch, PCMK_XA_FORMAT, &format);
if ((format > 1) && !with_digest) {
return;
}
@@ -418,7 +427,6 @@ process_v1_removals(xmlNode *target, xmlNode *patch)
xmlNode *cIter = NULL;
char *id = NULL;
- const char *name = NULL;
const char *value = NULL;
if ((target == NULL) || (patch == NULL)) {
@@ -431,18 +439,15 @@ process_v1_removals(xmlNode *target, xmlNode *patch)
subtract_xml_comment(target->parent, target, patch, &dummy);
}
- name = crm_element_name(target);
- CRM_CHECK(name != NULL, return);
- CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(patch),
- pcmk__str_casei),
- return);
+ CRM_CHECK(pcmk__xe_is(target, (const char *) patch->name), return);
CRM_CHECK(pcmk__str_eq(ID(target), ID(patch), pcmk__str_casei), return);
// Check for XML_DIFF_MARKER in a child
id = crm_element_value_copy(target, XML_ATTR_ID);
value = crm_element_value(patch, XML_DIFF_MARKER);
if ((value != NULL) && (strcmp(value, "removed:top") == 0)) {
- crm_trace("We are the root of the deletion: %s.id=%s", name, id);
+ crm_trace("We are the root of the deletion: %s.id=%s",
+ target->name, id);
free_xml(target);
free(id);
return;
@@ -482,18 +487,17 @@ process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch)
}
// Check for XML_DIFF_MARKER in a child
+ name = (const char *) patch->name;
value = crm_element_value(patch, XML_DIFF_MARKER);
if ((target == NULL) && (value != NULL)
&& (strcmp(value, "added:top") == 0)) {
id = ID(patch);
- name = crm_element_name(patch);
crm_trace("We are the root of the addition: %s.id=%s", name, id);
add_node_copy(parent, patch);
return;
} else if (target == NULL) {
id = ID(patch);
- name = crm_element_name(patch);
crm_err("Could not locate: %s.id=%s", name, id);
return;
}
@@ -502,17 +506,13 @@ process_v1_additions(xmlNode *parent, xmlNode *target, xmlNode *patch)
pcmk__xc_update(parent, target, patch);
}
- name = crm_element_name(target);
- CRM_CHECK(name != NULL, return);
- CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(patch),
- pcmk__str_casei),
- return);
+ CRM_CHECK(pcmk__xe_is(target, name), return);
CRM_CHECK(pcmk__str_eq(ID(target), ID(patch), pcmk__str_casei), return);
for (xIter = pcmk__xe_first_attr(patch); xIter != NULL;
xIter = xIter->next) {
const char *p_name = (const char *) xIter->name;
- const char *p_value = crm_element_value(patch, p_name);
+ const char *p_value = pcmk__xml_attr_value(xIter);
xml_remove_prop(target, p_name); // Preserve patch order
crm_xml_add(target, p_name, p_value);
@@ -547,7 +547,7 @@ find_patch_xml_node(const xmlNode *patchset, int format, bool added,
switch (format) {
case 1:
- label = added? "diff-added" : "diff-removed";
+ label = added? XML_TAG_DIFF_ADDED : XML_TAG_DIFF_REMOVED;
*patch_node = find_xml_node(patchset, label, FALSE);
cib_node = find_xml_node(*patch_node, "cib", FALSE);
if (cib_node != NULL) {
@@ -582,7 +582,7 @@ xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
};
- crm_element_value_int(patchset, "format", &format);
+ crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
/* Process removals */
if (!find_patch_xml_node(patchset, format, FALSE, &tmp)) {
@@ -614,12 +614,11 @@ xml_patch_versions(const xmlNode *patchset, int add[3], int del[3])
*
* \param[in] xml Root of current CIB
* \param[in] patchset Patchset to check
- * \param[in] format Patchset version
*
* \return Standard Pacemaker return code
*/
static int
-xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset, int format)
+xml_patch_version_check(const xmlNode *xml, const xmlNode *patchset)
{
int lpc = 0;
bool changed = FALSE;
@@ -701,8 +700,8 @@ apply_v1_patchset(xmlNode *xml, const xmlNode *patchset)
int root_nodes_seen = 0;
xmlNode *child_diff = NULL;
- xmlNode *added = find_xml_node(patchset, "diff-added", FALSE);
- xmlNode *removed = find_xml_node(patchset, "diff-removed", FALSE);
+ xmlNode *added = find_xml_node(patchset, XML_TAG_DIFF_ADDED, FALSE);
+ xmlNode *removed = find_xml_node(patchset, XML_TAG_DIFF_REMOVED, FALSE);
xmlNode *old = copy_xml(xml);
crm_trace("Subtraction Phase");
@@ -981,7 +980,7 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset)
for (xmlAttrPtr pIter = pcmk__xe_first_attr(attrs); pIter != NULL;
pIter = pIter->next) {
const char *name = (const char *) pIter->name;
- const char *value = crm_element_value(attrs, name);
+ const char *value = pcmk__xml_attr_value(pIter);
crm_xml_add(match, name, value);
}
@@ -1022,6 +1021,10 @@ apply_v2_patchset(xmlNode *xml, const xmlNode *patchset)
}
child = xmlDocCopyNode(change->children, match->doc, 1);
+ if (child == NULL) {
+ return ENOMEM;
+ }
+
if (match_child) {
crm_trace("Adding %s at position %d", child->name, position);
xmlAddPrevSibling(match_child, child);
@@ -1098,43 +1101,31 @@ xml_apply_patchset(xmlNode *xml, xmlNode *patchset, bool check_version)
int format = 1;
int rc = pcmk_ok;
xmlNode *old = NULL;
- const char *digest = crm_element_value(patchset, XML_ATTR_DIGEST);
+ const char *digest = NULL;
if (patchset == NULL) {
return rc;
}
- pcmk__if_tracing(
- {
- pcmk__output_t *logger_out = NULL;
-
- rc = pcmk_rc2legacy(pcmk__log_output_new(&logger_out));
- CRM_CHECK(rc == pcmk_ok, return rc);
+ pcmk__log_xml_patchset(LOG_TRACE, patchset);
- pcmk__output_set_log_level(logger_out, LOG_TRACE);
- rc = logger_out->message(logger_out, "xml-patchset", patchset);
- logger_out->finish(logger_out, pcmk_rc2exitc(rc), true,
- NULL);
- pcmk__output_free(logger_out);
- rc = pcmk_ok;
- },
- {}
- );
-
- crm_element_value_int(patchset, "format", &format);
if (check_version) {
- rc = pcmk_rc2legacy(xml_patch_version_check(xml, patchset, format));
+ rc = pcmk_rc2legacy(xml_patch_version_check(xml, patchset));
if (rc != pcmk_ok) {
return rc;
}
}
- if (digest) {
- // Make it available for logging if result doesn't have expected digest
- old = copy_xml(xml);
+ digest = crm_element_value(patchset, XML_ATTR_DIGEST);
+ if (digest != NULL) {
+ /* Make original XML available for logging in case result doesn't have
+ * expected digest
+ */
+ pcmk__if_tracing(old = copy_xml(xml), {});
}
if (rc == pcmk_ok) {
+ crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
switch (format) {
case 1:
rc = pcmk_rc2legacy(apply_v1_patchset(xml, patchset));
@@ -1195,9 +1186,9 @@ xmlNode *
diff_xml_object(xmlNode *old, xmlNode *new, gboolean suppress)
{
xmlNode *tmp1 = NULL;
- xmlNode *diff = create_xml_node(NULL, "diff");
- xmlNode *removed = create_xml_node(diff, "diff-removed");
- xmlNode *added = create_xml_node(diff, "diff-added");
+ xmlNode *diff = create_xml_node(NULL, XML_TAG_DIFF);
+ xmlNode *removed = create_xml_node(diff, XML_TAG_DIFF_REMOVED);
+ xmlNode *added = create_xml_node(diff, XML_TAG_DIFF_ADDED);
crm_xml_add(diff, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
@@ -1268,11 +1259,12 @@ subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
}
id = ID(left);
+ name = (const char *) left->name;
if (right == NULL) {
xmlNode *deleted = NULL;
crm_trace("Processing <%s " XML_ATTR_ID "=%s> (complete copy)",
- crm_element_name(left), id);
+ name, id);
deleted = add_node_copy(parent, left);
crm_xml_add(deleted, XML_DIFF_MARKER, marker);
@@ -1280,11 +1272,8 @@ subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
return deleted;
}
- name = crm_element_name(left);
CRM_CHECK(name != NULL, return NULL);
- CRM_CHECK(pcmk__str_eq(crm_element_name(left), crm_element_name(right),
- pcmk__str_casei),
- return NULL);
+ CRM_CHECK(pcmk__xe_is(left, (const char *) right->name), return NULL);
// Check for XML_DIFF_MARKER in a child
value = crm_element_value(right, XML_DIFF_MARKER);
@@ -1367,7 +1356,7 @@ subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
break;
} else {
- const char *left_value = crm_element_value(left, prop_name);
+ const char *left_value = pcmk__xml_attr_value(xIter);
xmlSetProp(diff, (pcmkXmlStr) prop_name, (pcmkXmlStr) value);
crm_xml_add(diff, prop_name, left_value);
@@ -1375,7 +1364,7 @@ subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
} else {
/* Only now do we need the left value */
- const char *left_value = crm_element_value(left, prop_name);
+ const char *left_value = pcmk__xml_attr_value(xIter);
if (strcmp(left_value, right_val) == 0) {
/* unchanged */
@@ -1386,8 +1375,7 @@ subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
xmlAttrPtr pIter = NULL;
crm_trace("Changes detected to %s in "
- "<%s " XML_ATTR_ID "=%s>",
- prop_name, crm_element_name(left), id);
+ "<%s " XML_ATTR_ID "=%s>", prop_name, name, id);
for (pIter = pcmk__xe_first_attr(left); pIter != NULL;
pIter = pIter->next) {
const char *p_name = (const char *) pIter->name;
@@ -1401,8 +1389,7 @@ subtract_xml_object(xmlNode *parent, xmlNode *left, xmlNode *right,
} else {
crm_trace("Changes detected to %s (%s -> %s) in "
"<%s " XML_ATTR_ID "=%s>",
- prop_name, left_value, right_val,
- crm_element_name(left), id);
+ prop_name, left_value, right_val, name, id);
crm_xml_add(diff, prop_name, left_value);
}
}
@@ -1434,8 +1421,8 @@ apply_xml_diff(xmlNode *old_xml, xmlNode *diff, xmlNode **new_xml)
const char *version = crm_element_value(diff, XML_ATTR_CRM_VERSION);
xmlNode *child_diff = NULL;
- xmlNode *added = find_xml_node(diff, "diff-added", FALSE);
- xmlNode *removed = find_xml_node(diff, "diff-removed", FALSE);
+ xmlNode *added = find_xml_node(diff, XML_TAG_DIFF_ADDED, FALSE);
+ xmlNode *removed = find_xml_node(diff, XML_TAG_DIFF_REMOVED, FALSE);
CRM_CHECK(new_xml != NULL, return FALSE);
diff --git a/lib/common/patchset_display.c b/lib/common/patchset_display.c
index 731d437..5cc0b52 100644
--- a/lib/common/patchset_display.c
+++ b/lib/common/patchset_display.c
@@ -47,7 +47,7 @@ xml_show_patchset_header(pcmk__output_t *out, const xmlNode *patchset)
xml_patch_versions(patchset, add, del);
if ((add[0] != del[0]) || (add[1] != del[1]) || (add[2] != del[2])) {
- const char *fmt = crm_element_value(patchset, "format");
+ const char *fmt = crm_element_value(patchset, PCMK_XA_FORMAT);
const char *digest = crm_element_value(patchset, XML_ATTR_DIGEST);
out->info(out, "Diff: --- %d.%d.%d %s", del[0], del[1], del[2], fmt);
@@ -80,7 +80,7 @@ static int
xml_show_patchset_v1_recursive(pcmk__output_t *out, const char *prefix,
const xmlNode *data, int depth, uint32_t options)
{
- if (!xml_has_children(data)
+ if ((data->children == NULL)
|| (crm_element_value(data, XML_DIFF_MARKER) != NULL)) {
// Found a change; clear the pcmk__xml_fmt_diff_short option if set
@@ -143,7 +143,7 @@ xml_show_patchset_v1(pcmk__output_t *out, const xmlNode *patchset,
* However, v1 patchsets can only exist during rolling upgrades from
* Pacemaker 1.1.11, so not worth worrying about.
*/
- removed = find_xml_node(patchset, "diff-removed", FALSE);
+ removed = find_xml_node(patchset, XML_TAG_DIFF_REMOVED, FALSE);
for (child = pcmk__xml_first_child(removed); child != NULL;
child = pcmk__xml_next(child)) {
int temp_rc = xml_show_patchset_v1_recursive(out, "- ", child, 0,
@@ -159,7 +159,7 @@ xml_show_patchset_v1(pcmk__output_t *out, const xmlNode *patchset,
}
is_first = true;
- added = find_xml_node(patchset, "diff-added", FALSE);
+ added = find_xml_node(patchset, XML_TAG_DIFF_ADDED, FALSE);
for (child = pcmk__xml_first_child(added); child != NULL;
child = pcmk__xml_next(child)) {
int temp_rc = xml_show_patchset_v1_recursive(out, "+ ", child, 0,
@@ -303,11 +303,11 @@ xml_show_patchset_v2(pcmk__output_t *out, const xmlNode *patchset)
*
* \note \p args should contain only the XML patchset
*/
-PCMK__OUTPUT_ARGS("xml-patchset", "xmlNodePtr")
+PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
static int
xml_patchset_default(pcmk__output_t *out, va_list args)
{
- xmlNodePtr patchset = va_arg(args, xmlNodePtr);
+ const xmlNode *patchset = va_arg(args, const xmlNode *);
int format = 1;
@@ -316,7 +316,7 @@ xml_patchset_default(pcmk__output_t *out, va_list args)
return pcmk_rc_no_output;
}
- crm_element_value_int(patchset, "format", &format);
+ crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
switch (format) {
case 1:
return xml_show_patchset_v1(out, patchset, pcmk__xml_fmt_pretty);
@@ -342,13 +342,13 @@ xml_patchset_default(pcmk__output_t *out, va_list args)
*
* \note \p args should contain only the XML patchset
*/
-PCMK__OUTPUT_ARGS("xml-patchset", "xmlNodePtr")
+PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
static int
xml_patchset_log(pcmk__output_t *out, va_list args)
{
static struct qb_log_callsite *patchset_cs = NULL;
- xmlNodePtr patchset = va_arg(args, xmlNodePtr);
+ const xmlNode *patchset = va_arg(args, const xmlNode *);
uint8_t log_level = pcmk__output_get_log_level(out);
int format = 1;
@@ -373,7 +373,7 @@ xml_patchset_log(pcmk__output_t *out, va_list args)
return pcmk_rc_no_output;
}
- crm_element_value_int(patchset, "format", &format);
+ crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
switch (format) {
case 1:
if (log_level < LOG_DEBUG) {
@@ -404,11 +404,11 @@ xml_patchset_log(pcmk__output_t *out, va_list args)
*
* \note \p args should contain only the XML patchset
*/
-PCMK__OUTPUT_ARGS("xml-patchset", "xmlNodePtr")
+PCMK__OUTPUT_ARGS("xml-patchset", "const xmlNode *")
static int
xml_patchset_xml(pcmk__output_t *out, va_list args)
{
- xmlNodePtr patchset = va_arg(args, xmlNodePtr);
+ const xmlNode *patchset = va_arg(args, const xmlNode *);
if (patchset != NULL) {
char *buf = dump_xml_formatted_with_text(patchset);
@@ -490,7 +490,7 @@ xml_log_patchset(uint8_t log_level, const char *function,
goto done;
}
- crm_element_value_int(patchset, "format", &format);
+ crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
switch (format) {
case 1:
if (log_level < LOG_DEBUG) {
diff --git a/lib/common/remote.c b/lib/common/remote.c
index 8c5969a..fe19296 100644
--- a/lib/common/remote.c
+++ b/lib/common/remote.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2008-2022 the Pacemaker project contributors
+ * Copyright 2008-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -167,7 +167,8 @@ set_minimum_dh_bits(const gnutls_session_t *session)
{
int dh_min_bits;
- pcmk__scan_min_int(getenv("PCMK_dh_min_bits"), &dh_min_bits, 0);
+ pcmk__scan_min_int(pcmk__env_option(PCMK__ENV_DH_MIN_BITS), &dh_min_bits,
+ 0);
/* This function is deprecated since GnuTLS 3.1.7, in favor of letting
* the priority string imply the DH requirements, but this is the only
@@ -186,8 +187,11 @@ get_bound_dh_bits(unsigned int dh_bits)
int dh_min_bits;
int dh_max_bits;
- pcmk__scan_min_int(getenv("PCMK_dh_min_bits"), &dh_min_bits, 0);
- pcmk__scan_min_int(getenv("PCMK_dh_max_bits"), &dh_max_bits, 0);
+ pcmk__scan_min_int(pcmk__env_option(PCMK__ENV_DH_MIN_BITS), &dh_min_bits,
+ 0);
+ pcmk__scan_min_int(pcmk__env_option(PCMK__ENV_DH_MAX_BITS), &dh_max_bits,
+ 0);
+
if ((dh_max_bits > 0) && (dh_max_bits < dh_min_bits)) {
crm_warn("Ignoring PCMK_dh_max_bits less than PCMK_dh_min_bits");
dh_max_bits = 0;
@@ -228,7 +232,7 @@ pcmk__new_tls_session(int csock, unsigned int conn_type,
* http://www.manpagez.com/info/gnutls/gnutls-2.10.4/gnutls_81.php#Echo-Server-with-anonymous-authentication
*/
- prio_base = getenv("PCMK_tls_priorities");
+ prio_base = pcmk__env_option(PCMK__ENV_TLS_PRIORITIES);
if (prio_base == NULL) {
prio_base = PCMK_GNUTLS_PRIORITIES;
}
@@ -485,7 +489,7 @@ remote_send_iovs(pcmk__remote_t *remote, struct iovec *iov, int iovs)
* \return Standard Pacemaker return code
*/
int
-pcmk__remote_send_xml(pcmk__remote_t *remote, xmlNode *msg)
+pcmk__remote_send_xml(pcmk__remote_t *remote, const xmlNode *msg)
{
int rc = pcmk_rc_ok;
static uint64_t id = 0;
@@ -558,16 +562,17 @@ pcmk__remote_message_xml(pcmk__remote_t *remote)
rc = BZ2_bzBuffToBuffDecompress(uncompressed + header->payload_offset, &size_u,
remote->buffer + header->payload_offset,
header->payload_compressed, 1, 0);
+ rc = pcmk__bzlib2rc(rc);
- if (rc != BZ_OK && header->version > REMOTE_MSG_VERSION) {
+ if (rc != pcmk_rc_ok && header->version > REMOTE_MSG_VERSION) {
crm_warn("Couldn't decompress v%d message, we only understand v%d",
header->version, REMOTE_MSG_VERSION);
free(uncompressed);
return NULL;
- } else if (rc != BZ_OK) {
- crm_err("Decompression failed: %s " CRM_XS " bzerror=%d",
- bz2_strerror(rc), rc);
+ } else if (rc != pcmk_rc_ok) {
+ crm_err("Decompression failed: %s " CRM_XS " rc=%d",
+ pcmk_rc_str(rc), rc);
free(uncompressed);
return NULL;
}
@@ -1079,13 +1084,16 @@ pcmk__connect_remote(const char *host, int port, int timeout, int *timer_id,
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_CANONNAME;
+
rc = getaddrinfo(server, NULL, &hints, &res);
- if (rc != 0) {
+ rc = pcmk__gaierror2rc(rc);
+
+ if (rc != pcmk_rc_ok) {
crm_err("Unable to get IP address info for %s: %s",
- server, gai_strerror(rc));
- rc = ENOTCONN;
+ server, pcmk_rc_str(rc));
goto async_cleanup;
}
+
if (!res || !res->ai_addr) {
crm_err("Unable to get IP address info for %s: no result", server);
rc = ENOTCONN;
@@ -1252,13 +1260,14 @@ crm_default_remote_port(void)
static int port = 0;
if (port == 0) {
- const char *env = getenv("PCMK_remote_port");
+ const char *env = pcmk__env_option(PCMK__ENV_REMOTE_PORT);
if (env) {
errno = 0;
port = strtol(env, NULL, 10);
if (errno || (port < 1) || (port > 65535)) {
- crm_warn("Environment variable PCMK_remote_port has invalid value '%s', using %d instead",
+ crm_warn("Environment variable PCMK_" PCMK__ENV_REMOTE_PORT
+ " has invalid value '%s', using %d instead",
env, DEFAULT_REMOTE_PORT);
port = DEFAULT_REMOTE_PORT;
}
diff --git a/lib/common/results.c b/lib/common/results.c
index 93d79eb..dde8b27 100644
--- a/lib/common/results.c
+++ b/lib/common/results.c
@@ -15,6 +15,7 @@
#include <bzlib.h>
#include <errno.h>
+#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <qb/qbdefs.h>
@@ -305,6 +306,18 @@ static const struct pcmk__rc_info {
"Bad XML patch format",
-pcmk_err_generic,
},
+ { "pcmk_rc_no_transaction",
+ "No active transaction found",
+ -pcmk_err_generic,
+ },
+ { "pcmk_rc_ns_resolution",
+ "Nameserver resolution error",
+ -pcmk_err_generic,
+ },
+ { "pcmk_rc_compression",
+ "Compression/decompression error",
+ -pcmk_err_generic,
+ },
};
/*!
@@ -716,6 +729,7 @@ pcmk_rc2exitc(int rc)
case ENOSYS:
case EOVERFLOW:
case pcmk_rc_underflow:
+ case pcmk_rc_compression:
return CRM_EX_SOFTWARE;
case EBADMSG:
@@ -759,10 +773,12 @@ pcmk_rc2exitc(int rc)
case ENODEV:
case ENOENT:
case ENXIO:
+ case pcmk_rc_no_transaction:
case pcmk_rc_unknown_format:
return CRM_EX_NOSUCH;
case pcmk_rc_node_unknown:
+ case pcmk_rc_ns_resolution:
return CRM_EX_NOHOST;
case ETIME:
@@ -837,37 +853,83 @@ pcmk_rc2ocf(int rc)
// Other functions
-const char *
-bz2_strerror(int rc)
+/*!
+ * \brief Map a getaddrinfo() return code to the most similar Pacemaker
+ * return code
+ *
+ * \param[in] gai getaddrinfo() return code
+ *
+ * \return Most similar Pacemaker return code
+ */
+int
+pcmk__gaierror2rc(int gai)
{
- // See ftp://sources.redhat.com/pub/bzip2/docs/manual_3.html#SEC17
- switch (rc) {
+ switch (gai) {
+ case 0:
+ return pcmk_rc_ok;
+
+ case EAI_AGAIN:
+ return EAGAIN;
+
+ case EAI_BADFLAGS:
+ case EAI_SERVICE:
+ return EINVAL;
+
+ case EAI_FAMILY:
+ return EAFNOSUPPORT;
+
+ case EAI_MEMORY:
+ return ENOMEM;
+
+ case EAI_NONAME:
+ return pcmk_rc_node_unknown;
+
+ case EAI_SOCKTYPE:
+ return ESOCKTNOSUPPORT;
+
+ case EAI_SYSTEM:
+ return errno;
+
+ default:
+ return pcmk_rc_ns_resolution;
+ }
+}
+
+/*!
+ * \brief Map a bz2 return code to the most similar Pacemaker return code
+ *
+ * \param[in] bz2 bz2 return code
+ *
+ * \return Most similar Pacemaker return code
+ */
+int
+pcmk__bzlib2rc(int bz2)
+{
+ switch (bz2) {
case BZ_OK:
case BZ_RUN_OK:
case BZ_FLUSH_OK:
case BZ_FINISH_OK:
case BZ_STREAM_END:
- return "Ok";
- case BZ_CONFIG_ERROR:
- return "libbz2 has been improperly compiled on your platform";
- case BZ_SEQUENCE_ERROR:
- return "library functions called in the wrong order";
- case BZ_PARAM_ERROR:
- return "parameter is out of range or otherwise incorrect";
+ return pcmk_rc_ok;
+
case BZ_MEM_ERROR:
- return "memory allocation failed";
+ return ENOMEM;
+
case BZ_DATA_ERROR:
- return "data integrity error is detected during decompression";
case BZ_DATA_ERROR_MAGIC:
- return "the compressed stream does not start with the correct magic bytes";
- case BZ_IO_ERROR:
- return "error reading or writing in the compressed file";
case BZ_UNEXPECTED_EOF:
- return "compressed file finishes before the logical end of stream is detected";
+ return pcmk_rc_bad_input;
+
+ case BZ_IO_ERROR:
+ return EIO;
+
case BZ_OUTBUFF_FULL:
- return "output data will not fit into the buffer provided";
+ return EFBIG;
+
+ default:
+ return pcmk_rc_compression;
}
- return "Data compression error";
}
crm_exit_t
@@ -1039,6 +1101,39 @@ pcmk__copy_result(const pcmk__action_result_t *src, pcmk__action_result_t *dst)
#include <crm/common/results_compat.h>
+const char *
+bz2_strerror(int rc)
+{
+ // See ftp://sources.redhat.com/pub/bzip2/docs/manual_3.html#SEC17
+ switch (rc) {
+ case BZ_OK:
+ case BZ_RUN_OK:
+ case BZ_FLUSH_OK:
+ case BZ_FINISH_OK:
+ case BZ_STREAM_END:
+ return "Ok";
+ case BZ_CONFIG_ERROR:
+ return "libbz2 has been improperly compiled on your platform";
+ case BZ_SEQUENCE_ERROR:
+ return "library functions called in the wrong order";
+ case BZ_PARAM_ERROR:
+ return "parameter is out of range or otherwise incorrect";
+ case BZ_MEM_ERROR:
+ return "memory allocation failed";
+ case BZ_DATA_ERROR:
+ return "data integrity error is detected during decompression";
+ case BZ_DATA_ERROR_MAGIC:
+ return "the compressed stream does not start with the correct magic bytes";
+ case BZ_IO_ERROR:
+ return "error reading or writing in the compressed file";
+ case BZ_UNEXPECTED_EOF:
+ return "compressed file finishes before the logical end of stream is detected";
+ case BZ_OUTBUFF_FULL:
+ return "output data will not fit into the buffer provided";
+ }
+ return "Data compression error";
+}
+
crm_exit_t
crm_errno2exit(int rc)
{
diff --git a/lib/common/scheduler.c b/lib/common/scheduler.c
new file mode 100644
index 0000000..20e6fdf
--- /dev/null
+++ b/lib/common/scheduler.c
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2004-2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <stdint.h> // uint32_t
+
+uint32_t pcmk__warnings = 0;
diff --git a/lib/common/schemas.c b/lib/common/schemas.c
index 88a3051..b3c09eb 100644
--- a/lib/common/schemas.c
+++ b/lib/common/schemas.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2022 the Pacemaker project contributors
+ * Copyright 2004-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -432,34 +432,8 @@ crm_schema_init(void)
NULL, NULL, FALSE, -1);
}
-#if 0
-static void
-relaxng_invalid_stderr(void *userData, xmlErrorPtr error)
-{
- /*
- Structure xmlError
- struct _xmlError {
- int domain : What part of the library raised this er
- int code : The error code, e.g. an xmlParserError
- char * message : human-readable informative error messag
- xmlErrorLevel level : how consequent is the error
- char * file : the filename
- int line : the line number if available
- char * str1 : extra string information
- char * str2 : extra string information
- char * str3 : extra string information
- int int1 : extra number information
- int int2 : column number of the error or 0 if N/A
- void * ctxt : the parser context if available
- void * node : the node in the tree
- }
- */
- crm_err("Structured error: line=%d, level=%d %s", error->line, error->level, error->message);
-}
-#endif
-
static gboolean
-validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
+validate_with_relaxng(xmlDocPtr doc, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context, const char *relaxng_file,
relaxng_ctx_cache_t **cached_ctx)
{
int rc = 0;
@@ -476,15 +450,14 @@ validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
crm_debug("Creating RNG parser context");
ctx = calloc(1, sizeof(relaxng_ctx_cache_t));
- xmlLoadExtDtdDefaultValue = 1;
ctx->parser = xmlRelaxNGNewParserCtxt(relaxng_file);
CRM_CHECK(ctx->parser != NULL, goto cleanup);
- if (to_logs) {
+ if (error_handler) {
xmlRelaxNGSetParserErrors(ctx->parser,
- (xmlRelaxNGValidityErrorFunc) xml_log,
- (xmlRelaxNGValidityWarningFunc) xml_log,
- GUINT_TO_POINTER(LOG_ERR));
+ (xmlRelaxNGValidityErrorFunc) error_handler,
+ (xmlRelaxNGValidityWarningFunc) error_handler,
+ error_handler_context);
} else {
xmlRelaxNGSetParserErrors(ctx->parser,
(xmlRelaxNGValidityErrorFunc) fprintf,
@@ -500,11 +473,11 @@ validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
ctx->valid = xmlRelaxNGNewValidCtxt(ctx->rng);
CRM_CHECK(ctx->valid != NULL, goto cleanup);
- if (to_logs) {
+ if (error_handler) {
xmlRelaxNGSetValidErrors(ctx->valid,
- (xmlRelaxNGValidityErrorFunc) xml_log,
- (xmlRelaxNGValidityWarningFunc) xml_log,
- GUINT_TO_POINTER(LOG_ERR));
+ (xmlRelaxNGValidityErrorFunc) error_handler,
+ (xmlRelaxNGValidityWarningFunc) error_handler,
+ error_handler_context);
} else {
xmlRelaxNGSetValidErrors(ctx->valid,
(xmlRelaxNGValidityErrorFunc) fprintf,
@@ -513,10 +486,6 @@ validate_with_relaxng(xmlDocPtr doc, gboolean to_logs, const char *relaxng_file,
}
}
- /* xmlRelaxNGSetValidStructuredErrors( */
- /* valid, relaxng_invalid_stderr, valid); */
-
- xmlLineNumbersDefault(1);
rc = xmlRelaxNGValidateDoc(ctx->valid, doc);
if (rc > 0) {
valid = FALSE;
@@ -590,39 +559,36 @@ crm_schema_cleanup(void)
}
static gboolean
-validate_with(xmlNode *xml, int method, gboolean to_logs)
+validate_with(xmlNode *xml, int method, xmlRelaxNGValidityErrorFunc error_handler, void* error_handler_context)
{
- xmlDocPtr doc = NULL;
gboolean valid = FALSE;
char *file = NULL;
+ struct schema_s *schema = NULL;
+ relaxng_ctx_cache_t **cache = NULL;
if (method < 0) {
return FALSE;
}
- if (known_schemas[method].validator == schema_validator_none) {
+ schema = &(known_schemas[method]);
+ if (schema->validator == schema_validator_none) {
return TRUE;
}
- CRM_CHECK(xml != NULL, return FALSE);
-
- if (pcmk__str_eq(known_schemas[method].name, "pacemaker-next",
- pcmk__str_none)) {
+ if (pcmk__str_eq(schema->name, "pacemaker-next", pcmk__str_none)) {
crm_warn("The pacemaker-next schema is deprecated and will be removed "
"in a future release.");
}
- doc = getDocPtr(xml);
file = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_rng,
- known_schemas[method].name);
+ schema->name);
crm_trace("Validating with %s (type=%d)",
- pcmk__s(file, "missing schema"), known_schemas[method].validator);
- switch (known_schemas[method].validator) {
+ pcmk__s(file, "missing schema"), schema->validator);
+ switch (schema->validator) {
case schema_validator_rng:
- valid =
- validate_with_relaxng(doc, to_logs, file,
- (relaxng_ctx_cache_t **) & (known_schemas[method].cache));
+ cache = (relaxng_ctx_cache_t **) &(schema->cache);
+ valid = validate_with_relaxng(xml->doc, error_handler, error_handler_context, file, cache);
break;
default:
crm_err("Unknown validator type: %d",
@@ -639,7 +605,7 @@ validate_with_silent(xmlNode *xml, int method)
{
bool rc, sl_backup = silent_logging;
silent_logging = TRUE;
- rc = validate_with(xml, method, TRUE);
+ rc = validate_with(xml, method, (xmlRelaxNGValidityErrorFunc) xml_log, GUINT_TO_POINTER(LOG_ERR));
silent_logging = sl_backup;
return rc;
}
@@ -676,7 +642,7 @@ dump_file(const char *filename)
}
gboolean
-validate_xml_verbose(xmlNode *xml_blob)
+validate_xml_verbose(const xmlNode *xml_blob)
{
int fd = 0;
xmlDoc *doc = NULL;
@@ -692,7 +658,7 @@ validate_xml_verbose(xmlNode *xml_blob)
dump_file(filename);
- doc = xmlParseFile(filename);
+ doc = xmlReadFile(filename, NULL, 0);
xml = xmlDocGetRootElement(doc);
rc = validate_xml(xml, NULL, FALSE);
free_xml(xml);
@@ -706,8 +672,16 @@ validate_xml_verbose(xmlNode *xml_blob)
gboolean
validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
{
+ return pcmk__validate_xml(xml_blob, validation, to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL, GUINT_TO_POINTER(LOG_ERR));
+}
+
+gboolean
+pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void* error_handler_context)
+{
int version = 0;
+ CRM_CHECK((xml_blob != NULL) && (xml_blob->doc != NULL), return FALSE);
+
if (validation == NULL) {
validation = crm_element_value(xml_blob, XML_ATTR_VALIDATION);
}
@@ -717,7 +691,7 @@ validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
bool valid = FALSE;
for (lpc = 0; lpc < xml_schema_max; lpc++) {
- if (validate_with(xml_blob, lpc, FALSE)) {
+ if (validate_with(xml_blob, lpc, NULL, NULL)) {
valid = TRUE;
crm_xml_add(xml_blob, XML_ATTR_VALIDATION,
known_schemas[lpc].name);
@@ -735,7 +709,7 @@ validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
if (strcmp(validation, PCMK__VALUE_NONE) == 0) {
return TRUE;
} else if (version < xml_schema_max) {
- return validate_with(xml_blob, version, to_logs);
+ return validate_with(xml_blob, version, error_handler, error_handler_context);
}
crm_err("Unknown validator: %s", validation);
@@ -884,47 +858,17 @@ cib_upgrade_err(void *ctx, const char *fmt, ...)
va_end(ap);
}
-
-/* Denotes temporary emergency fix for "xmldiff'ing not text-node-ready";
- proper fix is most likely to teach __xml_diff_object and friends to
- deal with XML_TEXT_NODE (and more?), i.e., those nodes currently
- missing "_private" field (implicitly as NULL) which clashes with
- unchecked accesses (e.g. in __xml_offset) -- the outcome may be that
- those unexpected XML nodes will simply be ignored for the purpose of
- diff'ing, or it may be made more robust, or per the user's preference
- (which then may be exposed as crm_diff switch).
-
- Said XML_TEXT_NODE may appear unexpectedly due to how upgrade-2.10.xsl
- is arranged.
-
- The emergency fix is simple: reparse XSLT output with blank-ignoring
- parser. */
-#ifndef PCMK_SCHEMAS_EMERGENCY_XSLT
-#define PCMK_SCHEMAS_EMERGENCY_XSLT 1
-#endif
-
static xmlNode *
apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs)
{
char *xform = NULL;
xmlNode *out = NULL;
xmlDocPtr res = NULL;
- xmlDocPtr doc = NULL;
xsltStylesheet *xslt = NULL;
-#if PCMK_SCHEMAS_EMERGENCY_XSLT != 0
- xmlChar *emergency_result;
- int emergency_txt_len;
- int emergency_res;
-#endif
-
- CRM_CHECK(xml != NULL, return FALSE);
- doc = getDocPtr(xml);
+
xform = pcmk__xml_artefact_path(pcmk__xml_artefact_ns_legacy_xslt,
transform);
- xmlLoadExtDtdDefaultValue = 1;
- xmlSubstituteEntitiesDefault(1);
-
/* for capturing, e.g., what's emitted via <xsl:message> */
if (to_logs) {
xsltSetGenericErrorFunc(NULL, cib_upgrade_err);
@@ -935,22 +879,12 @@ apply_transformation(xmlNode *xml, const char *transform, gboolean to_logs)
xslt = xsltParseStylesheetFile((pcmkXmlStr) xform);
CRM_CHECK(xslt != NULL, goto cleanup);
- res = xsltApplyStylesheet(xslt, doc, NULL);
+ res = xsltApplyStylesheet(xslt, xml->doc, NULL);
CRM_CHECK(res != NULL, goto cleanup);
xsltSetGenericErrorFunc(NULL, NULL); /* restore default one */
-
-#if PCMK_SCHEMAS_EMERGENCY_XSLT != 0
- emergency_res = xsltSaveResultToString(&emergency_result,
- &emergency_txt_len, res, xslt);
- xmlFreeDoc(res);
- CRM_CHECK(emergency_res == 0, goto cleanup);
- out = string2xml((const char *) emergency_result);
- free(emergency_result);
-#else
out = xmlDocGetRootElement(res);
-#endif
cleanup:
if (xslt) {
@@ -1055,12 +989,15 @@ update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
int max_stable_schemas = xml_latest_schema_index();
int lpc = 0, match = -1, rc = pcmk_ok;
int next = -1; /* -1 denotes "inactive" value */
+ xmlRelaxNGValidityErrorFunc error_handler =
+ to_logs ? (xmlRelaxNGValidityErrorFunc) xml_log : NULL;
CRM_CHECK(best != NULL, return -EINVAL);
*best = 0;
- CRM_CHECK(xml_blob != NULL, return -EINVAL);
- CRM_CHECK(*xml_blob != NULL, return -EINVAL);
+ CRM_CHECK((xml_blob != NULL) && (*xml_blob != NULL)
+ && ((*xml_blob)->doc != NULL),
+ return -EINVAL);
xml = *xml_blob;
value = crm_element_value_copy(xml, XML_ATTR_VALIDATION);
@@ -1090,7 +1027,7 @@ update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
known_schemas[lpc].name ? known_schemas[lpc].name : "<unset>",
lpc, max_stable_schemas);
- if (validate_with(xml, lpc, to_logs) == FALSE) {
+ if (validate_with(xml, lpc, error_handler, GUINT_TO_POINTER(LOG_ERR)) == FALSE) {
if (next != -1) {
crm_info("Configuration not valid for schema: %s",
known_schemas[lpc].name);
@@ -1155,7 +1092,7 @@ update_validation(xmlNode **xml_blob, int *best, int max, gboolean transform,
known_schemas[lpc].transform);
rc = -pcmk_err_transform_failed;
- } else if (validate_with(upgrade, next, to_logs)) {
+ } else if (validate_with(upgrade, next, error_handler, GUINT_TO_POINTER(LOG_ERR))) {
crm_info("Transformation %s.xsl successful",
known_schemas[lpc].transform);
lpc = next;
diff --git a/lib/common/strings.c b/lib/common/strings.c
index b245102..d9d2fda 100644
--- a/lib/common/strings.c
+++ b/lib/common/strings.c
@@ -417,10 +417,7 @@ crm_is_true(const char *s)
{
gboolean ret = FALSE;
- if (s != NULL) {
- crm_str_to_boolean(s, &ret);
- }
- return ret;
+ return (crm_str_to_boolean(s, &ret) < 0)? FALSE : ret;
}
int
@@ -768,12 +765,15 @@ pcmk__compress(const char *data, unsigned int length, unsigned int max,
*result_len = max;
rc = BZ2_bzBuffToBuffCompress(compressed, result_len, uncompressed, length,
CRM_BZ2_BLOCKS, 0, CRM_BZ2_WORK);
+ rc = pcmk__bzlib2rc(rc);
+
free(uncompressed);
- if (rc != BZ_OK) {
- crm_err("Compression of %d bytes failed: %s " CRM_XS " bzerror=%d",
- length, bz2_strerror(rc), rc);
+
+ if (rc != pcmk_rc_ok) {
+ crm_err("Compression of %d bytes failed: %s " CRM_XS " rc=%d",
+ length, pcmk_rc_str(rc), rc);
free(compressed);
- return pcmk_rc_error;
+ return rc;
}
#ifdef CLOCK_MONOTONIC
diff --git a/lib/common/tests/Makefile.am b/lib/common/tests/Makefile.am
index b147309..c0407e5 100644
--- a/lib/common/tests/Makefile.am
+++ b/lib/common/tests/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -9,6 +9,7 @@
SUBDIRS = \
acl \
+ actions \
agents \
cmdline \
flags \
@@ -17,7 +18,6 @@ SUBDIRS = \
iso8601 \
lists \
nvpair \
- operations \
options \
output \
results \
diff --git a/lib/common/tests/acl/Makefile.am b/lib/common/tests/acl/Makefile.am
index 50408f9..19903db 100644
--- a/lib/common/tests/acl/Makefile.am
+++ b/lib/common/tests/acl/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2021-2022 the Pacemaker project contributors
+# Copyright 2021-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -12,10 +12,9 @@ include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- pcmk__is_user_in_group_test \
- pcmk_acl_required_test \
- xml_acl_denied_test \
- xml_acl_enabled_test
+check_PROGRAMS = pcmk__is_user_in_group_test \
+ pcmk_acl_required_test \
+ xml_acl_denied_test \
+ xml_acl_enabled_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/operations/Makefile.am b/lib/common/tests/actions/Makefile.am
index 4687e1b..6890b84 100644
--- a/lib/common/tests/operations/Makefile.am
+++ b/lib/common/tests/actions/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,12 +11,12 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = copy_in_properties_test \
- expand_plus_plus_test \
- fix_plus_plus_recursive_test \
- parse_op_key_test \
- pcmk_is_probe_test \
- pcmk_xe_is_probe_test \
- pcmk_xe_mask_probe_failure_test
+check_PROGRAMS = copy_in_properties_test \
+ expand_plus_plus_test \
+ fix_plus_plus_recursive_test \
+ parse_op_key_test \
+ pcmk_is_probe_test \
+ pcmk_xe_is_probe_test \
+ pcmk_xe_mask_probe_failure_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/operations/copy_in_properties_test.c b/lib/common/tests/actions/copy_in_properties_test.c
index 7882551..7882551 100644
--- a/lib/common/tests/operations/copy_in_properties_test.c
+++ b/lib/common/tests/actions/copy_in_properties_test.c
diff --git a/lib/common/tests/operations/expand_plus_plus_test.c b/lib/common/tests/actions/expand_plus_plus_test.c
index 41471f9..41471f9 100644
--- a/lib/common/tests/operations/expand_plus_plus_test.c
+++ b/lib/common/tests/actions/expand_plus_plus_test.c
diff --git a/lib/common/tests/operations/fix_plus_plus_recursive_test.c b/lib/common/tests/actions/fix_plus_plus_recursive_test.c
index b3c7cc2..b3c7cc2 100644
--- a/lib/common/tests/operations/fix_plus_plus_recursive_test.c
+++ b/lib/common/tests/actions/fix_plus_plus_recursive_test.c
diff --git a/lib/common/tests/operations/parse_op_key_test.c b/lib/common/tests/actions/parse_op_key_test.c
index 1b1bfff..1b1bfff 100644
--- a/lib/common/tests/operations/parse_op_key_test.c
+++ b/lib/common/tests/actions/parse_op_key_test.c
diff --git a/lib/common/tests/operations/pcmk_is_probe_test.c b/lib/common/tests/actions/pcmk_is_probe_test.c
index 4a65e3f..4a65e3f 100644
--- a/lib/common/tests/operations/pcmk_is_probe_test.c
+++ b/lib/common/tests/actions/pcmk_is_probe_test.c
diff --git a/lib/common/tests/operations/pcmk_xe_is_probe_test.c b/lib/common/tests/actions/pcmk_xe_is_probe_test.c
index 62b21d9..62b21d9 100644
--- a/lib/common/tests/operations/pcmk_xe_is_probe_test.c
+++ b/lib/common/tests/actions/pcmk_xe_is_probe_test.c
diff --git a/lib/common/tests/operations/pcmk_xe_mask_probe_failure_test.c b/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c
index 9e38019..9e38019 100644
--- a/lib/common/tests/operations/pcmk_xe_mask_probe_failure_test.c
+++ b/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c
diff --git a/lib/common/tests/agents/Makefile.am b/lib/common/tests/agents/Makefile.am
index 7a54b7d..b3837d7 100644
--- a/lib/common/tests/agents/Makefile.am
+++ b/lib/common/tests/agents/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,10 +11,10 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = crm_generate_ra_key_test \
- crm_parse_agent_spec_test \
- pcmk__effective_rc_test \
- pcmk_get_ra_caps_test \
- pcmk_stonith_param_test
+check_PROGRAMS = crm_generate_ra_key_test \
+ crm_parse_agent_spec_test \
+ pcmk__effective_rc_test \
+ pcmk_get_ra_caps_test \
+ pcmk_stonith_param_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/agents/crm_parse_agent_spec_test.c b/lib/common/tests/agents/crm_parse_agent_spec_test.c
index cfd75f0..1d44459 100644
--- a/lib/common/tests/agents/crm_parse_agent_spec_test.c
+++ b/lib/common/tests/agents/crm_parse_agent_spec_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 the Pacemaker project contributors
+ * Copyright 2022-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -22,14 +22,22 @@ all_params_null(void **state) {
static void
no_prov_or_type(void **state) {
- assert_int_equal(crm_parse_agent_spec("ocf", NULL, NULL, NULL), -EINVAL);
- assert_int_equal(crm_parse_agent_spec("ocf:", NULL, NULL, NULL), -EINVAL);
- assert_int_equal(crm_parse_agent_spec("ocf::", NULL, NULL, NULL), -EINVAL);
+ char *std = NULL;
+ char *prov = NULL;
+ char *ty = NULL;
+
+ assert_int_equal(crm_parse_agent_spec("ocf", &std, &prov, &ty), -EINVAL);
+ assert_int_equal(crm_parse_agent_spec("ocf:", &std, &prov, &ty), -EINVAL);
+ assert_int_equal(crm_parse_agent_spec("ocf::", &std, &prov, &ty), -EINVAL);
}
static void
no_type(void **state) {
- assert_int_equal(crm_parse_agent_spec("ocf:pacemaker:", NULL, NULL, NULL), -EINVAL);
+ char *std = NULL;
+ char *prov = NULL;
+ char *ty = NULL;
+
+ assert_int_equal(crm_parse_agent_spec("ocf:pacemaker:", &std, &prov, &ty), -EINVAL);
}
static void
diff --git a/lib/common/tests/cmdline/Makefile.am b/lib/common/tests/cmdline/Makefile.am
index d781ed5..792425b 100644
--- a/lib/common/tests/cmdline/Makefile.am
+++ b/lib/common/tests/cmdline/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -12,6 +12,7 @@ include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
check_PROGRAMS = pcmk__cmdline_preproc_test \
- pcmk__quote_cmdline_test
+ pcmk__new_common_args_test \
+ pcmk__quote_cmdline_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c b/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c
index 863fbb9..299fec6 100644
--- a/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c
+++ b/lib/common/tests/cmdline/pcmk__cmdline_preproc_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 the Pacemaker project contributors
+ * Copyright 2020-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -112,6 +112,16 @@ negative_score_2(void **state) {
}
static void
+negative_score_3(void **state) {
+ const char *argv[] = { "crm_attribute", "-p", "-v", "-INFINITY", NULL };
+ const gchar *expected[] = { "crm_attribute", "-p", "-v", "-INFINITY", NULL };
+
+ gchar **processed = pcmk__cmdline_preproc((char **) argv, "pv");
+ LISTS_EQ(processed, expected);
+ g_strfreev(processed);
+}
+
+static void
string_arg_with_dash(void **state) {
const char *argv[] = { "crm_mon", "-n", "crm_mon_options", "-v", "--opt1 --opt2", NULL };
const gchar *expected[] = { "crm_mon", "-n", "crm_mon_options", "-v", "--opt1 --opt2", NULL };
@@ -151,6 +161,7 @@ PCMK__UNIT_TEST(NULL, NULL,
cmocka_unit_test(long_arg),
cmocka_unit_test(negative_score),
cmocka_unit_test(negative_score_2),
+ cmocka_unit_test(negative_score_3),
cmocka_unit_test(string_arg_with_dash),
cmocka_unit_test(string_arg_with_dash_2),
cmocka_unit_test(string_arg_with_dash_3))
diff --git a/lib/common/tests/cmdline/pcmk__new_common_args_test.c b/lib/common/tests/cmdline/pcmk__new_common_args_test.c
new file mode 100644
index 0000000..6b70465
--- /dev/null
+++ b/lib/common/tests/cmdline/pcmk__new_common_args_test.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+#include <crm/common/cmdline_internal.h>
+
+#include "mock_private.h"
+
+#include <glib.h>
+
+static void
+calloc_fails(void **state)
+{
+ pcmk__assert_exits(CRM_EX_OSERR,
+ {
+ pcmk__mock_calloc = true; // calloc() will return NULL
+ expect_value(__wrap_calloc, nmemb, 1);
+ expect_value(__wrap_calloc, size, sizeof(pcmk__common_args_t));
+ pcmk__new_common_args("boring summary");
+ pcmk__mock_calloc = false; // Use real calloc()
+ }
+ );
+}
+
+static void
+strdup_fails(void **state)
+{
+ pcmk__assert_exits(CRM_EX_OSERR,
+ {
+ pcmk__mock_strdup = true; // strdup() will return NULL
+ expect_string(__wrap_strdup, s, "boring summary");
+ pcmk__new_common_args("boring summary");
+ pcmk__mock_strdup = false; // Use the real strdup()
+ }
+ );
+}
+
+static void
+success(void **state)
+{
+ pcmk__common_args_t *args = pcmk__new_common_args("boring summary");
+ assert_string_equal(args->summary, "boring summary");
+ assert_null(args->output_as_descr);
+ assert_false(args->version);
+ assert_false(args->quiet);
+ assert_int_equal(args->verbosity, 0);
+ assert_null(args->output_ty);
+ assert_null(args->output_dest);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(calloc_fails),
+ cmocka_unit_test(strdup_fails),
+ cmocka_unit_test(success))
diff --git a/lib/common/tests/flags/Makefile.am b/lib/common/tests/flags/Makefile.am
index 16d8ffb..22a101a 100644
--- a/lib/common/tests/flags/Makefile.am
+++ b/lib/common/tests/flags/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,10 +11,9 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- pcmk__clear_flags_as_test \
- pcmk__set_flags_as_test \
- pcmk_all_flags_set_test \
- pcmk_any_flags_set_test
+check_PROGRAMS = pcmk__clear_flags_as_test \
+ pcmk__set_flags_as_test \
+ pcmk_all_flags_set_test \
+ pcmk_any_flags_set_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/io/Makefile.am b/lib/common/tests/io/Makefile.am
index c26482c..f7519d8 100644
--- a/lib/common/tests/io/Makefile.am
+++ b/lib/common/tests/io/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,8 +11,7 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- pcmk__full_path_test \
- pcmk__get_tmpdir_test
+check_PROGRAMS = pcmk__full_path_test \
+ pcmk__get_tmpdir_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/lists/Makefile.am b/lib/common/tests/lists/Makefile.am
index ae0c0b6..0fa1e15 100644
--- a/lib/common/tests/lists/Makefile.am
+++ b/lib/common/tests/lists/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2022 the Pacemaker project contributors
+# Copyright 2022-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -12,9 +12,8 @@ include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- pcmk__list_of_1_test \
- pcmk__list_of_multiple_test \
- pcmk__subtract_lists_test
+check_PROGRAMS = pcmk__list_of_1_test \
+ pcmk__list_of_multiple_test \
+ pcmk__subtract_lists_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/nvpair/Makefile.am b/lib/common/tests/nvpair/Makefile.am
index 7acaba3..7f406bd 100644
--- a/lib/common/tests/nvpair/Makefile.am
+++ b/lib/common/tests/nvpair/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2021-2022 the Pacemaker project contributors
+# Copyright 2021-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,8 +11,8 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = pcmk__xe_attr_is_true_test \
- pcmk__xe_get_bool_attr_test \
- pcmk__xe_set_bool_attr_test
+check_PROGRAMS = pcmk__xe_attr_is_true_test \
+ pcmk__xe_get_bool_attr_test \
+ pcmk__xe_set_bool_attr_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/options/Makefile.am b/lib/common/tests/options/Makefile.am
index 9a5fa98..cc1008e 100644
--- a/lib/common/tests/options/Makefile.am
+++ b/lib/common/tests/options/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2022 the Pacemaker project contributors
+# Copyright 2022-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,9 +11,8 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- pcmk__env_option_test \
- pcmk__set_env_option_test \
- pcmk__env_option_enabled_test
+check_PROGRAMS = pcmk__env_option_test \
+ pcmk__set_env_option_test \
+ pcmk__env_option_enabled_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/options/pcmk__set_env_option_test.c b/lib/common/tests/options/pcmk__set_env_option_test.c
index 753bf74..22fd795 100644
--- a/lib/common/tests/options/pcmk__set_env_option_test.c
+++ b/lib/common/tests/options/pcmk__set_env_option_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 the Pacemaker project contributors
+ * Copyright 2022-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -20,18 +20,18 @@ bad_input_string(void **state)
// Never call setenv()
pcmk__mock_setenv = true;
- pcmk__set_env_option(NULL, "new_value");
- pcmk__set_env_option("", "new_value");
- pcmk__set_env_option("name=val", "new_value");
+ pcmk__set_env_option(NULL, "new_value", true);
+ pcmk__set_env_option("", "new_value", true);
+ pcmk__set_env_option("name=val", "new_value", true);
pcmk__mock_setenv = false;
// Never call unsetenv()
pcmk__mock_unsetenv = true;
- pcmk__set_env_option(NULL, NULL);
- pcmk__set_env_option("", NULL);
- pcmk__set_env_option("name=val", NULL);
+ pcmk__set_env_option(NULL, NULL, true);
+ pcmk__set_env_option("", NULL, true);
+ pcmk__set_env_option("name=val", NULL, true);
pcmk__mock_unsetenv = false;
}
@@ -53,11 +53,11 @@ input_too_long_for_both(void **state)
// Never call setenv() or unsetenv()
pcmk__mock_setenv = true;
- pcmk__set_env_option(long_opt, "new_value");
+ pcmk__set_env_option(long_opt, "new_value", true);
pcmk__mock_setenv = false;
pcmk__mock_unsetenv = true;
- pcmk__set_env_option(long_opt, NULL);
+ pcmk__set_env_option(long_opt, NULL, true);
pcmk__mock_unsetenv = false;
}
@@ -87,7 +87,7 @@ input_too_long_for_pcmk(void **state)
expect_string(__wrap_setenv, value, "new_value");
expect_value(__wrap_setenv, overwrite, 1);
will_return(__wrap_setenv, 0);
- pcmk__set_env_option(long_opt, "new_value");
+ pcmk__set_env_option(long_opt, "new_value", true);
pcmk__mock_setenv = false;
@@ -96,7 +96,7 @@ input_too_long_for_pcmk(void **state)
expect_string(__wrap_unsetenv, name, buf);
will_return(__wrap_unsetenv, 0);
- pcmk__set_env_option(long_opt, NULL);
+ pcmk__set_env_option(long_opt, NULL, true);
pcmk__mock_unsetenv = false;
}
@@ -115,7 +115,7 @@ valid_inputs_set(void **state)
expect_string(__wrap_setenv, value, "new_value");
expect_value(__wrap_setenv, overwrite, 1);
will_return(__wrap_setenv, 0);
- pcmk__set_env_option("env_var", "new_value");
+ pcmk__set_env_option("env_var", "new_value", true);
// Empty string is also a valid value
expect_string(__wrap_setenv, name, "PCMK_env_var");
@@ -126,7 +126,7 @@ valid_inputs_set(void **state)
expect_string(__wrap_setenv, value, "");
expect_value(__wrap_setenv, overwrite, 1);
will_return(__wrap_setenv, 0);
- pcmk__set_env_option("env_var", "");
+ pcmk__set_env_option("env_var", "", true);
pcmk__mock_setenv = false;
}
@@ -141,7 +141,33 @@ valid_inputs_unset(void **state)
will_return(__wrap_unsetenv, 0);
expect_string(__wrap_unsetenv, name, "HA_env_var");
will_return(__wrap_unsetenv, 0);
- pcmk__set_env_option("env_var", NULL);
+ pcmk__set_env_option("env_var", NULL, true);
+
+ pcmk__mock_unsetenv = false;
+}
+
+static void
+disable_compat(void **state)
+{
+ // Make sure we set only "PCMK_<option>" and not "HA_<option>"
+ pcmk__mock_setenv = true;
+
+ expect_string(__wrap_setenv, name, "PCMK_env_var");
+ expect_string(__wrap_setenv, value, "new_value");
+ expect_value(__wrap_setenv, overwrite, 1);
+ will_return(__wrap_setenv, 0);
+ pcmk__set_env_option("env_var", "new_value", false);
+
+ pcmk__mock_setenv = false;
+
+ // Make sure we clear both "PCMK_<option>" and "HA_<option>"
+ pcmk__mock_unsetenv = true;
+
+ expect_string(__wrap_unsetenv, name, "PCMK_env_var");
+ will_return(__wrap_unsetenv, 0);
+ expect_string(__wrap_unsetenv, name, "HA_env_var");
+ will_return(__wrap_unsetenv, 0);
+ pcmk__set_env_option("env_var", NULL, false);
pcmk__mock_unsetenv = false;
}
@@ -151,4 +177,5 @@ PCMK__UNIT_TEST(NULL, NULL,
cmocka_unit_test(input_too_long_for_both),
cmocka_unit_test(input_too_long_for_pcmk),
cmocka_unit_test(valid_inputs_set),
- cmocka_unit_test(valid_inputs_unset))
+ cmocka_unit_test(valid_inputs_unset),
+ cmocka_unit_test(disable_compat))
diff --git a/lib/common/tests/output/Makefile.am b/lib/common/tests/output/Makefile.am
index 6ac7b5f..30f1494 100644
--- a/lib/common/tests/output/Makefile.am
+++ b/lib/common/tests/output/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2021-2022 the Pacemaker project contributors
+# Copyright 2021-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,14 +11,14 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = pcmk__call_message_test \
- pcmk__output_and_clear_error_test \
- pcmk__output_free_test \
- pcmk__output_new_test \
- pcmk__register_format_test \
- pcmk__register_formats_test \
- pcmk__register_message_test \
- pcmk__register_messages_test \
- pcmk__unregister_formats_test
+check_PROGRAMS = pcmk__call_message_test \
+ pcmk__output_and_clear_error_test \
+ pcmk__output_free_test \
+ pcmk__output_new_test \
+ pcmk__register_format_test \
+ pcmk__register_formats_test \
+ pcmk__register_message_test \
+ pcmk__register_messages_test \
+ pcmk__unregister_formats_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/output/pcmk__output_new_test.c b/lib/common/tests/output/pcmk__output_new_test.c
index de4268c..a05d9a7 100644
--- a/lib/common/tests/output/pcmk__output_new_test.c
+++ b/lib/common/tests/output/pcmk__output_new_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 the Pacemaker project contributors
+ * Copyright 2022-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -95,9 +95,15 @@ fopen_fails(void **state) {
pcmk__output_t *out = NULL;
pcmk__mock_fopen = true;
+#if defined(HAVE_FOPEN64) && defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64) && (SIZEOF_LONG < 8)
+ expect_string(__wrap_fopen64, pathname, "destfile");
+ expect_string(__wrap_fopen64, mode, "w");
+ will_return(__wrap_fopen64, EPERM);
+#else
expect_string(__wrap_fopen, pathname, "destfile");
expect_string(__wrap_fopen, mode, "w");
will_return(__wrap_fopen, EPERM);
+#endif
assert_int_equal(pcmk__output_new(&out, "text", "destfile", NULL), EPERM);
diff --git a/lib/common/tests/results/Makefile.am b/lib/common/tests/results/Makefile.am
index 8d51d12..a7d5663 100644
--- a/lib/common/tests/results/Makefile.am
+++ b/lib/common/tests/results/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2021-2022 the Pacemaker project contributors
+# Copyright 2021-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,6 +11,6 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = pcmk__results_test
+check_PROGRAMS = pcmk__results_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/results/pcmk__results_test.c b/lib/common/tests/results/pcmk__results_test.c
index 53665d1..016eb7f 100644
--- a/lib/common/tests/results/pcmk__results_test.c
+++ b/lib/common/tests/results/pcmk__results_test.c
@@ -47,15 +47,9 @@ test_for_pcmk_rc2exitc(void **state) {
assert_int_equal(pcmk_rc2exitc(-7777777), CRM_EX_ERROR);
}
-static void
-test_for_bz2_strerror(void **state) {
- assert_string_equal(bz2_strerror(BZ_STREAM_END), "Ok");
-}
-
PCMK__UNIT_TEST(NULL, NULL,
cmocka_unit_test(test_for_pcmk_rc_name),
cmocka_unit_test(test_for_pcmk_rc_str),
cmocka_unit_test(test_for_crm_exit_name),
cmocka_unit_test(test_for_crm_exit_str),
- cmocka_unit_test(test_for_pcmk_rc2exitc),
- cmocka_unit_test(test_for_bz2_strerror))
+ cmocka_unit_test(test_for_pcmk_rc2exitc))
diff --git a/lib/common/tests/scores/Makefile.am b/lib/common/tests/scores/Makefile.am
index 66ca073..cb96155 100644
--- a/lib/common/tests/scores/Makefile.am
+++ b/lib/common/tests/scores/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,9 +11,8 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- char2score_test \
- pcmk__add_scores_test \
- pcmk_readable_score_test
+check_PROGRAMS = char2score_test \
+ pcmk__add_scores_test \
+ pcmk_readable_score_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/scores/pcmk__add_scores_test.c b/lib/common/tests/scores/pcmk__add_scores_test.c
index 85ac232..1309659 100644
--- a/lib/common/tests/scores/pcmk__add_scores_test.c
+++ b/lib/common/tests/scores/pcmk__add_scores_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 the Pacemaker project contributors
+ * Copyright 2022-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -53,6 +53,8 @@ result_infinite(void **state)
assert_int_equal(pcmk__add_scores(INT_MAX, INT_MAX), CRM_SCORE_INFINITY);
assert_int_equal(pcmk__add_scores(INT_MIN, INT_MIN), -CRM_SCORE_INFINITY);
assert_int_equal(pcmk__add_scores(2000000, 50), CRM_SCORE_INFINITY);
+ assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY/2, CRM_SCORE_INFINITY/2), CRM_SCORE_INFINITY);
+ assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY/2, -CRM_SCORE_INFINITY/2), -CRM_SCORE_INFINITY);
assert_int_equal(pcmk__add_scores(-4000000, 50), -CRM_SCORE_INFINITY);
}
diff --git a/lib/common/tests/strings/Makefile.am b/lib/common/tests/strings/Makefile.am
index 9abb8e9..e66af0d 100644
--- a/lib/common/tests/strings/Makefile.am
+++ b/lib/common/tests/strings/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,31 +11,31 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- crm_get_msec_test \
- crm_is_true_test \
- crm_str_to_boolean_test \
- pcmk__add_word_test \
- pcmk__btoa_test \
- pcmk__char_in_any_str_test \
- pcmk__compress_test \
- pcmk__ends_with_test \
- pcmk__g_strcat_test \
- pcmk__guint_from_hash_test \
- pcmk__numeric_strcasecmp_test \
- pcmk__parse_ll_range_test \
- pcmk__s_test \
- pcmk__scan_double_test \
- pcmk__scan_min_int_test \
- pcmk__scan_port_test \
- pcmk__starts_with_test \
- pcmk__str_any_of_test \
- pcmk__str_in_list_test \
- pcmk__str_table_dup_test \
- pcmk__str_update_test \
- pcmk__strcmp_test \
- pcmk__strkey_table_test \
- pcmk__strikey_table_test \
- pcmk__trim_test
+check_PROGRAMS = crm_get_msec_test \
+ crm_is_true_test \
+ crm_str_to_boolean_test \
+ pcmk__add_word_test \
+ pcmk__btoa_test \
+ pcmk__char_in_any_str_test \
+ pcmk__compress_test \
+ pcmk__ends_with_test \
+ pcmk__g_strcat_test \
+ pcmk__guint_from_hash_test \
+ pcmk__numeric_strcasecmp_test \
+ pcmk__parse_ll_range_test \
+ pcmk__s_test \
+ pcmk__scan_double_test \
+ pcmk__scan_ll_test \
+ pcmk__scan_min_int_test \
+ pcmk__scan_port_test \
+ pcmk__starts_with_test \
+ pcmk__str_any_of_test \
+ pcmk__str_in_list_test \
+ pcmk__str_table_dup_test \
+ pcmk__str_update_test \
+ pcmk__strcmp_test \
+ pcmk__strkey_table_test \
+ pcmk__strikey_table_test \
+ pcmk__trim_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/strings/pcmk__compress_test.c b/lib/common/tests/strings/pcmk__compress_test.c
index 7480937..7b59d9d 100644
--- a/lib/common/tests/strings/pcmk__compress_test.c
+++ b/lib/common/tests/strings/pcmk__compress_test.c
@@ -33,7 +33,7 @@ max_too_small(void **state)
char *result = calloc(1024, sizeof(char));
unsigned int len;
- assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), pcmk_rc_error);
+ assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), EFBIG);
}
static void
diff --git a/lib/common/tests/strings/pcmk__guint_from_hash_test.c b/lib/common/tests/strings/pcmk__guint_from_hash_test.c
index e2b4762..225c5b3 100644
--- a/lib/common/tests/strings/pcmk__guint_from_hash_test.c
+++ b/lib/common/tests/strings/pcmk__guint_from_hash_test.c
@@ -59,6 +59,7 @@ conversion_errors(void **state)
g_hash_table_insert(tbl, strdup("negative"), strdup("-3"));
g_hash_table_insert(tbl, strdup("toobig"), strdup("20000000000000000"));
+ g_hash_table_insert(tbl, strdup("baddata"), strdup("asdf"));
assert_int_equal(pcmk__guint_from_hash(tbl, "negative", 456, &result), ERANGE);
assert_int_equal(result, 456);
@@ -66,6 +67,9 @@ conversion_errors(void **state)
assert_int_equal(pcmk__guint_from_hash(tbl, "toobig", 456, &result), ERANGE);
assert_int_equal(result, 456);
+ assert_int_equal(pcmk__guint_from_hash(tbl, "baddata", 456, &result), EINVAL);
+ assert_int_equal(result, 456);
+
g_hash_table_destroy(tbl);
}
diff --git a/lib/common/tests/strings/pcmk__scan_ll_test.c b/lib/common/tests/strings/pcmk__scan_ll_test.c
new file mode 100644
index 0000000..645ecb4
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__scan_ll_test.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+empty_input_string(void **state)
+{
+ long long result;
+
+ assert_int_equal(pcmk__scan_ll(NULL, &result, 47), pcmk_rc_ok);
+ assert_int_equal(result, 47);
+}
+
+static void
+bad_input_string(void **state)
+{
+ long long result;
+
+ assert_int_equal(pcmk__scan_ll("asdf", &result, 47), EINVAL);
+ assert_int_equal(result, 47);
+ assert_int_equal(pcmk__scan_ll("as12", &result, 47), EINVAL);
+ assert_int_equal(result, 47);
+}
+
+static void
+trailing_chars(void **state)
+{
+ long long result;
+
+ assert_int_equal(pcmk__scan_ll("12as", &result, 47), pcmk_rc_ok);
+ assert_int_equal(result, 12);
+}
+
+static void
+no_result_variable(void **state)
+{
+ assert_int_equal(pcmk__scan_ll("1234", NULL, 47), pcmk_rc_ok);
+ assert_int_equal(pcmk__scan_ll("asdf", NULL, 47), EINVAL);
+}
+
+static void
+typical_case(void **state)
+{
+ long long result;
+
+ assert_int_equal(pcmk__scan_ll("1234", &result, 47), pcmk_rc_ok);
+ assert_int_equal(result, 1234);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input_string),
+ cmocka_unit_test(bad_input_string),
+ cmocka_unit_test(trailing_chars),
+ cmocka_unit_test(no_result_variable),
+ cmocka_unit_test(typical_case))
diff --git a/lib/common/tests/utils/Makefile.am b/lib/common/tests/utils/Makefile.am
index edccf09..f028ce4 100644
--- a/lib/common/tests/utils/Makefile.am
+++ b/lib/common/tests/utils/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2020-2022 the Pacemaker project contributors
+# Copyright 2020-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,15 +11,17 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = \
- compare_version_test \
- crm_meta_name_test \
- crm_meta_value_test \
- crm_user_lookup_test \
- pcmk_daemon_user_test \
- pcmk_str_is_infinity_test \
- pcmk_str_is_minus_infinity_test \
- pcmk__getpid_s_test
+check_PROGRAMS = compare_version_test \
+ crm_meta_name_test \
+ crm_meta_value_test \
+ crm_user_lookup_test \
+ pcmk_daemon_user_test \
+ pcmk_str_is_infinity_test \
+ pcmk_str_is_minus_infinity_test \
+ pcmk__fail_attr_name_test \
+ pcmk__failcount_name_test \
+ pcmk__getpid_s_test \
+ pcmk__lastfailure_name_test
if WRAPPABLE_UNAME
check_PROGRAMS += pcmk_hostname_test
diff --git a/lib/common/tests/utils/pcmk__fail_attr_name_test.c b/lib/common/tests/utils/pcmk__fail_attr_name_test.c
new file mode 100644
index 0000000..c6c25fc
--- /dev/null
+++ b/lib/common/tests/utils/pcmk__fail_attr_name_test.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+null_arguments(void **state)
+{
+ assert_null(pcmk__fail_attr_name(NULL, NULL, NULL, 30000));
+ assert_null(pcmk__fail_attr_name(NULL, "myrsc", "monitor", 30000));
+ assert_null(pcmk__fail_attr_name("xyz", NULL, "monitor", 30000));
+ assert_null(pcmk__fail_attr_name("xyz", "myrsc", NULL, 30000));
+}
+
+static void
+standard_usage(void **state)
+{
+ char *s = NULL;
+
+ assert_string_equal(pcmk__fail_attr_name("xyz", "myrsc", "monitor", 30000),
+ "xyz-myrsc#monitor_30000");
+
+ free(s);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(null_arguments),
+ cmocka_unit_test(standard_usage))
diff --git a/lib/common/tests/utils/pcmk__failcount_name_test.c b/lib/common/tests/utils/pcmk__failcount_name_test.c
new file mode 100644
index 0000000..a801f4d
--- /dev/null
+++ b/lib/common/tests/utils/pcmk__failcount_name_test.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+null_arguments(void **state)
+{
+ assert_null(pcmk__failcount_name(NULL, NULL, 30000));
+ assert_null(pcmk__failcount_name("myrsc", NULL, 30000));
+ assert_null(pcmk__failcount_name(NULL, "monitor", 30000));
+}
+
+static void
+standard_usage(void **state)
+{
+ char *s = NULL;
+
+ assert_string_equal(pcmk__failcount_name("myrsc", "monitor", 30000),
+ "fail-count-myrsc#monitor_30000");
+
+ free(s);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(null_arguments),
+ cmocka_unit_test(standard_usage))
diff --git a/lib/common/tests/utils/pcmk__lastfailure_name_test.c b/lib/common/tests/utils/pcmk__lastfailure_name_test.c
new file mode 100644
index 0000000..eab01f2
--- /dev/null
+++ b/lib/common/tests/utils/pcmk__lastfailure_name_test.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+null_arguments(void **state)
+{
+ assert_null(pcmk__lastfailure_name(NULL, NULL, 30000));
+ assert_null(pcmk__lastfailure_name("myrsc", NULL, 30000));
+ assert_null(pcmk__lastfailure_name(NULL, "monitor", 30000));
+}
+
+static void
+standard_usage(void **state)
+{
+ char *s = NULL;
+
+ assert_string_equal(pcmk__lastfailure_name("myrsc", "monitor", 30000),
+ "last-failure-myrsc#monitor_30000");
+
+ free(s);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(null_arguments),
+ cmocka_unit_test(standard_usage))
diff --git a/lib/common/tests/xml/Makefile.am b/lib/common/tests/xml/Makefile.am
index 0ccdcc3..465c950 100644
--- a/lib/common/tests/xml/Makefile.am
+++ b/lib/common/tests/xml/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2022 the Pacemaker project contributors
+# Copyright 2022-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,7 +11,7 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = pcmk__xe_foreach_child_test \
- pcmk__xe_match_test
+check_PROGRAMS = pcmk__xe_foreach_child_test \
+ pcmk__xe_match_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c
index 9bcba87..ffb9171 100644
--- a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c
+++ b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 the Pacemaker project contributors
+ * Copyright 2022-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -14,7 +14,7 @@
static int compare_name_handler(xmlNode *xml, void *userdata) {
function_called();
- assert_string_equal((char *) userdata, crm_element_name(xml));
+ assert_string_equal((char *) userdata, (const char *) xml->name);
return pcmk_rc_ok;
}
@@ -140,7 +140,8 @@ const char *str3 =
static int any_of_handler(xmlNode *xml, void *userdata) {
function_called();
- assert_true(pcmk__str_any_of(crm_element_name(xml), "node1", "node2", "node3", NULL));
+ assert_true(pcmk__str_any_of((const char *) xml->name,
+ "node1", "node2", "node3", NULL));
return pcmk_rc_ok;
}
@@ -160,7 +161,7 @@ any_of_test(void **state) {
static int stops_on_first_handler(xmlNode *xml, void *userdata) {
function_called();
- if (pcmk__str_eq(crm_element_name(xml), "node1", pcmk__str_none)) {
+ if (pcmk__xe_is(xml, "node1")) {
return pcmk_rc_error;
} else {
return pcmk_rc_ok;
@@ -170,7 +171,7 @@ static int stops_on_first_handler(xmlNode *xml, void *userdata) {
static int stops_on_second_handler(xmlNode *xml, void *userdata) {
function_called();
- if (pcmk__str_eq(crm_element_name(xml), "node2", pcmk__str_none)) {
+ if (pcmk__xe_is(xml, "node2")) {
return pcmk_rc_error;
} else {
return pcmk_rc_ok;
@@ -180,7 +181,7 @@ static int stops_on_second_handler(xmlNode *xml, void *userdata) {
static int stops_on_third_handler(xmlNode *xml, void *userdata) {
function_called();
- if (pcmk__str_eq(crm_element_name(xml), "node3", pcmk__str_none)) {
+ if (pcmk__xe_is(xml, "node3")) {
return pcmk_rc_error;
} else {
return pcmk_rc_ok;
diff --git a/lib/common/tests/xpath/Makefile.am b/lib/common/tests/xpath/Makefile.am
index 94abeee..d4c504b 100644
--- a/lib/common/tests/xpath/Makefile.am
+++ b/lib/common/tests/xpath/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2021-2022 the Pacemaker project contributors
+# Copyright 2021-2023 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,6 +11,6 @@ include $(top_srcdir)/mk/tap.mk
include $(top_srcdir)/mk/unittest.mk
# Add "_test" to the end of all test program names to simplify .gitignore.
-check_PROGRAMS = pcmk__xpath_node_id_test
+check_PROGRAMS = pcmk__xpath_node_id_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/watchdog.c b/lib/common/watchdog.c
index ff2d273..e569214 100644
--- a/lib/common/watchdog.c
+++ b/lib/common/watchdog.c
@@ -20,10 +20,6 @@
#include <dirent.h>
#include <signal.h>
-#ifdef _POSIX_MEMLOCK
-# include <sys/mman.h>
-#endif
-
static pid_t sbd_pid = 0;
static void
@@ -56,6 +52,7 @@ panic_local(void)
int rc = pcmk_ok;
uid_t uid = geteuid();
pid_t ppid = getppid();
+ const char *panic_action = pcmk__env_option(PCMK__ENV_PANIC_ACTION);
if(uid != 0 && ppid > 1) {
/* We're a non-root pacemaker daemon (pacemaker-based,
@@ -93,13 +90,15 @@ panic_local(void)
/* We're either pacemakerd, or a pacemaker daemon running as root */
- if (pcmk__str_eq("crash", getenv("PCMK_panic_action"), pcmk__str_casei)) {
+ if (pcmk__str_eq(panic_action, "crash", pcmk__str_casei)) {
sysrq_trigger('c');
- } else if (pcmk__str_eq("sync-crash", getenv("PCMK_panic_action"), pcmk__str_casei)) {
+
+ } else if (pcmk__str_eq(panic_action, "sync-crash", pcmk__str_casei)) {
sync();
sysrq_trigger('c');
+
} else {
- if (pcmk__str_eq("sync-reboot", getenv("PCMK_panic_action"), pcmk__str_casei)) {
+ if (pcmk__str_eq(panic_action, "sync-reboot", pcmk__str_casei)) {
sync();
}
sysrq_trigger('b');
diff --git a/lib/common/xml.c b/lib/common/xml.c
index 22078ce..53ebff7 100644
--- a/lib/common/xml.c
+++ b/lib/common/xml.c
@@ -42,7 +42,8 @@
* parsing without XML_PARSE_RECOVER, and if that fails, try parsing again with
* it, logging a warning if it succeeds.
*/
-#define PCMK__XML_PARSE_OPTS (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER)
+#define PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER (XML_PARSE_NOBLANKS)
+#define PCMK__XML_PARSE_OPTS_WITH_RECOVER (XML_PARSE_NOBLANKS | XML_PARSE_RECOVER)
bool
pcmk__tracking_xml_changes(xmlNode *xml, bool lazy)
@@ -85,8 +86,8 @@ pcmk__set_xml_doc_flag(xmlNode *xml, enum xml_private_flags flag)
}
// Mark document, element, and all element's parents as changed
-static inline void
-mark_xml_node_dirty(xmlNode *xml)
+void
+pcmk__mark_xml_node_dirty(xmlNode *xml)
{
pcmk__set_xml_doc_flag(xml, pcmk__xf_dirty);
set_parent_flag(xml, pcmk__xf_dirty);
@@ -114,12 +115,15 @@ void
pcmk__mark_xml_created(xmlNode *xml)
{
xmlNode *cIter = NULL;
- xml_node_private_t *nodepriv = xml->_private;
+ xml_node_private_t *nodepriv = NULL;
+
+ CRM_ASSERT(xml != NULL);
+ nodepriv = xml->_private;
if (nodepriv && pcmk__tracking_xml_changes(xml, FALSE)) {
if (!pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
pcmk__set_xml_flags(nodepriv, pcmk__xf_created);
- mark_xml_node_dirty(xml);
+ pcmk__mark_xml_node_dirty(xml);
}
for (cIter = pcmk__xml_first_child(xml); cIter != NULL;
cIter = pcmk__xml_next(cIter)) {
@@ -128,17 +132,6 @@ pcmk__mark_xml_created(xmlNode *xml)
}
}
-void
-pcmk__mark_xml_attr_dirty(xmlAttr *a)
-{
- xmlNode *parent = a->parent;
- xml_node_private_t *nodepriv = a->_private;
-
- pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_modified);
- pcmk__clear_xml_flags(nodepriv, pcmk__xf_deleted);
- mark_xml_node_dirty(parent);
-}
-
#define XML_DOC_PRIVATE_MAGIC 0x81726354UL
#define XML_NODE_PRIVATE_MAGIC 0x54637281UL
@@ -250,7 +243,7 @@ new_private_data(xmlNode *node)
/* XML_ELEMENT_NODE doesn't get picked up here, node->doc is
* not hooked up at the point we are called
*/
- mark_xml_node_dirty(node);
+ pcmk__mark_xml_node_dirty(node);
}
break;
}
@@ -321,19 +314,6 @@ pcmk__xml_position(const xmlNode *xml, enum xml_private_flags ignore_if_set)
return position;
}
-// This also clears attribute's flags if not marked as deleted
-static bool
-marked_as_deleted(xmlAttrPtr a, void *user_data)
-{
- xml_node_private_t *nodepriv = a->_private;
-
- if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
- return true;
- }
- nodepriv->flags = pcmk__xf_none;
- return false;
-}
-
// Remove all attributes marked as deleted from an XML node
static void
accept_attr_deletions(xmlNode *xml)
@@ -342,7 +322,7 @@ accept_attr_deletions(xmlNode *xml)
((xml_node_private_t *) xml->_private)->flags = pcmk__xf_none;
// Remove this XML node's attributes that were marked as deleted
- pcmk__xe_remove_matching_attrs(xml, marked_as_deleted, NULL);
+ pcmk__xe_remove_matching_attrs(xml, pcmk__marked_as_deleted, NULL);
// Recursively do the same for this XML node's children
for (xmlNodePtr cIter = pcmk__xml_first_child(xml); cIter != NULL;
@@ -371,7 +351,7 @@ pcmk__xml_match(const xmlNode *haystack, const xmlNode *needle, bool exact)
const char *id = ID(needle);
const char *attr = (id == NULL)? NULL : XML_ATTR_ID;
- return pcmk__xe_match(haystack, crm_element_name(needle), attr, id);
+ return pcmk__xe_match(haystack, (const char *) needle->name, attr, id);
}
}
@@ -404,11 +384,7 @@ xmlNode *
find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
{
xmlNode *a_child = NULL;
- const char *name = "NULL";
-
- if (root != NULL) {
- name = crm_element_name(root);
- }
+ const char *name = (root == NULL)? "<NULL>" : (const char *) root->name;
if (search_path == NULL) {
crm_warn("Will never find <NULL>");
@@ -418,7 +394,6 @@ find_xml_node(const xmlNode *root, const char *search_path, gboolean must_find)
for (a_child = pcmk__xml_first_child(root); a_child != NULL;
a_child = pcmk__xml_next(a_child)) {
if (strcmp((const char *)a_child->name, search_path) == 0) {
-/* crm_trace("returning node (%s).", crm_element_name(a_child)); */
return a_child;
}
}
@@ -473,7 +448,7 @@ pcmk__xe_match(const xmlNode *parent, const char *node_name,
(attr_n? attr_n : ""),
(attr_n? "=" : ""),
(attr_n? attr_v : ""),
- crm_element_name(parent));
+ (const char *) parent->name);
return NULL;
}
@@ -643,31 +618,17 @@ pcmk__xe_remove_matching_attrs(xmlNode *element,
}
}
-xmlDoc *
-getDocPtr(xmlNode * node)
-{
- xmlDoc *doc = NULL;
-
- CRM_CHECK(node != NULL, return NULL);
-
- doc = node->doc;
- if (doc == NULL) {
- doc = xmlNewDoc((pcmkXmlStr) "1.0");
- xmlDocSetRootElement(doc, node);
- xmlSetTreeDoc(node, doc);
- }
- return doc;
-}
-
xmlNode *
add_node_copy(xmlNode * parent, xmlNode * src_node)
{
xmlNode *child = NULL;
- xmlDoc *doc = getDocPtr(parent);
- CRM_CHECK(src_node != NULL, return NULL);
+ CRM_CHECK((parent != NULL) && (src_node != NULL), return NULL);
- child = xmlDocCopyNode(src_node, doc, 1);
+ child = xmlDocCopyNode(src_node, parent->doc, 1);
+ if (child == NULL) {
+ return NULL;
+ }
xmlAddChild(parent, child);
pcmk__mark_xml_created(child);
return child;
@@ -686,13 +647,22 @@ create_xml_node(xmlNode * parent, const char *name)
if (parent == NULL) {
doc = xmlNewDoc((pcmkXmlStr) "1.0");
+ if (doc == NULL) {
+ return NULL;
+ }
+
node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
+ if (node == NULL) {
+ xmlFreeDoc(doc);
+ return NULL;
+ }
xmlDocSetRootElement(doc, node);
} else {
- doc = getDocPtr(parent);
- node = xmlNewDocRawNode(doc, NULL, (pcmkXmlStr) name, NULL);
- xmlAddChild(parent, node);
+ node = xmlNewChild(parent, NULL, (pcmkXmlStr) name, NULL);
+ if (node == NULL) {
+ return NULL;
+ }
}
pcmk__mark_xml_created(node);
return node;
@@ -823,7 +793,6 @@ copy_xml(xmlNode * src)
CRM_ASSERT(copy != NULL);
xmlDocSetRootElement(doc, copy);
- xmlSetTreeDoc(copy, doc);
return copy;
}
@@ -833,7 +802,7 @@ string2xml(const char *input)
xmlNode *xml = NULL;
xmlDocPtr output = NULL;
xmlParserCtxtPtr ctxt = NULL;
- xmlErrorPtr last_error = NULL;
+ const xmlError *last_error = NULL;
if (input == NULL) {
crm_err("Can't parse NULL input");
@@ -847,7 +816,17 @@ string2xml(const char *input)
xmlCtxtResetLastError(ctxt);
xmlSetGenericErrorFunc(ctxt, pcmk__log_xmllib_err);
output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
- PCMK__XML_PARSE_OPTS);
+ PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER);
+
+ if (output == NULL) {
+ output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
+ PCMK__XML_PARSE_OPTS_WITH_RECOVER);
+ if (output) {
+ crm_warn("Successfully recovered from XML errors "
+ "(note: a future release will treat this as a fatal failure)");
+ }
+ }
+
if (output) {
xml = xmlDocGetRootElement(output);
}
@@ -933,9 +912,11 @@ decompress_file(const char *filename)
}
bz_file = BZ2_bzReadOpen(&rc, input, 0, 0, NULL, 0);
- if (rc != BZ_OK) {
+ rc = pcmk__bzlib2rc(rc);
+
+ if (rc != pcmk_rc_ok) {
crm_err("Could not prepare to read compressed %s: %s "
- CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
+ CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc);
BZ2_bzReadClose(&rc, bz_file);
fclose(input);
return NULL;
@@ -957,9 +938,11 @@ decompress_file(const char *filename)
buffer[length] = '\0';
- if (rc != BZ_STREAM_END) {
- crm_err("Could not read compressed %s: %s "
- CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
+ rc = pcmk__bzlib2rc(rc);
+
+ if (rc != pcmk_rc_ok) {
+ crm_err("Could not read compressed %s: %s " CRM_XS " rc=%d",
+ filename, pcmk_rc_str(rc), rc);
free(buffer);
buffer = NULL;
}
@@ -1010,7 +993,7 @@ filename2xml(const char *filename)
xmlDocPtr output = NULL;
bool uncompressed = true;
xmlParserCtxtPtr ctxt = NULL;
- xmlErrorPtr last_error = NULL;
+ const xmlError *last_error = NULL;
/* create a parser context */
ctxt = xmlNewParserCtxt();
@@ -1026,16 +1009,45 @@ filename2xml(const char *filename)
if (pcmk__str_eq(filename, "-", pcmk__str_null_matches)) {
/* STDIN_FILENO == fileno(stdin) */
output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
- PCMK__XML_PARSE_OPTS);
+ PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER);
+
+ if (output == NULL) {
+ output = xmlCtxtReadFd(ctxt, STDIN_FILENO, "unknown.xml", NULL,
+ PCMK__XML_PARSE_OPTS_WITH_RECOVER);
+ if (output) {
+ crm_warn("Successfully recovered from XML errors "
+ "(note: a future release will treat this as a fatal failure)");
+ }
+ }
} else if (uncompressed) {
- output = xmlCtxtReadFile(ctxt, filename, NULL, PCMK__XML_PARSE_OPTS);
+ output = xmlCtxtReadFile(ctxt, filename, NULL,
+ PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER);
+
+ if (output == NULL) {
+ output = xmlCtxtReadFile(ctxt, filename, NULL,
+ PCMK__XML_PARSE_OPTS_WITH_RECOVER);
+ if (output) {
+ crm_warn("Successfully recovered from XML errors "
+ "(note: a future release will treat this as a fatal failure)");
+ }
+ }
} else {
char *input = decompress_file(filename);
output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
- PCMK__XML_PARSE_OPTS);
+ PCMK__XML_PARSE_OPTS_WITHOUT_RECOVER);
+
+ if (output == NULL) {
+ output = xmlCtxtReadDoc(ctxt, (pcmkXmlStr) input, NULL, NULL,
+ PCMK__XML_PARSE_OPTS_WITH_RECOVER);
+ if (output) {
+ crm_warn("Successfully recovered from XML errors "
+ "(note: a future release will treat this as a fatal failure)");
+ }
+ }
+
free(input);
}
@@ -1134,7 +1146,7 @@ crm_xml_set_id(xmlNode *xml, const char *format, ...)
* \internal
* \brief Write XML to a file stream
*
- * \param[in] xml_node XML to write
+ * \param[in] xml XML to write
* \param[in] filename Name of file being written (for logging only)
* \param[in,out] stream Open file stream corresponding to filename
* \param[in] compress Whether to compress XML before writing
@@ -1143,18 +1155,18 @@ crm_xml_set_id(xmlNode *xml, const char *format, ...)
* \return Standard Pacemaker return code
*/
static int
-write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
+write_xml_stream(const xmlNode *xml, const char *filename, FILE *stream,
bool compress, unsigned int *nbytes)
{
int rc = pcmk_rc_ok;
char *buffer = NULL;
*nbytes = 0;
- crm_log_xml_trace(xml_node, "writing");
+ crm_log_xml_trace(xml, "writing");
- buffer = dump_xml_formatted(xml_node);
+ buffer = dump_xml_formatted(xml);
CRM_CHECK(buffer && strlen(buffer),
- crm_log_xml_warn(xml_node, "formatting failed");
+ crm_log_xml_warn(xml, "formatting failed");
rc = pcmk_rc_error;
goto bail);
@@ -1164,24 +1176,30 @@ write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
rc = BZ_OK;
bz_file = BZ2_bzWriteOpen(&rc, stream, 5, 0, 30);
- if (rc != BZ_OK) {
+ rc = pcmk__bzlib2rc(rc);
+
+ if (rc != pcmk_rc_ok) {
crm_warn("Not compressing %s: could not prepare file stream: %s "
- CRM_XS " bzerror=%d", filename, bz2_strerror(rc), rc);
+ CRM_XS " rc=%d", filename, pcmk_rc_str(rc), rc);
} else {
BZ2_bzWrite(&rc, bz_file, buffer, strlen(buffer));
- if (rc != BZ_OK) {
+ rc = pcmk__bzlib2rc(rc);
+
+ if (rc != pcmk_rc_ok) {
crm_warn("Not compressing %s: could not compress data: %s "
- CRM_XS " bzerror=%d errno=%d",
- filename, bz2_strerror(rc), rc, errno);
+ CRM_XS " rc=%d errno=%d",
+ filename, pcmk_rc_str(rc), rc, errno);
}
}
- if (rc == BZ_OK) {
+ if (rc == pcmk_rc_ok) {
BZ2_bzWriteClose(&rc, bz_file, 0, &in, nbytes);
- if (rc != BZ_OK) {
+ rc = pcmk__bzlib2rc(rc);
+
+ if (rc != pcmk_rc_ok) {
crm_warn("Not compressing %s: could not write compressed data: %s "
- CRM_XS " bzerror=%d errno=%d",
- filename, bz2_strerror(rc), rc, errno);
+ CRM_XS " rc=%d errno=%d",
+ filename, pcmk_rc_str(rc), rc, errno);
*nbytes = 0; // retry without compression
} else {
crm_trace("Compressed XML for %s from %u bytes to %u",
@@ -1226,7 +1244,7 @@ write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
/*!
* \brief Write XML to a file descriptor
*
- * \param[in] xml_node XML to write
+ * \param[in] xml XML to write
* \param[in] filename Name of file being written (for logging only)
* \param[in] fd Open file descriptor corresponding to filename
* \param[in] compress Whether to compress XML before writing
@@ -1234,18 +1252,19 @@ write_xml_stream(xmlNode *xml_node, const char *filename, FILE *stream,
* \return Number of bytes written on success, -errno otherwise
*/
int
-write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress)
+write_xml_fd(const xmlNode *xml, const char *filename, int fd,
+ gboolean compress)
{
FILE *stream = NULL;
unsigned int nbytes = 0;
int rc = pcmk_rc_ok;
- CRM_CHECK(xml_node && (fd > 0), return -EINVAL);
+ CRM_CHECK((xml != NULL) && (fd > 0), return -EINVAL);
stream = fdopen(fd, "w");
if (stream == NULL) {
return -errno;
}
- rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
+ rc = write_xml_stream(xml, filename, stream, compress, &nbytes);
if (rc != pcmk_rc_ok) {
return pcmk_rc2legacy(rc);
}
@@ -1255,25 +1274,25 @@ write_xml_fd(xmlNode * xml_node, const char *filename, int fd, gboolean compress
/*!
* \brief Write XML to a file
*
- * \param[in] xml_node XML to write
+ * \param[in] xml XML to write
* \param[in] filename Name of file to write
* \param[in] compress Whether to compress XML before writing
*
* \return Number of bytes written on success, -errno otherwise
*/
int
-write_xml_file(xmlNode * xml_node, const char *filename, gboolean compress)
+write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
{
FILE *stream = NULL;
unsigned int nbytes = 0;
int rc = pcmk_rc_ok;
- CRM_CHECK(xml_node && filename, return -EINVAL);
+ CRM_CHECK((xml != NULL) && (filename != NULL), return -EINVAL);
stream = fopen(filename, "w");
if (stream == NULL) {
return -errno;
}
- rc = write_xml_stream(xml_node, filename, stream, compress, &nbytes);
+ rc = write_xml_stream(xml, filename, stream, compress, &nbytes);
if (rc != pcmk_rc_ok) {
return pcmk_rc2legacy(rc);
}
@@ -1382,37 +1401,6 @@ crm_xml_escape(const char *text)
/*!
* \internal
- * \brief Append an XML attribute to a buffer
- *
- * \param[in] attr Attribute to append
- * \param[in,out] buffer Where to append the content (must not be \p NULL)
- */
-static void
-dump_xml_attr(const xmlAttr *attr, GString *buffer)
-{
- char *p_value = NULL;
- const char *p_name = NULL;
- xml_node_private_t *nodepriv = NULL;
-
- if (attr == NULL || attr->children == NULL) {
- return;
- }
-
- nodepriv = attr->_private;
- if (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
- return;
- }
-
- p_name = (const char *) attr->name;
- p_value = crm_xml_escape((const char *)attr->children->content);
- pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"",
- NULL);
-
- free(p_value);
-}
-
-/*!
- * \internal
* \brief Append a string representation of an XML element to a buffer
*
* \param[in] data XML whose representation to append
@@ -1424,24 +1412,21 @@ static void
dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
int depth)
{
- const char *name = crm_element_name(data);
bool pretty = pcmk_is_set(options, pcmk__xml_fmt_pretty);
bool filtered = pcmk_is_set(options, pcmk__xml_fmt_filtered);
int spaces = pretty? (2 * depth) : 0;
- CRM_ASSERT(name != NULL);
-
for (int lpc = 0; lpc < spaces; lpc++) {
g_string_append_c(buffer, ' ');
}
- pcmk__g_strcat(buffer, "<", name, NULL);
+ pcmk__g_strcat(buffer, "<", data->name, NULL);
for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
attr = attr->next) {
if (!filtered || !pcmk__xa_filterable((const char *) (attr->name))) {
- dump_xml_attr(attr, buffer);
+ pcmk__dump_xml_attr(attr, buffer);
}
}
@@ -1457,16 +1442,16 @@ dump_xml_element(const xmlNode *data, uint32_t options, GString *buffer,
}
if (data->children) {
- xmlNode *xChild = NULL;
- for(xChild = data->children; xChild != NULL; xChild = xChild->next) {
- pcmk__xml2text(xChild, options, buffer, depth + 1);
+ for (const xmlNode *child = data->children; child != NULL;
+ child = child->next) {
+ pcmk__xml2text(child, options, buffer, depth + 1);
}
for (int lpc = 0; lpc < spaces; lpc++) {
g_string_append_c(buffer, ' ');
}
- pcmk__g_strcat(buffer, "</", name, ">", NULL);
+ pcmk__g_strcat(buffer, "</", data->name, ">", NULL);
if (pretty) {
g_string_append_c(buffer, '\n');
@@ -1559,7 +1544,45 @@ dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
}
}
-#define PCMK__XMLDUMP_STATS 0
+/*!
+ * \internal
+ * \brief Get a string representation of an XML element type
+ *
+ * \param[in] type XML element type
+ *
+ * \return String representation of \p type
+ */
+static const char *
+xml_element_type2str(xmlElementType type)
+{
+ static const char *const element_type_names[] = {
+ [XML_ELEMENT_NODE] = "element",
+ [XML_ATTRIBUTE_NODE] = "attribute",
+ [XML_TEXT_NODE] = "text",
+ [XML_CDATA_SECTION_NODE] = "CDATA section",
+ [XML_ENTITY_REF_NODE] = "entity reference",
+ [XML_ENTITY_NODE] = "entity",
+ [XML_PI_NODE] = "PI",
+ [XML_COMMENT_NODE] = "comment",
+ [XML_DOCUMENT_NODE] = "document",
+ [XML_DOCUMENT_TYPE_NODE] = "document type",
+ [XML_DOCUMENT_FRAG_NODE] = "document fragment",
+ [XML_NOTATION_NODE] = "notation",
+ [XML_HTML_DOCUMENT_NODE] = "HTML document",
+ [XML_DTD_NODE] = "DTD",
+ [XML_ELEMENT_DECL] = "element declaration",
+ [XML_ATTRIBUTE_DECL] = "attribute declaration",
+ [XML_ENTITY_DECL] = "entity declaration",
+ [XML_NAMESPACE_DECL] = "namespace declaration",
+ [XML_XINCLUDE_START] = "XInclude start",
+ [XML_XINCLUDE_END] = "XInclude end",
+ };
+
+ if ((type < 0) || (type >= PCMK__NELEM(element_type_names))) {
+ return "unrecognized type";
+ }
+ return element_type_names[type];
+}
/*!
* \internal
@@ -1571,7 +1594,8 @@ dump_xml_comment(const xmlNode *data, uint32_t options, GString *buffer,
* \param[in] depth Current indentation level
*/
void
-pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
+pcmk__xml2text(const xmlNode *data, uint32_t options, GString *buffer,
+ int depth)
{
if (data == NULL) {
crm_trace("Nothing to dump");
@@ -1581,60 +1605,6 @@ pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
CRM_ASSERT(buffer != NULL);
CRM_CHECK(depth >= 0, depth = 0);
- if (pcmk_is_set(options, pcmk__xml_fmt_full)) {
- /* libxml's serialization reuse is a good idea, sadly we cannot
- apply it for the filtered cases (preceding filtering pass
- would preclude further reuse of such in-situ modified XML
- in generic context and is likely not a win performance-wise),
- and there's also a historically unstable throughput argument
- (likely stemming from memory allocation overhead, eventhough
- that shall be minimized with defaults preset in crm_xml_init) */
-#if (PCMK__XMLDUMP_STATS - 0)
- time_t next, new = time(NULL);
-#endif
- xmlDoc *doc;
- xmlOutputBuffer *xml_buffer;
-
- doc = getDocPtr(data);
- /* doc will only be NULL if data is */
- CRM_CHECK(doc != NULL, return);
-
- xml_buffer = xmlAllocOutputBuffer(NULL);
- CRM_ASSERT(xml_buffer != NULL);
-
- /* XXX we could setup custom allocation scheme for the particular
- buffer, but it's subsumed with crm_xml_init that needs to
- be invoked prior to entering this function as such, since
- its other branch vitally depends on it -- what can be done
- about this all is to have a facade parsing functions that
- would 100% mark entering libxml code for us, since we don't
- do anything as crazy as swapping out the binary form of the
- parsed tree (but those would need to be strictly used as
- opposed to libxml's raw functions) */
-
- xmlNodeDumpOutput(xml_buffer, doc, data, 0,
- pcmk_is_set(options, pcmk__xml_fmt_pretty), NULL);
- /* attempt adding final NL - failing shouldn't be fatal here */
- (void) xmlOutputBufferWrite(xml_buffer, sizeof("\n") - 1, "\n");
- if (xml_buffer->buffer != NULL) {
- g_string_append(buffer,
- (const gchar *) xmlBufContent(xml_buffer->buffer));
- }
-
-#if (PCMK__XMLDUMP_STATS - 0)
- next = time(NULL);
- if ((now + 1) < next) {
- crm_log_xml_trace(data, "Long time");
- crm_err("xmlNodeDumpOutput() -> %lld bytes took %ds",
- (long long) buffer->len, next - now);
- }
-#endif
-
- /* asserted allocation before so there should be something to remove */
- (void) xmlOutputBufferClose(xml_buffer);
- return;
- }
-
switch(data->type) {
case XML_ELEMENT_NODE:
/* Handle below */
@@ -1642,11 +1612,6 @@ pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
break;
case XML_TEXT_NODE:
if (pcmk_is_set(options, pcmk__xml_fmt_text)) {
- /* @COMPAT: Remove when log_data_element() is removed. There are
- * no other internal code paths that set pcmk__xml_fmt_text.
- * Keep an empty case handler so that we don't log an unhandled
- * type warning.
- */
dump_xml_text(data, options, buffer, depth);
}
break;
@@ -1657,39 +1622,23 @@ pcmk__xml2text(xmlNodePtr data, uint32_t options, GString *buffer, int depth)
dump_xml_cdata(data, options, buffer, depth);
break;
default:
- crm_warn("Unhandled type: %d", data->type);
+ crm_warn("Cannot convert XML %s node to text " CRM_XS " type=%d",
+ xml_element_type2str(data->type), data->type);
break;
-
- /*
- XML_ATTRIBUTE_NODE = 2
- XML_ENTITY_REF_NODE = 5
- XML_ENTITY_NODE = 6
- XML_PI_NODE = 7
- XML_DOCUMENT_NODE = 9
- XML_DOCUMENT_TYPE_NODE = 10
- XML_DOCUMENT_FRAG_NODE = 11
- XML_NOTATION_NODE = 12
- XML_HTML_DOCUMENT_NODE = 13
- XML_DTD_NODE = 14
- XML_ELEMENT_DECL = 15
- XML_ATTRIBUTE_DECL = 16
- XML_ENTITY_DECL = 17
- XML_NAMESPACE_DECL = 18
- XML_XINCLUDE_START = 19
- XML_XINCLUDE_END = 20
- XML_DOCB_DOCUMENT_NODE = 21
- */
}
}
char *
-dump_xml_formatted_with_text(xmlNode * an_xml_node)
+dump_xml_formatted_with_text(const xmlNode *xml)
{
+ /* libxml's xmlNodeDumpOutput() would work here since we're not specifically
+ * filtering out any nodes. However, use pcmk__xml2text() for consistency,
+ * to escape attribute values, and to allow a const argument.
+ */
char *buffer = NULL;
GString *g_buffer = g_string_sized_new(1024);
- pcmk__xml2text(an_xml_node, pcmk__xml_fmt_pretty|pcmk__xml_fmt_full,
- g_buffer, 0);
+ pcmk__xml2text(xml, pcmk__xml_fmt_pretty|pcmk__xml_fmt_text, g_buffer, 0);
pcmk__str_update(&buffer, g_buffer->str);
g_string_free(g_buffer, TRUE);
@@ -1697,12 +1646,12 @@ dump_xml_formatted_with_text(xmlNode * an_xml_node)
}
char *
-dump_xml_formatted(xmlNode * an_xml_node)
+dump_xml_formatted(const xmlNode *xml)
{
char *buffer = NULL;
GString *g_buffer = g_string_sized_new(1024);
- pcmk__xml2text(an_xml_node, pcmk__xml_fmt_pretty, g_buffer, 0);
+ pcmk__xml2text(xml, pcmk__xml_fmt_pretty, g_buffer, 0);
pcmk__str_update(&buffer, g_buffer->str);
g_string_free(g_buffer, TRUE);
@@ -1710,30 +1659,46 @@ dump_xml_formatted(xmlNode * an_xml_node)
}
char *
-dump_xml_unformatted(xmlNode * an_xml_node)
+dump_xml_unformatted(const xmlNode *xml)
{
char *buffer = NULL;
GString *g_buffer = g_string_sized_new(1024);
- pcmk__xml2text(an_xml_node, 0, g_buffer, 0);
+ pcmk__xml2text(xml, 0, g_buffer, 0);
pcmk__str_update(&buffer, g_buffer->str);
g_string_free(g_buffer, TRUE);
return buffer;
}
-gboolean
-xml_has_children(const xmlNode * xml_root)
+int
+pcmk__xml2fd(int fd, xmlNode *cur)
{
- if (xml_root != NULL && xml_root->children != NULL) {
- return TRUE;
+ bool success;
+
+ xmlOutputBuffer *fd_out = xmlOutputBufferCreateFd(fd, NULL);
+ CRM_ASSERT(fd_out != NULL);
+ xmlNodeDumpOutput(fd_out, cur->doc, cur, 0, pcmk__xml_fmt_pretty, NULL);
+
+ success = xmlOutputBufferWrite(fd_out, sizeof("\n") - 1, "\n") != -1;
+
+ success = xmlOutputBufferClose(fd_out) != -1 && success;
+
+ if (!success) {
+ return EIO;
}
- return FALSE;
+
+ fsync(fd);
+ return pcmk_rc_ok;
}
void
xml_remove_prop(xmlNode * obj, const char *name)
{
+ if (crm_element_value(obj, name) == NULL) {
+ return;
+ }
+
if (pcmk__check_acl(obj, NULL, pcmk__xf_acl_write) == FALSE) {
crm_trace("Cannot remove %s from %s", name, obj->name);
@@ -1750,7 +1715,7 @@ xml_remove_prop(xmlNode * obj, const char *name)
}
void
-save_xml_to_file(xmlNode * xml, const char *desc, const char *filename)
+save_xml_to_file(const xmlNode *xml, const char *desc, const char *filename)
{
char *f = NULL;
@@ -1864,7 +1829,7 @@ mark_attr_moved(xmlNode *new_xml, const char *element, xmlAttr *old_attr,
old_attr->name, p_old, p_new, element);
// Mark document, element, and all element's parents as changed
- mark_xml_node_dirty(new_xml);
+ pcmk__mark_xml_node_dirty(new_xml);
// Mark attribute as changed
pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_moved);
@@ -1886,10 +1851,10 @@ xml_diff_old_attrs(xmlNode *old_xml, xmlNode *new_xml)
xmlAttr *attr_iter = pcmk__xe_first_attr(old_xml);
while (attr_iter != NULL) {
+ const char *name = (const char *) attr_iter->name;
xmlAttr *old_attr = attr_iter;
xmlAttr *new_attr = xmlHasProp(new_xml, attr_iter->name);
- const char *name = (const char *) attr_iter->name;
- const char *old_value = crm_element_value(old_xml, name);
+ const char *old_value = pcmk__xml_attr_value(attr_iter);
attr_iter = attr_iter->next;
if (new_attr == NULL) {
@@ -1943,7 +1908,7 @@ mark_created_attrs(xmlNode *new_xml)
const char *attr_name = (const char *) new_attr->name;
crm_trace("Created new attribute %s=%s in %s",
- attr_name, crm_element_value(new_xml, attr_name),
+ attr_name, pcmk__xml_attr_value(new_attr),
new_xml->name);
/* Check ACLs (we can't use the remove-then-create trick because it
@@ -2017,7 +1982,7 @@ mark_child_moved(xmlNode *old_child, xmlNode *new_parent, xmlNode *new_child,
crm_trace("Child element %s with id='%s' moved from position %d to %d under %s",
new_child->name, (ID(new_child)? ID(new_child) : "<no id>"),
p_old, p_new, new_parent->name);
- mark_xml_node_dirty(new_parent);
+ pcmk__mark_xml_node_dirty(new_parent);
pcmk__set_xml_flags(nodepriv, pcmk__xf_moved);
if (p_old > p_new) {
@@ -2102,9 +2067,10 @@ xml_calculate_significant_changes(xmlNode *old_xml, xmlNode *new_xml)
void
xml_calculate_changes(xmlNode *old_xml, xmlNode *new_xml)
{
- CRM_CHECK(pcmk__str_eq(crm_element_name(old_xml), crm_element_name(new_xml), pcmk__str_casei),
+ CRM_CHECK((old_xml != NULL) && (new_xml != NULL)
+ && pcmk__xe_is(old_xml, (const char *) new_xml->name)
+ && pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_none),
return);
- CRM_CHECK(pcmk__str_eq(ID(old_xml), ID(new_xml), pcmk__str_casei), return);
if(xml_tracking_changes(new_xml) == FALSE) {
xml_track_changes(new_xml, NULL, NULL, FALSE);
@@ -2118,10 +2084,13 @@ can_prune_leaf(xmlNode * xml_node)
{
xmlNode *cIter = NULL;
gboolean can_prune = TRUE;
- const char *name = crm_element_name(xml_node);
- if (pcmk__strcase_any_of(name, XML_TAG_RESOURCE_REF, XML_CIB_TAG_OBJ_REF,
- XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1, NULL)) {
+ CRM_CHECK(xml_node != NULL, return FALSE);
+
+ if (pcmk__strcase_any_of((const char *) xml_node->name,
+ XML_TAG_RESOURCE_REF, XML_CIB_TAG_OBJ_REF,
+ XML_ACL_TAG_ROLE_REF, XML_ACL_TAG_ROLE_REFv1,
+ NULL)) {
return FALSE;
}
@@ -2257,7 +2226,7 @@ pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
return;
}
- object_name = crm_element_name(update);
+ object_name = (const char *) update->name;
object_href_val = ID(update);
if (object_href_val != NULL) {
object_href = XML_ATTR_ID;
@@ -2294,9 +2263,7 @@ pcmk__xml_update(xmlNode *parent, xmlNode *target, xmlNode *update,
#endif
}
- CRM_CHECK(pcmk__str_eq(crm_element_name(target), crm_element_name(update),
- pcmk__str_casei),
- return);
+ CRM_CHECK(pcmk__xe_is(target, (const char *) update->name), return);
if (as_diff == FALSE) {
/* So that expand_plus_plus() gets called */
@@ -2345,7 +2312,7 @@ update_xml_child(xmlNode * child, xmlNode * to_update)
CRM_CHECK(child != NULL, return FALSE);
CRM_CHECK(to_update != NULL, return FALSE);
- if (!pcmk__str_eq(crm_element_name(to_update), crm_element_name(child), pcmk__str_none)) {
+ if (!pcmk__xe_is(to_update, (const char *) child->name)) {
can_update = FALSE;
} else if (!pcmk__str_eq(ID(to_update), ID(child), pcmk__str_none)) {
@@ -2379,7 +2346,7 @@ find_xml_children(xmlNode ** children, xmlNode * root,
CRM_CHECK(root != NULL, return FALSE);
CRM_CHECK(children != NULL, return FALSE);
- if (tag != NULL && !pcmk__str_eq(tag, crm_element_name(root), pcmk__str_casei)) {
+ if ((tag != NULL) && !pcmk__xe_is(root, tag)) {
} else if (value != NULL && !pcmk__str_eq(value, crm_element_value(root, field), pcmk__str_casei)) {
@@ -2422,7 +2389,7 @@ replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean
if (up_id == NULL || (child_id && strcmp(child_id, up_id) == 0)) {
can_delete = TRUE;
}
- if (!pcmk__str_eq(crm_element_name(update), crm_element_name(child), pcmk__str_casei)) {
+ if (!pcmk__xe_is(update, (const char *) child->name)) {
can_delete = FALSE;
}
if (can_delete && delete_only) {
@@ -2444,23 +2411,23 @@ replace_xml_child(xmlNode * parent, xmlNode * child, xmlNode * update, gboolean
free_xml(child);
} else {
- xmlNode *tmp = copy_xml(update);
- xmlDoc *doc = tmp->doc;
- xmlNode *old = NULL;
+ xmlNode *old = child;
+ xmlNode *new = xmlCopyNode(update, 1);
- xml_accept_changes(tmp);
- old = xmlReplaceNode(child, tmp);
+ CRM_ASSERT(new != NULL);
- if(xml_tracking_changes(tmp)) {
- /* Replaced sections may have included relevant ACLs */
- pcmk__apply_acl(tmp);
- }
+ // May be unnecessary but avoids slight changes to some test outputs
+ reset_xml_node_flags(new);
- xml_calculate_changes(old, tmp);
- xmlDocSetRootElement(doc, old);
- free_xml(old);
+ old = xmlReplaceNode(old, new);
+
+ if (xml_tracking_changes(new)) {
+ // Replaced sections may have included relevant ACLs
+ pcmk__apply_acl(new);
+ }
+ xml_calculate_changes(old, new);
+ xmlFreeNode(old);
}
- child = NULL;
return TRUE;
} else if (can_delete) {
@@ -2491,14 +2458,10 @@ sorted_xml(xmlNode *input, xmlNode *parent, gboolean recursive)
xmlNode *child = NULL;
GSList *nvpairs = NULL;
xmlNode *result = NULL;
- const char *name = NULL;
CRM_CHECK(input != NULL, return NULL);
- name = crm_element_name(input);
- CRM_CHECK(name != NULL, return NULL);
-
- result = create_xml_node(parent, name);
+ result = create_xml_node(parent, (const char *) input->name);
nvpairs = pcmk_xml_attrs2nvpairs(input);
nvpairs = pcmk_sort_nvpairs(nvpairs);
pcmk_nvpairs2xml_attrs(nvpairs, result);
@@ -2547,10 +2510,9 @@ xmlNode *
crm_next_same_xml(const xmlNode *sibling)
{
xmlNode *match = pcmk__xe_next(sibling);
- const char *name = crm_element_name(sibling);
while (match != NULL) {
- if (!strcmp(crm_element_name(match), name)) {
+ if (pcmk__xe_is(match, (const char *) sibling->name)) {
return match;
}
match = pcmk__xe_next(match);
@@ -2592,7 +2554,6 @@ crm_xml_cleanup(void)
xmlNode *
expand_idref(xmlNode * input, xmlNode * top)
{
- const char *tag = NULL;
const char *ref = NULL;
xmlNode *result = input;
@@ -2603,12 +2564,10 @@ expand_idref(xmlNode * input, xmlNode * top)
top = input;
}
- tag = crm_element_name(result);
ref = crm_element_value(result, XML_ATTR_IDREF);
-
if (ref != NULL) {
char *xpath_string = crm_strdup_printf("//%s[@" XML_ATTR_ID "='%s']",
- tag, ref);
+ result->name, ref);
result = get_xpath_object(xpath_string, top, LOG_ERR);
if (result == NULL) {
@@ -2630,7 +2589,7 @@ pcmk__xml_artefact_root(enum pcmk__xml_artefact_ns ns)
char *ret = NULL;
if (base == NULL) {
- base = getenv("PCMK_schema_directory");
+ base = pcmk__env_option(PCMK__ENV_SCHEMA_DIRECTORY);
}
if (pcmk__str_empty(base)) {
base = CRM_SCHEMA_DIRECTORY;
@@ -2741,6 +2700,21 @@ crm_destroy_xml(gpointer data)
free_xml(data);
}
+xmlDoc *
+getDocPtr(xmlNode *node)
+{
+ xmlDoc *doc = NULL;
+
+ CRM_CHECK(node != NULL, return NULL);
+
+ doc = node->doc;
+ if (doc == NULL) {
+ doc = xmlNewDoc((pcmkXmlStr) "1.0");
+ xmlDocSetRootElement(doc, node);
+ }
+ return doc;
+}
+
int
add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
{
@@ -2749,5 +2723,14 @@ add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
return 1;
}
+gboolean
+xml_has_children(const xmlNode * xml_root)
+{
+ if (xml_root != NULL && xml_root->children != NULL) {
+ return TRUE;
+ }
+ return FALSE;
+}
+
// LCOV_EXCL_STOP
// End deprecated API
diff --git a/lib/common/xml_attr.c b/lib/common/xml_attr.c
new file mode 100644
index 0000000..427d267
--- /dev/null
+++ b/lib/common/xml_attr.c
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2004-2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU Lesser General Public License
+ * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <bzlib.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/xmlIO.h> /* xmlAllocOutputBuffer */
+
+#include <crm/crm.h>
+#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
+#include <crm/common/xml_internal.h> // PCMK__XML_LOG_BASE, etc.
+#include "crmcommon_private.h"
+
+void
+pcmk__mark_xml_attr_dirty(xmlAttr *a)
+{
+ xmlNode *parent = a->parent;
+ xml_node_private_t *nodepriv = a->_private;
+
+ pcmk__set_xml_flags(nodepriv, pcmk__xf_dirty|pcmk__xf_modified);
+ pcmk__clear_xml_flags(nodepriv, pcmk__xf_deleted);
+ pcmk__mark_xml_node_dirty(parent);
+}
+
+// This also clears attribute's flags if not marked as deleted
+bool
+pcmk__marked_as_deleted(xmlAttrPtr a, void *user_data)
+{
+ xml_node_private_t *nodepriv = a->_private;
+
+ if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
+ return true;
+ }
+ nodepriv->flags = pcmk__xf_none;
+ return false;
+}
+
+/*!
+ * \internal
+ * \brief Append an XML attribute to a buffer
+ *
+ * \param[in] attr Attribute to append
+ * \param[in,out] buffer Where to append the content (must not be \p NULL)
+ */
+void
+pcmk__dump_xml_attr(const xmlAttr *attr, GString *buffer)
+{
+ char *p_value = NULL;
+ const char *p_name = NULL;
+ xml_node_private_t *nodepriv = NULL;
+
+ if (attr == NULL || attr->children == NULL) {
+ return;
+ }
+
+ nodepriv = attr->_private;
+ if (nodepriv && pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
+ return;
+ }
+
+ p_name = (const char *) attr->name;
+ p_value = crm_xml_escape((const char *)attr->children->content);
+ pcmk__g_strcat(buffer, " ", p_name, "=\"", pcmk__s(p_value, "<null>"), "\"",
+ NULL);
+
+ free(p_value);
+} \ No newline at end of file
diff --git a/lib/common/xml_display.c b/lib/common/xml_display.c
index e2d46ce..18cd3b9 100644
--- a/lib/common/xml_display.c
+++ b/lib/common/xml_display.c
@@ -92,7 +92,6 @@ static int
show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix,
const xmlNode *data, int depth, uint32_t options)
{
- const char *name = crm_element_name(data);
int spaces = pcmk_is_set(options, pcmk__xml_fmt_pretty)? (2 * depth) : 0;
int rc = pcmk_rc_no_output;
@@ -104,7 +103,7 @@ show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix,
for (int lpc = 0; lpc < spaces; lpc++) {
g_string_append_c(buffer, ' ');
}
- pcmk__g_strcat(buffer, "<", name, NULL);
+ pcmk__g_strcat(buffer, "<", data->name, NULL);
for (const xmlAttr *attr = pcmk__xe_first_attr(data); attr != NULL;
attr = attr->next) {
@@ -138,7 +137,7 @@ show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix,
free(p_copy);
}
- if (xml_has_children(data)
+ if ((data->children != NULL)
&& pcmk_is_set(options, pcmk__xml_fmt_children)) {
g_string_append_c(buffer, '>');
@@ -151,7 +150,7 @@ show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix,
buffer->str);
}
- if (!xml_has_children(data)) {
+ if (data->children == NULL) {
return rc;
}
@@ -171,7 +170,7 @@ show_xml_element(pcmk__output_t *out, GString *buffer, const char *prefix,
int temp_rc = out->info(out, "%s%s%*s</%s>",
pcmk__s(prefix, ""),
pcmk__str_empty(prefix)? "" : " ",
- spaces, "", name);
+ spaces, "", data->name);
rc = pcmk__output_select_rc(rc, temp_rc);
}
@@ -304,14 +303,14 @@ show_xml_changes_recursive(pcmk__output_t *out, const xmlNode *data, int depth,
nodepriv = attr->_private;
if (pcmk_is_set(nodepriv->flags, pcmk__xf_deleted)) {
- const char *value = crm_element_value(data, name);
+ const char *value = pcmk__xml_attr_value(attr);
temp_rc = out->info(out, "%s %*s @%s=%s",
PCMK__XML_PREFIX_DELETED, spaces, "", name,
value);
} else if (pcmk_is_set(nodepriv->flags, pcmk__xf_dirty)) {
- const char *value = crm_element_value(data, name);
+ const char *value = pcmk__xml_attr_value(attr);
if (pcmk_is_set(nodepriv->flags, pcmk__xf_created)) {
prefix = PCMK__XML_PREFIX_CREATED;
@@ -447,9 +446,6 @@ log_data_element(int log_level, const char *file, const char *function,
if (pcmk_is_set(legacy_options, xml_log_option_formatted)) {
options |= pcmk__xml_fmt_pretty;
}
- if (pcmk_is_set(legacy_options, xml_log_option_full_fledged)) {
- options |= pcmk__xml_fmt_full;
- }
if (pcmk_is_set(legacy_options, xml_log_option_open)) {
options |= pcmk__xml_fmt_open;
}
@@ -480,7 +476,7 @@ log_data_element(int log_level, const char *file, const char *function,
}
if (pcmk_is_set(options, pcmk__xml_fmt_pretty)
- && (!xml_has_children(data)
+ && ((data->children == NULL)
|| (crm_element_value(data, XML_DIFF_MARKER) != NULL))) {
if (pcmk_is_set(options, pcmk__xml_fmt_diff_plus)) {
diff --git a/lib/common/xpath.c b/lib/common/xpath.c
index 1f5c0a8..d90f1c5 100644
--- a/lib/common/xpath.c
+++ b/lib/common/xpath.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2022 the Pacemaker project contributors
+ * Copyright 2004-2023 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -136,9 +136,8 @@ dedupXpathResults(xmlXPathObjectPtr xpathObj)
/* the caller needs to check if the result contains a xmlDocPtr or xmlNodePtr */
xmlXPathObjectPtr
-xpath_search(xmlNode * xml_top, const char *path)
+xpath_search(const xmlNode *xml_top, const char *path)
{
- xmlDocPtr doc = NULL;
xmlXPathObjectPtr xpathObj = NULL;
xmlXPathContextPtr xpathCtx = NULL;
const xmlChar *xpathExpr = (pcmkXmlStr) path;
@@ -147,9 +146,7 @@ xpath_search(xmlNode * xml_top, const char *path)
CRM_CHECK(xml_top != NULL, return NULL);
CRM_CHECK(strlen(path) > 0, return NULL);
- doc = getDocPtr(xml_top);
-
- xpathCtx = xmlXPathNewContext(doc);
+ xpathCtx = xmlXPathNewContext(xml_top->doc);
CRM_ASSERT(xpathCtx != NULL);
xpathObj = xmlXPathEvalExpression(xpathExpr, xpathCtx);
@@ -298,9 +295,9 @@ pcmk__element_xpath(const xmlNode *xml)
if (parent == NULL) {
g_string_append_c(xpath, '/');
} else if (parent->parent == NULL) {
- g_string_append(xpath, TYPE(xml));
+ g_string_append(xpath, (const gchar *) xml->name);
} else {
- pcmk__g_strcat(xpath, "/", TYPE(xml), NULL);
+ pcmk__g_strcat(xpath, "/", (const char *) xml->name, NULL);
}
id = ID(xml);