1 /*
2  * Copyright 2004-2020 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 
12 #include <sys/param.h>
13 
14 #include <crm/crm.h>
15 
16 #include <stdio.h>
17 #include <sys/types.h>
18 #include <unistd.h>
19 
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <libgen.h>
24 
25 #include <crm/msg_xml.h>
26 #include <crm/common/xml.h>
27 #include <crm/common/xml_internal.h>
28 #include <crm/cib/internal.h>
29 
30 #define attr_msg(level, fmt, args...) do {	\
31 	if(to_console) {			\
32 	    printf(fmt"\n", ##args);		\
33 	} else {				\
34 	    do_crm_log(level, fmt , ##args);	\
35 	}					\
36     } while(0)
37 
38 /* could also check for possible truncation */
39 #define attr_snprintf(_str, _offset, _limit, ...) do {              \
40     _offset += snprintf(_str + _offset,                             \
41                         (_limit > _offset) ? _limit - _offset : 0,  \
42                         __VA_ARGS__);                               \
43     } while(0)
44 
45 #define XPATH_MAX 1024
46 
47 extern int
find_nvpair_attr_delegate(cib_t * the_cib,const char * attr,const char * section,const char * node_uuid,const char * attr_set_type,const char * set_name,const char * attr_id,const char * attr_name,gboolean to_console,char ** value,const char * user_name)48 find_nvpair_attr_delegate(cib_t * the_cib, const char *attr, const char *section,
49                           const char *node_uuid, const char *attr_set_type, const char *set_name,
50                           const char *attr_id, const char *attr_name, gboolean to_console,
51                           char **value, const char *user_name)
52 {
53     int offset = 0;
54     int rc = pcmk_ok;
55 
56     char *xpath_string = NULL;
57     xmlNode *xml_search = NULL;
58     const char *set_type = NULL;
59     const char *node_type = NULL;
60 
61     if (attr_set_type) {
62         set_type = attr_set_type;
63     } else {
64         set_type = XML_TAG_ATTR_SETS;
65     }
66 
67     CRM_ASSERT(value != NULL);
68     *value = NULL;
69 
70     if (pcmk__str_eq(section, XML_CIB_TAG_CRMCONFIG, pcmk__str_casei)) {
71         node_uuid = NULL;
72         set_type = XML_CIB_TAG_PROPSET;
73 
74     } else if (pcmk__strcase_any_of(section, XML_CIB_TAG_OPCONFIG, XML_CIB_TAG_RSCCONFIG,
75                                     NULL)) {
76         node_uuid = NULL;
77         set_type = XML_TAG_META_SETS;
78 
79     } else if (pcmk__str_eq(section, XML_CIB_TAG_TICKETS, pcmk__str_casei)) {
80         node_uuid = NULL;
81         section = XML_CIB_TAG_STATUS;
82         node_type = XML_CIB_TAG_TICKETS;
83 
84     } else if (node_uuid == NULL) {
85         return -EINVAL;
86     }
87 
88     xpath_string = calloc(1, XPATH_MAX);
89     if (xpath_string == NULL) {
90         crm_perror(LOG_CRIT, "Could not create xpath");
91         return -ENOMEM;
92     }
93 
94     attr_snprintf(xpath_string, offset, XPATH_MAX, "%.128s", get_object_path(section));
95 
96     if (pcmk__str_eq(node_type, XML_CIB_TAG_TICKETS, pcmk__str_casei)) {
97         attr_snprintf(xpath_string, offset, XPATH_MAX, "//%s", node_type);
98 
99     } else if (node_uuid) {
100         const char *node_type = XML_CIB_TAG_NODE;
101 
102         if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
103             node_type = XML_CIB_TAG_STATE;
104             set_type = XML_TAG_TRANSIENT_NODEATTRS;
105         }
106         attr_snprintf(xpath_string, offset, XPATH_MAX, "//%s[@id='%s']", node_type,
107                       node_uuid);
108     }
109 
110     if (set_name) {
111         attr_snprintf(xpath_string, offset, XPATH_MAX, "//%s[@id='%.128s']", set_type,
112                       set_name);
113     } else {
114         attr_snprintf(xpath_string, offset, XPATH_MAX, "//%s", set_type);
115     }
116 
117     attr_snprintf(xpath_string, offset, XPATH_MAX, "//nvpair[");
118     if (attr_id) {
119         attr_snprintf(xpath_string, offset, XPATH_MAX, "@id='%s'", attr_id);
120     }
121 
122     if (attr_name) {
123         if (attr_id) {
124             attr_snprintf(xpath_string, offset, XPATH_MAX, " and ");
125         }
126         attr_snprintf(xpath_string, offset, XPATH_MAX, "@name='%.128s'", attr_name);
127     }
128     attr_snprintf(xpath_string, offset, XPATH_MAX, "]");
129     CRM_LOG_ASSERT(offset > 0);
130 
131     rc = cib_internal_op(the_cib, CIB_OP_QUERY, NULL, xpath_string, NULL, &xml_search,
132                          cib_sync_call | cib_scope_local | cib_xpath, user_name);
133 
134     if (rc != pcmk_ok) {
135         crm_trace("Query failed for attribute %s (section=%s, node=%s, set=%s, xpath=%s): %s",
136                   attr_name, section, crm_str(node_uuid), crm_str(set_name), xpath_string,
137                   pcmk_strerror(rc));
138         goto done;
139     }
140 
141     crm_log_xml_debug(xml_search, "Match");
142     if (xml_has_children(xml_search)) {
143         xmlNode *child = NULL;
144 
145         rc = -ENOTUNIQ;
146         attr_msg(LOG_WARNING, "Multiple attributes match name=%s", attr_name);
147 
148         for (child = pcmk__xml_first_child(xml_search); child != NULL;
149              child = pcmk__xml_next(child)) {
150             attr_msg(LOG_INFO, "  Value: %s \t(id=%s)",
151                      crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child));
152         }
153 
154     } else {
155         const char *tmp = crm_element_value(xml_search, attr);
156 
157         if (tmp) {
158             *value = strdup(tmp);
159         }
160     }
161 
162   done:
163     free(xpath_string);
164     free_xml(xml_search);
165     return rc;
166 }
167 
168 int
update_attr_delegate(cib_t * the_cib,int call_options,const char * section,const char * node_uuid,const char * set_type,const char * set_name,const char * attr_id,const char * attr_name,const char * attr_value,gboolean to_console,const char * user_name,const char * node_type)169 update_attr_delegate(cib_t * the_cib, int call_options,
170                      const char *section, const char *node_uuid, const char *set_type,
171                      const char *set_name, const char *attr_id, const char *attr_name,
172                      const char *attr_value, gboolean to_console, const char *user_name,
173                      const char *node_type)
174 {
175     const char *tag = NULL;
176     int rc = pcmk_ok;
177     xmlNode *xml_top = NULL;
178     xmlNode *xml_obj = NULL;
179 
180     char *local_attr_id = NULL;
181     char *local_set_name = NULL;
182 
183     CRM_CHECK(section != NULL, return -EINVAL);
184     CRM_CHECK(attr_value != NULL, return -EINVAL);
185     CRM_CHECK(attr_name != NULL || attr_id != NULL, return -EINVAL);
186 
187     rc = find_nvpair_attr_delegate(the_cib, XML_ATTR_ID, section, node_uuid, set_type, set_name,
188                                    attr_id, attr_name, to_console, &local_attr_id, user_name);
189     if (rc == pcmk_ok) {
190         attr_id = local_attr_id;
191         goto do_modify;
192 
193     } else if (rc != -ENXIO) {
194         return rc;
195 
196         /* } else if(attr_id == NULL) { */
197         /*     return -EINVAL; */
198 
199     } else {
200         crm_trace("%s does not exist, create it", attr_name);
201         if (pcmk__str_eq(section, XML_CIB_TAG_TICKETS, pcmk__str_casei)) {
202             node_uuid = NULL;
203             section = XML_CIB_TAG_STATUS;
204             node_type = XML_CIB_TAG_TICKETS;
205 
206             xml_top = create_xml_node(xml_obj, XML_CIB_TAG_STATUS);
207             xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS);
208 
209         } else if (pcmk__str_eq(section, XML_CIB_TAG_NODES, pcmk__str_casei)) {
210 
211             if (node_uuid == NULL) {
212                 return -EINVAL;
213             }
214 
215             if (pcmk__str_eq(node_type, "remote", pcmk__str_casei)) {
216                 xml_top = create_xml_node(xml_obj, XML_CIB_TAG_NODES);
217                 xml_obj = create_xml_node(xml_top, XML_CIB_TAG_NODE);
218                 crm_xml_add(xml_obj, XML_ATTR_TYPE, "remote");
219                 crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid);
220                 crm_xml_add(xml_obj, XML_ATTR_UNAME, node_uuid);
221             } else {
222                 tag = XML_CIB_TAG_NODE;
223             }
224 
225         } else if (pcmk__str_eq(section, XML_CIB_TAG_STATUS, pcmk__str_casei)) {
226             tag = XML_TAG_TRANSIENT_NODEATTRS;
227             if (node_uuid == NULL) {
228                 return -EINVAL;
229             }
230 
231             xml_top = create_xml_node(xml_obj, XML_CIB_TAG_STATE);
232             crm_xml_add(xml_top, XML_ATTR_ID, node_uuid);
233             xml_obj = xml_top;
234 
235         } else {
236             tag = section;
237             node_uuid = NULL;
238         }
239 
240         if (set_name == NULL) {
241             if (pcmk__str_eq(section, XML_CIB_TAG_CRMCONFIG, pcmk__str_casei)) {
242                 local_set_name = strdup(CIB_OPTIONS_FIRST);
243 
244             } else if (pcmk__str_eq(node_type, XML_CIB_TAG_TICKETS, pcmk__str_casei)) {
245                 local_set_name = crm_strdup_printf("%s-%s", section,
246                                                    XML_CIB_TAG_TICKETS);
247 
248             } else if (node_uuid) {
249                 local_set_name = crm_strdup_printf("%s-%s", section, node_uuid);
250 
251                 if (set_type) {
252                     char *tmp_set_name = local_set_name;
253 
254                     local_set_name = crm_strdup_printf("%s-%s", tmp_set_name,
255                                                        set_type);
256                     free(tmp_set_name);
257                 }
258             } else {
259                 local_set_name = crm_strdup_printf("%s-options", section);
260             }
261             set_name = local_set_name;
262         }
263 
264         if (attr_id == NULL) {
265             local_attr_id = crm_strdup_printf("%s-%s", set_name, attr_name);
266             crm_xml_sanitize_id(local_attr_id);
267             attr_id = local_attr_id;
268 
269         } else if (attr_name == NULL) {
270             attr_name = attr_id;
271         }
272 
273         crm_trace("Creating %s/%s", section, tag);
274         if (tag != NULL) {
275             xml_obj = create_xml_node(xml_obj, tag);
276             crm_xml_add(xml_obj, XML_ATTR_ID, node_uuid);
277             if (xml_top == NULL) {
278                 xml_top = xml_obj;
279             }
280         }
281 
282         if (node_uuid == NULL && !pcmk__str_eq(node_type, XML_CIB_TAG_TICKETS, pcmk__str_casei)) {
283             if (pcmk__str_eq(section, XML_CIB_TAG_CRMCONFIG, pcmk__str_casei)) {
284                 xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_PROPSET);
285             } else {
286                 xml_obj = create_xml_node(xml_obj, XML_TAG_META_SETS);
287             }
288 
289         } else if (set_type) {
290             xml_obj = create_xml_node(xml_obj, set_type);
291 
292         } else {
293             xml_obj = create_xml_node(xml_obj, XML_TAG_ATTR_SETS);
294         }
295         crm_xml_add(xml_obj, XML_ATTR_ID, set_name);
296 
297         if (xml_top == NULL) {
298             xml_top = xml_obj;
299         }
300     }
301 
302   do_modify:
303     xml_obj = crm_create_nvpair_xml(xml_obj, attr_id, attr_name, attr_value);
304     if (xml_top == NULL) {
305         xml_top = xml_obj;
306     }
307 
308     crm_log_xml_trace(xml_top, "update_attr");
309     rc = cib_internal_op(the_cib, CIB_OP_MODIFY, NULL, section, xml_top, NULL,
310                          call_options | cib_quorum_override, user_name);
311 
312     if (rc < pcmk_ok) {
313         attr_msg(LOG_ERR, "Error setting %s=%s (section=%s, set=%s): %s",
314                  attr_name, attr_value, section, crm_str(set_name), pcmk_strerror(rc));
315         crm_log_xml_info(xml_top, "Update");
316     }
317 
318     free(local_set_name);
319     free(local_attr_id);
320     free_xml(xml_top);
321 
322     return rc;
323 }
324 
325 int
read_attr_delegate(cib_t * the_cib,const char * section,const char * node_uuid,const char * set_type,const char * set_name,const char * attr_id,const char * attr_name,char ** attr_value,gboolean to_console,const char * user_name)326 read_attr_delegate(cib_t * the_cib,
327                    const char *section, const char *node_uuid, const char *set_type,
328                    const char *set_name, const char *attr_id, const char *attr_name,
329                    char **attr_value, gboolean to_console, const char *user_name)
330 {
331     int rc = pcmk_ok;
332 
333     CRM_ASSERT(attr_value != NULL);
334     CRM_CHECK(section != NULL, return -EINVAL);
335     CRM_CHECK(attr_name != NULL || attr_id != NULL, return -EINVAL);
336 
337     *attr_value = NULL;
338 
339     rc = find_nvpair_attr_delegate(the_cib, XML_NVPAIR_ATTR_VALUE, section, node_uuid, set_type,
340                                    set_name, attr_id, attr_name, to_console, attr_value, user_name);
341     if (rc != pcmk_ok) {
342         crm_trace("Query failed for attribute %s (section=%s, node=%s, set=%s): %s",
343                   attr_name, section, crm_str(set_name), crm_str(node_uuid), pcmk_strerror(rc));
344     }
345     return rc;
346 }
347 
348 int
delete_attr_delegate(cib_t * the_cib,int options,const char * section,const char * node_uuid,const char * set_type,const char * set_name,const char * attr_id,const char * attr_name,const char * attr_value,gboolean to_console,const char * user_name)349 delete_attr_delegate(cib_t * the_cib, int options,
350                      const char *section, const char *node_uuid, const char *set_type,
351                      const char *set_name, const char *attr_id, const char *attr_name,
352                      const char *attr_value, gboolean to_console, const char *user_name)
353 {
354     int rc = pcmk_ok;
355     xmlNode *xml_obj = NULL;
356     char *local_attr_id = NULL;
357 
358     CRM_CHECK(section != NULL, return -EINVAL);
359     CRM_CHECK(attr_name != NULL || attr_id != NULL, return -EINVAL);
360 
361     if (attr_id == NULL) {
362         rc = find_nvpair_attr_delegate(the_cib, XML_ATTR_ID, section, node_uuid, set_type,
363                                        set_name, attr_id, attr_name, to_console, &local_attr_id,
364                                        user_name);
365         if (rc != pcmk_ok) {
366             return rc;
367         }
368         attr_id = local_attr_id;
369     }
370 
371     xml_obj = crm_create_nvpair_xml(NULL, attr_id, attr_name, attr_value);
372 
373     rc = cib_internal_op(the_cib, CIB_OP_DELETE, NULL, section, xml_obj, NULL,
374                          options | cib_quorum_override, user_name);
375 
376     if (rc == pcmk_ok) {
377         attr_msg(LOG_DEBUG, "Deleted %s %s: id=%s%s%s%s%s\n",
378                  section, node_uuid ? "attribute" : "option", local_attr_id,
379                  set_name ? " set=" : "", set_name ? set_name : "",
380                  attr_name ? " name=" : "", attr_name ? attr_name : "");
381     }
382 
383     free(local_attr_id);
384     free_xml(xml_obj);
385     return rc;
386 }
387 
388 /*!
389  * \internal
390  * \brief Parse node UUID from search result
391  *
392  * \param[in]  result     XML search result
393  * \param[out] uuid       If non-NULL, where to store parsed UUID
394  * \param[out] is_remote  If non-NULL, set TRUE if result is remote node
395  *
396  * \return pcmk_ok if UUID was successfully parsed, -ENXIO otherwise
397  */
398 static int
get_uuid_from_result(xmlNode * result,char ** uuid,int * is_remote)399 get_uuid_from_result(xmlNode *result, char **uuid, int *is_remote)
400 {
401     int rc = -ENXIO;
402     const char *tag;
403     const char *parsed_uuid = NULL;
404     int parsed_is_remote = FALSE;
405 
406     if (result == NULL) {
407         return rc;
408     }
409 
410     /* If there are multiple results, the first is sufficient */
411     tag = (const char *) (result->name);
412     if (pcmk__str_eq(tag, "xpath-query", pcmk__str_casei)) {
413         result = pcmk__xml_first_child(result);
414         CRM_CHECK(result != NULL, return rc);
415         tag = (const char *) (result->name);
416     }
417 
418     if (pcmk__str_eq(tag, XML_CIB_TAG_NODE, pcmk__str_casei)) {
419         /* Result is <node> tag from <nodes> section */
420 
421         if (pcmk__str_eq(crm_element_value(result, XML_ATTR_TYPE), "remote", pcmk__str_casei)) {
422             parsed_uuid = crm_element_value(result, XML_ATTR_UNAME);
423             parsed_is_remote = TRUE;
424         } else {
425             parsed_uuid = ID(result);
426             parsed_is_remote = FALSE;
427         }
428 
429     } else if (pcmk__str_eq(tag, XML_CIB_TAG_RESOURCE, pcmk__str_casei)) {
430         /* Result is <primitive> for ocf:pacemaker:remote resource */
431 
432         parsed_uuid = ID(result);
433         parsed_is_remote = TRUE;
434 
435     } else if (pcmk__str_eq(tag, XML_CIB_TAG_NVPAIR, pcmk__str_casei)) {
436         /* Result is remote-node parameter of <primitive> for guest node */
437 
438         parsed_uuid = crm_element_value(result, XML_NVPAIR_ATTR_VALUE);
439         parsed_is_remote = TRUE;
440 
441     } else if (pcmk__str_eq(tag, XML_CIB_TAG_STATE, pcmk__str_casei)) {
442         /* Result is <node_state> tag from <status> section */
443 
444         parsed_uuid = crm_element_value(result, XML_ATTR_UNAME);
445         if (crm_is_true(crm_element_value(result, XML_NODE_IS_REMOTE))) {
446             parsed_is_remote = TRUE;
447         }
448     }
449 
450     if (parsed_uuid) {
451         if (uuid) {
452             *uuid = strdup(parsed_uuid);
453         }
454         if (is_remote) {
455             *is_remote = parsed_is_remote;
456         }
457         rc = pcmk_ok;
458     }
459 
460     return rc;
461 }
462 
463 /* Search string to find a node by name, as:
464  * - cluster or remote node in nodes section
465  * - remote node in resources section
466  * - guest node in resources section
467  * - orphaned remote node or bundle guest node in status section
468  */
469 #define XPATH_UPPER_TRANS "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
470 #define XPATH_LOWER_TRANS "abcdefghijklmnopqrstuvwxyz"
471 #define XPATH_NODE \
472     "/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_NODES \
473         "/" XML_CIB_TAG_NODE "[translate(@" XML_ATTR_UNAME ",'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \
474     "|/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RESOURCES \
475         "/" XML_CIB_TAG_RESOURCE \
476         "[@class='ocf'][@provider='pacemaker'][@type='remote'][translate(@id,'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \
477     "|/" XML_TAG_CIB "/" XML_CIB_TAG_CONFIGURATION "/" XML_CIB_TAG_RESOURCES \
478         "/" XML_CIB_TAG_RESOURCE "/" XML_TAG_META_SETS "/" XML_CIB_TAG_NVPAIR \
479         "[@name='" XML_RSC_ATTR_REMOTE_NODE "'][translate(@value,'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']" \
480     "|/" XML_TAG_CIB "/" XML_CIB_TAG_STATUS "/" XML_CIB_TAG_STATE \
481         "[@" XML_NODE_IS_REMOTE "='true'][translate(@" XML_ATTR_UUID ",'" XPATH_UPPER_TRANS "','" XPATH_LOWER_TRANS "') ='%s']"
482 
483 int
query_node_uuid(cib_t * the_cib,const char * uname,char ** uuid,int * is_remote_node)484 query_node_uuid(cib_t * the_cib, const char *uname, char **uuid, int *is_remote_node)
485 {
486     int rc = pcmk_ok;
487     char *xpath_string;
488     xmlNode *xml_search = NULL;
489     char *host_lowercase = g_ascii_strdown(uname, -1);
490 
491     CRM_ASSERT(uname != NULL);
492 
493     if (uuid) {
494         *uuid = NULL;
495     }
496     if (is_remote_node) {
497         *is_remote_node = FALSE;
498     }
499 
500     xpath_string = crm_strdup_printf(XPATH_NODE, host_lowercase, host_lowercase, host_lowercase, host_lowercase);
501     if (cib_internal_op(the_cib, CIB_OP_QUERY, NULL, xpath_string, NULL,
502                         &xml_search, cib_sync_call|cib_scope_local|cib_xpath,
503                         NULL) == pcmk_ok) {
504         rc = get_uuid_from_result(xml_search, uuid, is_remote_node);
505     } else {
506         rc = -ENXIO;
507     }
508     free(xpath_string);
509     free_xml(xml_search);
510     g_free(host_lowercase);
511 
512     if (rc != pcmk_ok) {
513         crm_debug("Could not map node name '%s' to a UUID: %s",
514                   uname, pcmk_strerror(rc));
515     } else {
516         crm_info("Mapped node name '%s' to UUID %s", uname, (uuid? *uuid : ""));
517     }
518     return rc;
519 }
520 
521 int
query_node_uname(cib_t * the_cib,const char * uuid,char ** uname)522 query_node_uname(cib_t * the_cib, const char *uuid, char **uname)
523 {
524     int rc = pcmk_ok;
525     xmlNode *a_child = NULL;
526     xmlNode *xml_obj = NULL;
527     xmlNode *fragment = NULL;
528     const char *child_name = NULL;
529 
530     CRM_ASSERT(uname != NULL);
531     CRM_ASSERT(uuid != NULL);
532 
533     rc = the_cib->cmds->query(the_cib, XML_CIB_TAG_NODES, &fragment,
534                               cib_sync_call | cib_scope_local);
535     if (rc != pcmk_ok) {
536         return rc;
537     }
538 
539     xml_obj = fragment;
540     CRM_CHECK(pcmk__str_eq(crm_element_name(xml_obj), XML_CIB_TAG_NODES, pcmk__str_casei),
541               return -ENOMSG);
542     CRM_ASSERT(xml_obj != NULL);
543     crm_log_xml_trace(xml_obj, "Result section");
544 
545     rc = -ENXIO;
546     *uname = NULL;
547 
548     for (a_child = pcmk__xml_first_child(xml_obj); a_child != NULL;
549          a_child = pcmk__xml_next(a_child)) {
550 
551         if (pcmk__str_eq((const char *)a_child->name, XML_CIB_TAG_NODE,
552                          pcmk__str_none)) {
553             child_name = ID(a_child);
554             if (pcmk__str_eq(uuid, child_name, pcmk__str_casei)) {
555                 child_name = crm_element_value(a_child, XML_ATTR_UNAME);
556                 if (child_name != NULL) {
557                     *uname = strdup(child_name);
558                     rc = pcmk_ok;
559                 }
560                 break;
561             }
562         }
563     }
564 
565     free_xml(fragment);
566     return rc;
567 }
568 
569 int
set_standby(cib_t * the_cib,const char * uuid,const char * scope,const char * standby_value)570 set_standby(cib_t * the_cib, const char *uuid, const char *scope, const char *standby_value)
571 {
572     int rc = pcmk_ok;
573     char *attr_id = NULL;
574 
575     CRM_CHECK(uuid != NULL, return -EINVAL);
576     CRM_CHECK(standby_value != NULL, return -EINVAL);
577 
578     if (pcmk__strcase_any_of(scope, "reboot", XML_CIB_TAG_STATUS, NULL)) {
579         scope = XML_CIB_TAG_STATUS;
580         attr_id = crm_strdup_printf("transient-standby-%.256s", uuid);
581 
582     } else {
583         scope = XML_CIB_TAG_NODES;
584         attr_id = crm_strdup_printf("standby-%.256s", uuid);
585     }
586 
587     rc = update_attr_delegate(the_cib, cib_sync_call, scope, uuid, NULL, NULL,
588                               attr_id, "standby", standby_value, TRUE, NULL, NULL);
589 
590     free(attr_id);
591     return rc;
592 }
593