1    	/*
2    	 * Copyright 2004-2026 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 General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <stdbool.h>
13   	#include <stdint.h>
14   	
15   	#include <crm_resource.h>
16   	#include <crm/common/output.h>
17   	#include <crm/common/results.h>
18   	
19   	#define cons_string(x) x?x:"NA"
20   	static int
21   	print_constraint(xmlNode *xml_obj, void *userdata)
22   	{
23   	    pcmk_scheduler_t *scheduler = (pcmk_scheduler_t *) userdata;
24   	    pcmk__output_t *out = scheduler->priv->out;
25   	    const char *id = pcmk__xe_get(xml_obj, PCMK_XA_ID);
26   	
27   	    if (id == NULL) {
28   	        return pcmk_rc_ok;
29   	    }
30   	
31   	    if (!pcmk__xe_is(xml_obj, PCMK_XE_RSC_COLOCATION)) {
32   	        return pcmk_rc_ok;
33   	    }
34   	
35   	    out->info(out, "Constraint %s %s %s %s %s %s %s",
36   	              xml_obj->name,
37   	              cons_string(pcmk__xe_get(xml_obj, PCMK_XA_ID)),
38   	              cons_string(pcmk__xe_get(xml_obj, PCMK_XA_RSC)),
39   	              cons_string(pcmk__xe_get(xml_obj, PCMK_XA_WITH_RSC)),
40   	              cons_string(pcmk__xe_get(xml_obj, PCMK_XA_SCORE)),
41   	              cons_string(pcmk__xe_get(xml_obj, PCMK_XA_RSC_ROLE)),
42   	              cons_string(pcmk__xe_get(xml_obj, PCMK_XA_WITH_RSC_ROLE)));
43   	
44   	    return pcmk_rc_ok;
45   	}
46   	
47   	void
48   	cli_resource_print_cts_constraints(pcmk_scheduler_t *scheduler)
49   	{
50   	    pcmk__xe_foreach_child(pcmk_find_cib_element(scheduler->input,
51   	                                                 PCMK_XE_CONSTRAINTS),
52   	                           NULL, print_constraint, scheduler);
53   	}
54   	
55   	void
56   	cli_resource_print_cts(pcmk_resource_t *rsc, pcmk__output_t *out)
57   	{
58   	    const char *host = NULL;
59   	    bool needs_quorum = true;
60   	    const char *rtype = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
61   	    const char *rprov = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
62   	    const char *rclass = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
63   	    pcmk_node_t *node = pcmk__current_node(rsc);
64   	
65   	    if (pcmk__is_set(rsc->flags, pcmk__rsc_fence_device)) {
66   	        needs_quorum = false;
67   	    } else {
68   	        // @TODO check requires in resource meta-data and rsc_defaults
69   	    }
70   	
71   	    if (node != NULL) {
72   	        host = node->priv->name;
73   	    }
74   	
75   	    out->info(out, "Resource: %s %s %s %s %s %s %s %s %d %lld %#.16llx",
76   	              rsc->priv->xml->name, rsc->id,
77   	              pcmk__s(rsc->priv->history_id, rsc->id),
78   	              ((rsc->priv->parent == NULL)? "NA" : rsc->priv->parent->id),
79   	              rprov ? rprov : "NA", rclass, rtype, host ? host : "NA", needs_quorum, rsc->flags,
80   	              rsc->flags);
81   	
82   	    g_list_foreach(rsc->priv->children, (GFunc) cli_resource_print_cts, out);
83   	}
84   	
85   	// \return Standard Pacemaker return code
86   	int
87   	cli_resource_print_operations(const char *rsc_id, const char *host_uname,
88   	                              bool active, pcmk_scheduler_t *scheduler)
89   	{
90   	    pcmk__output_t *out = scheduler->priv->out;
91   	    int rc = pcmk_rc_no_output;
92   	    GList *ops = find_operations(rsc_id, host_uname, active, scheduler);
93   	
94   	    if (!ops) {
95   	        return rc;
96   	    }
97   	
98   	    out->begin_list(out, NULL, NULL, "Resource Operations");
99   	    rc = pcmk_rc_ok;
100  	
101  	    for (GList *lpc = ops; lpc != NULL; lpc = lpc->next) {
102  	        xmlNode *xml_op = (xmlNode *) lpc->data;
103  	        out->message(out, "node-and-op", scheduler, xml_op);
104  	    }
105  	
106  	    out->end_list(out);
107  	    g_list_free_full(ops, (GDestroyNotify) pcmk__xml_free);
108  	    return rc;
109  	}
110  	
111  	// \return Standard Pacemaker return code
112  	int
113  	cli_resource_print(pcmk_resource_t *rsc, bool expanded)
114  	{
115  	    pcmk_scheduler_t *scheduler = NULL;
116  	    pcmk__output_t *out = NULL;
117  	    GList *all = NULL;
118  	
119  	    pcmk__assert(rsc != NULL);
120  	
121  	    scheduler = rsc->priv->scheduler;
122  	    out = scheduler->priv->out;
123  	    all = g_list_prepend(all, (gpointer) "*");
124  	
125  	    out->begin_list(out, NULL, NULL, "Resource Config");
126  	    out->message(out, (const char *) rsc->priv->xml->name, pcmk_show_pending,
127  	                 rsc, all, all);
128  	    out->message(out, "resource-config", rsc, !expanded);
129  	    out->end_list(out);
130  	
131  	    g_list_free(all);
132  	    return pcmk_rc_ok;
133  	}
134  	
135  	PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *")
136  	static int
137  	attribute_changed_default(pcmk__output_t *out, va_list args)
138  	{
139  	    attr_update_data_t *ud = va_arg(args, attr_update_data_t *);
140  	
141  	    out->info(out, "Set '%s' option: "
142  	              PCMK_XA_ID "=%s%s%s%s%s value=%s",
143  	              ud->given_rsc_id, ud->found_attr_id,
144  	              ((ud->attr_set_id == NULL)? "" : " " PCMK__XA_SET "="),
145  	              pcmk__s(ud->attr_set_id, ""),
146  	              ((ud->attr_name == NULL)? "" : " " PCMK_XA_NAME "="),
147  	              pcmk__s(ud->attr_name, ""), ud->attr_value);
148  	
149  	    return pcmk_rc_ok;
150  	}
151  	
152  	PCMK__OUTPUT_ARGS("attribute-changed", "attr_update_data_t *")
153  	static int
154  	attribute_changed_xml(pcmk__output_t *out, va_list args)
155  	{
156  	    attr_update_data_t *ud = va_arg(args, attr_update_data_t *);
157  	
158  	    pcmk__output_xml_create_parent(out,
159  	                                   (const char *) ud->rsc->priv->xml->name,
160  	                                   PCMK_XA_ID, ud->rsc->id,
161  	                                   NULL);
162  	
163  	    pcmk__output_xml_create_parent(out, ud->attr_set_type,
164  	                                   PCMK_XA_ID, ud->attr_set_id,
165  	                                   NULL);
166  	
167  	    pcmk__output_create_xml_node(out, PCMK_XE_NVPAIR,
168  	                                 PCMK_XA_ID, ud->found_attr_id,
169  	                                 PCMK_XA_VALUE, ud->attr_value,
170  	                                 PCMK_XA_NAME, ud->attr_name,
171  	                                 NULL);
172  	
173  	    pcmk__output_xml_pop_parent(out);
174  	    pcmk__output_xml_pop_parent(out);
175  	
176  	    return pcmk_rc_ok;
177  	}
178  	
179  	PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *")
180  	static int
181  	attribute_changed_list_default(pcmk__output_t *out, va_list args)
182  	{
183  	    GList *results = va_arg(args, GList *);
184  	
185  	    if (results == NULL) {
186  	        return pcmk_rc_no_output;
187  	    }
188  	
189  	    for (GList *iter = results; iter != NULL; iter = iter->next) {
190  	        attr_update_data_t *ud = iter->data;
191  	        out->message(out, "attribute-changed", ud);
192  	    }
193  	
194  	    return pcmk_rc_ok;
195  	}
196  	
197  	PCMK__OUTPUT_ARGS("attribute-changed-list", "GList *")
198  	static int
199  	attribute_changed_list_xml(pcmk__output_t *out, va_list args)
200  	{
201  	    GList *results = va_arg(args, GList *);
202  	
203  	    if (results == NULL) {
204  	        return pcmk_rc_no_output;
205  	    }
206  	
207  	    pcmk__output_xml_create_parent(out, PCMK__XE_RESOURCE_SETTINGS, NULL);
208  	
209  	    for (GList *iter = results; iter != NULL; iter = iter->next) {
210  	        attr_update_data_t *ud = iter->data;
211  	        out->message(out, "attribute-changed", ud);
212  	    }
213  	
214  	    pcmk__output_xml_pop_parent(out);
215  	    return pcmk_rc_ok;
216  	}
217  	
218  	PCMK__OUTPUT_ARGS("attribute-list", "pcmk_resource_t *", "const char *",
219  	                  "const char *")
220  	static int
221  	attribute_list_default(pcmk__output_t *out, va_list args) {
222  	    pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
223  	    const char *attr = va_arg(args, char *);
224  	    const char *value = va_arg(args, const char *);
225  	
226  	    if (value != NULL) {
227  	        out->begin_list(out, NULL, NULL, "Attributes");
228  	        out->list_item(out, attr, "%s", value);
229  	        out->end_list(out);
230  	        return pcmk_rc_ok;
231  	    } else {
232  	        out->err(out, "Attribute '%s' not found for '%s'", attr, rsc->id);
233  	    }
234  	    return pcmk_rc_ok;
235  	}
236  	
237  	PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
238  	                  "const char *", "const char *", "crm_exit_t", "const char *")
239  	static int
240  	agent_status_default(pcmk__output_t *out, va_list args) {
241  	    int status = va_arg(args, int);
242  	    const char *action = va_arg(args, const char *);
243  	    const char *name = va_arg(args, const char *);
244  	    const char *class = va_arg(args, const char *);
245  	    const char *provider = va_arg(args, const char *);
246  	    const char *type = va_arg(args, const char *);
247  	    crm_exit_t rc = va_arg(args, crm_exit_t);
248  	    const char *exit_reason = va_arg(args, const char *);
249  	
250  	    if (status == PCMK_EXEC_DONE) {
251  	        /* Operation <action> [for <resource>] (<class>[:<provider>]:<agent>)
252  	         * returned <exit-code> (<exit-description>[: <exit-reason>])
253  	         */
254  	        out->info(out, "Operation %s%s%s (%s%s%s:%s) returned %d (%s%s%s)",
255  	                  action,
256  	                  ((name == NULL)? "" : " for "), ((name == NULL)? "" : name),
257  	                  class,
258  	                  ((provider == NULL)? "" : ":"),
259  	                  ((provider == NULL)? "" : provider),
260  	                  type, (int) rc, crm_exit_str(rc),
261  	                  ((exit_reason == NULL)? "" : ": "),
262  	                  ((exit_reason == NULL)? "" : exit_reason));
263  	    } else {
264  	        /* Operation <action> [for <resource>] (<class>[:<provider>]:<agent>)
265  	         * could not be executed (<execution-status>[: <exit-reason>])
266  	         */
267  	        out->err(out,
268  	                 "Operation %s%s%s (%s%s%s:%s) could not be executed (%s%s%s)",
269  	                 action,
270  	                 ((name == NULL)? "" : " for "), ((name == NULL)? "" : name),
271  	                 class,
272  	                 ((provider == NULL)? "" : ":"),
273  	                 ((provider == NULL)? "" : provider),
274  	                 type, pcmk_exec_status_str(status),
275  	                 ((exit_reason == NULL)? "" : ": "),
276  	                 ((exit_reason == NULL)? "" : exit_reason));
277  	    }
278  	
279  	    return pcmk_rc_ok;
280  	}
281  	
282  	PCMK__OUTPUT_ARGS("agent-status", "int", "const char *", "const char *", "const char *",
283  	                  "const char *", "const char *", "crm_exit_t", "const char *")
284  	static int
285  	agent_status_xml(pcmk__output_t *out, va_list args) {
286  	    int status = va_arg(args, int);
287  	    const char *action G_GNUC_UNUSED = va_arg(args, const char *);
288  	    const char *name G_GNUC_UNUSED = va_arg(args, const char *);
289  	    const char *class G_GNUC_UNUSED = va_arg(args, const char *);
290  	    const char *provider G_GNUC_UNUSED = va_arg(args, const char *);
291  	    const char *type G_GNUC_UNUSED = va_arg(args, const char *);
292  	    crm_exit_t rc = va_arg(args, crm_exit_t);
293  	    const char *exit_reason = va_arg(args, const char *);
294  	
295  	    char *exit_s = pcmk__itoa(rc);
296  	    const char *message = crm_exit_str(rc);
297  	    char *status_s = pcmk__itoa(status);
298  	    const char *execution_message = pcmk_exec_status_str(status);
299  	
300  	    pcmk__output_create_xml_node(out, PCMK_XE_AGENT_STATUS,
301  	                                 PCMK_XA_CODE, exit_s,
302  	                                 PCMK_XA_MESSAGE, message,
303  	                                 PCMK_XA_EXECUTION_CODE, status_s,
304  	                                 PCMK_XA_EXECUTION_MESSAGE, execution_message,
305  	                                 PCMK_XA_REASON, exit_reason,
306  	                                 NULL);
307  	
308  	    free(exit_s);
309  	    free(status_s);
310  	
311  	    return pcmk_rc_ok;
312  	}
313  	
314  	PCMK__OUTPUT_ARGS("attribute-list", "pcmk_resource_t *", "const char *",
315  	                  "const char *")
316  	static int
317  	attribute_list_text(pcmk__output_t *out, va_list args) {
318  	    pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
319  	    const char *attr = va_arg(args, char *);
320  	    const char *value = va_arg(args, const char *);
321  	
322  	    if (value != NULL) {
323  	        pcmk__formatted_printf(out, "%s\n", value);
324  	        return pcmk_rc_ok;
325  	    } else {
326  	        out->err(out, "Attribute '%s' not found for '%s'", attr, rsc->id);
327  	    }
328  	    return pcmk_rc_ok;
329  	}
330  	PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
331  	static int
332  	override_default(pcmk__output_t *out, va_list args) {
333  	    const char *rsc_name = va_arg(args, const char *);
334  	    const char *name = va_arg(args, const char *);
335  	    const char *value = va_arg(args, const char *);
336  	
337  	    if (rsc_name == NULL) {
338  	        out->list_item(out, NULL, "Overriding the cluster configuration with '%s' = '%s'",
339  	                       name, value);
340  	    } else {
341  	        out->list_item(out, NULL, "Overriding the cluster configuration for '%s' with '%s' = '%s'",
342  	                       rsc_name, name, value);
343  	    }
344  	
345  	    return pcmk_rc_ok;
346  	}
347  	
348  	PCMK__OUTPUT_ARGS("override", "const char *", "const char *", "const char *")
349  	static int
350  	override_xml(pcmk__output_t *out, va_list args) {
351  	    const char *rsc_name = va_arg(args, const char *);
352  	    const char *name = va_arg(args, const char *);
353  	    const char *value = va_arg(args, const char *);
354  	
355  	    xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_OVERRIDE,
356  	                                                   PCMK_XA_NAME, name,
357  	                                                   PCMK_XA_VALUE, value,
358  	                                                   NULL);
359  	
360  	    if (rsc_name != NULL) {
361  	        pcmk__xe_set(node, PCMK_XA_RSC, rsc_name);
362  	    }
363  	
364  	    return pcmk_rc_ok;
365  	}
366  	
367  	// Does not modify overrides or its contents
368  	PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
369  	                  "const char *", "const char *", "const char *", "GHashTable *",
370  	                  "crm_exit_t", "int", "const char *", "const char *", "const char *")
371  	static int
372  	resource_agent_action_default(pcmk__output_t *out, va_list args) {
373  	    int verbose = va_arg(args, int);
374  	
375  	    const char *class = va_arg(args, const char *);
376  	    const char *provider = va_arg(args, const char *);
377  	    const char *type = va_arg(args, const char *);
378  	    const char *rsc_name = va_arg(args, const char *);
379  	    const char *action = va_arg(args, const char *);
380  	    GHashTable *overrides = va_arg(args, GHashTable *);
381  	    crm_exit_t rc = va_arg(args, crm_exit_t);
382  	    int status = va_arg(args, int);
383  	    const char *exit_reason = va_arg(args, const char *);
384  	    const char *stdout_data = va_arg(args, const char *);
385  	    const char *stderr_data = va_arg(args, const char *);
386  	
387  	    if (overrides) {
388  	        GHashTableIter iter;
389  	        const char *name = NULL;
390  	        const char *value = NULL;
391  	
392  	        out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES);
393  	
394  	        g_hash_table_iter_init(&iter, overrides);
395  	        while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
396  	            out->message(out, "override", rsc_name, name, value);
397  	        }
398  	
399  	        out->end_list(out);
400  	    }
401  	
402  	    out->message(out, "agent-status", status, action, rsc_name, class, provider,
403  	                 type, rc, exit_reason);
404  	
405  	    /* hide output for validate-all if not in verbose */
406  	    if ((verbose == 0)
407  	        && pcmk__str_eq(action, PCMK_ACTION_VALIDATE_ALL, pcmk__str_casei)) {
408  	        return pcmk_rc_ok;
409  	    }
410  	
411  	    if (stdout_data || stderr_data) {
412  	        xmlNodePtr doc = NULL;
413  	
414  	        if (stdout_data != NULL) {
415  	            doc = pcmk__xml_parse(stdout_data);
416  	        }
417  	        if (doc != NULL) {
418  	            out->output_xml(out, PCMK_XE_COMMAND, stdout_data);
419  	            pcmk__xml_free(doc);
420  	        } else {
421  	            out->subprocess_output(out, rc, stdout_data, stderr_data);
422  	        }
423  	    }
424  	
425  	    return pcmk_rc_ok;
426  	}
427  	
428  	// Does not modify overrides or its contents
429  	PCMK__OUTPUT_ARGS("resource-agent-action", "int", "const char *", "const char *",
430  	                  "const char *", "const char *", "const char *", "GHashTable *",
431  	                  "crm_exit_t", "int", "const char *", "const char *", "const char *")
432  	static int
433  	resource_agent_action_xml(pcmk__output_t *out, va_list args) {
434  	    int verbose G_GNUC_UNUSED = va_arg(args, int);
435  	
436  	    const char *class = va_arg(args, const char *);
437  	    const char *provider = va_arg(args, const char *);
438  	    const char *type = va_arg(args, const char *);
439  	    const char *rsc_name = va_arg(args, const char *);
440  	    const char *action = va_arg(args, const char *);
441  	    GHashTable *overrides = va_arg(args, GHashTable *);
442  	    crm_exit_t rc = va_arg(args, crm_exit_t);
443  	    int status = va_arg(args, int);
444  	    const char *exit_reason = va_arg(args, const char *);
445  	    const char *stdout_data = va_arg(args, const char *);
446  	    const char *stderr_data = va_arg(args, const char *);
447  	
448  	    xmlNodePtr node = NULL;
449  	
450  	    node = pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE_AGENT_ACTION,
451  	                                          PCMK_XA_ACTION, action,
452  	                                          PCMK_XA_CLASS, class,
453  	                                          PCMK_XA_TYPE, type,
454  	                                          NULL);
455  	
456  	    if (rsc_name) {
457  	        pcmk__xe_set(node, PCMK_XA_RSC, rsc_name);
458  	    }
459  	
460  	    pcmk__xe_set(node, PCMK_XA_PROVIDER, provider);
461  	
462  	    if (overrides) {
463  	        GHashTableIter iter;
464  	        const char *name = NULL;
465  	        const char *value = NULL;
466  	
467  	        out->begin_list(out, NULL, NULL, PCMK_XE_OVERRIDES);
468  	
469  	        g_hash_table_iter_init(&iter, overrides);
470  	        while (g_hash_table_iter_next(&iter, (gpointer *) &name, (gpointer *) &value)) {
471  	            out->message(out, "override", rsc_name, name, value);
472  	        }
473  	
474  	        out->end_list(out);
475  	    }
476  	
477  	    out->message(out, "agent-status", status, action, rsc_name, class, provider,
478  	                 type, rc, exit_reason);
479  	
480  	    if (stdout_data || stderr_data) {
481  	        xmlNodePtr doc = NULL;
482  	
483  	        if (stdout_data != NULL) {
484  	            doc = pcmk__xml_parse(stdout_data);
485  	        }
486  	        if (doc != NULL) {
487  	            out->output_xml(out, PCMK_XE_COMMAND, stdout_data);
488  	            pcmk__xml_free(doc);
489  	        } else {
490  	            out->subprocess_output(out, rc, stdout_data, stderr_data);
491  	        }
492  	    }
493  	
494  	    pcmk__output_xml_pop_parent(out);
495  	    return pcmk_rc_ok;
496  	}
497  	
498  	PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *")
499  	static int
500  	resource_check_list_default(pcmk__output_t *out, va_list args) {
501  	    resource_checks_t *checks = va_arg(args, resource_checks_t *);
502  	
503  	    const pcmk_resource_t *parent = pe__const_top_resource(checks->rsc, false);
504  	    const pcmk_scheduler_t *scheduler = checks->rsc->priv->scheduler;
505  	
506  	    if (checks->flags == 0) {
507  	        return pcmk_rc_no_output;
508  	    }
509  	
510  	    out->begin_list(out, NULL, NULL, "Resource Checks");
511  	
512  	    if (pcmk__is_set(checks->flags, rsc_remain_stopped)) {
513  	        out->list_item(out, "check", "Configuration specifies '%s' should remain stopped",
514  	                       parent->id);
515  	    }
516  	
517  	    if (pcmk__is_set(checks->flags, rsc_unpromotable)) {
518  	        out->list_item(out, "check", "Configuration specifies '%s' should not be promoted",
519  	                       parent->id);
520  	    }
521  	
522  	    if (pcmk__is_set(checks->flags, rsc_unmanaged)) {
523  	        out->list_item(out, "check", "Configuration prevents cluster from stopping or starting unmanaged '%s'",
524  	                       parent->id);
525  	    }
526  	
527  	    if (pcmk__is_set(checks->flags, rsc_locked)) {
528  	        out->list_item(out, "check", "'%s' is locked to node %s due to shutdown",
529  	                       parent->id, checks->lock_node);
530  	    }
531  	
532  	    if (pcmk__is_set(checks->flags, rsc_node_health)) {
533  	        out->list_item(out, "check",
534  	                       "'%s' cannot run on unhealthy nodes due to "
535  	                       PCMK_OPT_NODE_HEALTH_STRATEGY "='%s'",
536  	                       parent->id,
537  	                       pcmk__cluster_option(scheduler->priv->options,
538  	                                            PCMK_OPT_NODE_HEALTH_STRATEGY));
539  	    }
540  	
541  	    out->end_list(out);
542  	    return pcmk_rc_ok;
543  	}
544  	
545  	PCMK__OUTPUT_ARGS("resource-check-list", "resource_checks_t *")
546  	static int
547  	resource_check_list_xml(pcmk__output_t *out, va_list args) {
548  	    resource_checks_t *checks = va_arg(args, resource_checks_t *);
549  	
550  	    const pcmk_resource_t *parent = pe__const_top_resource(checks->rsc, false);
551  	
552  	    xmlNodePtr node = pcmk__output_create_xml_node(out, PCMK_XE_CHECK,
553  	                                                   PCMK_XA_ID, parent->id,
554  	                                                   NULL);
555  	
556  	    if (pcmk__is_set(checks->flags, rsc_remain_stopped)) {
557  	        pcmk__xe_set_bool(node, PCMK_XA_REMAIN_STOPPED, true);
558  	    }
559  	
560  	    if (pcmk__is_set(checks->flags, rsc_unpromotable)) {
561  	        pcmk__xe_set_bool(node, PCMK_XA_PROMOTABLE, false);
562  	    }
563  	
564  	    if (pcmk__is_set(checks->flags, rsc_unmanaged)) {
565  	        pcmk__xe_set_bool(node, PCMK_XA_UNMANAGED, true);
566  	    }
567  	
568  	    if (pcmk__is_set(checks->flags, rsc_locked)) {
569  	        pcmk__xe_set(node, PCMK_XA_LOCKED_TO_HYPHEN, checks->lock_node);
570  	    }
571  	
572  	    if (pcmk__is_set(checks->flags, rsc_node_health)) {
573  	        pcmk__xe_set_bool(node, PCMK_XA_UNHEALTHY, true);
574  	    }
575  	
576  	    return pcmk_rc_ok;
577  	}
578  	
579  	PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *")
580  	static int
581  	resource_search_list_default(pcmk__output_t *out, va_list args)
582  	{
583  	    GList *nodes = va_arg(args, GList *);
584  	    const gchar *requested_name = va_arg(args, const gchar *);
585  	
586  	    bool printed = false;
587  	    int rc = pcmk_rc_no_output;
588  	
589  	    if (!out->is_quiet(out) && nodes == NULL) {
590  	        out->err(out, "resource %s is NOT running", requested_name);
591  	        return rc;
592  	    }
593  	
594  	    for (GList *lpc = nodes; lpc != NULL; lpc = lpc->next) {
595  	        node_info_t *ni = (node_info_t *) lpc->data;
596  	
597  	        if (!printed) {
598  	            out->begin_list(out, NULL, NULL, "Nodes");
599  	            printed = true;
600  	            rc = pcmk_rc_ok;
601  	        }
602  	
603  	        if (out->is_quiet(out)) {
604  	            out->list_item(out, "node", "%s", ni->node_name);
605  	        } else {
606  	            const char *role_text = "";
607  	
608  	            if (ni->promoted) {
609  	                role_text = " " PCMK_ROLE_PROMOTED;
610  	            }
611  	            out->list_item(out, "node", "resource %s is running on: %s%s",
612  	                           requested_name, ni->node_name, role_text);
613  	        }
614  	    }
615  	
616  	    if (printed) {
617  	        out->end_list(out);
618  	    }
619  	
620  	    return rc;
621  	}
622  	
623  	PCMK__OUTPUT_ARGS("resource-search-list", "GList *", "const gchar *")
624  	static int
625  	resource_search_list_xml(pcmk__output_t *out, va_list args)
626  	{
627  	    GList *nodes = va_arg(args, GList *);
628  	    const gchar *requested_name = va_arg(args, const gchar *);
629  	
630  	    pcmk__output_xml_create_parent(out, PCMK_XE_NODES,
631  	                                   PCMK_XA_RESOURCE, requested_name,
632  	                                   NULL);
633  	
634  	    for (GList *lpc = nodes; lpc != NULL; lpc = lpc->next) {
635  	        node_info_t *ni = (node_info_t *) lpc->data;
636  	        xmlNodePtr sub_node = pcmk__output_create_xml_text_node(out,
637  	                                                                PCMK_XE_NODE,
638  	                                                                ni->node_name);
639  	
640  	        if (ni->promoted) {
641  	            pcmk__xe_set(sub_node, PCMK_XA_STATE, "promoted");
642  	        }
643  	    }
644  	
645  	    pcmk__output_xml_pop_parent(out);
646  	    return pcmk_rc_ok;
647  	}
648  	
649  	PCMK__OUTPUT_ARGS("resource-reasons-list", "GList *", "pcmk_resource_t *",
650  	                  "pcmk_node_t *")
651  	static int
652  	resource_reasons_list_default(pcmk__output_t *out, va_list args)
653  	{
654  	    GList *resources = va_arg(args, GList *);
655  	    pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
656  	    pcmk_node_t *node = va_arg(args, pcmk_node_t *);
657  	
658  	    const char *host_uname = (node == NULL)? NULL : node->priv->name;
659  	
660  	    out->begin_list(out, NULL, NULL, "Resource Reasons");
661  	
(1) Event path: Condition "rsc == NULL", taking true branch.
(2) Event path: Condition "host_uname == NULL", taking true branch.
662  	    if ((rsc == NULL) && (host_uname == NULL)) {
663  	        GList *lpc = NULL;
664  	        GList *hosts = NULL;
665  	
(3) Event path: Condition "lpc != NULL", taking true branch.
666  	        for (lpc = resources; lpc != NULL; lpc = lpc->next) {
667  	            pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
668  	
669  	            rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current);
670  	
(4) Event path: Condition "hosts == NULL", taking true branch.
671  	            if (hosts == NULL) {
672  	                out->list_item(out, "reason", "Resource %s is not running", rsc->id);
(5) Event path: Falling through to end of if statement.
673  	            } else {
674  	                out->list_item(out, "reason", "Resource %s is running", rsc->id);
675  	            }
676  	
677  	            cli_resource_check(out, rsc, NULL);
CID (unavailable; MK=25da8137be3191bd3f3476f3c8744485) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(6) Event assign_union_field: The union field "in" of "_pp" is written.
(7) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
678  	            g_clear_pointer(&hosts, g_list_free);
679  	        }
680  	
681  	    } else if ((rsc != NULL) && (host_uname != NULL)) {
682  	        if (resource_is_running_on(rsc, host_uname)) {
683  	            out->list_item(out, "reason", "Resource %s is running on host %s",
684  	                           rsc->id, host_uname);
685  	        } else {
686  	            out->list_item(out, "reason", "Resource %s is not running on host %s",
687  	                           rsc->id, host_uname);
688  	        }
689  	
690  	        cli_resource_check(out, rsc, node);
691  	
692  	    } else if ((rsc == NULL) && (host_uname != NULL)) {
693  	        const char* host_uname =  node->priv->name;
694  	        GList *allResources = node->priv->assigned_resources;
695  	        GList *activeResources = node->details->running_rsc;
696  	        GList *unactiveResources = pcmk__subtract_lists(allResources, activeResources, (GCompareFunc) strcmp);
697  	        GList *lpc = NULL;
698  	
699  	        for (lpc = activeResources; lpc != NULL; lpc = lpc->next) {
700  	            pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
701  	            out->list_item(out, "reason", "Resource %s is running on host %s",
702  	                           rsc->id, host_uname);
703  	            cli_resource_check(out, rsc, node);
704  	        }
705  	
706  	        for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) {
707  	            pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
708  	            out->list_item(out, "reason", "Resource %s is assigned to host %s but not running",
709  	                           rsc->id, host_uname);
710  	            cli_resource_check(out, rsc, node);
711  	        }
712  	
713  	        g_list_free(allResources);
714  	        g_list_free(activeResources);
715  	        g_list_free(unactiveResources);
716  	
717  	    } else if ((rsc != NULL) && (host_uname == NULL)) {
718  	        GList *hosts = NULL;
719  	
720  	        rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current);
721  	        out->list_item(out, "reason", "Resource %s is %srunning",
722  	                       rsc->id, (hosts? "" : "not "));
723  	        cli_resource_check(out, rsc, NULL);
724  	        g_list_free(hosts);
725  	    }
726  	
727  	    out->end_list(out);
728  	    return pcmk_rc_ok;
729  	}
730  	
731  	PCMK__OUTPUT_ARGS("resource-reasons-list", "GList *", "pcmk_resource_t *",
732  	                  "pcmk_node_t *")
733  	static int
734  	resource_reasons_list_xml(pcmk__output_t *out, va_list args)
735  	{
736  	    GList *resources = va_arg(args, GList *);
737  	    pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
738  	    pcmk_node_t *node = va_arg(args, pcmk_node_t *);
739  	
740  	    const char *host_uname = (node == NULL)? NULL : node->priv->name;
741  	
742  	    xmlNodePtr xml_node = pcmk__output_xml_create_parent(out, PCMK_XE_REASON,
743  	                                                         NULL);
744  	
745  	    if ((rsc == NULL) && (host_uname == NULL)) {
746  	        GList *lpc = NULL;
747  	        GList *hosts = NULL;
748  	
749  	        pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
750  	
751  	        for (lpc = resources; lpc != NULL; lpc = lpc->next) {
752  	            pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
753  	            const char *running = NULL;
754  	
755  	            rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current);
756  	            running = pcmk__btoa(hosts != NULL);
757  	
758  	            pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE,
759  	                                           PCMK_XA_ID, rsc->id,
760  	                                           PCMK_XA_RUNNING, running,
761  	                                           NULL);
762  	
763  	            cli_resource_check(out, rsc, NULL);
764  	            pcmk__output_xml_pop_parent(out);
765  	            g_clear_pointer(&hosts, g_list_free);
766  	        }
767  	
768  	        pcmk__output_xml_pop_parent(out);
769  	
770  	    } else if ((rsc != NULL) && (host_uname != NULL)) {
771  	        if (resource_is_running_on(rsc, host_uname)) {
772  	            pcmk__xe_set(xml_node, PCMK_XA_RUNNING_ON, host_uname);
773  	        }
774  	
775  	        cli_resource_check(out, rsc, node);
776  	
777  	    } else if ((rsc == NULL) && (host_uname != NULL)) {
778  	        const char* host_uname =  node->priv->name;
779  	        GList *allResources = node->priv->assigned_resources;
780  	        GList *activeResources = node->details->running_rsc;
781  	        GList *unactiveResources = pcmk__subtract_lists(allResources, activeResources, (GCompareFunc) strcmp);
782  	        GList *lpc = NULL;
783  	
784  	        pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCES, NULL);
785  	
786  	        for (lpc = activeResources; lpc != NULL; lpc = lpc->next) {
787  	            pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
788  	
789  	            pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE,
790  	                                           PCMK_XA_ID, rsc->id,
791  	                                           PCMK_XA_RUNNING, PCMK_VALUE_TRUE,
792  	                                           PCMK_XA_HOST, host_uname,
793  	                                           NULL);
794  	
795  	            cli_resource_check(out, rsc, node);
796  	            pcmk__output_xml_pop_parent(out);
797  	        }
798  	
799  	        for(lpc = unactiveResources; lpc != NULL; lpc = lpc->next) {
800  	            pcmk_resource_t *rsc = (pcmk_resource_t *) lpc->data;
801  	
802  	            pcmk__output_xml_create_parent(out, PCMK_XE_RESOURCE,
803  	                                           PCMK_XA_ID, rsc->id,
804  	                                           PCMK_XA_RUNNING, PCMK_VALUE_FALSE,
805  	                                           PCMK_XA_HOST, host_uname,
806  	                                           NULL);
807  	
808  	            cli_resource_check(out, rsc, node);
809  	            pcmk__output_xml_pop_parent(out);
810  	        }
811  	
812  	        pcmk__output_xml_pop_parent(out);
813  	        g_list_free(allResources);
814  	        g_list_free(activeResources);
815  	        g_list_free(unactiveResources);
816  	
817  	    } else if ((rsc != NULL) && (host_uname == NULL)) {
818  	        GList *hosts = NULL;
819  	
820  	        rsc->priv->fns->location(rsc, &hosts, pcmk__rsc_node_current);
821  	        pcmk__xe_set(xml_node, PCMK_XA_RUNNING, pcmk__btoa(hosts != NULL));
822  	        cli_resource_check(out, rsc, NULL);
823  	        g_list_free(hosts);
824  	    }
825  	
826  	    pcmk__output_xml_pop_parent(out);
827  	    return pcmk_rc_ok;
828  	}
829  	
830  	static void
831  	add_resource_name(pcmk_resource_t *rsc, pcmk__output_t *out)
832  	{
833  	    if (rsc->priv->children == NULL) {
834  	        /* Sometimes PCMK_XE_RESOURCE might act as a PCMK_XA_NAME instead of an
835  	         * XML element name, depending on whether pcmk__output_enable_list_element
836  	         * was called.
837  	         */
838  	        out->list_item(out, PCMK_XE_RESOURCE, "%s", rsc->id);
839  	    } else {
840  	        g_list_foreach(rsc->priv->children, (GFunc) add_resource_name, out);
841  	    }
842  	}
843  	
844  	PCMK__OUTPUT_ARGS("resource-names-list", "GList *")
845  	static int
846  	resource_names(pcmk__output_t *out, va_list args) {
847  	    GList *resources = va_arg(args, GList *);
848  	
849  	    if (resources == NULL) {
850  	        out->err(out, "NO resources configured\n");
851  	        return pcmk_rc_no_output;
852  	    }
853  	
854  	    out->begin_list(out, NULL, NULL, "Resource Names");
855  	    g_list_foreach(resources, (GFunc) add_resource_name, out);
856  	    out->end_list(out);
857  	    return pcmk_rc_ok;
858  	}
859  	
860  	static pcmk__message_entry_t fmt_functions[] = {
861  	    { "agent-status", "default", agent_status_default },
862  	    { "agent-status", "xml", agent_status_xml },
863  	    { "attribute-changed", "default", attribute_changed_default },
864  	    { "attribute-changed", "xml", attribute_changed_xml },
865  	    { "attribute-changed-list", "default", attribute_changed_list_default },
866  	    { "attribute-changed-list", "xml", attribute_changed_list_xml },
867  	    { "attribute-list", "default", attribute_list_default },
868  	    { "attribute-list", "text", attribute_list_text },
869  	    { "override", "default", override_default },
870  	    { "override", "xml", override_xml },
871  	    { "resource-agent-action", "default", resource_agent_action_default },
872  	    { "resource-agent-action", "xml", resource_agent_action_xml },
873  	    { "resource-check-list", "default", resource_check_list_default },
874  	    { "resource-check-list", "xml", resource_check_list_xml },
875  	    { "resource-search-list", "default", resource_search_list_default },
876  	    { "resource-search-list", "xml", resource_search_list_xml },
877  	    { "resource-reasons-list", "default", resource_reasons_list_default },
878  	    { "resource-reasons-list", "xml", resource_reasons_list_xml },
879  	    { "resource-names-list", "default", resource_names },
880  	
881  	    { NULL, NULL, NULL }
882  	};
883  	
884  	void
885  	crm_resource_register_messages(pcmk__output_t *out) {
886  	    pcmk__register_messages(out, fmt_functions);
887  	}
888