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> // true, false, bool
13 #include <stdlib.h> // NULL, free
14 #include <sys/types.h> // time_t
15 #include <time.h> // time
16 #include <unistd.h> // unlink
17
18 #include <glib.h> // g_hash_table_destroy
19 #include <libxml/tree.h> // xmlNode
20
21 #include <crm_config.h> // PCMK_SCHEDULER_INPUT_DIR
22 #include <crm/crm.h> // CRM_OP_HELLO, CRM_OP_PECALC
23 #include <crm/common/ipc.h> // crm_ipc_flags
24 #include <crm/common/options.h> // PCMK_OPT_CLUSTER_DELAY
25 #include <crm/common/results.h> // CRM_EX_*, pcmk_rc_ok, etc.
26 #include <crm/common/scheduler.h> // pcmk__scheduler, pcmk_free_scheduler
27 #include <crm/common/scheduler_types.h> // pcmk_scheduler_t
28 #include <crm/common/xml_names.h> // PCMK_XA_EXECUTION_DATE
29 #include <pacemaker-internal.h> // pcmk__schedule_actions
30
31 #include "pacemaker-schedulerd.h" // logger_out, schedulerd_*
32
33 static GHashTable *schedulerd_handlers = NULL;
34
35 static pcmk_scheduler_t *
36 init_scheduler(void)
37 {
38 pcmk_scheduler_t *scheduler = pcmk_new_scheduler();
39
40 pcmk__mem_assert(scheduler);
41 scheduler->priv->out = logger_out;
42 return scheduler;
43 }
44
45 static xmlNode *
46 handle_pecalc_request(pcmk__request_t *request)
47 {
48 static struct series_s {
49 const char *name;
50 const char *param;
51
52 /* Maximum number of inputs of this kind to save to disk.
53 * If -1, save all; if 0, save none.
54 */
55 int wrap;
56 } series[] = {
57 { "pe-error", PCMK_OPT_PE_ERROR_SERIES_MAX, -1 },
58 { "pe-warn", PCMK_OPT_PE_WARN_SERIES_MAX, 5000 },
59 { "pe-input", PCMK_OPT_PE_INPUT_SERIES_MAX, 4000 },
60 };
61
62 xmlNode *msg = request->xml;
63 xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL, NULL);
64 xmlNode *xml_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
65
66 static char *last_digest = NULL;
67 static char *filename = NULL;
68
69 unsigned int seq = 0U;
70 int series_id = 0;
71 int series_wrap = 0;
72 char *digest = NULL;
73 const char *value = NULL;
74 time_t execution_date = time(NULL);
75 xmlNode *converted = NULL;
76 xmlNode *reply = NULL;
77 bool is_repoke = false;
78 bool process = true;
79 pcmk_scheduler_t *scheduler = init_scheduler();
80
81 pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
82 NULL, CRM_EX_INDETERMINATE);
83
84 digest = pcmk__digest_xml(xml_data, false);
85 converted = pcmk__xml_copy(NULL, xml_data);
86 if (pcmk__update_configured_schema(&converted, true) != pcmk_rc_ok) {
87 scheduler->priv->graph = pcmk__xe_create(NULL,
88 PCMK__XE_TRANSITION_GRAPH);
89 pcmk__xe_set_int(scheduler->priv->graph, "transition_id", 0);
90 pcmk__xe_set_int(scheduler->priv->graph, PCMK_OPT_CLUSTER_DELAY, 0);
91 process = false;
92 free(digest);
93
94 } else if (pcmk__str_eq(digest, last_digest, pcmk__str_casei)) {
95 is_repoke = true;
96 free(digest);
97
98 } else {
99 free(last_digest);
100 last_digest = digest;
101 }
102
103 if (process) {
104 scheduler->input = converted;
105 pcmk__set_scheduler_flags(scheduler,
106 pcmk__sched_no_counts
107 |pcmk__sched_show_utilization);
108 pcmk__schedule_actions(scheduler);
109
110 // Don't free converted as part of scheduler
111 scheduler->input = NULL;
112 }
113
114 // Get appropriate index into series[] array
115 if (pcmk__is_set(scheduler->flags, pcmk__sched_processing_error)
116 || pcmk__config_has_error) {
117 series_id = 0;
118 } else if (pcmk__is_set(scheduler->flags, pcmk__sched_processing_warning)
119 || pcmk__config_has_warning) {
120 series_id = 1;
121 } else {
122 series_id = 2;
123 }
124
125 value = pcmk__cluster_option(scheduler->priv->options,
126 series[series_id].param);
127 if ((value == NULL)
128 || (pcmk__scan_min_int(value, &series_wrap, -1) != pcmk_rc_ok)) {
129 series_wrap = series[series_id].wrap;
130 }
131
132 if (pcmk__read_series_sequence(PCMK_SCHEDULER_INPUT_DIR, series[series_id].name,
133 &seq) != pcmk_rc_ok) {
134 // @TODO maybe handle errors better ...
135 seq = 0U;
136 }
137 pcmk__trace("Series %s: wrap=%d, seq=%u, pref=%s", series[series_id].name,
138 series_wrap, seq, value);
139
140 reply = pcmk__new_reply(msg, scheduler->priv->graph);
141
142 if (reply == NULL) {
143 pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
144 "Failed building ping reply for client %s",
145 pcmk__client_name(request->ipc_client));
146 goto done;
147 }
148
149 if (series_wrap == 0) { // Don't save any inputs of this kind
150 g_clear_pointer(&filename, free);
151
152 } else if (!is_repoke) { // Input changed, save to disk
153 free(filename);
154 filename = pcmk__series_filename(PCMK_SCHEDULER_INPUT_DIR,
155 series[series_id].name, seq, true);
156 }
157
158 pcmk__xe_set(reply, PCMK__XA_CRM_TGRAPH_IN, filename);
159
160 pcmk__log_transition_summary(scheduler, filename);
161
162 if (series_wrap == 0) {
163 pcmk__debug("Not saving input to disk (disabled by configuration)");
164
165 } else if (is_repoke) {
166 pcmk__info("Input has not changed since last time, not saving to disk");
167
168 } else {
169 unlink(filename);
170 pcmk__xe_set_time(xml_data, PCMK_XA_EXECUTION_DATE, execution_date);
171 pcmk__xml_write_file(xml_data, filename, true);
172 pcmk__write_series_sequence(PCMK_SCHEDULER_INPUT_DIR, series[series_id].name,
173 ++seq, series_wrap);
174 }
175
176 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
177
178 done:
179 pcmk__xml_free(converted);
180 pcmk_free_scheduler(scheduler);
181
182 return reply;
183 }
184
185 static xmlNode *
186 handle_unknown_request(pcmk__request_t *request)
187 {
188 pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
189 NULL, CRM_EX_PROTOCOL);
190
191 pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
192 "Unknown request type '%s' (bug?)",
193 pcmk__s(request->op, ""));
194 return NULL;
195 }
196
197 static xmlNode *
198 handle_hello_request(pcmk__request_t *request)
199 {
200 pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
201 NULL, CRM_EX_INDETERMINATE);
202
203 pcmk__trace("Received IPC hello from %s",
204 pcmk__client_name(request->ipc_client));
205
206 pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
207 return NULL;
208 }
209
210 static void
211 schedulerd_register_handlers(void)
212 {
213 pcmk__server_command_t handlers[] = {
214 { CRM_OP_HELLO, handle_hello_request },
215 { CRM_OP_PECALC, handle_pecalc_request },
216 { NULL, handle_unknown_request },
217 };
218
219 schedulerd_handlers = pcmk__register_handlers(handlers);
220 }
221
222 void
223 schedulerd_unregister_handlers(void)
224 {
|
CID (unavailable; MK=a46ec59c181f1e09d1a8663075e63264) (#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". |
225 g_clear_pointer(&schedulerd_handlers, g_hash_table_destroy);
226 }
227
228 void
229 schedulerd_handle_request(pcmk__request_t *request)
230 {
231 xmlNode *reply = NULL;
232 char *log_msg = NULL;
233 const char *exec_status_s = NULL;
234 const char *reason = NULL;
235
236 if (schedulerd_handlers == NULL) {
237 schedulerd_register_handlers();
238 }
239
240 reply = pcmk__process_request(request, schedulerd_handlers);
241
242 if (reply != NULL) {
243 pcmk__log_xml_trace(reply, "Reply");
244
245 pcmk__ipc_send_xml(request->ipc_client, request->ipc_id, reply,
246 crm_ipc_server_event);
247 pcmk__xml_free(reply);
248 }
249
250 exec_status_s = pcmk_exec_status_str(request->result.execution_status);
251 reason = request->result.exit_reason;
252
253 log_msg = pcmk__assert_asprintf("Processed %s request from %s %s: %s%s%s%s",
254 request->op,
255 pcmk__request_origin_type(request),
256 pcmk__request_origin(request),
257 exec_status_s,
258 (reason == NULL)? "" : " (",
259 pcmk__s(reason, ""),
260 (reason == NULL)? "" : ")");
261
262 if (!pcmk__result_ok(&request->result)) {
263 pcmk__warn("%s", log_msg);
264 } else {
265 pcmk__debug("%s", log_msg);
266 }
267
268 free(log_msg);
269 pcmk__reset_request(request);
270 }
271