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"
<xpath-query uname="node2"/>
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
<crm_feature_set/>
This commit fixes the issues by handling non-element matches separately.
Besides, instead of being displayed with a vague element name "xpath-query", now a matching attribute is displayed with the corresponding element name and the ID if present, so that the output is meaningful:
$ cibadmin --query --xpath "/cib/@crm_feature_set"
<cib crm_feature_set="3.20.5"/>
Multiple matches are all displayed:
$ cibadmin --query --xpath "//@uname"
<xpath-query>
<node id="1" uname="node1"/>
<node id="2" uname="node2"/>
<node_state id="1" uname="node1"/>
<node_state id="2" uname="node2"/>
</xpath-query>
Also, 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']
And --node-path option is respected even if --no-children is also specified.