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 <inttypes.h>               // PRIx64
13   	#include <sys/param.h>
14   	#include <stdio.h>
15   	#include <stdint.h>                 // uint8_t, uint64_t
16   	#include <string.h>
17   	#include <time.h>
18   	
19   	#include <crm/crm.h>
20   	#include <crm/lrmd.h>
21   	#include <crm/cib.h>
22   	#include <crm/common/xml.h>
23   	#include <crm/cluster/election_internal.h>
24   	#include <crm/cluster.h>
25   	
26   	#include <pacemaker-controld.h>
27   	
28   	//! Triggers an FSA invocation
29   	static crm_trigger_t *fsa_trigger = NULL;
30   	
31   	static void do_state_transition(enum crmd_fsa_state cur_state,
32   	                                enum crmd_fsa_state next_state,
33   	                                fsa_data_t *msg_data);
34   	
35   	void s_crmd_fsa_actions(fsa_data_t * fsa_data);
36   	
37   	static void
38   	do_fsa_action(fsa_data_t * fsa_data, long long an_action,
39   	              void (*function) (long long action,
40   	                                enum crmd_fsa_cause cause,
41   	                                enum crmd_fsa_state cur_state,
42   	                                enum crmd_fsa_input cur_input, fsa_data_t * msg_data))
43   	{
44   	    controld_clear_fsa_action_flags(an_action);
45   	    function(an_action, fsa_data->fsa_cause, controld_globals.fsa_state,
46   	             fsa_data->fsa_input, fsa_data);
47   	}
48   	
49   	static const uint64_t startup_actions =
50   	    A_STARTUP | A_CIB_START | A_LRM_CONNECT | A_HA_CONNECT | A_READCONFIG |
51   	    A_STARTED | A_CL_JOIN_QUERY;
52   	
53   	// A_LOG, A_WARN, A_ERROR
54   	static void
55   	do_log(long long action, enum crmd_fsa_cause cause,
56   	       enum crmd_fsa_state cur_state,
57   	       enum crmd_fsa_input current_input, fsa_data_t *msg_data)
58   	{
59   	    uint8_t log_type = LOG_TRACE;
60   	
61   	    pcmk__assert(msg_data != NULL);
62   	
63   	    // @TODO Should the order of precedence be reversed?
64   	    if (pcmk__is_set(action, A_LOG)) {
65   	        log_type = LOG_INFO;
66   	    } else if (pcmk__is_set(action, A_WARN)) {
67   	        log_type = LOG_WARNING;
68   	    } else if (pcmk__is_set(action, A_ERROR)) {
69   	        log_type = LOG_ERR;
70   	    }
71   	
72   	    do_crm_log(log_type, "Input %s received in state %s from %s",
73   	               fsa_input2string(msg_data->fsa_input),
74   	               fsa_state2string(cur_state), msg_data->origin);
75   	
76   	    if (msg_data->data != NULL) {
77   	        pcmk__log_xml_debug(msg_data->data->msg, __func__);
78   	    }
79   	}
80   	
81   	/*!
82   	 * \internal
83   	 * \brief Initialize the FSA trigger
84   	 */
85   	void
86   	controld_init_fsa_trigger(void)
87   	{
88   	    fsa_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, crm_fsa_trigger, NULL);
89   	}
90   	
91   	/*!
92   	 * \internal
93   	 * \brief Destroy the FSA trigger
94   	 */
95   	void
96   	controld_destroy_fsa_trigger(void)
97   	{
98   	    // This basically will not work, since mainloop has a reference to it
CID (unavailable; MK=3880079d3ed1a02ccbd3cca0687b0df9) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(1) Event assign_union_field: The union field "in" of "_pp" is written.
(2) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
99   	    g_clear_pointer(&fsa_trigger, mainloop_destroy_trigger);
100  	}
101  	
102  	/*!
103  	 * \internal
104  	 * \brief Trigger an FSA invocation
105  	 *
106  	 * \param[in] fn    Calling function name
107  	 * \param[in] line  Line number where call occurred
108  	 */
109  	void
110  	controld_trigger_fsa_as(const char *fn, int line)
111  	{
112  	    if (fsa_trigger != NULL) {
113  	        pcmk__trace("%s:%d - Triggered FSA invocation", fn, line);
114  	        mainloop_set_trigger(fsa_trigger);
115  	    }
116  	}
117  	
118  	static void
119  	log_fsa_input(fsa_data_t *stored_msg)
120  	{
121  	    pcmk__assert(stored_msg != NULL);
122  	    pcmk__trace("Processing queued input %llu", stored_msg->id);
123  	
124  	    if (stored_msg->data == NULL) {
125  	        pcmk__trace("FSA processing input from %s", stored_msg->origin);
126  	
127  	    } else {
128  	        pcmk__trace("FSA processing XML message from %s", stored_msg->origin);
129  	        pcmk__log_xml_trace(stored_msg->data->xml, "FSA message data");
130  	    }
131  	}
132  	
133  	static void
134  	log_fsa_data(gpointer data, gpointer user_data)
135  	{
136  	    fsa_data_t *fsa_data = data;
137  	    unsigned int *offset = user_data;
138  	
139  	    pcmk__trace("queue[%u.%llu]: input %s submitted by %s (%p) (cause=%s)",
140  	                (*offset)++, fsa_data->id,
141  	                fsa_input2string(fsa_data->fsa_input), fsa_data->origin,
142  	                fsa_data->data, fsa_cause2string(fsa_data->fsa_cause));
143  	}
144  	
145  	enum crmd_fsa_state
146  	s_crmd_fsa(enum crmd_fsa_cause cause)
147  	{
148  	    controld_globals_t *globals = &controld_globals;
149  	    fsa_data_t *fsa_data = NULL;
150  	    uint64_t register_copy = controld_globals.fsa_input_register;
151  	    uint64_t new_actions = A_NOTHING;
152  	    enum crmd_fsa_state last_state;
153  	
154  	    pcmk__trace("FSA invoked with Cause: %s\tState: %s",
155  	                fsa_cause2string(cause),
156  	                fsa_state2string(globals->fsa_state));
157  	
158  	    fsa_dump_actions(controld_globals.fsa_actions, "Initial");
159  	
160  	    controld_clear_global_flags(controld_fsa_is_stalled);
161  	    if ((controld_fsa_message_queue_length() == 0)
162  	        && (controld_globals.fsa_actions != A_NOTHING)) {
163  	
164  	        /* Fake the first message so that we can enter the loop.
165  	         *
166  	         * There are already actions to perform. Everything in the loop is a
167  	         * no-op for this fake message, except the following:
168  	         * * controld_clear_fsa_action_flags(startup_actions)
169  	         * * s_crmd_fsa_actions(fsa_data)
170  	         *
171  	         * We would still need this fake message even if we changed the loop
172  	         * condition. s_crmd_fsa_actions() needs an fsa_data_t object so that we
173  	         * can process the already-pending actions. So a larger refactor would
174  	         * be required in order to get rid of this.
175  	         *
176  	         * We can't call controld_fsa_append() because it currently won't add a
177  	         * message with I_NULL and A_NOTHING (like this one).
178  	         */
179  	        fsa_data = pcmk__assert_alloc(1, sizeof(fsa_data_t));
180  	        fsa_data->fsa_input = I_NULL;
181  	        fsa_data->fsa_cause = C_FSA_INTERNAL;
182  	        fsa_data->origin = __func__;
183  	
184  	        if (controld_globals.fsa_message_queue == NULL) {
185  	            controld_globals.fsa_message_queue = g_queue_new();
186  	        }
187  	        g_queue_push_tail(controld_globals.fsa_message_queue, fsa_data);
188  	    }
189  	
190  	    while ((controld_fsa_message_queue_length() > 0)
191  	           && !pcmk__is_set(controld_globals.flags, controld_fsa_is_stalled)) {
192  	
193  	        pcmk__trace("Checking messages (%u remaining)",
194  	                    controld_fsa_message_queue_length());
195  	
196  	        fsa_data =
197  	            (fsa_data_t *) g_queue_pop_head(controld_globals.fsa_message_queue);
198  	
199  	        if (fsa_data == NULL) {
200  	            continue;
201  	        }
202  	
203  	        log_fsa_input(fsa_data);
204  	
205  	        /* add any actions back to the queue */
206  	        controld_set_fsa_action_flags(fsa_data->actions);
207  	        fsa_dump_actions(fsa_data->actions, "Restored actions");
208  	
209  	        /* get the next batch of actions */
210  	        new_actions = controld_fsa_get_action(fsa_data->fsa_input);
211  	        controld_set_fsa_action_flags(new_actions);
212  	        fsa_dump_actions(new_actions, "New actions");
213  	
214  	        if (fsa_data->fsa_input != I_NULL && fsa_data->fsa_input != I_ROUTER) {
215  	            pcmk__debug("Processing %s: [ state=%s cause=%s origin=%s ]",
216  	                        fsa_input2string(fsa_data->fsa_input),
217  	                        fsa_state2string(globals->fsa_state),
218  	                        fsa_cause2string(fsa_data->fsa_cause),
219  	                        fsa_data->origin);
220  	        }
221  	
222  	        /* logging : *before* the state is changed */
223  	        if (pcmk__is_set(controld_globals.fsa_actions, A_ERROR)) {
224  	            do_fsa_action(fsa_data, A_ERROR, do_log);
225  	        }
226  	        if (pcmk__is_set(controld_globals.fsa_actions, A_WARN)) {
227  	            do_fsa_action(fsa_data, A_WARN, do_log);
228  	        }
229  	        if (pcmk__is_set(controld_globals.fsa_actions, A_LOG)) {
230  	            do_fsa_action(fsa_data, A_LOG, do_log);
231  	        }
232  	
233  	        /* update state variables */
234  	        last_state = globals->fsa_state;
235  	        globals->fsa_state = controld_fsa_get_next_state(fsa_data->fsa_input);
236  	
237  	        /*
238  	         * Remove certain actions during shutdown
239  	         */
240  	        if ((globals->fsa_state == S_STOPPING)
241  	            || pcmk__is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
242  	            controld_clear_fsa_action_flags(startup_actions);
243  	        }
244  	
245  	        /*
246  	         * Hook for change of state.
247  	         * Allows actions to be added or removed when entering a state
248  	         */
249  	        if (last_state != globals->fsa_state) {
250  	            do_state_transition(last_state, globals->fsa_state, fsa_data);
251  	        }
252  	
253  	        /* start doing things... */
254  	        s_crmd_fsa_actions(fsa_data);
255  	        delete_fsa_input(fsa_data);
256  	    }
257  	
258  	    if ((controld_fsa_message_queue_length() > 0)
259  	        || (controld_globals.fsa_actions != A_NOTHING)
260  	        || pcmk__is_set(controld_globals.flags, controld_fsa_is_stalled)) {
261  	
262  	        pcmk__debug("Exiting the FSA: queue=%u, fsa_actions=%" PRIx64
263  	                    ", stalled=%s",
264  	                    controld_fsa_message_queue_length(),
265  	                    controld_globals.fsa_actions,
266  	                    pcmk__flag_text(controld_globals.flags,
267  	                                    controld_fsa_is_stalled));
268  	    } else {
269  	        pcmk__trace("Exiting the FSA");
270  	    }
271  	
272  	    /* cleanup inputs? */
273  	    if (register_copy != controld_globals.fsa_input_register) {
274  	        uint64_t same = register_copy & controld_globals.fsa_input_register;
275  	
276  	        fsa_dump_inputs(LOG_DEBUG, "Added",
277  	                        controld_globals.fsa_input_register ^ same);
278  	        fsa_dump_inputs(LOG_DEBUG, "Removed", register_copy ^ same);
279  	    }
280  	
281  	    fsa_dump_actions(controld_globals.fsa_actions, "Remaining");
282  	    pcmk__if_tracing(
283  	        {
284  	            unsigned int offset = 0;
285  	
286  	            g_queue_foreach(controld_globals.fsa_message_queue, log_fsa_data,
287  	                            &offset);
288  	        },
289  	        {}
290  	    );
291  	
292  	    return globals->fsa_state;
293  	}
294  	
295  	void
296  	s_crmd_fsa_actions(fsa_data_t * fsa_data)
297  	{
298  	    /*
299  	     * Process actions in order of priority but do only one
300  	     * action at a time to avoid complicating the ordering.
301  	     */
302  	    CRM_CHECK(fsa_data != NULL, return);
303  	    while ((controld_globals.fsa_actions != A_NOTHING)
304  	           && !pcmk__is_set(controld_globals.flags, controld_fsa_is_stalled)) {
305  	
306  	        /* regular action processing in order of action priority
307  	         *
308  	         * Make sure all actions that connect to required systems
309  	         * are performed first
310  	         */
311  	        if (pcmk__is_set(controld_globals.fsa_actions, A_ERROR)) {
312  	            do_fsa_action(fsa_data, A_ERROR, do_log);
313  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_WARN)) {
314  	            do_fsa_action(fsa_data, A_WARN, do_log);
315  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_LOG)) {
316  	            do_fsa_action(fsa_data, A_LOG, do_log);
317  	
318  	            /* get out of here NOW! before anything worse happens */
319  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_EXIT_1)) {
320  	            do_fsa_action(fsa_data, A_EXIT_1, do_exit);
321  	
322  	            /* sub-system restart */
323  	        } else if (pcmk__all_flags_set(controld_globals.fsa_actions,
324  	                                       O_LRM_RECONNECT)) {
325  	            do_fsa_action(fsa_data, O_LRM_RECONNECT, do_lrm_control);
326  	
327  	        } else if (pcmk__all_flags_set(controld_globals.fsa_actions,
328  	                                       O_CIB_RESTART)) {
329  	            do_fsa_action(fsa_data, O_CIB_RESTART, do_cib_control);
330  	
331  	        } else if (pcmk__all_flags_set(controld_globals.fsa_actions,
332  	                                       O_PE_RESTART)) {
333  	            do_fsa_action(fsa_data, O_PE_RESTART, do_pe_control);
334  	
335  	        } else if (pcmk__all_flags_set(controld_globals.fsa_actions,
336  	                                       O_TE_RESTART)) {
337  	            do_fsa_action(fsa_data, O_TE_RESTART, do_te_control);
338  	
339  	            /* essential start tasks */
340  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_STARTUP)) {
341  	            do_fsa_action(fsa_data, A_STARTUP, do_startup);
342  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_CIB_START)) {
343  	            do_fsa_action(fsa_data, A_CIB_START, do_cib_control);
344  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_HA_CONNECT)) {
345  	            do_fsa_action(fsa_data, A_HA_CONNECT, do_ha_control);
346  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_READCONFIG)) {
347  	            do_fsa_action(fsa_data, A_READCONFIG, do_read_config);
348  	
349  	            /* sub-system start/connect */
350  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_LRM_CONNECT)) {
351  	            do_fsa_action(fsa_data, A_LRM_CONNECT, do_lrm_control);
352  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_TE_START)) {
353  	            do_fsa_action(fsa_data, A_TE_START, do_te_control);
354  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_PE_START)) {
355  	            do_fsa_action(fsa_data, A_PE_START, do_pe_control);
356  	
357  	            /* Timers */
358  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
359  	                                A_DC_TIMER_STOP)) {
360  	            do_fsa_action(fsa_data, A_DC_TIMER_STOP, do_timer_control);
361  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
362  	                                A_INTEGRATE_TIMER_STOP)) {
363  	            do_fsa_action(fsa_data, A_INTEGRATE_TIMER_STOP, do_timer_control);
364  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
365  	                                A_INTEGRATE_TIMER_START)) {
366  	            do_fsa_action(fsa_data, A_INTEGRATE_TIMER_START, do_timer_control);
367  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
368  	                                A_FINALIZE_TIMER_STOP)) {
369  	            do_fsa_action(fsa_data, A_FINALIZE_TIMER_STOP, do_timer_control);
370  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
371  	                                A_FINALIZE_TIMER_START)) {
372  	            do_fsa_action(fsa_data, A_FINALIZE_TIMER_START, do_timer_control);
373  	
374  	            /*
375  	             * Highest priority actions
376  	             */
377  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_MSG_ROUTE)) {
378  	            do_fsa_action(fsa_data, A_MSG_ROUTE, do_msg_route);
379  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_RECOVER)) {
380  	            do_fsa_action(fsa_data, A_RECOVER, do_recover);
381  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
382  	                                A_CL_JOIN_RESULT)) {
383  	            do_fsa_action(fsa_data, A_CL_JOIN_RESULT,
384  	                          do_cl_join_finalize_respond);
385  	
386  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
387  	                                A_CL_JOIN_REQUEST)) {
388  	            do_fsa_action(fsa_data, A_CL_JOIN_REQUEST,
389  	                          do_cl_join_offer_respond);
390  	
391  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_SHUTDOWN_REQ)) {
392  	            do_fsa_action(fsa_data, A_SHUTDOWN_REQ, do_shutdown_req);
393  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
394  	                                A_ELECTION_VOTE)) {
395  	            do_fsa_action(fsa_data, A_ELECTION_VOTE, do_election_vote);
396  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
397  	                                A_ELECTION_COUNT)) {
398  	            do_fsa_action(fsa_data, A_ELECTION_COUNT, do_election_count_vote);
399  	
400  	            /*
401  	             * High priority actions
402  	             */
403  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_STARTED)) {
404  	            do_fsa_action(fsa_data, A_STARTED, do_started);
405  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
406  	                                A_CL_JOIN_QUERY)) {
407  	            do_fsa_action(fsa_data, A_CL_JOIN_QUERY, do_cl_join_query);
408  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
409  	                                A_DC_TIMER_START)) {
410  	            do_fsa_action(fsa_data, A_DC_TIMER_START, do_timer_control);
411  	
412  	            /*
413  	             * Medium priority actions
414  	             * - Membership
415  	             */
416  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_DC_TAKEOVER)) {
417  	            do_fsa_action(fsa_data, A_DC_TAKEOVER, do_dc_takeover);
418  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_DC_RELEASE)) {
419  	            do_fsa_action(fsa_data, A_DC_RELEASE, do_dc_release);
420  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
421  	                                A_DC_JOIN_FINAL)) {
422  	            do_fsa_action(fsa_data, A_DC_JOIN_FINAL, do_dc_join_final);
423  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
424  	                                A_ELECTION_CHECK)) {
425  	            do_fsa_action(fsa_data, A_ELECTION_CHECK, do_election_check);
426  	
427  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
428  	                                A_ELECTION_START)) {
429  	            do_fsa_action(fsa_data, A_ELECTION_START, do_election_vote);
430  	
431  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
432  	                                A_DC_JOIN_OFFER_ALL)) {
433  	            do_fsa_action(fsa_data, A_DC_JOIN_OFFER_ALL, do_dc_join_offer_all);
434  	
435  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
436  	                                A_DC_JOIN_OFFER_ONE)) {
437  	            do_fsa_action(fsa_data, A_DC_JOIN_OFFER_ONE, do_dc_join_offer_one);
438  	
439  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
440  	                                A_DC_JOIN_PROCESS_REQ)) {
441  	            do_fsa_action(fsa_data, A_DC_JOIN_PROCESS_REQ,
442  	                          do_dc_join_filter_offer);
443  	
444  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
445  	                                A_DC_JOIN_PROCESS_ACK)) {
446  	            do_fsa_action(fsa_data, A_DC_JOIN_PROCESS_ACK, do_dc_join_ack);
447  	
448  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
449  	                                A_DC_JOIN_FINALIZE)) {
450  	            do_fsa_action(fsa_data, A_DC_JOIN_FINALIZE, do_dc_join_finalize);
451  	
452  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
453  	                                A_CL_JOIN_ANNOUNCE)) {
454  	            do_fsa_action(fsa_data, A_CL_JOIN_ANNOUNCE, do_cl_join_announce);
455  	
456  	            /*
457  	             * Low(er) priority actions
458  	             * Make sure the CIB is always updated before invoking the
459  	             * scheduler, and the scheduler before the transition engine.
460  	             */
461  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_TE_HALT)) {
462  	            do_fsa_action(fsa_data, A_TE_HALT, do_te_invoke);
463  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_TE_CANCEL)) {
464  	            do_fsa_action(fsa_data, A_TE_CANCEL, do_te_invoke);
465  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_PE_INVOKE)) {
466  	            do_fsa_action(fsa_data, A_PE_INVOKE, do_pe_invoke);
467  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_TE_INVOKE)) {
468  	            do_fsa_action(fsa_data, A_TE_INVOKE, do_te_invoke);
469  	
470  	            /* Shutdown actions */
471  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_DC_RELEASED)) {
472  	            do_fsa_action(fsa_data, A_DC_RELEASED, do_dc_release);
473  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_PE_STOP)) {
474  	            do_fsa_action(fsa_data, A_PE_STOP, do_pe_control);
475  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_TE_STOP)) {
476  	            do_fsa_action(fsa_data, A_TE_STOP, do_te_control);
477  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_SHUTDOWN)) {
478  	            do_fsa_action(fsa_data, A_SHUTDOWN, do_shutdown);
479  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
480  	                               A_LRM_DISCONNECT)) {
481  	            do_fsa_action(fsa_data, A_LRM_DISCONNECT, do_lrm_control);
482  	
483  	        } else if (pcmk__is_set(controld_globals.fsa_actions,
484  	                                A_HA_DISCONNECT)) {
485  	            do_fsa_action(fsa_data, A_HA_DISCONNECT, do_ha_control);
486  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_CIB_STOP)) {
487  	            do_fsa_action(fsa_data, A_CIB_STOP, do_cib_control);
488  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_STOP)) {
489  	            do_fsa_action(fsa_data, A_STOP, do_stop);
490  	
491  	            /* exit gracefully */
492  	        } else if (pcmk__is_set(controld_globals.fsa_actions, A_EXIT_0)) {
493  	            do_fsa_action(fsa_data, A_EXIT_0, do_exit);
494  	
495  	            /* Error checking and reporting */
496  	        } else {
497  	            pcmk__err("Action %s not supported " QB_XS " %" PRIx64,
498  	                      fsa_action2string(controld_globals.fsa_actions),
499  	                      controld_globals.fsa_actions);
500  	            register_fsa_error(I_ERROR, fsa_data);
501  	        }
502  	    }
503  	}
504  	
505  	static void
506  	check_join_counts(fsa_data_t *msg_data)
507  	{
508  	    int count;
509  	    guint npeers;
510  	
511  	    count = crmd_join_phase_count(controld_join_finalized);
512  	    if (count > 0) {
513  	        pcmk__err("%d cluster node%s failed to confirm join", count,
514  	                  pcmk__plural_s(count));
515  	        crmd_join_phase_log(LOG_NOTICE);
516  	        return;
517  	    }
518  	
519  	    npeers = pcmk__cluster_num_active_nodes();
520  	    count = crmd_join_phase_count(controld_join_confirmed);
521  	    if (count == npeers) {
522  	        if (npeers == 1) {
523  	            pcmk__debug("Sole active cluster node is fully joined");
524  	        } else {
525  	            pcmk__debug("All %d active cluster nodes are fully joined", count);
526  	        }
527  	
528  	    } else if (count > npeers) {
529  	        pcmk__err("New election needed because more nodes confirmed join "
530  	                  "than are in membership (%d > %u)", count, npeers);
531  	        controld_fsa_append(C_FSA_INTERNAL, I_ELECTION, NULL);
532  	
533  	    } else if (controld_globals.membership_id != controld_globals.peer_seq) {
534  	        pcmk__info("New join needed because membership changed (%llu -> %llu)",
535  	                   controld_globals.membership_id, controld_globals.peer_seq);
536  	        controld_fsa_prepend(C_FSA_INTERNAL, I_NODE_JOIN, NULL);
537  	
538  	    } else {
539  	        pcmk__warn("Only %d of %u active cluster nodes fully joined (%d did "
540  	                   "not respond to offer)",
541  	                   count, npeers,
542  	                   crmd_join_phase_count(controld_join_welcomed));
543  	    }
544  	}
545  	
546  	static void
547  	do_state_transition(enum crmd_fsa_state cur_state,
548  	                    enum crmd_fsa_state next_state, fsa_data_t *msg_data)
549  	{
550  	    int level = LOG_INFO;
551  	    int count = 0;
552  	    gboolean clear_recovery_bit = TRUE;
553  	#if 0
554  	    uint64_t original_fsa_actions = controld_globals.fsa_actions;
555  	#endif
556  	
557  	    enum crmd_fsa_cause cause = msg_data->fsa_cause;
558  	    enum crmd_fsa_input current_input = msg_data->fsa_input;
559  	
560  	    const char *state_from = fsa_state2string(cur_state);
561  	    const char *state_to = fsa_state2string(next_state);
562  	    const char *input = fsa_input2string(current_input);
563  	
564  	    CRM_LOG_ASSERT(cur_state != next_state);
565  	
566  	    if (cur_state == S_IDLE || next_state == S_IDLE) {
567  	        level = LOG_NOTICE;
568  	    } else if (cur_state == S_NOT_DC || next_state == S_NOT_DC) {
569  	        level = LOG_NOTICE;
570  	    } else if (cur_state == S_ELECTION) {
571  	        level = LOG_NOTICE;
572  	    } else if (cur_state == S_STARTING) {
573  	        level = LOG_NOTICE;
574  	    } else if (next_state == S_RECOVERY) {
575  	        level = LOG_WARNING;
576  	    }
577  	
578  	    do_crm_log(level, "State transition %s -> %s "
579  	               QB_XS " input=%s cause=%s origin=%s",
580  	               state_from, state_to, input, fsa_cause2string(cause),
581  	               msg_data->origin);
582  	
583  	    if (next_state != S_ELECTION && cur_state != S_RELEASE_DC) {
584  	        controld_stop_current_election_timeout();
585  	    }
586  	    if (next_state == S_INTEGRATION) {
587  	        controld_set_fsa_action_flags(A_INTEGRATE_TIMER_START);
588  	    } else {
589  	        controld_set_fsa_action_flags(A_INTEGRATE_TIMER_STOP);
590  	    }
591  	
592  	    if (next_state == S_FINALIZE_JOIN) {
593  	        controld_set_fsa_action_flags(A_FINALIZE_TIMER_START);
594  	    } else {
595  	        controld_set_fsa_action_flags(A_FINALIZE_TIMER_STOP);
596  	    }
597  	
598  	    if (next_state != S_PENDING) {
599  	        controld_set_fsa_action_flags(A_DC_TIMER_STOP);
600  	    }
601  	    if (next_state != S_IDLE) {
602  	        controld_stop_recheck_timer();
603  	    }
604  	
605  	    if (cur_state == S_FINALIZE_JOIN && next_state == S_POLICY_ENGINE) {
606  	        populate_cib_nodes(controld_node_update_quick|controld_node_update_all,
607  	                           __func__);
608  	    }
609  	
610  	    switch (next_state) {
611  	        case S_PENDING:
612  	            {
613  	                cib_t *cib_conn = controld_globals.cib_conn;
614  	                cib_conn->cmds->set_secondary(cib_conn, cib_none);
615  	            }
616  	            update_dc(NULL);
617  	            break;
618  	
619  	        case S_ELECTION:
620  	            update_dc(NULL);
621  	            break;
622  	
623  	        case S_NOT_DC:
624  	            controld_reset_counter_election_timer();
625  	            controld_purge_fencing_cleanup();
626  	
627  	            if (pcmk__is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
628  	                pcmk__info("(Re)Issuing shutdown request now that we have a "
629  	                           "new DC");
630  	                controld_set_fsa_action_flags(A_SHUTDOWN_REQ);
631  	            }
632  	            CRM_LOG_ASSERT(controld_globals.dc_name != NULL);
633  	            if (controld_globals.dc_name == NULL) {
634  	                pcmk__err("Reached S_NOT_DC without a DC being recorded");
635  	            }
636  	            break;
637  	
638  	        case S_RECOVERY:
639  	            clear_recovery_bit = FALSE;
640  	            break;
641  	
642  	        case S_FINALIZE_JOIN:
643  	            CRM_LOG_ASSERT(AM_I_DC);
644  	            if (cause == C_TIMER_POPPED) {
645  	                pcmk__warn("Progressed to state %s after %s",
646  	                           fsa_state2string(next_state),
647  	                           fsa_cause2string(cause));
648  	            }
649  	            count = crmd_join_phase_count(controld_join_welcomed);
650  	            if (count > 0) {
651  	                pcmk__warn("%d cluster node%s failed to respond to join offer",
652  	                           count, pcmk__plural_s(count));
653  	                crmd_join_phase_log(LOG_NOTICE);
654  	
655  	            } else {
656  	                pcmk__debug("All cluster nodes (%d) responded to join offer",
657  	                            crmd_join_phase_count(controld_join_integrated));
658  	            }
659  	            break;
660  	
661  	        case S_POLICY_ENGINE:
662  	            controld_reset_counter_election_timer();
663  	            CRM_LOG_ASSERT(AM_I_DC);
664  	            if (cause == C_TIMER_POPPED) {
665  	                pcmk__info("Progressed to state %s after %s",
666  	                           fsa_state2string(next_state),
667  	                           fsa_cause2string(cause));
668  	            }
669  	            check_join_counts(msg_data);
670  	            break;
671  	
672  	        case S_STOPPING:
673  	        case S_TERMINATE:
674  	            /* possibly redundant */
675  	            controld_set_fsa_input_flags(R_SHUTDOWN);
676  	            break;
677  	
678  	        case S_IDLE:
679  	            CRM_LOG_ASSERT(AM_I_DC);
680  	            if (pcmk__is_set(controld_globals.fsa_input_register, R_SHUTDOWN)) {
681  	                pcmk__info("(Re)Issuing shutdown request now that we are the "
682  	                           "DC");
683  	                controld_set_fsa_action_flags(A_SHUTDOWN_REQ);
684  	            }
685  	            controld_start_recheck_timer();
686  	            break;
687  	
688  	        default:
689  	            break;
690  	    }
691  	
692  	    if (clear_recovery_bit && next_state != S_PENDING) {
693  	        controld_clear_fsa_action_flags(A_RECOVER);
694  	    } else if (clear_recovery_bit == FALSE) {
695  	        controld_set_fsa_action_flags(A_RECOVER);
696  	    }
697  	
698  	#if 0
699  	    if (original_fsa_actions != controld_globals.fsa_actions) {
700  	        fsa_dump_actions(original_fsa_actions ^ controld_globals.fsa_actions,
701  	                         "New actions");
702  	    }
703  	#endif
704  	}
705