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   	
14   	#include <crm/crm.h>
15   	#include <crm/common/xml.h>
16   	
17   	#include <pacemaker-controld.h>
18   	
19   	static pcmk__graph_t *
20   	create_blank_graph(void)
21   	{
22   	    pcmk__graph_t *graph = pcmk__unpack_graph(NULL, NULL);
23   	
24   	    graph->complete = true;
25   	    graph->abort_reason = "DC Takeover";
26   	    graph->completion_action = pcmk__graph_restart;
27   	    return graph;
28   	}
29   	
30   	// A_TE_START, A_TE_STOP, O_TE_RESTART
31   	void
32   	do_te_control(long long action, enum crmd_fsa_cause cause,
33   	              enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
34   	              fsa_data_t *msg_data)
35   	{
36   	    cib_t *cib_conn = controld_globals.cib_conn;
37   	
(1) Event path: Condition "pcmk__is_set(action, 70368744177664UL /* 1UL << 46 */)", taking true branch.
38   	    if (pcmk__is_set(action, A_TE_STOP)) {
CID (unavailable; MK=3f10c91452afa4a18b2b7ec68f5203ee) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(2) Event assign_union_field: The union field "in" of "_pp" is written.
(3) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
39   	        g_clear_pointer(&controld_globals.transition_graph, pcmk__free_graph);
40   	
41   	        if (cib_conn != NULL) {
42   	            cib_conn->cmds->del_notify_callback(cib_conn,
43   	                                                PCMK__VALUE_CIB_DIFF_NOTIFY,
44   	                                                te_update_diff);
45   	        }
46   	
47   	        controld_clear_fsa_input_flags(R_TE_CONNECTED);
48   	        pcmk__info("Transitioner is now inactive");
49   	    }
50   	
51   	    if (!pcmk__is_set(action, A_TE_START)) {
52   	        return;
53   	    }
54   	
55   	    if (pcmk__is_set(controld_globals.fsa_input_register, R_TE_CONNECTED)) {
56   	        pcmk__debug("The transitioner is already active");
57   	        return;
58   	    }
59   	
60   	    if (cur_state == S_STOPPING) {
61   	        pcmk__info("Ignoring request to start the transitioner while shutting "
62   	                   "down");
63   	        return;
64   	    }
65   	
66   	    if ((cib_conn == NULL)
67   	        || (cib_conn->cmds->add_notify_callback(cib_conn,
68   	                                                PCMK__VALUE_CIB_DIFF_NOTIFY,
69   	                                                te_update_diff) != pcmk_ok)) {
70   	        pcmk__err("Could not set CIB notification callback");
71   	        return;
72   	    }
73   	
74   	    if (controld_globals.te_uuid == NULL) {
75   	        controld_globals.te_uuid = pcmk__generate_uuid();
76   	        pcmk__info("Registering TE UUID: %s", controld_globals.te_uuid);
77   	    }
78   	
79   	    controld_register_graph_functions();
80   	    pcmk__free_graph(controld_globals.transition_graph);
81   	    controld_globals.transition_graph = create_blank_graph();
82   	    controld_set_fsa_input_flags(R_TE_CONNECTED);
83   	    pcmk__debug("Transitioner is now active");
84   	}
85   	
86   	// A_TE_INVOKE, A_TE_CANCEL
87   	void
88   	do_te_invoke(long long action, enum crmd_fsa_cause cause,
89   	             enum crmd_fsa_state cur_state, enum crmd_fsa_input current_input,
90   	             fsa_data_t *msg_data)
91   	{
92   	    ha_msg_input_t *input = NULL;
93   	    xmlNode *graph_data = NULL;
94   	    const char *ref = NULL;
95   	    const char *graph_input = NULL;
96   	
97   	    if (!AM_I_DC) {
98   	        pcmk__notice("Not invoking the TE because we are not the DC");
99   	        return;
100  	    }
101  	
102  	    if (pcmk__is_set(action, A_TE_INVOKE)
103  	        && (controld_globals.fsa_state != S_TRANSITION_ENGINE)) {
104  	
105  	        pcmk__notice("No need to invoke the TE (%s) while in state %s",
106  	                     fsa_action2string(action),
107  	                     fsa_state2string(controld_globals.fsa_state));
108  	        return;
109  	    }
110  	
111  	    if (pcmk__is_set(action, A_TE_CANCEL)) {
112  	        pcmk__debug("Cancelling the transition: %sactive",
113  	                    (controld_globals.transition_graph->complete? "in" : ""));
114  	
115  	        abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
116  	                         "Peer cancelled", NULL);
117  	        if (!controld_globals.transition_graph->complete) {
118  	            controld_fsa_stall(msg_data, action);
119  	        }
120  	        return;
121  	    }
122  	
123  	    if (pcmk__is_set(action, A_TE_HALT)) {
124  	        abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_wait, "Peer halt",
125  	                         NULL);
126  	        if (!controld_globals.transition_graph->complete) {
127  	            controld_fsa_stall(msg_data, action);
128  	        }
129  	        return;
130  	    }
131  	
132  	    if (!pcmk__is_set(action, A_TE_INVOKE)) {
133  	        return;
134  	    }
135  	
136  	    pcmk__assert((msg_data != NULL) && (msg_data->data != NULL));
137  	
138  	    input = msg_data->data;
139  	    graph_data = input->xml;
140  	    if (graph_data == NULL) {
141  	        pcmk__log_xml_err(input->msg, "Bad command");
142  	        register_fsa_error(I_FAIL, msg_data);
143  	        return;
144  	    }
145  	
146  	    if (!controld_globals.transition_graph->complete) {
147  	        pcmk__info("Another transition is already active");
148  	        abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
149  	                         "Transition active", NULL);
150  	        return;
151  	    }
152  	
153  	    ref = pcmk__xe_get(input->msg, PCMK_XA_REFERENCE);
154  	
155  	    if ((controld_globals.fsa_pe_ref == NULL)
156  	        || !pcmk__str_eq(controld_globals.fsa_pe_ref, ref,
157  	                         pcmk__str_none)) {
158  	        pcmk__info("Transition is redundant: %s expected but %s received",
159  	                   pcmk__s(controld_globals.fsa_pe_ref, "no reference"),
160  	                   pcmk__s(ref, "no reference"));
161  	        abort_transition(PCMK_SCORE_INFINITY, pcmk__graph_restart,
162  	                         "Transition redundant", NULL);
163  	    }
164  	
165  	    if (controld_is_started_transition_timer()) {
166  	        pcmk__debug("The transitioner wait for a transition timer");
167  	        return;
168  	    }
169  	
170  	    CRM_CHECK(graph_data != NULL,
171  	              pcmk__err("Input raised by %s is invalid", msg_data->origin);
172  	              pcmk__log_xml_err(input->msg, "Bad command");
173  	              return);
174  	
175  	    graph_input = pcmk__xe_get(input->msg, PCMK__XA_CRM_TGRAPH_IN);
176  	    pcmk__free_graph(controld_globals.transition_graph);
177  	    controld_globals.transition_graph = pcmk__unpack_graph(graph_data,
178  	                                                           graph_input);
179  	
180  	    CRM_CHECK(controld_globals.transition_graph != NULL,
181  	              controld_globals.transition_graph = create_blank_graph();
182  	              return);
183  	
184  	    pcmk__info("Processing graph %d (ref=%s) derived from %s",
185  	               controld_globals.transition_graph->id, ref, graph_input);
186  	
187  	    te_reset_job_counts();
188  	
189  	    trigger_graph();
190  	    pcmk__log_graph(LOG_TRACE, controld_globals.transition_graph);
191  	
192  	    if (graph_data != input->xml) {
193  	        pcmk__xml_free(graph_data);
194  	    }
195  	}
196