1    	/*
2    	 * Copyright 2009-2023 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 <stdio.h>
14   	#include <libxml/tree.h>
15   	#include <libxml/xpath.h>
16   	
17   	#include <crm/crm.h>
18   	#include <crm/msg_xml.h>
19   	#include <crm/common/xml.h>
20   	
21   	#include <crm/cluster/internal.h>
22   	
23   	#include <crm/cib.h>
24   	#include <crm/cib/internal.h>
25   	
26   	#include <pacemaker-fenced.h>
27   	
28   	static xmlNode *local_cib = NULL;
29   	static cib_t *cib_api = NULL;
30   	static bool have_cib_devices = FALSE;
31   	
32   	/*!
33   	 * \internal
34   	 * \brief Check whether a node has a specific attribute name/value
35   	 *
36   	 * \param[in] node    Name of node to check
37   	 * \param[in] name    Name of an attribute to look for
38   	 * \param[in] value   The value the named attribute needs to be set to in order to be considered a match
39   	 *
40   	 * \return TRUE if the locally cached CIB has the specified node attribute
41   	 */
42   	gboolean
43   	node_has_attr(const char *node, const char *name, const char *value)
44   	{
45   	    GString *xpath = NULL;
46   	    xmlNode *match;
47   	
48   	    CRM_CHECK((local_cib != NULL) && (node != NULL) && (name != NULL)
49   	              && (value != NULL), return FALSE);
50   	
51   	    /* Search for the node's attributes in the CIB. While the schema allows
52   	     * multiple sets of instance attributes, and allows instance attributes to
53   	     * use id-ref to reference values elsewhere, that is intended for resources,
54   	     * so we ignore that here.
55   	     */
56   	    xpath = g_string_sized_new(256);
57   	    pcmk__g_strcat(xpath,
58   	                   "//" XML_CIB_TAG_NODES "/" XML_CIB_TAG_NODE
59   	                   "[@" XML_ATTR_UNAME "='", node, "']/" XML_TAG_ATTR_SETS
60   	                   "/" XML_CIB_TAG_NVPAIR
61   	                   "[@" XML_NVPAIR_ATTR_NAME "='", name, "' "
62   	                   "and @" XML_NVPAIR_ATTR_VALUE "='", value, "']", NULL);
63   	
64   	    match = get_xpath_object((const char *) xpath->str, local_cib, LOG_NEVER);
65   	
66   	    g_string_free(xpath, TRUE);
67   	    return (match != NULL);
68   	}
69   	
70   	static void
71   	add_topology_level(xmlNode *match)
72   	{
73   	    char *desc = NULL;
74   	    pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
75   	
76   	    CRM_CHECK(match != NULL, return);
77   	
78   	    fenced_register_level(match, &desc, &result);
79   	    fenced_send_level_notification(STONITH_OP_LEVEL_ADD, &result, desc);
80   	    pcmk__reset_result(&result);
81   	    free(desc);
82   	}
83   	
84   	static void
85   	topology_remove_helper(const char *node, int level)
86   	{
87   	    char *desc = NULL;
88   	    pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
89   	    xmlNode *data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
90   	
91   	    crm_xml_add(data, F_STONITH_ORIGIN, __func__);
92   	    crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
93   	    crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
94   	
95   	    fenced_unregister_level(data, &desc, &result);
96   	    fenced_send_level_notification(STONITH_OP_LEVEL_DEL, &result, desc);
97   	    pcmk__reset_result(&result);
98   	    free_xml(data);
99   	    free(desc);
100  	}
101  	
102  	static void
103  	remove_topology_level(xmlNode *match)
104  	{
105  	    int index = 0;
106  	    char *key = NULL;
107  	
108  	    CRM_CHECK(match != NULL, return);
109  	
110  	    key = stonith_level_key(match, fenced_target_by_unknown);
111  	    crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
112  	    topology_remove_helper(key, index);
113  	    free(key);
114  	}
115  	
116  	static void
117  	register_fencing_topology(xmlXPathObjectPtr xpathObj)
118  	{
119  	    int max = numXpathResults(xpathObj), lpc = 0;
120  	
121  	    for (lpc = 0; lpc < max; lpc++) {
122  	        xmlNode *match = getXpathResult(xpathObj, lpc);
123  	
124  	        remove_topology_level(match);
125  	        add_topology_level(match);
126  	    }
127  	}
128  	
129  	/* Fencing
130  	<diff crm_feature_set="3.0.6">
131  	  <diff-removed>
132  	    <fencing-topology>
133  	      <fencing-level id="f-p1.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="removed:top"/>
134  	      <fencing-level id="f-p1.2" target="pcmk-1" index="2" devices="power" __crm_diff_marker__="removed:top"/>
135  	      <fencing-level devices="disk,network" id="f-p2.1"/>
136  	    </fencing-topology>
137  	  </diff-removed>
138  	  <diff-added>
139  	    <fencing-topology>
140  	      <fencing-level id="f-p.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="added:top"/>
141  	      <fencing-level id="f-p2.1" target="pcmk-2" index="1" devices="disk,something"/>
142  	      <fencing-level id="f-p3.1" target="pcmk-2" index="2" devices="power" __crm_diff_marker__="added:top"/>
143  	    </fencing-topology>
144  	  </diff-added>
145  	</diff>
146  	*/
147  	
148  	void
149  	fencing_topology_init(void)
150  	{
151  	    xmlXPathObjectPtr xpathObj = NULL;
152  	    const char *xpath = "//" XML_TAG_FENCING_LEVEL;
153  	
154  	    crm_trace("Full topology refresh");
155  	    free_topology_list();
156  	    init_topology_list();
157  	
158  	    /* Grab everything */
159  	    xpathObj = xpath_search(local_cib, xpath);
160  	    register_fencing_topology(xpathObj);
161  	
162  	    freeXpathObject(xpathObj);
163  	}
164  	
165  	static void
166  	remove_cib_device(xmlXPathObjectPtr xpathObj)
167  	{
168  	    int max = numXpathResults(xpathObj), lpc = 0;
169  	
170  	    for (lpc = 0; lpc < max; lpc++) {
171  	        const char *rsc_id = NULL;
172  	        const char *standard = NULL;
173  	        xmlNode *match = getXpathResult(xpathObj, lpc);
174  	
175  	        CRM_LOG_ASSERT(match != NULL);
176  	        if(match != NULL) {
177  	            standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
178  	        }
179  	
180  	        if (!pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
181  	            continue;
182  	        }
183  	
184  	        rsc_id = crm_element_value(match, XML_ATTR_ID);
185  	
186  	        stonith_device_remove(rsc_id, true);
187  	    }
188  	}
189  	
190  	static void
191  	update_stonith_watchdog_timeout_ms(xmlNode *cib)
192  	{
193  	    long timeout_ms = 0;
194  	    xmlNode *stonith_watchdog_xml = NULL;
195  	    const char *value = NULL;
196  	
197  	    stonith_watchdog_xml = get_xpath_object("//nvpair[@name='stonith-watchdog-timeout']",
198  						    cib, LOG_NEVER);
199  	    if (stonith_watchdog_xml) {
200  	        value = crm_element_value(stonith_watchdog_xml, XML_NVPAIR_ATTR_VALUE);
201  	    }
202  	    if (value) {
203  	        timeout_ms = crm_get_msec(value);
204  	    }
205  	
206  	    if (timeout_ms < 0) {
207  	        timeout_ms = pcmk__auto_watchdog_timeout();
208  	    }
209  	
210  	    stonith_watchdog_timeout_ms = timeout_ms;
211  	}
212  	
213  	/*!
214  	 * \internal
215  	 * \brief Update all STONITH device definitions based on current CIB
216  	 */
217  	static void
218  	cib_devices_update(void)
219  	{
220  	    GHashTableIter iter;
221  	    stonith_device_t *device = NULL;
222  	
223  	    crm_info("Updating devices to version %s.%s.%s",
224  	             crm_element_value(local_cib, XML_ATTR_GENERATION_ADMIN),
225  	             crm_element_value(local_cib, XML_ATTR_GENERATION),
226  	             crm_element_value(local_cib, XML_ATTR_NUMUPDATES));
227  	
228  	    g_hash_table_iter_init(&iter, device_list);
229  	    while (g_hash_table_iter_next(&iter, NULL, (void **)&device)) {
230  	        if (device->cib_registered) {
231  	            device->dirty = TRUE;
232  	        }
233  	    }
234  	
235  	    /* have list repopulated if cib has a watchdog-fencing-resource
236  	       TODO: keep a cached list for queries happening while we are refreshing
237  	     */
238  	    g_list_free_full(stonith_watchdog_targets, free);
239  	    stonith_watchdog_targets = NULL;
240  	
241  	    fenced_scheduler_run(local_cib);
242  	
243  	    g_hash_table_iter_init(&iter, device_list);
244  	    while (g_hash_table_iter_next(&iter, NULL, (void **)&device)) {
245  	        if (device->dirty) {
246  	            g_hash_table_iter_remove(&iter);
247  	        }
248  	    }
249  	}
250  	
251  	static void
252  	update_cib_stonith_devices_v1(const char *event, xmlNode * msg)
253  	{
254  	    const char *reason = "none";
255  	    gboolean needs_update = FALSE;
256  	    xmlXPathObjectPtr xpath_obj = NULL;
257  	
258  	    /* process new constraints */
259  	    xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_CONS_TAG_RSC_LOCATION);
260  	    if (numXpathResults(xpath_obj) > 0) {
261  	        int max = numXpathResults(xpath_obj), lpc = 0;
262  	
263  	        /* Safest and simplest to always recompute */
264  	        needs_update = TRUE;
265  	        reason = "new location constraint";
266  	
267  	        for (lpc = 0; lpc < max; lpc++) {
268  	            xmlNode *match = getXpathResult(xpath_obj, lpc);
269  	
270  	            crm_log_xml_trace(match, "new constraint");
271  	        }
272  	    }
273  	    freeXpathObject(xpath_obj);
274  	
275  	    /* process deletions */
276  	    xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_RESOURCE);
277  	    if (numXpathResults(xpath_obj) > 0) {
278  	        remove_cib_device(xpath_obj);
279  	    }
280  	    freeXpathObject(xpath_obj);
281  	
282  	    /* process additions */
283  	    xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_RESOURCE);
284  	    if (numXpathResults(xpath_obj) > 0) {
285  	        int max = numXpathResults(xpath_obj), lpc = 0;
286  	
287  	        for (lpc = 0; lpc < max; lpc++) {
288  	            const char *rsc_id = NULL;
289  	            const char *standard = NULL;
290  	            xmlNode *match = getXpathResult(xpath_obj, lpc);
291  	
292  	            rsc_id = crm_element_value(match, XML_ATTR_ID);
293  	            standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
294  	
295  	            if (!pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
296  	                continue;
297  	            }
298  	
299  	            crm_trace("Fencing resource %s was added or modified", rsc_id);
300  	            reason = "new resource";
301  	            needs_update = TRUE;
302  	        }
303  	    }
304  	    freeXpathObject(xpath_obj);
305  	
306  	    if(needs_update) {
307  	        crm_info("Updating device list from CIB: %s", reason);
308  	        cib_devices_update();
309  	    }
310  	}
311  	
312  	static void
313  	update_cib_stonith_devices_v2(const char *event, xmlNode * msg)
314  	{
315  	    xmlNode *change = NULL;
316  	    char *reason = NULL;
317  	    bool needs_update = FALSE;
318  	    xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
319  	
320  	    for (change = pcmk__xml_first_child(patchset); change != NULL;
321  	         change = pcmk__xml_next(change)) {
322  	        const char *op = crm_element_value(change, XML_DIFF_OP);
323  	        const char *xpath = crm_element_value(change, XML_DIFF_PATH);
324  	        const char *shortpath = NULL;
325  	
326  	        if ((op == NULL) ||
327  	            (strcmp(op, "move") == 0) ||
328  	            strstr(xpath, "/"XML_CIB_TAG_STATUS)) {
329  	            continue;
330  	        } else if (pcmk__str_eq(op, "delete", pcmk__str_casei) && strstr(xpath, "/"XML_CIB_TAG_RESOURCE)) {
331  	            const char *rsc_id = NULL;
332  	            char *search = NULL;
333  	            char *mutable = NULL;
334  	
335  	            if (strstr(xpath, XML_TAG_ATTR_SETS) ||
336  	                strstr(xpath, XML_TAG_META_SETS)) {
337  	                needs_update = TRUE;
338  	                pcmk__str_update(&reason,
339  	                                 "(meta) attribute deleted from resource");
340  	                break;
341  	            }
342  	            pcmk__str_update(&mutable, xpath);
343  	            rsc_id = strstr(mutable, "primitive[@" XML_ATTR_ID "=\'");
344  	            if (rsc_id != NULL) {
345  	                rsc_id += strlen("primitive[@" XML_ATTR_ID "=\'");
346  	                search = strchr(rsc_id, '\'');
347  	            }
348  	            if (search != NULL) {
349  	                *search = 0;
350  	                stonith_device_remove(rsc_id, true);
351  	                /* watchdog_device_update called afterwards
352  	                   to fall back to implicit definition if needed */
353  	            } else {
354  	                crm_warn("Ignoring malformed CIB update (resource deletion)");
355  	            }
356  	            free(mutable);
357  	
358  	        } else if (strstr(xpath, "/"XML_CIB_TAG_RESOURCES) ||
359  	                   strstr(xpath, "/"XML_CIB_TAG_CONSTRAINTS) ||
360  	                   strstr(xpath, "/"XML_CIB_TAG_RSCCONFIG)) {
(22) Event example_assign: Example 3: Assigning: "shortpath" = return value from "strrchr(xpath, 47)".
(23) Event example_checked: Example 3 (cont.): "shortpath" has its value checked in "shortpath".
Also see events: [returned_null][dereference][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked]
361  	            shortpath = strrchr(xpath, '/'); CRM_ASSERT(shortpath);
362  	            reason = crm_strdup_printf("%s %s", op, shortpath+1);
363  	            needs_update = TRUE;
364  	            break;
365  	        }
366  	    }
367  	
368  	    if(needs_update) {
369  	        crm_info("Updating device list from CIB: %s", reason);
370  	        cib_devices_update();
371  	    } else {
372  	        crm_trace("No updates for device list found in CIB");
373  	    }
374  	    free(reason);
375  	}
376  	
377  	static void
378  	update_cib_stonith_devices(const char *event, xmlNode * msg)
379  	{
380  	    int format = 1;
381  	    xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
382  	
383  	    CRM_ASSERT(patchset);
384  	    crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
385  	    switch(format) {
386  	        case 1:
387  	            update_cib_stonith_devices_v1(event, msg);
388  	            break;
389  	        case 2:
390  	            update_cib_stonith_devices_v2(event, msg);
391  	            break;
392  	        default:
393  	            crm_warn("Unknown patch format: %d", format);
394  	    }
395  	}
396  	
397  	static void
398  	watchdog_device_update(void)
399  	{
400  	    if (stonith_watchdog_timeout_ms > 0) {
401  	        if (!g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID) &&
402  	            !stonith_watchdog_targets) {
403  	            /* getting here watchdog-fencing enabled, no device there yet
404  	               and reason isn't stonith_watchdog_targets preventing that
405  	             */
406  	            int rc;
407  	            xmlNode *xml;
408  	
409  	            xml = create_device_registration_xml(
410  	                    STONITH_WATCHDOG_ID,
411  	                    st_namespace_internal,
412  	                    STONITH_WATCHDOG_AGENT,
413  	                    NULL, /* stonith_device_register will add our
414  	                             own name as PCMK_STONITH_HOST_LIST param
415  	                             so we can skip that here
416  	                           */
417  	                    NULL);
418  	            rc = stonith_device_register(xml, TRUE);
419  	            free_xml(xml);
420  	            if (rc != pcmk_ok) {
421  	                rc = pcmk_legacy2rc(rc);
422  	                exit_code = CRM_EX_FATAL;
423  	                crm_crit("Cannot register watchdog pseudo fence agent: %s",
424  	                         pcmk_rc_str(rc));
425  	                stonith_shutdown(0);
426  	            }
427  	        }
428  	
429  	    } else if (g_hash_table_lookup(device_list, STONITH_WATCHDOG_ID) != NULL) {
430  	        /* be silent if no device - todo parameter to stonith_device_remove */
431  	        stonith_device_remove(STONITH_WATCHDOG_ID, true);
432  	    }
433  	}
434  	
435  	/*!
436  	 * \internal
437  	 * \brief Query the full CIB
438  	 *
439  	 * \return Standard Pacemaker return code
440  	 */
441  	static int
442  	fenced_query_cib(void)
443  	{
444  	    int rc = pcmk_ok;
445  	
446  	    crm_trace("Re-requesting full CIB");
447  	    rc = cib_api->cmds->query(cib_api, NULL, &local_cib,
448  	                              cib_scope_local|cib_sync_call);
449  	    rc = pcmk_legacy2rc(rc);
450  	    if (rc == pcmk_rc_ok) {
451  	        CRM_ASSERT(local_cib != NULL);
452  	    } else {
453  	        crm_err("Couldn't retrieve the CIB: %s " CRM_XS " rc=%d",
454  	                pcmk_rc_str(rc), rc);
455  	    }
456  	    return rc;
457  	}
458  	
459  	static void
460  	remove_fencing_topology(xmlXPathObjectPtr xpathObj)
461  	{
462  	    int max = numXpathResults(xpathObj), lpc = 0;
463  	
464  	    for (lpc = 0; lpc < max; lpc++) {
465  	        xmlNode *match = getXpathResult(xpathObj, lpc);
466  	
467  	        CRM_LOG_ASSERT(match != NULL);
468  	        if (match && crm_element_value(match, XML_DIFF_MARKER)) {
469  	            /* Deletion */
470  	            int index = 0;
471  	            char *target = stonith_level_key(match, fenced_target_by_unknown);
472  	
473  	            crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
474  	            if (target == NULL) {
475  	                crm_err("Invalid fencing target in element %s", ID(match));
476  	
477  	            } else if (index <= 0) {
478  	                crm_err("Invalid level for %s in element %s", target, ID(match));
479  	
480  	            } else {
481  	                topology_remove_helper(target, index);
482  	            }
483  	            /* } else { Deal with modifications during the 'addition' stage */
484  	        }
485  	    }
486  	}
487  	
488  	static void
489  	update_fencing_topology(const char *event, xmlNode * msg)
490  	{
491  	    int format = 1;
492  	    const char *xpath;
493  	    xmlXPathObjectPtr xpathObj = NULL;
494  	    xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
495  	
496  	    CRM_ASSERT(patchset);
497  	    crm_element_value_int(patchset, PCMK_XA_FORMAT, &format);
498  	
499  	    if(format == 1) {
500  	        /* Process deletions (only) */
501  	        xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_FENCING_LEVEL;
502  	        xpathObj = xpath_search(msg, xpath);
503  	
504  	        remove_fencing_topology(xpathObj);
505  	        freeXpathObject(xpathObj);
506  	
507  	        /* Process additions and changes */
508  	        xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_TAG_FENCING_LEVEL;
509  	        xpathObj = xpath_search(msg, xpath);
510  	
511  	        register_fencing_topology(xpathObj);
512  	        freeXpathObject(xpathObj);
513  	
514  	    } else if(format == 2) {
515  	        xmlNode *change = NULL;
516  	        int add[] = { 0, 0, 0 };
517  	        int del[] = { 0, 0, 0 };
518  	
519  	        xml_patch_versions(patchset, add, del);
520  	
521  	        for (change = pcmk__xml_first_child(patchset); change != NULL;
522  	             change = pcmk__xml_next(change)) {
523  	            const char *op = crm_element_value(change, XML_DIFF_OP);
524  	            const char *xpath = crm_element_value(change, XML_DIFF_PATH);
525  	
526  	            if(op == NULL) {
527  	                continue;
528  	
529  	            } else if(strstr(xpath, "/" XML_TAG_FENCING_LEVEL) != NULL) {
530  	                /* Change to a specific entry */
531  	
532  	                crm_trace("Handling %s operation %d.%d.%d for %s", op, add[0], add[1], add[2], xpath);
533  	                if(strcmp(op, "move") == 0) {
534  	                    continue;
535  	
536  	                } else if(strcmp(op, "create") == 0) {
537  	                    add_topology_level(change->children);
538  	
539  	                } else if(strcmp(op, "modify") == 0) {
540  	                    xmlNode *match = first_named_child(change, XML_DIFF_RESULT);
541  	
542  	                    if(match) {
543  	                        remove_topology_level(match->children);
544  	                        add_topology_level(match->children);
545  	                    }
546  	
547  	                } else if(strcmp(op, "delete") == 0) {
548  	                    /* Nuclear option, all we have is the path and an id... not enough to remove a specific entry */
549  	                    crm_info("Re-initializing fencing topology after %s operation %d.%d.%d for %s",
550  	                             op, add[0], add[1], add[2], xpath);
551  	                    fencing_topology_init();
552  	                    return;
553  	                }
554  	
555  	            } else if (strstr(xpath, "/" XML_TAG_FENCING_TOPOLOGY) != NULL) {
556  	                /* Change to the topology in general */
557  	                crm_info("Re-initializing fencing topology after top-level %s operation  %d.%d.%d for %s",
558  	                         op, add[0], add[1], add[2], xpath);
559  	                fencing_topology_init();
560  	                return;
561  	
562  	            } else if (strstr(xpath, "/" XML_CIB_TAG_CONFIGURATION)) {
563  	                /* Changes to the whole config section, possibly including the topology as a whild */
564  	                if(first_named_child(change, XML_TAG_FENCING_TOPOLOGY) == NULL) {
565  	                    crm_trace("Nothing for us in %s operation %d.%d.%d for %s.",
566  	                              op, add[0], add[1], add[2], xpath);
567  	
568  	                } else if(strcmp(op, "delete") == 0 || strcmp(op, "create") == 0) {
569  	                    crm_info("Re-initializing fencing topology after top-level %s operation %d.%d.%d for %s.",
570  	                             op, add[0], add[1], add[2], xpath);
571  	                    fencing_topology_init();
572  	                    return;
573  	                }
574  	
575  	            } else {
576  	                crm_trace("Nothing for us in %s operation %d.%d.%d for %s",
577  	                          op, add[0], add[1], add[2], xpath);
578  	            }
579  	        }
580  	
581  	    } else {
582  	        crm_warn("Unknown patch format: %d", format);
583  	    }
584  	}
585  	
586  	static void
587  	update_cib_cache_cb(const char *event, xmlNode * msg)
588  	{
589  	    long timeout_ms_saved = stonith_watchdog_timeout_ms;
590  	    bool need_full_refresh = false;
591  	
592  	    if(!have_cib_devices) {
593  	        crm_trace("Skipping updates until we get a full dump");
594  	        return;
595  	
596  	    } else if(msg == NULL) {
597  	        crm_trace("Missing %s update", event);
598  	        return;
599  	    }
600  	
601  	    /* Maintain a local copy of the CIB so that we have full access
602  	     * to device definitions, location constraints, and node attributes
603  	     */
604  	    if (local_cib != NULL) {
605  	        int rc = pcmk_ok;
606  	        xmlNode *patchset = NULL;
607  	
608  	        crm_element_value_int(msg, F_CIB_RC, &rc);
609  	        if (rc != pcmk_ok) {
610  	            return;
611  	        }
612  	
613  	        patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
614  	        rc = xml_apply_patchset(local_cib, patchset, TRUE);
615  	        switch (rc) {
616  	            case pcmk_ok:
617  	            case -pcmk_err_old_data:
618  	                break;
619  	            case -pcmk_err_diff_resync:
620  	            case -pcmk_err_diff_failed:
621  	                crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
622  	                free_xml(local_cib);
623  	                local_cib = NULL;
624  	                break;
625  	            default:
626  	                crm_warn("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
627  	                free_xml(local_cib);
628  	                local_cib = NULL;
629  	        }
630  	    }
631  	
632  	    if (local_cib == NULL) {
633  	        if (fenced_query_cib() != pcmk_rc_ok) {
634  	            return;
635  	        }
636  	        need_full_refresh = true;
637  	    }
638  	
639  	    pcmk__refresh_node_caches_from_cib(local_cib);
640  	    update_stonith_watchdog_timeout_ms(local_cib);
641  	
642  	    if (timeout_ms_saved != stonith_watchdog_timeout_ms) {
643  	        need_full_refresh = true;
644  	    }
645  	
646  	    if (need_full_refresh) {
647  	        fencing_topology_init();
648  	        cib_devices_update();
649  	    } else {
650  	        // Partial refresh
651  	        update_fencing_topology(event, msg);
652  	        update_cib_stonith_devices(event, msg);
653  	    }
654  	
655  	    watchdog_device_update();
656  	}
657  	
658  	static void
659  	init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
660  	{
661  	    crm_info("Updating device list from CIB");
662  	    have_cib_devices = TRUE;
663  	    local_cib = copy_xml(output);
664  	
665  	    pcmk__refresh_node_caches_from_cib(local_cib);
666  	    update_stonith_watchdog_timeout_ms(local_cib);
667  	
668  	    fencing_topology_init();
669  	    cib_devices_update();
670  	    watchdog_device_update();
671  	}
672  	
673  	static void
674  	cib_connection_destroy(gpointer user_data)
675  	{
676  	    if (stonith_shutdown_flag) {
677  	        crm_info("Connection to the CIB manager closed");
678  	        return;
679  	    } else {
680  	        crm_crit("Lost connection to the CIB manager, shutting down");
681  	    }
682  	    if (cib_api) {
683  	        cib_api->cmds->signoff(cib_api);
684  	    }
685  	    stonith_shutdown(0);
686  	}
687  	
688  	/*!
689  	 * \internal
690  	 * \brief Disconnect from CIB manager
691  	 */
692  	void
693  	fenced_cib_cleanup(void)
694  	{
695  	    if (cib_api != NULL) {
696  	        cib_api->cmds->del_notify_callback(cib_api, T_CIB_DIFF_NOTIFY,
697  	                                           update_cib_cache_cb);
698  	        cib__clean_up_connection(&cib_api);
699  	    }
700  	    free_xml(local_cib);
701  	    local_cib = NULL;
702  	}
703  	
704  	void
705  	setup_cib(void)
706  	{
707  	    int rc, retries = 0;
708  	
709  	    cib_api = cib_new();
710  	    if (cib_api == NULL) {
711  	        crm_err("No connection to the CIB manager");
712  	        return;
713  	    }
714  	
715  	    do {
716  	        sleep(retries);
717  	        rc = cib_api->cmds->signon(cib_api, CRM_SYSTEM_STONITHD, cib_command);
718  	    } while (rc == -ENOTCONN && ++retries < 5);
719  	
720  	    if (rc != pcmk_ok) {
721  	        crm_err("Could not connect to the CIB manager: %s (%d)", pcmk_strerror(rc), rc);
722  	
723  	    } else if (pcmk_ok !=
724  	               cib_api->cmds->add_notify_callback(cib_api, T_CIB_DIFF_NOTIFY, update_cib_cache_cb)) {
725  	        crm_err("Could not set CIB notification callback");
726  	
727  	    } else {
728  	        rc = cib_api->cmds->query(cib_api, NULL, NULL, cib_scope_local);
729  	        cib_api->cmds->register_callback(cib_api, rc, 120, FALSE, NULL, "init_cib_cache_cb",
730  	                                         init_cib_cache_cb);
731  	        cib_api->cmds->set_connection_dnotify(cib_api, cib_connection_destroy);
732  	        crm_info("Watching for fencing topology changes");
733  	    }
734  	}
735