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 <sys/param.h>
14   	#include <sys/types.h>
15   	#include <sys/stat.h>
16   	
17   	#include <crm/crm.h>
18   	#include <crm/common/xml.h>
19   	#include <crm/cluster/internal.h>
20   	#include <crm/cluster/election_internal.h>
21   	
22   	#include <pacemaker-controld.h>
23   	
24   	static qb_ipcs_service_t *ipcs = NULL;
25   	
26   	static crm_trigger_t *config_read_trigger = NULL;
27   	
28   	#if SUPPORT_COROSYNC
29   	extern gboolean crm_connect_corosync(pcmk_cluster_t *cluster);
30   	#endif
31   	
32   	static void crm_shutdown(int nsig);
33   	static gboolean crm_read_options(gpointer user_data);
34   	
35   	// A_HA_CONNECT
36   	void
37   	do_ha_control(long long action, enum crmd_fsa_cause cause,
38   	              enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
39   	              fsa_data_t *msg_data)
40   	{
41   	    bool connected = false;
42   	
43   	    if (controld_globals.cluster == NULL) {
44   	        controld_globals.cluster = pcmk_cluster_new();
45   	    }
46   	
47   	    if (pcmk__is_set(action, A_HA_DISCONNECT)) {
48   	        pcmk_cluster_disconnect(controld_globals.cluster);
49   	        controld_set_fsa_input_flags(R_HA_DISCONNECTED);
50   	        pcmk__info("Disconnected from the cluster");
51   	    }
52   	
53   	    if (pcmk__is_set(action, A_HA_CONNECT)) {
54   	        pcmk__cluster_set_status_callback(&peer_update_callback);
55   	        pcmk__cluster_set_autoreap(false);
56   	
57   	#if SUPPORT_COROSYNC
58   	        if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
59   	            connected = crm_connect_corosync(controld_globals.cluster);
60   	        }
61   	#endif // SUPPORT_COROSYNC
62   	
63   	        if (connected) {
64   	            pcmk__node_status_t *node = controld_get_local_node_status();
65   	
66   	            controld_election_init();
67   	
68   	            pcmk__str_update(&(controld_globals.our_uuid),
69   	                             pcmk__cluster_get_xml_id(node));
70   	
71   	            if (controld_globals.our_uuid == NULL) {
72   	                pcmk__err("Could not obtain local node UUID");
73   	                connected = false;
74   	            }
75   	        }
76   	
77   	        if (!connected) {
78   	            controld_set_fsa_input_flags(R_HA_DISCONNECTED);
79   	            register_fsa_error(I_ERROR, msg_data);
80   	            return;
81   	        }
82   	
83   	        populate_cib_nodes(controld_node_update_none, __func__);
84   	        controld_clear_fsa_input_flags(R_HA_DISCONNECTED);
85   	        pcmk__info("Connected to the cluster");
86   	    }
87   	
88   	    if ((action & ~(A_HA_CONNECT|A_HA_DISCONNECT)) != 0) {
89   	        pcmk__err("Unexpected action %s in %s", fsa_action2string(action),
90   	                  __func__);
91   	    }
92   	}
93   	
94   	// A_SHUTDOWN
95   	void
96   	do_shutdown(long long action, enum crmd_fsa_cause cause,
97   	            enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
98   	            fsa_data_t *msg_data)
99   	{
100  	    // Just in case
101  	    controld_set_fsa_input_flags(R_SHUTDOWN);
102  	    controld_disconnect_fencer(false);
103  	}
104  	
105  	// A_SHUTDOWN_REQ
106  	void
107  	do_shutdown_req(long long action, enum crmd_fsa_cause cause,
108  	                enum crmd_fsa_state cur_state,
109  	                enum crmd_fsa_input current_input, fsa_data_t *msg_data)
110  	{
111  	    xmlNode *msg = NULL;
112  	
113  	    pcmk__info("Sending shutdown request to all peers (DC is %s)",
114  	               pcmk__s(controld_globals.dc_name, "not set"));
115  	
116  	    controld_set_fsa_input_flags(R_SHUTDOWN);
117  	    msg = pcmk__new_request(pcmk_ipc_controld, CRM_SYSTEM_CRMD, NULL,
118  	                            CRM_SYSTEM_CRMD, CRM_OP_SHUTDOWN_REQ, NULL);
119  	
120  	    if (!pcmk__cluster_send_message(NULL, pcmk_ipc_controld, msg)) {
121  	        register_fsa_error(I_ERROR, msg_data);
122  	    }
123  	    pcmk__xml_free(msg);
124  	}
125  	
126  	void
127  	crmd_fast_exit(crm_exit_t exit_code)
128  	{
129  	    if (pcmk__is_set(controld_globals.fsa_input_register, R_STAYDOWN)) {
130  	        pcmk__warn("Inhibiting respawn " QB_XS " remapping exit code %d to %d",
131  	                   exit_code, CRM_EX_FATAL);
132  	        exit_code = CRM_EX_FATAL;
133  	
134  	    } else if ((exit_code == CRM_EX_OK)
135  	               && pcmk__is_set(controld_globals.fsa_input_register,
136  	                               R_IN_RECOVERY)) {
137  	        pcmk__err("Could not recover from internal error");
138  	        exit_code = CRM_EX_ERROR;
139  	    }
140  	
141  	    if (controld_globals.logger_out != NULL) {
142  	        controld_globals.logger_out->finish(controld_globals.logger_out,
143  	                                            exit_code, true, NULL);
144  	        g_clear_pointer(&controld_globals.logger_out, pcmk__output_free);
145  	    }
146  	
147  	    crm_exit(exit_code);
148  	}
149  	
150  	crm_exit_t
151  	crmd_exit(crm_exit_t exit_code)
152  	{
153  	    GMainLoop *mloop = controld_globals.mainloop;
154  	
155  	    static bool in_progress = FALSE;
156  	
(1) Event path: Condition "in_progress", taking false branch.
157  	    if (in_progress && (exit_code == CRM_EX_OK)) {
158  	        pcmk__debug("Exit is already in progress");
159  	        return exit_code;
160  	
(2) Event path: Condition "in_progress", taking false branch.
161  	    } else if(in_progress) {
162  	        pcmk__notice("Error during shutdown process, exiting now with status "
163  	                     "%d (%s)",
164  	                     exit_code, crm_exit_str(exit_code));
165  	        crm_write_blackbox(SIGTRAP, NULL);
166  	        crmd_fast_exit(exit_code);
167  	    }
168  	
169  	    in_progress = TRUE;
(3) Event path: Switch case default.
(4) Event path: Condition "trace_cs == NULL", taking true branch.
(5) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(6) Event path: Breaking from switch.
170  	    pcmk__trace("Preparing to exit with status %d (%s)", exit_code,
171  	                crm_exit_str(exit_code));
172  	
173  	    /* Suppress secondary errors resulting from us disconnecting everything */
174  	    controld_set_fsa_input_flags(R_HA_DISCONNECTED);
175  	
176  	/* Close all IPC servers and clients to ensure any and all shared memory files are cleaned up */
177  	
(7) Event path: Condition "ipcs", taking true branch.
178  	    if(ipcs) {
(8) Event path: Switch case default.
(9) Event path: Condition "trace_cs == NULL", taking true branch.
(10) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(11) Event path: Breaking from switch.
179  	        pcmk__trace("Closing IPC server");
180  	        mainloop_del_ipc_server(ipcs);
181  	        ipcs = NULL;
182  	    }
183  	
184  	    controld_close_attrd_ipc();
185  	    controld_shutdown_schedulerd_ipc();
186  	    controld_disconnect_fencer(TRUE);
187  	
(12) Event path: Condition "exit_code == CRM_EX_OK", taking true branch.
(13) Event path: Condition "controld_globals.mainloop == NULL", taking false branch.
188  	    if ((exit_code == CRM_EX_OK) && (controld_globals.mainloop == NULL)) {
189  	        pcmk__debug("No mainloop detected");
190  	        exit_code = CRM_EX_ERROR;
191  	    }
192  	
193  	    /* On an error, just get out.
194  	     *
195  	     * Otherwise, make the effort to have mainloop exit gracefully so
196  	     * that it (mostly) cleans up after itself and valgrind has less
197  	     * to report on - allowing real errors stand out
198  	     */
(14) Event path: Condition "exit_code != CRM_EX_OK", taking false branch.
199  	    if (exit_code != CRM_EX_OK) {
200  	        pcmk__notice("Forcing immediate exit with status %d (%s)", exit_code,
201  	                     crm_exit_str(exit_code));
202  	        crm_write_blackbox(SIGTRAP, NULL);
203  	        crmd_fast_exit(exit_code);
204  	    }
205  	
206  	/* Clean up as much memory as possible for valgrind */
207  	
208  	    controld_clear_fsa_input_flags(R_MEMBERSHIP);
209  	
210  	    g_queue_free_full(controld_globals.fsa_message_queue,
211  	                      (GDestroyNotify) delete_fsa_input);
212  	    controld_globals.fsa_message_queue = NULL;
213  	
214  	    controld_free_node_pending_timers();
215  	    election_reset(controld_globals.cluster); // Stop any election timer
216  	
217  	    /* Tear down the CIB manager connection, but don't free it yet -- it could
218  	     * be used when we drain the mainloop later.
219  	     */
220  	
221  	    controld_disconnect_cib_manager();
222  	
223  	    verify_stopped(controld_globals.fsa_state, LOG_WARNING);
224  	    controld_clear_fsa_input_flags(R_LRM_CONNECTED);
225  	
226  	    controld_execd_state_table_free();
227  	    controld_remote_proxy_table_free();
228  	
(15) Event path: Condition "_p", taking true branch.
229  	    g_clear_pointer(&config_read_trigger, mainloop_destroy_trigger);
230  	
231  	    controld_destroy_fsa_trigger();
232  	    controld_destroy_transition_trigger();
233  	
234  	    pcmk__client_cleanup();
235  	    pcmk__cluster_destroy_node_caches();
236  	
237  	    controld_free_fsa_timers();
238  	    controld_cleanup_fencing_history_sync(NULL, true);
239  	    controld_free_sched_timer();
240  	
(16) Event path: Condition "_p", taking true branch.
241  	    g_clear_pointer(&controld_globals.our_uuid, free);
(17) Event path: Condition "_p", taking true branch.
242  	    g_clear_pointer(&controld_globals.dc_name, free);
(18) Event path: Condition "_p", taking true branch.
243  	    g_clear_pointer(&controld_globals.dc_version, free);
(19) Event path: Condition "_p", taking true branch.
244  	    g_clear_pointer(&controld_globals.cluster_name, free);
(20) Event path: Condition "_p", taking true branch.
245  	    g_clear_pointer(&controld_globals.te_uuid, free);
246  	
247  	    free_max_generation();
248  	    controld_destroy_failed_sync_table();
249  	    controld_destroy_outside_events_table();
250  	
251  	    mainloop_destroy_signal(SIGPIPE);
252  	    mainloop_destroy_signal(SIGUSR1);
253  	    mainloop_destroy_signal(SIGTERM);
254  	    mainloop_destroy_signal(SIGTRAP);
255  	    /* leave SIGCHLD engaged as we might still want to drain some service-actions */
256  	
(21) Event path: Condition "mloop", taking true branch.
257  	    if (mloop) {
258  	        GMainContext *ctx = g_main_loop_get_context(controld_globals.mainloop);
259  	
260  	        /* Don't re-enter this block */
261  	        controld_globals.mainloop = NULL;
262  	
263  	        /* no signals on final draining anymore */
264  	        mainloop_destroy_signal(SIGCHLD);
265  	
(22) Event path: Switch case default.
(23) Event path: Condition "trace_cs == NULL", taking true branch.
(24) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(25) Event path: Breaking from switch.
266  	        pcmk__trace("Draining mainloop %d %d", g_main_loop_is_running(mloop),
267  	                    g_main_context_pending(ctx));
268  	
269  	        {
270  	            int lpc = 0;
271  	
(26) Event path: Condition "g_main_context_pending(ctx)", taking true branch.
(27) Event path: Condition "lpc < 10", taking true branch.
(33) Event path: Condition "g_main_context_pending(ctx)", taking true branch.
(34) Event path: Condition "lpc < 10", taking false branch.
272  	            while((g_main_context_pending(ctx) && lpc < 10)) {
273  	                lpc++;
(28) Event path: Switch case default.
(29) Event path: Condition "trace_cs == NULL", taking true branch.
(30) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(31) Event path: Breaking from switch.
274  	                pcmk__trace("Iteration %d", lpc);
275  	                g_main_context_dispatch(ctx);
(32) Event path: Jumping back to the beginning of the loop.
276  	            }
277  	        }
278  	
(35) Event path: Switch case default.
(36) Event path: Condition "trace_cs == NULL", taking true branch.
(37) Event path: Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch.
(38) Event path: Breaking from switch.
279  	        pcmk__trace("Closing mainloop %d %d", g_main_loop_is_running(mloop),
280  	                    g_main_context_pending(ctx));
281  	        g_main_loop_quit(mloop);
282  	
283  	        /* Won't do anything yet, since we're inside it now */
284  	        g_main_loop_unref(mloop);
(39) Event path: Falling through to end of if statement.
285  	    } else {
286  	        mainloop_destroy_signal(SIGCHLD);
287  	    }
288  	
289  	    throttle_fini();
CID (unavailable; MK=5c326536f0feb9065dceabdb28748ed0) (#7 of 8): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(40) Event assign_union_field: The union field "in" of "_pp" is written.
(41) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
290  	    g_clear_pointer(&controld_globals.cib_conn, cib_delete);
291  	    g_clear_pointer(&controld_globals.cluster, pcmk_cluster_free);
292  	
293  	    /* Graceful */
294  	    pcmk__trace("Done preparing for exit with status %d (%s)", exit_code,
295  	                crm_exit_str(exit_code));
296  	    return exit_code;
297  	}
298  	
299  	// A_EXIT_0, A_EXIT_1
300  	void
301  	do_exit(long long action, enum crmd_fsa_cause cause,
302  	        enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
303  	        fsa_data_t *msg_data)
304  	{
305  	    crm_exit_t exit_code = CRM_EX_OK;
306  	
307  	    if (pcmk__is_set(action, A_EXIT_1)) {
308  	        exit_code = CRM_EX_ERROR;
309  	        pcmk__err("Exiting now due to errors");
310  	    }
311  	    verify_stopped(cur_state, LOG_ERR);
312  	    crmd_exit(exit_code);
313  	}
314  	
315  	// A_STARTUP
316  	void
317  	do_startup(long long action, enum crmd_fsa_cause cause,
318  	           enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
319  	           fsa_data_t *msg_data)
320  	{
321  	    mainloop_add_signal(SIGTERM, crm_shutdown);
322  	    mainloop_add_signal(SIGPIPE, NULL); // Ignore SIGPIPE
323  	
324  	    config_read_trigger = mainloop_add_trigger(G_PRIORITY_HIGH,
325  	                                               crm_read_options, NULL);
326  	    controld_init_fsa_trigger();
327  	    controld_init_transition_trigger();
328  	
329  	    controld_globals.cib_conn = cib_new();
330  	
331  	    controld_execd_state_table_init();
332  	    controld_remote_proxy_table_init();
333  	
334  	    if (!controld_init_fsa_timers()) {
335  	        register_fsa_error(I_ERROR, msg_data);
336  	    }
337  	}
338  	
339  	// \return libqb error code (0 on success, -errno on error)
340  	static int32_t
341  	accept_controller_client(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
342  	{
343  	    pcmk__trace("Accepting new IPC client connection");
344  	    if (pcmk__new_client(c, uid, gid) == NULL) {
345  	        return -ENOMEM;
346  	    }
347  	    return 0;
348  	}
349  	
350  	// \return libqb error code (0 on success, -errno on error)
351  	static int32_t
352  	dispatch_controller_ipc(qb_ipcs_connection_t * c, void *data, size_t size)
353  	{
354  	    int rc = pcmk_rc_ok;
355  	    uint32_t id = 0;
356  	    uint32_t flags = 0;
357  	    pcmk__client_t *client = pcmk__find_client(c);
358  	    xmlNode *msg = NULL;
359  	
360  	    // Sanity-check, and parse XML from IPC data
361  	    CRM_CHECK(client != NULL, return 0);
362  	    if (data == NULL) {
363  	        pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c));
364  	        return 0;
365  	    }
366  	
367  	    rc = pcmk__ipc_msg_append(&client->buffer, data);
368  	
369  	    if (rc == pcmk_rc_ipc_more) {
370  	        /* We haven't read the complete message yet, so just return. */
371  	        return 0;
372  	
373  	    } else if (rc == pcmk_rc_ok) {
374  	        /* We've read the complete message and there's already a header on
375  	         * the front.  Pass it off for processing.
376  	         */
377  	        msg = pcmk__client_data2xml(client, &id, &flags);
378  	        g_byte_array_free(client->buffer, TRUE);
379  	        client->buffer = NULL;
380  	
381  	    } else {
382  	        /* Some sort of error occurred reassembling the message.  All we can
383  	         * do is clean up, log an error and return.
384  	         */
385  	        pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc));
386  	
387  	        if (client->buffer != NULL) {
388  	            g_byte_array_free(client->buffer, TRUE);
389  	            client->buffer = NULL;
390  	        }
391  	
392  	        return 0;
393  	    }
394  	
395  	    if (msg == NULL) {
396  	        pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_PROTOCOL);
397  	        return 0;
398  	    }
399  	    pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_INDETERMINATE);
400  	
401  	    pcmk__assert(client->user != NULL);
402  	    pcmk__update_acl_user(msg, PCMK__XA_CRM_USER, client->user);
403  	
404  	    pcmk__xe_set(msg, PCMK__XA_CRM_SYS_FROM, client->id);
405  	    if (controld_authorize_ipc_message(msg, client, NULL)) {
406  	        pcmk__trace("Processing IPC message from client %s",
407  	                    pcmk__client_name(client));
408  	        route_message(C_IPC_MESSAGE, msg);
409  	    }
410  	
411  	    controld_trigger_fsa();
412  	    pcmk__xml_free(msg);
413  	    return 0;
414  	}
415  	
416  	static int32_t
417  	ipc_client_disconnected(qb_ipcs_connection_t *c)
418  	{
419  	    pcmk__client_t *client = pcmk__find_client(c);
420  	
421  	    if (client) {
422  	        pcmk__trace("Disconnecting %sregistered client %s (%p/%p)",
423  	                    (client->userdata? "" : "un"), pcmk__client_name(client),
424  	                    c, client);
425  	        free(client->userdata);
426  	        pcmk__free_client(client);
427  	        controld_trigger_fsa();
428  	    }
429  	    return 0;
430  	}
431  	
432  	static void
433  	ipc_connection_destroyed(qb_ipcs_connection_t *c)
434  	{
435  	    pcmk__trace("Connection %p", c);
436  	    ipc_client_disconnected(c);
437  	}
438  	
439  	// A_STOP
440  	void
441  	do_stop(long long action, enum crmd_fsa_cause cause,
442  	        enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
443  	        fsa_data_t *msg_data)
444  	{
445  	    pcmk__trace("Stopping IPC server");
446  	    mainloop_del_ipc_server(ipcs);
447  	    ipcs = NULL;
448  	    controld_fsa_append(C_FSA_INTERNAL, I_TERMINATE, NULL);
449  	}
450  	
451  	// A_STARTED
452  	void
453  	do_started(long long action, enum crmd_fsa_cause cause,
454  	           enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
455  	           fsa_data_t *msg_data)
456  	{
457  	    static struct qb_ipcs_service_handlers crmd_callbacks = {
458  	        .connection_accept = accept_controller_client,
459  	        .connection_created = NULL,
460  	        .msg_process = dispatch_controller_ipc,
461  	        .connection_closed = ipc_client_disconnected,
462  	        .connection_destroyed = ipc_connection_destroyed,
463  	    };
464  	
465  	    if (cur_state != S_STARTING) {
466  	        pcmk__err("Start cancelled: current state is %s",
467  	                  fsa_state2string(cur_state));
468  	        return;
469  	    }
470  	
471  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_MEMBERSHIP)) {
472  	        pcmk__info("Delaying start: no membership data (%.16" PRIx64 ")",
473  	                   R_MEMBERSHIP);
474  	        controld_fsa_stall(NULL, action);
475  	        return;
476  	    }
477  	
478  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_LRM_CONNECTED)) {
479  	        pcmk__info("Delaying start: not connected to executor (%.16" PRIx64 ")",
480  	                   R_LRM_CONNECTED);
481  	        controld_fsa_stall(NULL, action);
482  	        return;
483  	    }
484  	
485  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_CIB_CONNECTED)) {
486  	        pcmk__info("Delaying start: not connected to CIB manager "
487  	                   "(%.16" PRIx64 ")",
488  	                   R_CIB_CONNECTED);
489  	        controld_fsa_stall(NULL, action);
490  	        return;
491  	    }
492  	
493  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_READ_CONFIG)) {
494  	        pcmk__info("Delaying start: config not read (%.16" PRIx64 ")",
495  	                   R_READ_CONFIG);
496  	        controld_fsa_stall(NULL, action);
497  	        return;
498  	    }
499  	
500  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_PEER_DATA)) {
501  	        pcmk__info("Delaying start: no peer data (%.16" PRIx64 ")",
502  	                   R_PEER_DATA);
503  	        controld_fsa_stall(NULL, action);
504  	        return;
505  	    }
506  	
507  	    pcmk__debug("Initializing IPC server");
508  	    ipcs = pcmk__serve_controld_ipc(&crmd_callbacks);
509  	
510  	    if (ipcs == NULL) {
511  	        pcmk__err("Failed to create IPC server: shutting down and inhibiting "
512  	                  "respawn");
513  	        register_fsa_error(I_ERROR, msg_data);
514  	
515  	    } else {
516  	        pcmk__notice("Pacemaker controller successfully started and accepting "
517  	                     "connections");
518  	    }
519  	    controld_set_fsa_input_flags(R_ST_REQUIRED);
520  	    controld_timer_fencer_connect(GINT_TO_POINTER(TRUE));
521  	
522  	    controld_fsa_append(msg_data->fsa_cause, I_PENDING, NULL);
523  	}
524  	
525  	// A_RECOVER
526  	void
527  	do_recover(long long action, enum crmd_fsa_cause cause,
528  	           enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
529  	           fsa_data_t *msg_data)
530  	{
531  	    pcmk__warn("Fast-tracking shutdown in response to errors");
532  	    controld_set_fsa_input_flags(R_IN_RECOVERY);
533  	    controld_fsa_append(C_FSA_INTERNAL, I_TERMINATE, NULL);
534  	}
535  	
536  	static void
537  	config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
538  	{
539  	    const char *value = NULL;
540  	    GHashTable *config_hash = NULL;
541  	    crm_time_t *now = crm_time_new(NULL);
542  	    xmlNode *crmconfig = NULL;
543  	    xmlNode *alerts = NULL;
544  	    pcmk_rule_input_t rule_input = {
545  	        .now = now,
546  	    };
547  	
548  	    if (rc != pcmk_ok) {
549  	        pcmk__err("Local CIB query resulted in an error: %s",
550  	                  pcmk_strerror(rc));
551  	        register_fsa_error(I_ERROR, NULL);
552  	
553  	        if (rc == -EACCES || rc == -pcmk_err_schema_validation) {
554  	            pcmk__err("The cluster is mis-configured - shutting down and "
555  	                      "staying down");
556  	            controld_set_fsa_input_flags(R_STAYDOWN);
557  	        }
558  	        goto bail;
559  	    }
560  	
561  	    crmconfig = output;
562  	    if ((crmconfig != NULL) && !pcmk__xe_is(crmconfig, PCMK_XE_CRM_CONFIG)) {
563  	        crmconfig = pcmk__xe_first_child(crmconfig, PCMK_XE_CRM_CONFIG, NULL,
564  	                                         NULL);
565  	    }
566  	    if (!crmconfig) {
567  	        pcmk__err("Local CIB query for " PCMK_XE_CRM_CONFIG " section failed");
568  	        register_fsa_error(I_ERROR, NULL);
569  	        goto bail;
570  	    }
571  	
572  	    pcmk__debug("Call %d : Parsing CIB options", call_id);
573  	    config_hash = pcmk__strkey_table(free, free);
574  	    pcmk__unpack_nvpair_blocks(crmconfig, PCMK_XE_CLUSTER_PROPERTY_SET,
575  	                               PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input,
576  	                               config_hash, NULL, crmconfig->doc);
577  	
578  	    // Validate all options, and use defaults if not already present in hash
579  	    pcmk__validate_cluster_options(config_hash);
580  	
581  	    /* Validate the watchdog timeout in the context of the local node
582  	     * environment. If invalid, the controller will exit with a fatal error.
583  	     *
584  	     * We do this via a wrapper in the controller, so that we call
585  	     * pcmk__valid_fencing_watchdog_timeout() only if watchdog fencing is
586  	     * enabled for the local node. Otherwise, we may exit unnecessarily.
587  	     *
588  	     * A validator function in libcrmcommon can't act as such a wrapper, because
589  	     * it doesn't have a fencer API connection or the local node name.
590  	     */
591  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_FENCING_WATCHDOG_TIMEOUT);
592  	    controld_validate_fencing_watchdog_timeout(value);
593  	
594  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_NO_QUORUM_POLICY);
595  	    if (pcmk__strcase_any_of(value, PCMK_VALUE_FENCE, PCMK_VALUE_FENCE_LEGACY,
596  	                             NULL)
597  	        && (pcmk__locate_sbd() != 0)) {
598  	        controld_set_global_flags(controld_no_quorum_panic);
599  	    }
600  	
601  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_SHUTDOWN_LOCK);
602  	    if (pcmk__is_true(value)) {
603  	        controld_set_global_flags(controld_shutdown_lock_enabled);
604  	    } else {
605  	        controld_clear_global_flags(controld_shutdown_lock_enabled);
606  	    }
607  	
608  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_SHUTDOWN_LOCK_LIMIT);
609  	    pcmk_parse_interval_spec(value, &controld_globals.shutdown_lock_limit);
610  	    controld_globals.shutdown_lock_limit /= 1000;
611  	
612  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_NODE_PENDING_TIMEOUT);
613  	    pcmk_parse_interval_spec(value, &controld_globals.node_pending_timeout);
614  	    controld_globals.node_pending_timeout /= 1000;
615  	
616  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_CLUSTER_NAME);
617  	    pcmk__str_update(&(controld_globals.cluster_name), value);
618  	
619  	    // Let subcomponents initialize their own static variables
620  	    controld_configure_election(config_hash);
621  	    controld_configure_fencing(config_hash);
622  	    controld_configure_fsa_timers(config_hash);
623  	    controld_configure_throttle(config_hash);
624  	
625  	    alerts = pcmk__xe_first_child(output, PCMK_XE_ALERTS, NULL, NULL);
626  	    crmd_unpack_alerts(alerts);
627  	
628  	    controld_set_fsa_input_flags(R_READ_CONFIG);
629  	    controld_trigger_fsa();
630  	
631  	    g_hash_table_destroy(config_hash);
632  	  bail:
633  	    crm_time_free(now);
634  	}
635  	
636  	/*!
637  	 * \internal
638  	 * \brief Trigger read and processing of the configuration
639  	 *
640  	 * \param[in] fn    Calling function name
641  	 * \param[in] line  Line number where call occurred
642  	 */
643  	void
644  	controld_trigger_config_as(const char *fn, int line)
645  	{
646  	    if (config_read_trigger != NULL) {
647  	        pcmk__trace("%s:%d - Triggered config processing", fn, line);
648  	        mainloop_set_trigger(config_read_trigger);
649  	    }
650  	}
651  	
652  	gboolean
653  	crm_read_options(gpointer user_data)
654  	{
655  	    cib_t *cib_conn = controld_globals.cib_conn;
656  	    int call_id = cib_conn->cmds->query(cib_conn,
657  	                                        "//" PCMK_XE_CRM_CONFIG
658  	                                        " | //" PCMK_XE_ALERTS,
659  	                                        NULL, cib_xpath);
660  	
661  	    fsa_register_cib_callback(call_id, NULL, config_query_callback);
662  	    pcmk__trace("Querying the CIB... call %d", call_id);
663  	    return TRUE;
664  	}
665  	
666  	// A_READCONFIG
667  	void
668  	do_read_config(long long action, enum crmd_fsa_cause cause,
669  	               enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
670  	               fsa_data_t *msg_data)
671  	{
672  	    throttle_init();
673  	    controld_trigger_config();
674  	}
675  	
676  	static void
677  	crm_shutdown(int nsig)
678  	{
679  	    const char *value = NULL;
680  	    guint default_period_ms = 0;
681  	
682  	    if ((controld_globals.mainloop == NULL)
683  	        || !g_main_loop_is_running(controld_globals.mainloop)) {
684  	        crmd_exit(CRM_EX_OK);
685  	        return;
686  	    }
687  	
688  	    if (pcmk__is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
689  	        pcmk__err("Escalating shutdown");
690  	        controld_fsa_prepend(C_SHUTDOWN, I_ERROR, NULL);
691  	        return;
692  	    }
693  	
694  	    controld_set_fsa_input_flags(R_SHUTDOWN);
695  	    controld_fsa_append(C_SHUTDOWN, I_SHUTDOWN, NULL);
696  	
697  	    /* If shutdown timer doesn't have a period set, use the default
698  	     *
699  	     * @TODO: Evaluate whether this is still necessary. As long as
700  	     * config_query_callback() has been run at least once, it doesn't look like
701  	     * anything could have changed the timer period since then.
702  	     */
703  	    value = pcmk__cluster_option(NULL, PCMK_OPT_SHUTDOWN_ESCALATION);
704  	    pcmk_parse_interval_spec(value, &default_period_ms);
705  	    controld_shutdown_start_countdown(default_period_ms);
706  	}
707