From 6a70c351dd5caaaf961b4d04919ddef3afe11877 Mon Sep 17 00:00:00 2001 From: Yan Gao Date: Fri, 31 Oct 2025 16:39:42 +0100 Subject: [PATCH 1/4] Fix: libcib: Prevent based or cibadmin from crashing when handling an XPath query for an attribute With 5fe98f4d25 in 3.0.1, if an XPath query for an attribute resulted in a single match, for example: $ cibadmin --query --xpath "/cib/@crm_feature_set" , based would encounter a fatal assertion in pcmk__xml_copy(): error: pcmk__xml_copy: Triggered fatal assertion at xml.c:844 : src->type == XML_ELEMENT_NODE The assertion wouldn't be triggered if there were multiple matches for an attribute, despite the fact that only the last match would be displayed: $ cibadmin --query --xpath "//@uname" But in case --node-path option was anyhow specified, cibadmin would encounter a fatal assertion in output_xml_id(): error: output_xml_id: Triggered fatal assertion at cibadmin.c:865 : id != NULL If --no-children option was anyhow specified, the attribute name would be displayed as an element name and the value wouldn't be shown: $ cibadmin --query --xpath "/cib/@crm_feature_set" --no-children This commit fixes the issues by handling non-element matches separately. By creating an "xpath-query" XML element for a single match, it's displayed in the same format as for the last match in case of multiple matches so far: $ cibadmin --query --xpath "/cib/@crm_feature_set" --- lib/cib/cib_ops.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index 31506960b84..ba74c7d4748 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -766,6 +766,15 @@ cib_process_xpath(const char *op, int options, const char *section, break; } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { + if (match->type != XML_ELEMENT_NODE) { + // Create an element for a single match of a non-element + if (*answer == NULL) { + *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); + } + + pcmk__xml_copy(*answer, match); + continue; + } if (options & cib_no_children) { xmlNode *shallow = pcmk__xe_create(*answer, From c5df440749d58bc8f8aa0a6021c18c26853030fd Mon Sep 17 00:00:00 2001 From: Yan Gao Date: Fri, 31 Oct 2025 23:03:58 +0100 Subject: [PATCH 2/4] API: libcib: Meaningful output from XPath query for an attribute With 5fe98f4d25 in 3.0.1, a matching attribute was displayed with a vague element name "xpath-query". Besides, if there were multiple matches for an attribute, only the last one would be displayed. Now a matching attribute is displayed with the corresponding element name and the ID if present: $ cibadmin --query --xpath "/cib/@crm_feature_set" Multiple matches are all displayed: $ cibadmin --query --xpath "//@uname" --- lib/cib/cib_ops.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index ba74c7d4748..fb3b3ffcb38 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -767,12 +767,23 @@ cib_process_xpath(const char *op, int options, const char *section, } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { if (match->type != XML_ELEMENT_NODE) { - // Create an element for a single match of a non-element + xmlNode *match_element = pcmk__xpath_match_element(match); + xmlNode *brief = NULL; + + if (match_element == NULL) { + continue; + } + + // Create an element with the corresponding element name + brief = pcmk__xe_create(*answer, + (const char *) match_element->name); + pcmk__xe_set(brief, PCMK_XA_ID, pcmk__xe_id(match_element)); + pcmk__xml_copy(brief, match); + if (*answer == NULL) { - *answer = pcmk__xe_create(NULL, PCMK__XE_XPATH_QUERY); + *answer = brief; } - pcmk__xml_copy(*answer, match); continue; } From dc27f406aab24d856ef4d9934f50280f81c1080b Mon Sep 17 00:00:00 2001 From: Yan Gao Date: Mon, 3 Nov 2025 23:16:40 +0100 Subject: [PATCH 3/4] Fix: libcib: Respect cib_xpath_address option for a query even if cib_no_children is also specified So that it accommodates cibadmin --node-path. Since as long as --node-path is specified. "cibadmin-node-path" message applies, even if --no-children is also specified. --- lib/cib/cib_ops.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index fb3b3ffcb38..f5e65e2c73d 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -787,17 +787,7 @@ cib_process_xpath(const char *op, int options, const char *section, continue; } - if (options & cib_no_children) { - xmlNode *shallow = pcmk__xe_create(*answer, - (const char *) match->name); - - pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none); - - if (*answer == NULL) { - *answer = shallow; - } - - } else if (options & cib_xpath_address) { + if (options & cib_xpath_address) { // @COMPAT cib_xpath_address is deprecated since 3.0.2 char *path = NULL; xmlNode *parent = match; @@ -828,6 +818,16 @@ cib_process_xpath(const char *op, int options, const char *section, pcmk__xe_set(parent, PCMK_XA_ID, path); free(path); + } else if (options & cib_no_children) { + xmlNode *shallow = pcmk__xe_create(*answer, + (const char *) match->name); + + pcmk__xe_copy_attrs(shallow, match, pcmk__xaf_none); + + if (*answer == NULL) { + *answer = shallow; + } + } else if (*answer) { pcmk__xml_copy(*answer, match); From 9efad104f45784ce629bd1ad91b44717eb3572f5 Mon Sep 17 00:00:00 2001 From: Yan Gao Date: Mon, 3 Nov 2025 23:37:35 +0100 Subject: [PATCH 4/4] Fix: libcib: Handle cib_xpath_address for an XPath query for an attribute Although cibadmin --node-path option is deprecated, the previous behavior is restored by handling the corresponding element of a non-element match instead of returning nothing: $ cibadmin --query --xpath "//@uname" --node-path /cib/configuration/nodes/node[@id='1'] /cib/configuration/nodes/node[@id='2'] /cib/status/node_state[@id='1'] /cib/status/node_state[@id='2'] --- lib/cib/cib_ops.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/cib/cib_ops.c b/lib/cib/cib_ops.c index f5e65e2c73d..48f05f58656 100644 --- a/lib/cib/cib_ops.c +++ b/lib/cib/cib_ops.c @@ -766,7 +766,18 @@ cib_process_xpath(const char *op, int options, const char *section, break; } else if (pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) { - if (match->type != XML_ELEMENT_NODE) { + if (match->type != XML_ELEMENT_NODE + && pcmk__is_set(options, cib_xpath_address)) { + /* @COMPAT cib_xpath_address is deprecated since 3.0.2 + * For a non-element, handle cib_xpath_address with its + * corresponding element. + */ + match = pcmk__xpath_match_element(match); + if (match == NULL) { + continue; + } + + } else if (match->type != XML_ELEMENT_NODE) { xmlNode *match_element = pcmk__xpath_match_element(match); xmlNode *brief = NULL;