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 lrm_state_destroy_all();
226
|
(15) Event path: |
Condition "_p", taking true branch. |
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
|
(16) Event path: |
Condition "_p", taking true branch. |
239 g_clear_pointer(&controld_globals.our_uuid, free);
|
(17) Event path: |
Condition "_p", taking true branch. |
240 g_clear_pointer(&controld_globals.dc_name, free);
|
(18) Event path: |
Condition "_p", taking true branch. |
241 g_clear_pointer(&controld_globals.dc_version, free);
|
(19) Event path: |
Condition "_p", taking true branch. |
242 g_clear_pointer(&controld_globals.cluster_name, free);
|
(20) Event path: |
Condition "_p", taking true branch. |
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
|
(21) Event path: |
Condition "mloop", taking true branch. |
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
|
(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. |
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
|
(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. |
270 while((g_main_context_pending(ctx) && lpc < 10)) {
271 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. |
272 pcmk__trace("Iteration %d", lpc);
273 g_main_context_dispatch(ctx);
|
(32) Event path: |
Jumping back to the beginning of the loop. |
274 }
275 }
276
|
(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. |
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);
|
(39) Event path: |
Falling through to end of if statement. |
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
|
CID (unavailable; MK=5c326536f0feb9065dceabdb28748ed0) (#7 of 7): 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". |
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