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 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 <stdbool.h>                // bool, true, false
13   	#include <stdint.h>                 // uint32_t
14   	
15   	#include <crm/common/output.h>
16   	#include <crm/pengine/status.h>
17   	#include <crm/pengine/complex.h>
18   	#include <crm/pengine/internal.h>
19   	#include <crm/common/xml.h>
20   	#include <pe_status_private.h>
21   	
22   	/*!
23   	 * \internal
24   	 * \brief Check whether a resource is active on multiple nodes
25   	 */
26   	static bool
27   	is_multiply_active(const pcmk_resource_t *rsc)
28   	{
29   	    unsigned int count = 0;
30   	
31   	    if (pcmk__is_primitive(rsc)) {
32   	        pe__find_active_requires(rsc, &count);
33   	    }
34   	    return count > 1;
35   	}
36   	
37   	static void
38   	native_priority_to_node(pcmk_resource_t *rsc, pcmk_node_t *node,
39   	                        gboolean failed)
40   	{
41   	    int priority = 0;
42   	    const bool promoted = (rsc->priv->orig_role == pcmk_role_promoted);
43   	
44   	    if ((rsc->priv->priority == 0) || failed) {
45   	        return;
46   	    }
47   	
48   	    if (promoted) {
49   	        // Promoted instance takes base priority + 1
50   	        priority = rsc->priv->priority + 1;
51   	
52   	    } else {
53   	        priority = rsc->priv->priority;
54   	    }
55   	
56   	    node->priv->priority += priority;
57   	    pcmk__rsc_trace(rsc, "%s now has priority %d with %s'%s' (priority: %d%s)",
58   	                    pcmk__node_name(node), node->priv->priority,
59   	                    (promoted? "promoted " : ""),
60   	                    rsc->id, rsc->priv->priority, (promoted? " + 1" : ""));
61   	
62   	    /* Priority of a resource running on a guest node is added to the cluster
63   	     * node as well. */
64   	    if ((node->priv->remote != NULL)
65   	        && (node->priv->remote->priv->launcher != NULL)) {
66   	        const pcmk_resource_t *launcher = NULL;
67   	
68   	        launcher = node->priv->remote->priv->launcher;
69   	        for (GList *gIter = launcher->priv->active_nodes;
70   	             gIter != NULL; gIter = gIter->next) {
71   	
72   	            pcmk_node_t *a_node = gIter->data;
73   	
74   	            a_node->priv->priority += priority;
75   	            pcmk__rsc_trace(rsc,
76   	                            "%s now has priority %d with %s'%s' "
77   	                            "(priority: %d%s) from guest node %s",
78   	                            pcmk__node_name(a_node), a_node->priv->priority,
79   	                            (promoted? "promoted " : ""), rsc->id,
80   	                            rsc->priv->priority, (promoted? " + 1" : ""),
81   	                            pcmk__node_name(node));
82   	        }
83   	    }
84   	}
85   	
86   	void
87   	native_add_running(pcmk_resource_t *rsc, pcmk_node_t *node,
88   	                   pcmk_scheduler_t *scheduler, gboolean failed)
89   	{
90   	    pcmk_resource_t *parent = rsc->priv->parent;
91   	
92   	    CRM_CHECK(node != NULL, return);
93   	
94   	    for (GList *gIter = rsc->priv->active_nodes;
95   	         gIter != NULL; gIter = gIter->next) {
96   	
97   	        pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
98   	
99   	        if (pcmk__same_node(a_node, node)) {
100  	            return;
101  	        }
102  	    }
103  	
104  	    if (pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
105  	        pcmk__rsc_trace(rsc, "Adding %s to %s", rsc->id, pcmk__node_name(node));
106  	    } else {
107  	        pcmk__rsc_trace(rsc, "Adding %s to %s (unmanaged)", rsc->id,
108  	                        pcmk__node_name(node));
109  	    }
110  	
111  	    rsc->priv->active_nodes = g_list_append(rsc->priv->active_nodes, node);
112  	    if (pcmk__is_primitive(rsc)) {
113  	        node->details->running_rsc = g_list_append(node->details->running_rsc, rsc);
114  	        native_priority_to_node(rsc, node, failed);
115  	        if (node->details->maintenance) {
116  	            pcmk__clear_rsc_flags(rsc, pcmk__rsc_managed);
117  	            pcmk__set_rsc_flags(rsc, pcmk__rsc_maintenance);
118  	        }
119  	    }
120  	
121  	    if (!pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
122  	        pcmk_resource_t *p = parent;
123  	
124  	        pcmk__rsc_info(rsc, "resource %s isn't managed", rsc->id);
125  	        resource_location(rsc, node, PCMK_SCORE_INFINITY,
126  	                          "not_managed_default", scheduler);
127  	
128  	        while(p) {
129  	            /* add without the additional location constraint */
130  	            p->priv->active_nodes = g_list_append(p->priv->active_nodes, node);
131  	            p = p->priv->parent;
132  	        }
133  	        return;
134  	    }
135  	
136  	    if (is_multiply_active(rsc)) {
137  	        switch (rsc->priv->multiply_active_policy) {
138  	            case pcmk__multiply_active_stop:
139  	                {
140  	                    GHashTableIter gIter;
141  	                    pcmk_node_t *local_node = NULL;
142  	
143  	                    /* make sure it doesn't come up again */
144  	                    g_clear_pointer(&rsc->priv->allowed_nodes,
145  	                                    g_hash_table_destroy);
146  	                    rsc->priv->allowed_nodes =
147  	                        pe__node_list2table(scheduler->nodes);
148  	                    g_hash_table_iter_init(&gIter, rsc->priv->allowed_nodes);
149  	                    while (g_hash_table_iter_next(&gIter, NULL, (void **)&local_node)) {
150  	                        local_node->assign->score = -PCMK_SCORE_INFINITY;
151  	                    }
152  	                }
153  	                break;
154  	            case pcmk__multiply_active_block:
155  	                pcmk__clear_rsc_flags(rsc, pcmk__rsc_managed);
156  	                pcmk__set_rsc_flags(rsc, pcmk__rsc_blocked);
157  	
158  	                /* If the resource belongs to a group or bundle configured with
159  	                 * PCMK_META_MULTIPLE_ACTIVE=PCMK_VALUE_BLOCK, block the entire
160  	                 * entity.
161  	                 */
162  	                if ((pcmk__is_group(parent) || pcmk__is_bundle(parent))
163  	                    && (parent->priv->multiply_active_policy
164  	                        == pcmk__multiply_active_block)) {
165  	
166  	                    for (GList *gIter = parent->priv->children;
167  	                         gIter != NULL; gIter = gIter->next) {
168  	                        pcmk_resource_t *child = gIter->data;
169  	
170  	                        pcmk__clear_rsc_flags(child, pcmk__rsc_managed);
171  	                        pcmk__set_rsc_flags(child, pcmk__rsc_blocked);
172  	                    }
173  	                }
174  	                break;
175  	
176  	            // pcmk__multiply_active_restart, pcmk__multiply_active_unexpected
177  	            default:
178  	                /* The scheduler will do the right thing because the relevant
179  	                 * variables and flags are set when unpacking the history.
180  	                 */
181  	                break;
182  	        }
183  	        pcmk__debug("%s is active on multiple nodes including %s: %s",
184  	                    rsc->id, pcmk__node_name(node),
185  	                    pcmk__multiply_active_text(rsc));
186  	
187  	    } else {
188  	        pcmk__rsc_trace(rsc, "Resource %s is active on %s",
189  	                        rsc->id, pcmk__node_name(node));
190  	    }
191  	
192  	    if (parent != NULL) {
193  	        native_add_running(parent, node, scheduler, FALSE);
194  	    }
195  	}
196  	
197  	static void
198  	recursive_clear_unique(pcmk_resource_t *rsc, gpointer user_data)
199  	{
200  	    pcmk__clear_rsc_flags(rsc, pcmk__rsc_unique);
201  	    pcmk__insert_meta(rsc->priv, PCMK_META_GLOBALLY_UNIQUE,
202  	                      PCMK_VALUE_FALSE);
203  	    g_list_foreach(rsc->priv->children, (GFunc) recursive_clear_unique,
204  	                   NULL);
205  	}
206  	
207  	bool
208  	native_unpack(pcmk_resource_t *rsc)
209  	{
210  	    pcmk_resource_t *parent = uber_parent(rsc);
211  	    const char *standard = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
212  	    uint32_t ra_caps = pcmk_get_ra_caps(standard);
213  	
214  	    pcmk__rsc_trace(rsc, "Processing resource %s...", rsc->id);
215  	
216  	    // Only some agent standards support unique and promotable clones
217  	    if (!pcmk__is_set(ra_caps, pcmk_ra_cap_unique)
218  	        && pcmk__is_set(rsc->flags, pcmk__rsc_unique)
219  	        && pcmk__is_clone(parent)) {
220  	
221  	        /* @COMPAT We should probably reject this situation as an error (as we
222  	         * do for promotable below) rather than warn and convert, but that would
223  	         * be a backward-incompatible change that we should probably do with a
224  	         * transform at a schema major version bump.
225  	         */
226  	        pe__force_anon(standard, parent, rsc->id, rsc->priv->scheduler);
227  	
228  	        /* Clear PCMK_META_GLOBALLY_UNIQUE on the parent and all its descendants
229  	         * unpacked so far (clearing the parent should make any future children
230  	         * unpacking correct). We have to clear this resource explicitly because
231  	         * it isn't hooked into the parent's children yet.
232  	         */
233  	        recursive_clear_unique(parent, NULL);
234  	        recursive_clear_unique(rsc, NULL);
235  	    }
236  	    if (!pcmk__is_set(ra_caps, pcmk_ra_cap_promotable)
237  	        && pcmk__is_set(parent->flags, pcmk__rsc_promotable)) {
238  	
239  	        pcmk__config_err("Resource %s is of type %s and therefore "
240  	                         "cannot be used as a promotable clone resource",
241  	                         rsc->id, standard);
242  	        return FALSE;
243  	    }
244  	    return TRUE;
245  	}
246  	
247  	static bool
248  	rsc_is_on_node(pcmk_resource_t *rsc, const pcmk_node_t *node, int flags)
249  	{
250  	    pcmk__rsc_trace(rsc, "Checking whether %s is on %s",
251  	                    rsc->id, pcmk__node_name(node));
252  	
253  	    if (pcmk__is_set(flags, pcmk_rsc_match_current_node)
254  	        && (rsc->priv->active_nodes != NULL)) {
255  	
256  	        for (GList *iter = rsc->priv->active_nodes;
257  	             iter != NULL; iter = iter->next) {
258  	
259  	            if (pcmk__same_node((pcmk_node_t *) iter->data, node)) {
260  	                return true;
261  	            }
262  	        }
263  	
264  	    } else if (!pcmk__is_set(flags, pcmk_rsc_match_current_node)
265  	               && (rsc->priv->assigned_node != NULL)
266  	               && pcmk__same_node(rsc->priv->assigned_node, node)) {
267  	        return true;
268  	    }
269  	    return false;
270  	}
271  	
272  	pcmk_resource_t *
273  	native_find_rsc(pcmk_resource_t *rsc, const char *id,
274  	                const pcmk_node_t *on_node, uint32_t flags)
275  	{
276  	    bool match = false;
277  	    pcmk_resource_t *result = NULL;
278  	
279  	    CRM_CHECK(id && rsc && rsc->id, return NULL);
280  	
281  	    if (pcmk__is_set(flags, pcmk_rsc_match_clone_only)) {
282  	        const char *rid = pcmk__xe_id(rsc->priv->xml);
283  	
284  	        if (!pcmk__is_clone(pe__const_top_resource(rsc, false))) {
285  	            match = false;
286  	
287  	        } else if (!strcmp(id, rsc->id) || pcmk__str_eq(id, rid, pcmk__str_none)) {
288  	            match = true;
289  	        }
290  	
291  	    } else if (!strcmp(id, rsc->id)) {
292  	        match = true;
293  	
294  	    } else if (pcmk__is_set(flags, pcmk_rsc_match_history)
295  	               && pcmk__str_eq(rsc->priv->history_id, id, pcmk__str_none)) {
296  	        match = true;
297  	
298  	    } else if (pcmk__is_set(flags, pcmk_rsc_match_basename)
299  	               || (pcmk__is_set(flags, pcmk_rsc_match_anon_basename)
300  	                   && !pcmk__is_set(rsc->flags, pcmk__rsc_unique))) {
301  	        match = pe_base_name_eq(rsc, id);
302  	    }
303  	
304  	    if (match && on_node) {
305  	        if (!rsc_is_on_node(rsc, on_node, flags)) {
306  	            match = false;
307  	        }
308  	    }
309  	
310  	    if (match) {
311  	        return rsc;
312  	    }
313  	
314  	    for (GList *gIter = rsc->priv->children;
315  	         gIter != NULL; gIter = gIter->next) {
316  	
317  	        pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
318  	
319  	        result = rsc->priv->fns->find_rsc(child, id, on_node, flags);
320  	        if (result) {
321  	            return result;
322  	        }
323  	    }
324  	    return NULL;
325  	}
326  	
327  	bool
328  	native_active(const pcmk_resource_t *rsc, bool all)
329  	{
330  	    for (GList *gIter = rsc->priv->active_nodes;
331  	         gIter != NULL; gIter = gIter->next) {
332  	
333  	        pcmk_node_t *a_node = (pcmk_node_t *) gIter->data;
334  	
335  	        if (a_node->details->unclean) {
336  	            pcmk__rsc_trace(rsc, "Resource %s: %s is unclean",
337  	                            rsc->id, pcmk__node_name(a_node));
338  	            return TRUE;
339  	        } else if (!a_node->details->online
340  	                   && pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
341  	            pcmk__rsc_trace(rsc, "Resource %s: %s is offline",
342  	                            rsc->id, pcmk__node_name(a_node));
343  	        } else {
344  	            pcmk__rsc_trace(rsc, "Resource %s active on %s",
345  	                            rsc->id, pcmk__node_name(a_node));
346  	            return TRUE;
347  	        }
348  	    }
349  	    return FALSE;
350  	}
351  	
352  	struct print_data_s {
353  	    long options;
354  	    void *print_data;
355  	};
356  	
357  	static const char *
358  	native_pending_state(const pcmk_resource_t *rsc)
359  	{
360  	    const char *pending_state = NULL;
361  	
362  	    if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_START,
363  	                     pcmk__str_none)) {
364  	        pending_state = "Starting";
365  	
366  	    } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_STOP,
367  	                            pcmk__str_none)) {
368  	        pending_state = "Stopping";
369  	
370  	    } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MIGRATE_TO,
371  	                            pcmk__str_none)) {
372  	        pending_state = "Migrating";
373  	
374  	    } else if (pcmk__str_eq(rsc->priv->pending_action,
375  	                            PCMK_ACTION_MIGRATE_FROM, pcmk__str_none)) {
376  	       /* Work might be done in here. */
377  	        pending_state = "Migrating";
378  	
379  	    } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_PROMOTE,
380  	                            pcmk__str_none)) {
381  	        pending_state = "Promoting";
382  	
383  	    } else if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_DEMOTE,
384  	                            pcmk__str_none)) {
385  	        pending_state = "Demoting";
386  	    }
387  	
388  	    return pending_state;
389  	}
390  	
391  	static const char *
392  	native_pending_action(const pcmk_resource_t *rsc)
393  	{
394  	    const char *pending_action = NULL;
395  	
396  	    if (pcmk__str_eq(rsc->priv->pending_action, PCMK_ACTION_MONITOR,
397  	                     pcmk__str_none)) {
398  	        pending_action = "Monitoring";
399  	
400  	    /* Pending probes are not printed, even if pending
401  	     * operations are requested. If someone ever requests that
402  	     * behavior, uncomment this and the corresponding part of
403  	     * unpack.c:unpack_rsc_op().
404  	     */
405  	#if 0
406  	    } else if (pcmk__str_eq(rsc->private->pending_action, "probe",
407  	                            pcmk__str_none)) {
408  	        pending_action = "Checking";
409  	#endif
410  	    }
411  	
412  	    return pending_action;
413  	}
414  	
415  	static enum rsc_role_e
416  	native_displayable_role(const pcmk_resource_t *rsc)
417  	{
418  	    enum rsc_role_e role = rsc->priv->orig_role;
419  	
420  	    if ((role == pcmk_role_started)
421  	        && pcmk__is_set(pe__const_top_resource(rsc, false)->flags,
422  	                        pcmk__rsc_promotable)) {
423  	
424  	        role = pcmk_role_unpromoted;
425  	    }
426  	    return role;
427  	}
428  	
429  	static const char *
430  	native_displayable_state(const pcmk_resource_t *rsc, bool print_pending)
431  	{
432  	    const char *rsc_state = NULL;
433  	
434  	    if (print_pending) {
435  	        rsc_state = native_pending_state(rsc);
436  	    }
437  	    if (rsc_state == NULL) {
438  	        rsc_state = pcmk_role_text(native_displayable_role(rsc));
439  	    }
440  	    return rsc_state;
441  	}
442  	
443  	// Append a flag to resource description string's flags list
444  	static bool
445  	add_output_flag(GString *s, const char *flag_desc, bool have_flags)
446  	{
447  	    g_string_append(s, (have_flags? ", " : " ("));
448  	    g_string_append(s, flag_desc);
449  	    return true;
450  	}
451  	
452  	// Append a node name to resource description string's node list
453  	static bool
454  	add_output_node(GString *s, const char *node, bool have_nodes)
455  	{
456  	    g_string_append(s, (have_nodes? " " : " [ "));
457  	    g_string_append(s, node);
458  	    return true;
459  	}
460  	
461  	/*!
462  	 * \internal
463  	 * \brief Create a string description of a resource
464  	 *
465  	 * \param[in] rsc          Resource to describe
466  	 * \param[in] name         Desired identifier for the resource
467  	 * \param[in] node         If not NULL, node that resource is "on"
468  	 * \param[in] show_opts    Bitmask of pcmk_show_opt_e.
469  	 * \param[in] target_role  Resource's target role
470  	 * \param[in] show_nodes   Whether to display nodes when multiply active
471  	 *
472  	 * \return Newly allocated string description of resource
473  	 * \note Caller must free the result with g_free().
474  	 */
475  	gchar *
476  	pcmk__native_output_string(const pcmk_resource_t *rsc, const char *name,
477  	                           const pcmk_node_t *node, uint32_t show_opts,
478  	                           const char *target_role, bool show_nodes)
479  	{
480  	    const char *class = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
481  	    const char *provider = NULL;
482  	    const char *kind = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
483  	    GString *outstr = NULL;
484  	    bool have_flags = false;
485  	
486  	    if (!pcmk__is_primitive(rsc)) {
487  	        return NULL;
488  	    }
489  	
490  	    CRM_CHECK(name != NULL, name = "unknown");
491  	    CRM_CHECK(kind != NULL, kind = "unknown");
492  	    CRM_CHECK(class != NULL, class = "unknown");
493  	
494  	    if (pcmk__is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
495  	        provider = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
496  	    }
497  	
498  	    if ((node == NULL) && (rsc->priv->lock_node != NULL)) {
499  	        node = rsc->priv->lock_node;
500  	    }
501  	    if (pcmk__any_flags_set(show_opts, pcmk_show_rsc_only)
502  	        || pcmk__list_of_multiple(rsc->priv->active_nodes)) {
503  	        node = NULL;
504  	    }
505  	
506  	    outstr = g_string_sized_new(128);
507  	
508  	    // Resource name and agent
509  	    pcmk__g_strcat(outstr,
510  	                   name, "\t(", class, ((provider == NULL)? "" : ":"),
511  	                   pcmk__s(provider, ""), ":", kind, "):\t", NULL);
512  	
513  	    // State on node
514  	    if (pcmk__is_set(rsc->flags, pcmk__rsc_removed)) {
515  	        /* @COMPAT "ORPHANED" is deprecated since 3.0.2. Replace with "REMOVED"
516  	         * at a compatibility break.
517  	         */
518  	        g_string_append(outstr, " ORPHANED");
519  	    }
520  	    if (pcmk__is_set(rsc->flags, pcmk__rsc_failed)) {
521  	        enum rsc_role_e role = native_displayable_role(rsc);
522  	
523  	        g_string_append(outstr, " FAILED");
524  	        if (role > pcmk_role_unpromoted) {
525  	            pcmk__add_word(&outstr, 0, pcmk_role_text(role));
526  	        }
527  	    } else {
528  	        const bool show_pending = pcmk__is_set(show_opts, pcmk_show_pending);
529  	
530  	        pcmk__add_word(&outstr, 0, native_displayable_state(rsc, show_pending));
531  	    }
532  	    if (node) {
533  	        pcmk__add_word(&outstr, 0, pcmk__node_name(node));
534  	    }
535  	
536  	    // Failed probe operation
537  	    if (native_displayable_role(rsc) == pcmk_role_stopped) {
538  	        xmlNode *probe_op = pe__failed_probe_for_rsc(rsc,
539  	                                                     node ? node->priv->name : NULL);
540  	        if (probe_op != NULL) {
541  	            int rc;
542  	
543  	            pcmk__scan_min_int(pcmk__xe_get(probe_op, PCMK__XA_RC_CODE), &rc,
544  	                               0);
545  	            pcmk__g_strcat(outstr, " (", crm_exit_str(rc), ") ", NULL);
546  	        }
547  	    }
548  	
549  	    // Flags, as: (<flag> [...])
550  	    if (node && !(node->details->online) && node->details->unclean) {
551  	        have_flags = add_output_flag(outstr, "UNCLEAN", have_flags);
552  	    }
553  	    if ((node != NULL) && pcmk__same_node(node, rsc->priv->lock_node)) {
554  	        have_flags = add_output_flag(outstr, "LOCKED", have_flags);
555  	    }
556  	    if (pcmk__is_set(show_opts, pcmk_show_pending)) {
557  	        const char *pending_action = native_pending_action(rsc);
558  	
559  	        if (pending_action != NULL) {
560  	            have_flags = add_output_flag(outstr, pending_action, have_flags);
561  	        }
562  	    }
563  	    if (target_role != NULL) {
564  	        switch (pcmk_parse_role(target_role)) {
565  	            case pcmk_role_unknown:
566  	                pcmk__config_err("Invalid " PCMK_META_TARGET_ROLE
567  	                                 " %s for resource %s", target_role, rsc->id);
568  	                break;
569  	
570  	            case pcmk_role_stopped:
571  	                have_flags = add_output_flag(outstr, "disabled", have_flags);
572  	                break;
573  	
574  	            case pcmk_role_unpromoted:
575  	                if (pcmk__is_set(pe__const_top_resource(rsc, false)->flags,
576  	                                 pcmk__rsc_promotable)) {
577  	                    have_flags = add_output_flag(outstr,
578  	                                                 PCMK_META_TARGET_ROLE ":",
579  	                                                 have_flags);
580  	                    g_string_append(outstr, target_role);
581  	                }
582  	                break;
583  	
584  	            default:
585  	                /* Only show target role if it limits our abilities (i.e. ignore
586  	                 * Started, as it is the default anyways, and doesn't prevent
587  	                 * the resource from becoming promoted).
588  	                 */
589  	                break;
590  	        }
591  	    }
592  	
593  	    // Blocked or maintenance implies unmanaged
594  	    if (pcmk__any_flags_set(rsc->flags,
595  	                            pcmk__rsc_blocked|pcmk__rsc_maintenance)) {
596  	        if (pcmk__is_set(rsc->flags, pcmk__rsc_blocked)) {
597  	            have_flags = add_output_flag(outstr, "blocked", have_flags);
598  	
599  	        } else if (pcmk__is_set(rsc->flags, pcmk__rsc_maintenance)) {
600  	            have_flags = add_output_flag(outstr, "maintenance", have_flags);
601  	        }
602  	    } else if (!pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
603  	        have_flags = add_output_flag(outstr, "unmanaged", have_flags);
604  	    }
605  	
606  	    if (pcmk__is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
607  	        have_flags = add_output_flag(outstr, "failure ignored", have_flags);
608  	    }
609  	
610  	
611  	    if (have_flags) {
612  	        g_string_append_c(outstr, ')');
613  	    }
614  	
615  	    // User-supplied description
616  	    if (pcmk__any_flags_set(show_opts, pcmk_show_rsc_only|pcmk_show_description)
617  	        || pcmk__list_of_multiple(rsc->priv->active_nodes)) {
618  	        const char *desc = pcmk__xe_get(rsc->priv->xml, PCMK_XA_DESCRIPTION);
619  	
620  	        if (desc) {
621  	            g_string_append(outstr, " (");
622  	            g_string_append(outstr, desc);
623  	            g_string_append(outstr, ")");
624  	
625  	        }
626  	    }
627  	
628  	    if (show_nodes && !pcmk__is_set(show_opts, pcmk_show_rsc_only)
629  	        && pcmk__list_of_multiple(rsc->priv->active_nodes)) {
630  	        bool have_nodes = false;
631  	
632  	        for (GList *iter = rsc->priv->active_nodes;
633  	             iter != NULL; iter = iter->next) {
634  	
635  	            pcmk_node_t *n = (pcmk_node_t *) iter->data;
636  	
637  	            have_nodes = add_output_node(outstr, n->priv->name, have_nodes);
638  	        }
639  	        if (have_nodes) {
640  	            g_string_append(outstr, " ]");
641  	        }
642  	    }
643  	
644  	    return g_string_free(outstr, FALSE);
645  	}
646  	
647  	int
648  	pe__common_output_html(pcmk__output_t *out, const pcmk_resource_t *rsc,
649  	                       const char *name, const pcmk_node_t *node,
650  	                       uint32_t show_opts)
651  	{
652  	    const char *kind = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
653  	    const char *target_role = NULL;
654  	    const char *cl = NULL;
655  	
656  	    xmlNode *child = NULL;
657  	    gchar *content = NULL;
658  	
659  	    pcmk__assert((kind != NULL) && pcmk__is_primitive(rsc));
660  	
661  	    if (pcmk__is_true(g_hash_table_lookup(rsc->priv->meta,
662  	                                          PCMK__META_INTERNAL_RSC))
663  	        && !pcmk__is_set(show_opts, pcmk_show_implicit_rscs)) {
664  	
665  	        pcmk__trace("skipping print of internal resource %s", rsc->id);
666  	        return pcmk_rc_no_output;
667  	    }
668  	    target_role = g_hash_table_lookup(rsc->priv->meta,
669  	                                      PCMK_META_TARGET_ROLE);
670  	
671  	    if (!pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
672  	        cl = PCMK__VALUE_RSC_MANAGED;
673  	
674  	    } else if (pcmk__is_set(rsc->flags, pcmk__rsc_failed)) {
675  	        cl = PCMK__VALUE_RSC_FAILED;
676  	
677  	    } else if (pcmk__is_primitive(rsc)
678  	               && (rsc->priv->active_nodes == NULL)) {
679  	        cl = PCMK__VALUE_RSC_FAILED;
680  	
681  	    } else if (pcmk__list_of_multiple(rsc->priv->active_nodes)) {
682  	        cl = PCMK__VALUE_RSC_MULTIPLE;
683  	
684  	    } else if (pcmk__is_set(rsc->flags, pcmk__rsc_ignore_failure)) {
685  	        cl = PCMK__VALUE_RSC_FAILURE_IGNORED;
686  	
687  	    } else {
688  	        cl = PCMK__VALUE_RSC_OK;
689  	    }
690  	
691  	    child = pcmk__output_create_html_node(out, "li", NULL, NULL, NULL);
692  	    child = pcmk__html_create(child, PCMK__XE_SPAN, NULL, cl);
693  	    content = pcmk__native_output_string(rsc, name, node, show_opts,
694  	                                         target_role, true);
695  	    pcmk__xe_set_content(child, "%s", content);
696  	    g_free(content);
697  	
698  	    return pcmk_rc_ok;
699  	}
700  	
701  	int
702  	pe__common_output_text(pcmk__output_t *out, const pcmk_resource_t *rsc,
703  	                       const char *name, const pcmk_node_t *node,
704  	                       uint32_t show_opts)
705  	{
706  	    const char *target_role = NULL;
707  	
708  	    pcmk__assert(pcmk__is_primitive(rsc));
709  	
710  	    if (pcmk__is_true(g_hash_table_lookup(rsc->priv->meta,
711  	                                          PCMK__META_INTERNAL_RSC))
712  	        && !pcmk__is_set(show_opts, pcmk_show_implicit_rscs)) {
713  	
714  	        pcmk__trace("skipping print of internal resource %s", rsc->id);
715  	        return pcmk_rc_no_output;
716  	    }
717  	    target_role = g_hash_table_lookup(rsc->priv->meta,
718  	                                      PCMK_META_TARGET_ROLE);
719  	
720  	    {
721  	        gchar *s = pcmk__native_output_string(rsc, name, node, show_opts,
722  	                                              target_role, true);
723  	
724  	        out->list_item(out, NULL, "%s", s);
725  	        g_free(s);
726  	    }
727  	
728  	    return pcmk_rc_ok;
729  	}
730  	
731  	PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
732  	                  "GList *")
733  	int
734  	pe__resource_xml(pcmk__output_t *out, va_list args)
735  	{
736  	    uint32_t show_opts = va_arg(args, uint32_t);
737  	    pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
738  	    GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
739  	    GList *only_rsc = va_arg(args, GList *);
740  	
741  	    int rc = pcmk_rc_no_output;
742  	    const bool print_pending = pcmk__is_set(show_opts, pcmk_show_pending);
743  	    const char *class = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
744  	    const char *prov = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
745  	
746  	    char *ra_name = NULL;
747  	    const char *rsc_state = native_displayable_state(rsc, print_pending);
748  	    const char *target_role = NULL;
749  	    const char *active = pcmk__btoa(rsc->priv->fns->active(rsc, true));
750  	    const char *removed = pcmk__flag_text(rsc->flags, pcmk__rsc_removed);
751  	    const char *blocked = pcmk__flag_text(rsc->flags, pcmk__rsc_blocked);
752  	    const char *maintenance = pcmk__flag_text(rsc->flags,
753  	                                              pcmk__rsc_maintenance);
754  	    const char *managed = pcmk__flag_text(rsc->flags, pcmk__rsc_managed);
755  	    const char *failed = pcmk__flag_text(rsc->flags, pcmk__rsc_failed);
756  	    const char *ignored = pcmk__flag_text(rsc->flags, pcmk__rsc_ignore_failure);
757  	    char *nodes_running_on = NULL;
758  	    const char *pending = print_pending? native_pending_action(rsc) : NULL;
759  	    const char *locked_to = NULL;
760  	    const char *desc = pe__resource_description(rsc, show_opts);
761  	
762  	    pcmk__assert(pcmk__is_primitive(rsc));
763  	
764  	    if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
765  	        return pcmk_rc_no_output;
766  	    }
767  	
768  	    // Resource information
769  	    ra_name = pcmk__assert_asprintf("%s%s%s:%s", class,
770  	                                    ((prov == NULL)? "" : ":"),
771  	                                    ((prov == NULL)? "" : prov),
772  	                                    pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE));
773  	
774  	    target_role = g_hash_table_lookup(rsc->priv->meta,
775  	                                      PCMK_META_TARGET_ROLE);
776  	
777  	    nodes_running_on = pcmk__itoa(g_list_length(rsc->priv->active_nodes));
778  	
779  	    if (rsc->priv->lock_node != NULL) {
780  	        locked_to = rsc->priv->lock_node->priv->name;
781  	    }
782  	
783  	    // @COMPAT PCMK_XA_ORPHANED is deprecated since 3.0.2
784  	    rc = pe__name_and_nvpairs_xml(out, true, PCMK_XE_RESOURCE,
785  	                                  PCMK_XA_ID, rsc_printable_id(rsc),
786  	                                  PCMK_XA_RESOURCE_AGENT, ra_name,
787  	                                  PCMK_XA_ROLE, rsc_state,
788  	                                  PCMK_XA_TARGET_ROLE, target_role,
789  	                                  PCMK_XA_ACTIVE, active,
790  	                                  PCMK_XA_ORPHANED, removed,
791  	                                  PCMK_XA_REMOVED, removed,
792  	                                  PCMK_XA_BLOCKED, blocked,
793  	                                  PCMK_XA_MAINTENANCE, maintenance,
794  	                                  PCMK_XA_MANAGED, managed,
795  	                                  PCMK_XA_FAILED, failed,
796  	                                  PCMK_XA_FAILURE_IGNORED, ignored,
797  	                                  PCMK_XA_NODES_RUNNING_ON, nodes_running_on,
798  	                                  PCMK_XA_PENDING, pending,
799  	                                  PCMK_XA_LOCKED_TO, locked_to,
800  	                                  PCMK_XA_DESCRIPTION, desc,
801  	                                  NULL);
802  	    free(ra_name);
803  	    free(nodes_running_on);
804  	
805  	    pcmk__assert(rc == pcmk_rc_ok);
806  	
807  	    for (GList *gIter = rsc->priv->active_nodes;
808  	         gIter != NULL; gIter = gIter->next) {
809  	
810  	        pcmk_node_t *node = (pcmk_node_t *) gIter->data;
811  	        const char *cached = pcmk__btoa(node->details->online);
812  	
813  	        rc = pe__name_and_nvpairs_xml(out, false, PCMK_XE_NODE,
814  	                                      PCMK_XA_NAME, node->priv->name,
815  	                                      PCMK_XA_ID, node->priv->id,
816  	                                      PCMK_XA_CACHED, cached,
817  	                                      NULL);
818  	        pcmk__assert(rc == pcmk_rc_ok);
819  	    }
820  	
821  	    pcmk__output_xml_pop_parent(out);
822  	    return rc;
823  	}
824  	
825  	PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
826  	                  "GList *")
827  	int
828  	pe__resource_html(pcmk__output_t *out, va_list args)
829  	{
830  	    uint32_t show_opts = va_arg(args, uint32_t);
831  	    pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
832  	    GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
833  	    GList *only_rsc = va_arg(args, GList *);
834  	
835  	    const pcmk_node_t *node = pcmk__current_node(rsc);
836  	
837  	    if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
838  	        return pcmk_rc_no_output;
839  	    }
840  	
841  	    pcmk__assert(pcmk__is_primitive(rsc));
842  	
843  	    if (node == NULL) {
844  	        // This is set only if a non-probe action is pending on this node
845  	        node = rsc->priv->pending_node;
846  	    }
847  	    return pe__common_output_html(out, rsc, rsc_printable_id(rsc), node, show_opts);
848  	}
849  	
850  	PCMK__OUTPUT_ARGS("primitive", "uint32_t", "pcmk_resource_t *", "GList *",
851  	                  "GList *")
852  	int
853  	pe__resource_text(pcmk__output_t *out, va_list args)
854  	{
855  	    uint32_t show_opts = va_arg(args, uint32_t);
856  	    pcmk_resource_t *rsc = va_arg(args, pcmk_resource_t *);
857  	    GList *only_node G_GNUC_UNUSED = va_arg(args, GList *);
858  	    GList *only_rsc = va_arg(args, GList *);
859  	
860  	    const pcmk_node_t *node = pcmk__current_node(rsc);
861  	
862  	    pcmk__assert(pcmk__is_primitive(rsc));
863  	
864  	    if (rsc->priv->fns->is_filtered(rsc, only_rsc, true)) {
865  	        return pcmk_rc_no_output;
866  	    }
867  	
868  	    if (node == NULL) {
869  	        // This is set only if a non-probe action is pending on this node
870  	        node = rsc->priv->pending_node;
871  	    }
872  	    return pe__common_output_text(out, rsc, rsc_printable_id(rsc), node, show_opts);
873  	}
874  	
875  	void
876  	native_free(pcmk_resource_t * rsc)
877  	{
878  	    pcmk__rsc_trace(rsc, "Freeing resource action list (not the data)");
879  	    common_free(rsc);
880  	}
881  	
882  	enum rsc_role_e
883  	native_resource_state(const pcmk_resource_t *rsc, bool current)
884  	{
885  	    enum rsc_role_e role = rsc->priv->next_role;
886  	
887  	    if (current) {
888  	        role = rsc->priv->orig_role;
889  	    }
890  	    pcmk__rsc_trace(rsc, "%s state: %s", rsc->id, pcmk_role_text(role));
891  	    return role;
892  	}
893  	
894  	/*!
895  	 * \internal
896  	 * \brief List nodes where a resource (or any of its children) is
897  	 *
898  	 * \param[in]  rsc      Resource to check
899  	 * \param[out] list     List to add result to
900  	 * \param[in]  target   Which resource conditions to target (group of
901  	 *                      enum pcmk__rsc_node flags)
902  	 *
903  	 * \return If list contains only one node, that node, or NULL otherwise
904  	 */
905  	pcmk_node_t *
906  	native_location(const pcmk_resource_t *rsc, GList **list, uint32_t target)
907  	{
908  	    pcmk_node_t *one = NULL;
909  	    GList *result = NULL;
910  	
911  	    if (rsc->priv->children != NULL) {
912  	
913  	        for (GList *gIter = rsc->priv->children;
914  	             gIter != NULL; gIter = gIter->next) {
915  	
916  	            pcmk_resource_t *child = (pcmk_resource_t *) gIter->data;
917  	
918  	            child->priv->fns->location(child, &result, target);
919  	        }
920  	
921  	    } else {
922  	        if (pcmk__is_set(target, pcmk__rsc_node_current)) {
923  	            result = g_list_copy(rsc->priv->active_nodes);
924  	        }
925  	        if (pcmk__is_set(target, pcmk__rsc_node_pending)
926  	            && (rsc->priv->pending_node != NULL)
927  	            && !pe_find_node_id(result, rsc->priv->pending_node->priv->id)) {
928  	            result = g_list_append(result, (gpointer) rsc->priv->pending_node);
929  	        }
930  	        if (pcmk__is_set(target, pcmk__rsc_node_assigned)
931  	            && (rsc->priv->assigned_node != NULL)) {
932  	            result = g_list_append(result, rsc->priv->assigned_node);
933  	        }
934  	    }
935  	
936  	    if (result && (result->next == NULL)) {
937  	        one = result->data;
938  	    }
939  	
940  	    if (list) {
941  	        GList *gIter = result;
942  	
943  	        for (; gIter != NULL; gIter = gIter->next) {
944  	            pcmk_node_t *node = (pcmk_node_t *) gIter->data;
945  	
946  	            if ((*list == NULL)
947  	                || (pe_find_node_id(*list, node->priv->id) == NULL)) {
948  	                *list = g_list_append(*list, node);
949  	            }
950  	        }
951  	    }
952  	
953  	    g_list_free(result);
954  	    return one;
955  	}
956  	
957  	static void
958  	get_rscs_brief(GList *rsc_list, GHashTable * rsc_table, GHashTable * active_table)
959  	{
960  	    GList *gIter = rsc_list;
961  	
962  	    for (; gIter != NULL; gIter = gIter->next) {
963  	        pcmk_resource_t *rsc = (pcmk_resource_t *) gIter->data;
964  	
965  	        const char *class = pcmk__xe_get(rsc->priv->xml, PCMK_XA_CLASS);
966  	        const char *kind = pcmk__xe_get(rsc->priv->xml, PCMK_XA_TYPE);
967  	
968  	        GString *buffer = NULL;
969  	
970  	        int *rsc_counter = NULL;
971  	        int *active_counter = NULL;
972  	
973  	        if (!pcmk__is_primitive(rsc)) {
974  	            continue;
975  	        }
976  	
977  	        buffer = g_string_sized_new(128);
978  	
979  	        g_string_append(buffer, class);
980  	        if (pcmk__is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)) {
981  	            const char *prov = pcmk__xe_get(rsc->priv->xml, PCMK_XA_PROVIDER);
982  	
983  	            if (prov != NULL) {
984  	                pcmk__g_strcat(buffer, ":", prov, NULL);
985  	            }
986  	        }
987  	        pcmk__g_strcat(buffer, ":", kind, NULL);
988  	
989  	        if (rsc_table) {
990  	            rsc_counter = g_hash_table_lookup(rsc_table, buffer->str);
991  	            if (rsc_counter == NULL) {
992  	                rsc_counter = pcmk__assert_alloc(1, sizeof(int));
993  	                *rsc_counter = 0;
994  	                g_hash_table_insert(rsc_table, strdup(buffer->str),
995  	                                    rsc_counter);
996  	            }
997  	            (*rsc_counter)++;
998  	        }
999  	
1000 	        if (active_table) {
1001 	            for (GList *gIter2 = rsc->priv->active_nodes;
1002 	                 gIter2 != NULL; gIter2 = gIter2->next) {
1003 	
1004 	                pcmk_node_t *node = (pcmk_node_t *) gIter2->data;
1005 	                GHashTable *node_table = NULL;
1006 	
1007 	                if (!node->details->unclean && !node->details->online
1008 	                    && pcmk__is_set(rsc->flags, pcmk__rsc_managed)) {
1009 	                    continue;
1010 	                }
1011 	
1012 	                node_table = g_hash_table_lookup(active_table,
1013 	                                                 node->priv->name);
1014 	                if (node_table == NULL) {
1015 	                    node_table = pcmk__strkey_table(free, free);
1016 	                    g_hash_table_insert(active_table,
1017 	                                        strdup(node->priv->name),
1018 	                                        node_table);
1019 	                }
1020 	
1021 	                active_counter = g_hash_table_lookup(node_table, buffer->str);
1022 	                if (active_counter == NULL) {
1023 	                    active_counter = pcmk__assert_alloc(1, sizeof(int));
1024 	                    *active_counter = 0;
1025 	                    g_hash_table_insert(node_table, strdup(buffer->str),
1026 	                                        active_counter);
1027 	                }
1028 	                (*active_counter)++;
1029 	            }
1030 	        }
1031 	
1032 	        g_string_free(buffer, TRUE);
1033 	    }
1034 	}
1035 	
1036 	static void
1037 	destroy_node_table(gpointer data)
1038 	{
1039 	    g_clear_pointer(&data, g_hash_table_destroy);
1040 	}
1041 	
1042 	int
1043 	pe__rscs_brief_output(pcmk__output_t *out, GList *rsc_list, uint32_t show_opts)
1044 	{
1045 	    GHashTable *rsc_table = pcmk__strkey_table(free, free);
1046 	    GHashTable *active_table = pcmk__strkey_table(free, destroy_node_table);
1047 	    GList *sorted_rscs;
1048 	    int rc = pcmk_rc_no_output;
1049 	
1050 	    get_rscs_brief(rsc_list, rsc_table, active_table);
1051 	
1052 	    /* Make a list of the rsc_table keys so that it can be sorted.  This is to make sure
1053 	     * output order stays consistent between systems.
1054 	     */
1055 	    sorted_rscs = g_hash_table_get_keys(rsc_table);
1056 	    sorted_rscs = g_list_sort(sorted_rscs, (GCompareFunc) strcmp);
1057 	
(1) Event path: Condition "gIter", taking true branch.
(21) Event path: Condition "gIter", taking false branch.
1058 	    for (GList *gIter = sorted_rscs; gIter; gIter = gIter->next) {
1059 	        char *type = (char *) gIter->data;
1060 	        int *rsc_counter = g_hash_table_lookup(rsc_table, type);
1061 	
1062 	        GList *sorted_nodes = NULL;
1063 	        int active_counter_all = 0;
1064 	
1065 	        /* Also make a list of the active_table keys so it can be sorted.  If there's
1066 	         * more than one instance of a type of resource running, we need the nodes to
1067 	         * be sorted to make sure output order stays consistent between systems.
1068 	         */
1069 	        sorted_nodes = g_hash_table_get_keys(active_table);
1070 	        sorted_nodes = g_list_sort(sorted_nodes, (GCompareFunc) pcmk__numeric_strcasecmp);
1071 	
(2) Event path: Condition "gIter2", taking true branch.
(5) Event path: Condition "gIter2", taking true branch.
(16) Event path: Condition "gIter2", taking false branch.
1072 	        for (GList *gIter2 = sorted_nodes; gIter2; gIter2 = gIter2->next) {
1073 	            char *node_name = (char *) gIter2->data;
1074 	            GHashTable *node_table = g_hash_table_lookup(active_table, node_name);
1075 	            int *active_counter = NULL;
1076 	
(3) Event path: Condition "node_table == NULL", taking true branch.
(6) Event path: Condition "node_table == NULL", taking false branch.
1077 	            if (node_table == NULL) {
(4) Event path: Continuing loop.
1078 	                continue;
1079 	            }
1080 	
1081 	            active_counter = g_hash_table_lookup(node_table, type);
1082 	
(7) Event path: Condition "active_counter == NULL", taking false branch.
(8) Event path: Condition "*active_counter == 0", taking false branch.
1083 	            if (active_counter == NULL || *active_counter == 0) {
1084 	                continue;
1085 	
1086 	            } else {
1087 	                active_counter_all += *active_counter;
1088 	            }
1089 	
(9) Event path: Condition "pcmk__is_set(show_opts, pcmk_show_rsc_only)", taking true branch.
1090 	            if (pcmk__is_set(show_opts, pcmk_show_rsc_only)) {
1091 	                node_name = NULL;
1092 	            }
1093 	
(10) Event path: Condition "pcmk__is_set(show_opts, pcmk_show_inactive_rscs)", taking true branch.
1094 	            if (pcmk__is_set(show_opts, pcmk_show_inactive_rscs)) {
(11) Event path: Condition "rsc_counter", taking true branch.
(12) Event path: Condition "*active_counter > 0", taking true branch.
(13) Event path: Condition "node_name", taking false branch.
1095 	                out->list_item(out, NULL, "%d/%d\t(%s):\tActive %s",
1096 	                               *active_counter,
1097 	                               rsc_counter ? *rsc_counter : 0, type,
1098 	                               (*active_counter > 0) && node_name ? node_name : "");
(14) Event path: Falling through to end of if statement.
1099 	            } else {
1100 	                out->list_item(out, NULL, "%d\t(%s):\tActive %s",
1101 	                               *active_counter, type,
1102 	                               (*active_counter > 0) && node_name ? node_name : "");
1103 	            }
1104 	
1105 	            rc = pcmk_rc_ok;
(15) Event path: Jumping back to the beginning of the loop.
1106 	        }
1107 	
(17) Event path: Condition "pcmk__is_set(show_opts, pcmk_show_inactive_rscs)", taking true branch.
(18) Event path: Condition "active_counter_all == 0", taking true branch.
1108 	        if (pcmk__is_set(show_opts, pcmk_show_inactive_rscs)
1109 	            && (active_counter_all == 0)) {
1110 	
(19) Event path: Condition "rsc_counter", taking true branch.
1111 	            out->list_item(out, NULL, "%d/%d\t(%s):\tActive",
1112 	                           active_counter_all,
1113 	                           rsc_counter ? *rsc_counter : 0, type);
1114 	            rc = pcmk_rc_ok;
1115 	        }
1116 	
1117 	        g_list_free(sorted_nodes);
(20) Event path: Jumping back to the beginning of the loop.
1118 	    }
1119 	
(22) Event path: Condition "_p", taking true branch.
1120 	    g_clear_pointer(&rsc_table, g_hash_table_destroy);
CID (unavailable; MK=ba127d9e7d6720c58230df4a723c1d37) (#2 of 3): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(23) Event assign_union_field: The union field "in" of "_pp" is written.
(24) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
1121 	    g_clear_pointer(&active_table, g_hash_table_destroy);
1122 	    g_clear_pointer(&sorted_rscs, g_list_free);
1123 	    return rc;
1124 	}
1125 	
1126 	bool
1127 	pe__native_is_filtered(const pcmk_resource_t *rsc, const GList *only_rsc,
1128 	                       bool check_parent)
1129 	{
1130 	    if (pcmk__str_in_list(rsc_printable_id(rsc), only_rsc,
1131 	                          pcmk__str_star_matches)
1132 	        || pcmk__str_in_list(rsc->id, only_rsc, pcmk__str_star_matches)) {
1133 	
1134 	        return false;
1135 	    }
1136 	
1137 	    if (check_parent && (rsc->priv->parent != NULL)) {
1138 	        const pcmk_resource_t *up = pe__const_top_resource(rsc, true);
1139 	
1140 	        return up->priv->fns->is_filtered(up, only_rsc, false);
1141 	    }
1142 	
1143 	    return true;
1144 	}
1145 	
1146 	/*!
1147 	 * \internal
1148 	 * \brief Get maximum primitive resource instances per node
1149 	 *
1150 	 * \param[in] rsc  Primitive resource to check
1151 	 *
1152 	 * \return Maximum number of \p rsc instances that can be active on one node
1153 	 */
1154 	unsigned int
1155 	pe__primitive_max_per_node(const pcmk_resource_t *rsc)
1156 	{
1157 	    pcmk__assert(pcmk__is_primitive(rsc));
1158 	    return 1U;
1159 	}
1160