root/daemons/schedulerd/schedulerd_messages.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. init_scheduler
  2. handle_pecalc_request
  3. handle_unknown_request
  4. handle_hello_request
  5. schedulerd_register_handlers
  6. pe_ipc_accept
  7. pe_ipc_dispatch
  8. pe_ipc_closed
  9. pe_ipc_destroy

   1 /*
   2  * Copyright 2004-2025 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 <crm/crm.h>
  13 #include <crm/common/xml.h>
  14 #include <pacemaker-internal.h>
  15 
  16 #include <stdbool.h>
  17 #include <sys/stat.h>
  18 #include <sys/types.h>
  19 #include <unistd.h>
  20 
  21 #include "pacemaker-schedulerd.h"
  22 
  23 static GHashTable *schedulerd_handlers = NULL;
  24 
  25 static pcmk_scheduler_t *
  26 init_scheduler(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  27 {
  28     pcmk_scheduler_t *scheduler = pcmk_new_scheduler();
  29 
  30     pcmk__mem_assert(scheduler);
  31     scheduler->priv->out = logger_out;
  32     return scheduler;
  33 }
  34 
  35 static xmlNode *
  36 handle_pecalc_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     static struct series_s {
  39         const char *name;
  40         const char *param;
  41 
  42         /* Maximum number of inputs of this kind to save to disk.
  43          * If -1, save all; if 0, save none.
  44          */
  45         int wrap;
  46     } series[] = {
  47         { "pe-error", PCMK_OPT_PE_ERROR_SERIES_MAX, -1 },
  48         { "pe-warn",  PCMK_OPT_PE_WARN_SERIES_MAX, 5000 },
  49         { "pe-input", PCMK_OPT_PE_INPUT_SERIES_MAX, 4000 },
  50     };
  51 
  52     xmlNode *msg = request->xml;
  53     xmlNode *wrapper = pcmk__xe_first_child(msg, PCMK__XE_CRM_XML, NULL, NULL);
  54     xmlNode *xml_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
  55 
  56     static char *last_digest = NULL;
  57     static char *filename = NULL;
  58 
  59     unsigned int seq = 0U;
  60     int series_id = 0;
  61     int series_wrap = 0;
  62     char *digest = NULL;
  63     const char *value = NULL;
  64     time_t execution_date = time(NULL);
  65     xmlNode *converted = NULL;
  66     xmlNode *reply = NULL;
  67     bool is_repoke = false;
  68     bool process = true;
  69     pcmk_scheduler_t *scheduler = init_scheduler();
  70 
  71     pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
  72                        PCMK__XE_ACK, NULL, CRM_EX_INDETERMINATE);
  73 
  74     digest = pcmk__digest_xml(xml_data, false);
  75     converted = pcmk__xml_copy(NULL, xml_data);
  76     if (pcmk__update_configured_schema(&converted, true) != pcmk_rc_ok) {
  77         scheduler->priv->graph = pcmk__xe_create(NULL,
  78                                                  PCMK__XE_TRANSITION_GRAPH);
  79         crm_xml_add_int(scheduler->priv->graph, "transition_id", 0);
  80         crm_xml_add_int(scheduler->priv->graph, PCMK_OPT_CLUSTER_DELAY, 0);
  81         process = false;
  82         free(digest);
  83 
  84     } else if (pcmk__str_eq(digest, last_digest, pcmk__str_casei)) {
  85         is_repoke = true;
  86         free(digest);
  87 
  88     } else {
  89         free(last_digest);
  90         last_digest = digest;
  91     }
  92 
  93     if (process) {
  94         scheduler->input = converted;
  95         pcmk__set_scheduler_flags(scheduler,
  96                                   pcmk__sched_no_counts
  97                                   |pcmk__sched_show_utilization);
  98         pcmk__schedule_actions(scheduler);
  99 
 100         // Don't free converted as part of scheduler
 101         scheduler->input = NULL;
 102     }
 103 
 104     // Get appropriate index into series[] array
 105     if (pcmk_is_set(scheduler->flags, pcmk__sched_processing_error)
 106         || pcmk__config_has_error) {
 107         series_id = 0;
 108     } else if (pcmk_is_set(scheduler->flags, pcmk__sched_processing_warning)
 109                || pcmk__config_has_warning) {
 110         series_id = 1;
 111     } else {
 112         series_id = 2;
 113     }
 114 
 115     value = pcmk__cluster_option(scheduler->priv->options,
 116                                  series[series_id].param);
 117     if ((value == NULL)
 118         || (pcmk__scan_min_int(value, &series_wrap, -1) != pcmk_rc_ok)) {
 119         series_wrap = series[series_id].wrap;
 120     }
 121 
 122     if (pcmk__read_series_sequence(PCMK_SCHEDULER_INPUT_DIR, series[series_id].name,
 123                                    &seq) != pcmk_rc_ok) {
 124         // @TODO maybe handle errors better ...
 125         seq = 0U;
 126     }
 127     crm_trace("Series %s: wrap=%d, seq=%u, pref=%s",
 128               series[series_id].name, series_wrap, seq, value);
 129 
 130     reply = pcmk__new_reply(msg, scheduler->priv->graph);
 131 
 132     if (reply == NULL) {
 133         pcmk__format_result(&request->result, CRM_EX_ERROR, PCMK_EXEC_ERROR,
 134                             "Failed building ping reply for client %s",
 135                             pcmk__client_name(request->ipc_client));
 136         goto done;
 137     }
 138 
 139     if (series_wrap == 0) { // Don't save any inputs of this kind
 140         free(filename);
 141         filename = NULL;
 142 
 143     } else if (!is_repoke) { // Input changed, save to disk
 144         free(filename);
 145         filename = pcmk__series_filename(PCMK_SCHEDULER_INPUT_DIR,
 146                                          series[series_id].name, seq, true);
 147     }
 148 
 149     crm_xml_add(reply, PCMK__XA_CRM_TGRAPH_IN, filename);
 150 
 151     pcmk__log_transition_summary(scheduler, filename);
 152 
 153     if (series_wrap == 0) {
 154         crm_debug("Not saving input to disk (disabled by configuration)");
 155 
 156     } else if (is_repoke) {
 157         crm_info("Input has not changed since last time, not saving to disk");
 158 
 159     } else {
 160         unlink(filename);
 161         crm_xml_add_ll(xml_data, PCMK_XA_EXECUTION_DATE,
 162                        (long long) execution_date);
 163         pcmk__xml_write_file(xml_data, filename, true);
 164         pcmk__write_series_sequence(PCMK_SCHEDULER_INPUT_DIR, series[series_id].name,
 165                                     ++seq, series_wrap);
 166     }
 167 
 168     pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 169 
 170 done:
 171     pcmk__xml_free(converted);
 172     pcmk_free_scheduler(scheduler);
 173 
 174     return reply;
 175 }
 176 
 177 static xmlNode *
 178 handle_unknown_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 179 {
 180     pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
 181                        PCMK__XE_ACK, NULL, CRM_EX_INVALID_PARAM);
 182 
 183     pcmk__format_result(&request->result, CRM_EX_PROTOCOL, PCMK_EXEC_INVALID,
 184                         "Unknown IPC request type '%s' (bug?)",
 185                         pcmk__client_name(request->ipc_client));
 186     return NULL;
 187 }
 188 
 189 static xmlNode *
 190 handle_hello_request(pcmk__request_t *request)
     /* [previous][next][first][last][top][bottom][index][help] */
 191 {
 192     pcmk__ipc_send_ack(request->ipc_client, request->ipc_id, request->ipc_flags,
 193                        PCMK__XE_ACK, NULL, CRM_EX_INDETERMINATE);
 194 
 195     crm_trace("Received IPC hello from %s", pcmk__client_name(request->ipc_client));
 196 
 197     pcmk__set_result(&request->result, CRM_EX_OK, PCMK_EXEC_DONE, NULL);
 198     return NULL;
 199 }
 200 
 201 static void
 202 schedulerd_register_handlers(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204     pcmk__server_command_t handlers[] = {
 205         { CRM_OP_HELLO, handle_hello_request },
 206         { CRM_OP_PECALC, handle_pecalc_request },
 207         { NULL, handle_unknown_request },
 208     };
 209 
 210     schedulerd_handlers = pcmk__register_handlers(handlers);
 211 }
 212 
 213 static int32_t
 214 pe_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216     crm_trace("Connection %p", c);
 217     if (pcmk__new_client(c, uid, gid) == NULL) {
 218         return -ENOMEM;
 219     }
 220     return 0;
 221 }
 222 
 223 static int32_t
 224 pe_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226     int rc = pcmk_rc_ok;
 227     uint32_t id = 0;
 228     uint32_t flags = 0;
 229     xmlNode *msg = NULL;
 230     pcmk__client_t *c = pcmk__find_client(qbc);
 231     const char *sys_to = NULL;
 232 
 233     CRM_CHECK(c != NULL, return 0);
 234 
 235     if (schedulerd_handlers == NULL) {
 236         schedulerd_register_handlers();
 237     }
 238 
 239     rc = pcmk__ipc_msg_append(&c->buffer, data);
 240 
 241     if (rc == pcmk_rc_ipc_more) {
 242         /* We haven't read the complete message yet, so just return. */
 243         return 0;
 244 
 245     } else if (rc == pcmk_rc_ok) {
 246         /* We've read the complete message and there's already a header on
 247          * the front.  Pass it off for processing.
 248          */
 249         msg = pcmk__client_data2xml(c, &id, &flags);
 250         g_byte_array_free(c->buffer, TRUE);
 251         c->buffer = NULL;
 252 
 253     } else {
 254         /* Some sort of error occurred reassembling the message.  All we can
 255          * do is clean up, log an error and return.
 256          */
 257         crm_err("Error when reading IPC message: %s", pcmk_rc_str(rc));
 258 
 259         if (c->buffer != NULL) {
 260             g_byte_array_free(c->buffer, TRUE);
 261             c->buffer = NULL;
 262         }
 263 
 264         return 0;
 265     }
 266 
 267     if (msg == NULL) {
 268         pcmk__ipc_send_ack(c, id, flags, PCMK__XE_ACK, NULL, CRM_EX_PROTOCOL);
 269         return 0;
 270     }
 271 
 272     sys_to = crm_element_value(msg, PCMK__XA_CRM_SYS_TO);
 273 
 274     if (pcmk__str_eq(crm_element_value(msg, PCMK__XA_SUBT),
 275                      PCMK__VALUE_RESPONSE, pcmk__str_none)) {
 276         pcmk__ipc_send_ack(c, id, flags, PCMK__XE_ACK, NULL,
 277                            CRM_EX_INDETERMINATE);
 278         crm_info("Ignoring IPC reply from %s", pcmk__client_name(c));
 279 
 280     } else if (!pcmk__str_eq(sys_to, CRM_SYSTEM_PENGINE, pcmk__str_none)) {
 281         pcmk__ipc_send_ack(c, id, flags, PCMK__XE_ACK, NULL,
 282                            CRM_EX_INDETERMINATE);
 283         crm_info("Ignoring invalid IPC message: to '%s' not "
 284                  CRM_SYSTEM_PENGINE, pcmk__s(sys_to, ""));
 285 
 286     } else {
 287         char *log_msg = NULL;
 288         const char *reason = NULL;
 289         xmlNode *reply = NULL;
 290 
 291         pcmk__request_t request = {
 292             .ipc_client     = c,
 293             .ipc_id         = id,
 294             .ipc_flags      = flags,
 295             .peer           = NULL,
 296             .xml            = msg,
 297             .call_options   = 0,
 298             .result         = PCMK__UNKNOWN_RESULT,
 299         };
 300 
 301         request.op = crm_element_value_copy(request.xml, PCMK__XA_CRM_TASK);
 302         CRM_CHECK(request.op != NULL, return 0);
 303 
 304         reply = pcmk__process_request(&request, schedulerd_handlers);
 305 
 306         if (reply != NULL) {
 307             pcmk__ipc_send_xml(c, id, reply, crm_ipc_server_event);
 308             pcmk__xml_free(reply);
 309         }
 310 
 311         reason = request.result.exit_reason;
 312 
 313         log_msg = crm_strdup_printf("Processed %s request from %s %s: %s%s%s%s",
 314                                     request.op, pcmk__request_origin_type(&request),
 315                                     pcmk__request_origin(&request),
 316                                     pcmk_exec_status_str(request.result.execution_status),
 317                                     (reason == NULL)? "" : " (",
 318                                     (reason == NULL)? "" : reason,
 319                                     (reason == NULL)? "" : ")");
 320 
 321         if (!pcmk__result_ok(&request.result)) {
 322             crm_warn("%s", log_msg);
 323         } else {
 324             crm_debug("%s", log_msg);
 325         }
 326 
 327         free(log_msg);
 328         pcmk__reset_request(&request);
 329     }
 330 
 331     pcmk__xml_free(msg);
 332     return 0;
 333 }
 334 
 335 /* Error code means? */
 336 static int32_t
 337 pe_ipc_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 338 {
 339     pcmk__client_t *client = pcmk__find_client(c);
 340 
 341     if (client == NULL) {
 342         return 0;
 343     }
 344     crm_trace("Connection %p", c);
 345     pcmk__free_client(client);
 346     return 0;
 347 }
 348 
 349 static void
 350 pe_ipc_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 351 {
 352     crm_trace("Connection %p", c);
 353     pe_ipc_closed(c);
 354 }
 355 
 356 struct qb_ipcs_service_handlers ipc_callbacks = {
 357     .connection_accept = pe_ipc_accept,
 358     .connection_created = NULL,
 359     .msg_process = pe_ipc_dispatch,
 360     .connection_closed = pe_ipc_closed,
 361     .connection_destroyed = pe_ipc_destroy
 362 };

/* [previous][next][first][last][top][bottom][index][help] */