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  	{
(1) Event path: Condition "pcmk__is_set(controld_globals.fsa_input_register, 8UL /* 1UL << 3 */)", taking true branch.
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  	
(2) Event path: Falling through to end of if statement.
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  	
(3) Event path: Condition "controld_globals.logger_out != NULL", taking true branch.
141  	    if (controld_globals.logger_out != NULL) {
142  	        controld_globals.logger_out->finish(controld_globals.logger_out,
143  	                                            exit_code, true, NULL);
CID (unavailable; MK=41dc8edabc2a9521de2acb8b9e5cc6bd) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(4) Event assign_union_field: The union field "in" of "_pp" is written.
(5) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
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  	
157  	    if (in_progress && (exit_code == CRM_EX_OK)) {
158  	        pcmk__debug("Exit is already in progress");
159  	        return exit_code;
160  	
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;
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  	
178  	    if(ipcs) {
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  	
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  	     */
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  	    lrm_state_destroy_all();
226  	
227  	    g_clear_pointer(&config_read_trigger, mainloop_destroy_trigger);
228  	
229  	    controld_destroy_fsa_trigger();
230  	    controld_destroy_transition_trigger();
231  	
232  	    pcmk__client_cleanup();
233  	    pcmk__cluster_destroy_node_caches();
234  	
235  	    controld_free_fsa_timers();
236  	    controld_cleanup_fencing_history_sync(NULL, true);
237  	    controld_free_sched_timer();
238  	
239  	    g_clear_pointer(&controld_globals.our_uuid, free);
240  	    g_clear_pointer(&controld_globals.dc_name, free);
241  	    g_clear_pointer(&controld_globals.dc_version, free);
242  	    g_clear_pointer(&controld_globals.cluster_name, free);
243  	    g_clear_pointer(&controld_globals.te_uuid, free);
244  	
245  	    free_max_generation();
246  	    controld_destroy_failed_sync_table();
247  	    controld_destroy_outside_events_table();
248  	
249  	    mainloop_destroy_signal(SIGPIPE);
250  	    mainloop_destroy_signal(SIGUSR1);
251  	    mainloop_destroy_signal(SIGTERM);
252  	    mainloop_destroy_signal(SIGTRAP);
253  	    /* leave SIGCHLD engaged as we might still want to drain some service-actions */
254  	
255  	    if (mloop) {
256  	        GMainContext *ctx = g_main_loop_get_context(controld_globals.mainloop);
257  	
258  	        /* Don't re-enter this block */
259  	        controld_globals.mainloop = NULL;
260  	
261  	        /* no signals on final draining anymore */
262  	        mainloop_destroy_signal(SIGCHLD);
263  	
264  	        pcmk__trace("Draining mainloop %d %d", g_main_loop_is_running(mloop),
265  	                    g_main_context_pending(ctx));
266  	
267  	        {
268  	            int lpc = 0;
269  	
270  	            while((g_main_context_pending(ctx) && lpc < 10)) {
271  	                lpc++;
272  	                pcmk__trace("Iteration %d", lpc);
273  	                g_main_context_dispatch(ctx);
274  	            }
275  	        }
276  	
277  	        pcmk__trace("Closing mainloop %d %d", g_main_loop_is_running(mloop),
278  	                    g_main_context_pending(ctx));
279  	        g_main_loop_quit(mloop);
280  	
281  	        /* Won't do anything yet, since we're inside it now */
282  	        g_main_loop_unref(mloop);
283  	    } else {
284  	        mainloop_destroy_signal(SIGCHLD);
285  	    }
286  	
287  	    cib_delete(controld_globals.cib_conn);
288  	    controld_globals.cib_conn = NULL;
289  	
290  	    throttle_fini();
291  	
292  	    g_clear_pointer(&controld_globals.cluster, pcmk_cluster_free);
293  	
294  	    /* Graceful */
295  	    pcmk__trace("Done preparing for exit with status %d (%s)", exit_code,
296  	                crm_exit_str(exit_code));
297  	    return exit_code;
298  	}
299  	
300  	// A_EXIT_0, A_EXIT_1
301  	void
302  	do_exit(long long action, enum crmd_fsa_cause cause,
303  	        enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
304  	        fsa_data_t *msg_data)
305  	{
306  	    crm_exit_t exit_code = CRM_EX_OK;
307  	
308  	    if (pcmk__is_set(action, A_EXIT_1)) {
309  	        exit_code = CRM_EX_ERROR;
310  	        pcmk__err("Exiting now due to errors");
311  	    }
312  	    verify_stopped(cur_state, LOG_ERR);
313  	    crmd_exit(exit_code);
314  	}
315  	
316  	// A_STARTUP
317  	void
318  	do_startup(long long action, enum crmd_fsa_cause cause,
319  	           enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
320  	           fsa_data_t *msg_data)
321  	{
322  	    mainloop_add_signal(SIGTERM, crm_shutdown);
323  	    mainloop_add_signal(SIGPIPE, NULL); // Ignore SIGPIPE
324  	
325  	    config_read_trigger = mainloop_add_trigger(G_PRIORITY_HIGH,
326  	                                               crm_read_options, NULL);
327  	    controld_init_fsa_trigger();
328  	    controld_init_transition_trigger();
329  	
330  	    controld_globals.cib_conn = cib_new();
331  	
332  	    lrm_state_init_local();
333  	    if (!controld_init_fsa_timers()) {
334  	        register_fsa_error(I_ERROR, msg_data);
335  	    }
336  	}
337  	
338  	// \return libqb error code (0 on success, -errno on error)
339  	static int32_t
340  	accept_controller_client(qb_ipcs_connection_t *c, uid_t uid, gid_t gid)
341  	{
342  	    pcmk__trace("Accepting new IPC client connection");
343  	    if (pcmk__new_client(c, uid, gid) == NULL) {
344  	        return -ENOMEM;
345  	    }
346  	    return 0;
347  	}
348  	
349  	// \return libqb error code (0 on success, -errno on error)
350  	static int32_t
351  	dispatch_controller_ipc(qb_ipcs_connection_t * c, void *data, size_t size)
352  	{
353  	    int rc = pcmk_rc_ok;
354  	    uint32_t id = 0;
355  	    uint32_t flags = 0;
356  	    pcmk__client_t *client = pcmk__find_client(c);
357  	    xmlNode *msg = NULL;
358  	
359  	    // Sanity-check, and parse XML from IPC data
360  	    CRM_CHECK(client != NULL, return 0);
361  	    if (data == NULL) {
362  	        pcmk__debug("No IPC data from PID %d", pcmk__client_pid(c));
363  	        return 0;
364  	    }
365  	
366  	    rc = pcmk__ipc_msg_append(&client->buffer, data);
367  	
368  	    if (rc == pcmk_rc_ipc_more) {
369  	        /* We haven't read the complete message yet, so just return. */
370  	        return 0;
371  	
372  	    } else if (rc == pcmk_rc_ok) {
373  	        /* We've read the complete message and there's already a header on
374  	         * the front.  Pass it off for processing.
375  	         */
376  	        msg = pcmk__client_data2xml(client, &id, &flags);
377  	        g_byte_array_free(client->buffer, TRUE);
378  	        client->buffer = NULL;
379  	
380  	    } else {
381  	        /* Some sort of error occurred reassembling the message.  All we can
382  	         * do is clean up, log an error and return.
383  	         */
384  	        pcmk__err("Error when reading IPC message: %s", pcmk_rc_str(rc));
385  	
386  	        if (client->buffer != NULL) {
387  	            g_byte_array_free(client->buffer, TRUE);
388  	            client->buffer = NULL;
389  	        }
390  	
391  	        return 0;
392  	    }
393  	
394  	    if (msg == NULL) {
395  	        pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_PROTOCOL);
396  	        return 0;
397  	    }
398  	    pcmk__ipc_send_ack(client, id, flags, NULL, CRM_EX_INDETERMINATE);
399  	
400  	    pcmk__assert(client->user != NULL);
401  	    pcmk__update_acl_user(msg, PCMK__XA_CRM_USER, client->user);
402  	
403  	    pcmk__xe_set(msg, PCMK__XA_CRM_SYS_FROM, client->id);
404  	    if (controld_authorize_ipc_message(msg, client, NULL)) {
405  	        pcmk__trace("Processing IPC message from client %s",
406  	                    pcmk__client_name(client));
407  	        route_message(C_IPC_MESSAGE, msg);
408  	    }
409  	
410  	    controld_trigger_fsa();
411  	    pcmk__xml_free(msg);
412  	    return 0;
413  	}
414  	
415  	static int32_t
416  	ipc_client_disconnected(qb_ipcs_connection_t *c)
417  	{
418  	    pcmk__client_t *client = pcmk__find_client(c);
419  	
420  	    if (client) {
421  	        pcmk__trace("Disconnecting %sregistered client %s (%p/%p)",
422  	                    (client->userdata? "" : "un"), pcmk__client_name(client),
423  	                    c, client);
424  	        free(client->userdata);
425  	        pcmk__free_client(client);
426  	        controld_trigger_fsa();
427  	    }
428  	    return 0;
429  	}
430  	
431  	static void
432  	ipc_connection_destroyed(qb_ipcs_connection_t *c)
433  	{
434  	    pcmk__trace("Connection %p", c);
435  	    ipc_client_disconnected(c);
436  	}
437  	
438  	// A_STOP
439  	void
440  	do_stop(long long action, enum crmd_fsa_cause cause,
441  	        enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
442  	        fsa_data_t *msg_data)
443  	{
444  	    pcmk__trace("Stopping IPC server");
445  	    mainloop_del_ipc_server(ipcs);
446  	    ipcs = NULL;
447  	    controld_fsa_append(C_FSA_INTERNAL, I_TERMINATE, NULL);
448  	}
449  	
450  	// A_STARTED
451  	void
452  	do_started(long long action, enum crmd_fsa_cause cause,
453  	           enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
454  	           fsa_data_t *msg_data)
455  	{
456  	    static struct qb_ipcs_service_handlers crmd_callbacks = {
457  	        .connection_accept = accept_controller_client,
458  	        .connection_created = NULL,
459  	        .msg_process = dispatch_controller_ipc,
460  	        .connection_closed = ipc_client_disconnected,
461  	        .connection_destroyed = ipc_connection_destroyed,
462  	    };
463  	
464  	    if (cur_state != S_STARTING) {
465  	        pcmk__err("Start cancelled: current state is %s",
466  	                  fsa_state2string(cur_state));
467  	        return;
468  	    }
469  	
470  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_MEMBERSHIP)) {
471  	        pcmk__info("Delaying start: no membership data (%.16" PRIx64 ")",
472  	                   R_MEMBERSHIP);
473  	        controld_fsa_stall(NULL, action);
474  	        return;
475  	    }
476  	
477  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_LRM_CONNECTED)) {
478  	        pcmk__info("Delaying start: not connected to executor (%.16" PRIx64 ")",
479  	                   R_LRM_CONNECTED);
480  	        controld_fsa_stall(NULL, action);
481  	        return;
482  	    }
483  	
484  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_CIB_CONNECTED)) {
485  	        pcmk__info("Delaying start: not connected to CIB manager "
486  	                   "(%.16" PRIx64 ")",
487  	                   R_CIB_CONNECTED);
488  	        controld_fsa_stall(NULL, action);
489  	        return;
490  	    }
491  	
492  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_READ_CONFIG)) {
493  	        pcmk__info("Delaying start: config not read (%.16" PRIx64 ")",
494  	                   R_READ_CONFIG);
495  	        controld_fsa_stall(NULL, action);
496  	        return;
497  	    }
498  	
499  	    if (!pcmk__is_set(controld_globals.fsa_input_register, R_PEER_DATA)) {
500  	        pcmk__info("Delaying start: no peer data (%.16" PRIx64 ")",
501  	                   R_PEER_DATA);
502  	        controld_fsa_stall(NULL, action);
503  	        return;
504  	    }
505  	
506  	    pcmk__debug("Initializing IPC server");
507  	    ipcs = pcmk__serve_controld_ipc(&crmd_callbacks);
508  	
509  	    if (ipcs == NULL) {
510  	        pcmk__err("Failed to create IPC server: shutting down and inhibiting "
511  	                  "respawn");
512  	        register_fsa_error(I_ERROR, msg_data);
513  	
514  	    } else {
515  	        pcmk__notice("Pacemaker controller successfully started and accepting "
516  	                     "connections");
517  	    }
518  	    controld_set_fsa_input_flags(R_ST_REQUIRED);
519  	    controld_timer_fencer_connect(GINT_TO_POINTER(TRUE));
520  	
521  	    controld_fsa_append(msg_data->fsa_cause, I_PENDING, NULL);
522  	}
523  	
524  	// A_RECOVER
525  	void
526  	do_recover(long long action, enum crmd_fsa_cause cause,
527  	           enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
528  	           fsa_data_t *msg_data)
529  	{
530  	    pcmk__warn("Fast-tracking shutdown in response to errors");
531  	    controld_set_fsa_input_flags(R_IN_RECOVERY);
532  	    controld_fsa_append(C_FSA_INTERNAL, I_TERMINATE, NULL);
533  	}
534  	
535  	static void
536  	config_query_callback(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
537  	{
538  	    const char *value = NULL;
539  	    GHashTable *config_hash = NULL;
540  	    crm_time_t *now = crm_time_new(NULL);
541  	    xmlNode *crmconfig = NULL;
542  	    xmlNode *alerts = NULL;
543  	    pcmk_rule_input_t rule_input = {
544  	        .now = now,
545  	    };
546  	
547  	    if (rc != pcmk_ok) {
548  	        pcmk__err("Local CIB query resulted in an error: %s",
549  	                  pcmk_strerror(rc));
550  	        register_fsa_error(I_ERROR, NULL);
551  	
552  	        if (rc == -EACCES || rc == -pcmk_err_schema_validation) {
553  	            pcmk__err("The cluster is mis-configured - shutting down and "
554  	                      "staying down");
555  	            controld_set_fsa_input_flags(R_STAYDOWN);
556  	        }
557  	        goto bail;
558  	    }
559  	
560  	    crmconfig = output;
561  	    if ((crmconfig != NULL) && !pcmk__xe_is(crmconfig, PCMK_XE_CRM_CONFIG)) {
562  	        crmconfig = pcmk__xe_first_child(crmconfig, PCMK_XE_CRM_CONFIG, NULL,
563  	                                         NULL);
564  	    }
565  	    if (!crmconfig) {
566  	        pcmk__err("Local CIB query for " PCMK_XE_CRM_CONFIG " section failed");
567  	        register_fsa_error(I_ERROR, NULL);
568  	        goto bail;
569  	    }
570  	
571  	    pcmk__debug("Call %d : Parsing CIB options", call_id);
572  	    config_hash = pcmk__strkey_table(free, free);
573  	    pcmk_unpack_nvpair_blocks(crmconfig, PCMK_XE_CLUSTER_PROPERTY_SET,
574  	                              PCMK_VALUE_CIB_BOOTSTRAP_OPTIONS, &rule_input,
575  	                              config_hash, NULL);
576  	
577  	    // Validate all options, and use defaults if not already present in hash
578  	    pcmk__validate_cluster_options(config_hash);
579  	
580  	    /* Validate the watchdog timeout in the context of the local node
581  	     * environment. If invalid, the controller will exit with a fatal error.
582  	     *
583  	     * We do this via a wrapper in the controller, so that we call
584  	     * pcmk__valid_fencing_watchdog_timeout() only if watchdog fencing is
585  	     * enabled for the local node. Otherwise, we may exit unnecessarily.
586  	     *
587  	     * A validator function in libcrmcommon can't act as such a wrapper, because
588  	     * it doesn't have a fencer API connection or the local node name.
589  	     */
590  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_FENCING_WATCHDOG_TIMEOUT);
591  	    controld_validate_fencing_watchdog_timeout(value);
592  	
593  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_NO_QUORUM_POLICY);
594  	    if (pcmk__strcase_any_of(value, PCMK_VALUE_FENCE, PCMK_VALUE_FENCE_LEGACY,
595  	                             NULL)
596  	        && (pcmk__locate_sbd() != 0)) {
597  	        controld_set_global_flags(controld_no_quorum_panic);
598  	    }
599  	
600  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_SHUTDOWN_LOCK);
601  	    if (pcmk__is_true(value)) {
602  	        controld_set_global_flags(controld_shutdown_lock_enabled);
603  	    } else {
604  	        controld_clear_global_flags(controld_shutdown_lock_enabled);
605  	    }
606  	
607  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_SHUTDOWN_LOCK_LIMIT);
608  	    pcmk_parse_interval_spec(value, &controld_globals.shutdown_lock_limit);
609  	    controld_globals.shutdown_lock_limit /= 1000;
610  	
611  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_NODE_PENDING_TIMEOUT);
612  	    pcmk_parse_interval_spec(value, &controld_globals.node_pending_timeout);
613  	    controld_globals.node_pending_timeout /= 1000;
614  	
615  	    value = g_hash_table_lookup(config_hash, PCMK_OPT_CLUSTER_NAME);
616  	    pcmk__str_update(&(controld_globals.cluster_name), value);
617  	
618  	    // Let subcomponents initialize their own static variables
619  	    controld_configure_election(config_hash);
620  	    controld_configure_fencing(config_hash);
621  	    controld_configure_fsa_timers(config_hash);
622  	    controld_configure_throttle(config_hash);
623  	
624  	    alerts = pcmk__xe_first_child(output, PCMK_XE_ALERTS, NULL, NULL);
625  	    crmd_unpack_alerts(alerts);
626  	
627  	    controld_set_fsa_input_flags(R_READ_CONFIG);
628  	    controld_trigger_fsa();
629  	
630  	    g_hash_table_destroy(config_hash);
631  	  bail:
632  	    crm_time_free(now);
633  	}
634  	
635  	/*!
636  	 * \internal
637  	 * \brief Trigger read and processing of the configuration
638  	 *
639  	 * \param[in] fn    Calling function name
640  	 * \param[in] line  Line number where call occurred
641  	 */
642  	void
643  	controld_trigger_config_as(const char *fn, int line)
644  	{
645  	    if (config_read_trigger != NULL) {
646  	        pcmk__trace("%s:%d - Triggered config processing", fn, line);
647  	        mainloop_set_trigger(config_read_trigger);
648  	    }
649  	}
650  	
651  	gboolean
652  	crm_read_options(gpointer user_data)
653  	{
654  	    cib_t *cib_conn = controld_globals.cib_conn;
655  	    int call_id = cib_conn->cmds->query(cib_conn,
656  	                                        "//" PCMK_XE_CRM_CONFIG
657  	                                        " | //" PCMK_XE_ALERTS,
658  	                                        NULL, cib_xpath);
659  	
660  	    fsa_register_cib_callback(call_id, NULL, config_query_callback);
661  	    pcmk__trace("Querying the CIB... call %d", call_id);
662  	    return TRUE;
663  	}
664  	
665  	// A_READCONFIG
666  	void
667  	do_read_config(long long action, enum crmd_fsa_cause cause,
668  	               enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
669  	               fsa_data_t *msg_data)
670  	{
671  	    throttle_init();
672  	    controld_trigger_config();
673  	}
674  	
675  	static void
676  	crm_shutdown(int nsig)
677  	{
678  	    const char *value = NULL;
679  	    guint default_period_ms = 0;
680  	
681  	    if ((controld_globals.mainloop == NULL)
682  	        || !g_main_loop_is_running(controld_globals.mainloop)) {
683  	        crmd_exit(CRM_EX_OK);
684  	        return;
685  	    }
686  	
687  	    if (pcmk__is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
688  	        pcmk__err("Escalating shutdown");
689  	        controld_fsa_prepend(C_SHUTDOWN, I_ERROR, NULL);
690  	        return;
691  	    }
692  	
693  	    controld_set_fsa_input_flags(R_SHUTDOWN);
694  	    controld_fsa_append(C_SHUTDOWN, I_SHUTDOWN, NULL);
695  	
696  	    /* If shutdown timer doesn't have a period set, use the default
697  	     *
698  	     * @TODO: Evaluate whether this is still necessary. As long as
699  	     * config_query_callback() has been run at least once, it doesn't look like
700  	     * anything could have changed the timer period since then.
701  	     */
702  	    value = pcmk__cluster_option(NULL, PCMK_OPT_SHUTDOWN_ESCALATION);
703  	    pcmk_parse_interval_spec(value, &default_period_ms);
704  	    controld_shutdown_start_countdown(default_period_ms);
705  	}
706