root/daemons/schedulerd/pacemaker-schedulerd.c

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

DEFINITIONS

This source file includes following definitions.
  1. init_working_set
  2. handle_pecalc_op
  3. process_pe_message
  4. pe_ipc_accept
  5. pe_ipc_dispatch
  6. pe_ipc_closed
  7. pe_ipc_destroy
  8. main
  9. pengine_shutdown

   1 /*
   2  * Copyright 2004-2021 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 <stdio.h>
  14 #include <stdbool.h>
  15 #include <sys/stat.h>
  16 #include <sys/types.h>
  17 #include <unistd.h>
  18 
  19 #include <stdlib.h>
  20 #include <errno.h>
  21 #include <fcntl.h>
  22 
  23 #include <libxml/parser.h>
  24 
  25 #include <crm/common/ipc_internal.h>
  26 #include <crm/common/mainloop.h>
  27 #include <crm/pengine/internal.h>
  28 #include <pacemaker-internal.h>
  29 #include <crm/msg_xml.h>
  30 
  31 #define OPTARGS "hVc"
  32 
  33 static GMainLoop *mainloop = NULL;
  34 static qb_ipcs_service_t *ipcs = NULL;
  35 static pe_working_set_t *sched_data_set = NULL;
  36 static pcmk__output_t *out = NULL;
  37 
  38 pcmk__supported_format_t formats[] = {
  39     PCMK__SUPPORTED_FORMAT_LOG,
  40     PCMK__SUPPORTED_FORMAT_NONE,
  41     PCMK__SUPPORTED_FORMAT_TEXT,
  42     { NULL, NULL, NULL }
  43 };
  44 
  45 void pengine_shutdown(int nsig);
  46 
  47 static void
  48 init_working_set(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  49 {
  50     crm_config_error = FALSE;
  51     crm_config_warning = FALSE;
  52 
  53     was_processing_error = FALSE;
  54     was_processing_warning = FALSE;
  55 
  56     if (sched_data_set == NULL) {
  57         sched_data_set = pe_new_working_set();
  58         CRM_ASSERT(sched_data_set != NULL);
  59         pe__set_working_set_flags(sched_data_set,
  60                                   pe_flag_no_counts|pe_flag_no_compat);
  61         pe__set_working_set_flags(sched_data_set,
  62                                   pe_flag_show_utilization);
  63         sched_data_set->priv = out;
  64     } else {
  65         pe_reset_working_set(sched_data_set);
  66     }
  67 }
  68 
  69 static void
  70 handle_pecalc_op(xmlNode *msg, xmlNode *xml_data, pcmk__client_t *sender)
     /* [previous][next][first][last][top][bottom][index][help] */
  71 {
  72     static struct series_s {
  73         const char *name;
  74         const char *param;
  75 
  76         /* Maximum number of inputs of this kind to save to disk.
  77          * If -1, save all; if 0, save none.
  78          */
  79         int wrap;
  80     } series[] = {
  81         { "pe-error", "pe-error-series-max", -1 },
  82         { "pe-warn",  "pe-warn-series-max",  5000 },
  83         { "pe-input", "pe-input-series-max", 4000 },
  84     };
  85     static char *last_digest = NULL;
  86     static char *filename = NULL;
  87 
  88     unsigned int seq;
  89     int series_id = 0;
  90     int series_wrap = 0;
  91     char *digest = NULL;
  92     const char *value = NULL;
  93     time_t execution_date = time(NULL);
  94     xmlNode *converted = NULL;
  95     xmlNode *reply = NULL;
  96     bool is_repoke = false;
  97     bool process = true;
  98 
  99     init_working_set();
 100 
 101     digest = calculate_xml_versioned_digest(xml_data, FALSE, FALSE,
 102                                             CRM_FEATURE_SET);
 103     converted = copy_xml(xml_data);
 104     if (!cli_config_update(&converted, NULL, TRUE)) {
 105         sched_data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH);
 106         crm_xml_add_int(sched_data_set->graph, "transition_id", 0);
 107         crm_xml_add_int(sched_data_set->graph, "cluster-delay", 0);
 108         process = false;
 109         free(digest);
 110 
 111     } else if (pcmk__str_eq(digest, last_digest, pcmk__str_casei)) {
 112         is_repoke = true;
 113         free(digest);
 114 
 115     } else {
 116         free(last_digest);
 117         last_digest = digest;
 118     }
 119 
 120     if (process) {
 121         pcmk__schedule_actions(sched_data_set, converted, NULL);
 122     }
 123 
 124     // Get appropriate index into series[] array
 125     if (was_processing_error) {
 126         series_id = 0;
 127     } else if (was_processing_warning) {
 128         series_id = 1;
 129     } else {
 130         series_id = 2;
 131     }
 132 
 133     value = pe_pref(sched_data_set->config_hash, series[series_id].param);
 134     if ((value == NULL)
 135         || (pcmk__scan_min_int(value, &series_wrap, -1) != pcmk_rc_ok)) {
 136         series_wrap = series[series_id].wrap;
 137     }
 138 
 139     if (pcmk__read_series_sequence(PE_STATE_DIR, series[series_id].name,
 140                                    &seq) != pcmk_rc_ok) {
 141         // @TODO maybe handle errors better ...
 142         seq = 0;
 143     }
 144     crm_trace("Series %s: wrap=%d, seq=%u, pref=%s",
 145               series[series_id].name, series_wrap, seq, value);
 146 
 147     sched_data_set->input = NULL;
 148     reply = create_reply(msg, sched_data_set->graph);
 149     CRM_ASSERT(reply != NULL);
 150 
 151     if (series_wrap == 0) { // Don't save any inputs of this kind
 152         free(filename);
 153         filename = NULL;
 154 
 155     } else if (!is_repoke) { // Input changed, save to disk
 156         free(filename);
 157         filename = pcmk__series_filename(PE_STATE_DIR,
 158                                          series[series_id].name, seq, true);
 159     }
 160 
 161     crm_xml_add(reply, F_CRM_TGRAPH_INPUT, filename);
 162     crm_xml_add_int(reply, "graph-errors", was_processing_error);
 163     crm_xml_add_int(reply, "graph-warnings", was_processing_warning);
 164     crm_xml_add_int(reply, "config-errors", crm_config_error);
 165     crm_xml_add_int(reply, "config-warnings", crm_config_warning);
 166 
 167     if (pcmk__ipc_send_xml(sender, 0, reply,
 168                            crm_ipc_server_event) != pcmk_rc_ok) {
 169         int graph_file_fd = 0;
 170         char *graph_file = NULL;
 171         umask(S_IWGRP | S_IWOTH | S_IROTH);
 172 
 173         graph_file = crm_strdup_printf("%s/pengine.graph.XXXXXX",
 174                                        PE_STATE_DIR);
 175         graph_file_fd = mkstemp(graph_file);
 176 
 177         crm_err("Couldn't send transition graph to peer, writing to %s instead",
 178                 graph_file);
 179 
 180         crm_xml_add(reply, F_CRM_TGRAPH, graph_file);
 181         write_xml_fd(sched_data_set->graph, graph_file, graph_file_fd, FALSE);
 182 
 183         free(graph_file);
 184         free_xml(first_named_child(reply, F_CRM_DATA));
 185         CRM_ASSERT(pcmk__ipc_send_xml(sender, 0, reply,
 186                                       crm_ipc_server_event) == pcmk_rc_ok);
 187     }
 188 
 189     free_xml(reply);
 190     pcmk__log_transition_summary(filename);
 191 
 192     if (series_wrap == 0) {
 193         crm_debug("Not saving input to disk (disabled by configuration)");
 194 
 195     } else if (is_repoke) {
 196         crm_info("Input has not changed since last time, not saving to disk");
 197 
 198     } else {
 199         unlink(filename);
 200         crm_xml_add_ll(xml_data, "execution-date", (long long) execution_date);
 201         write_xml_file(xml_data, filename, TRUE);
 202         pcmk__write_series_sequence(PE_STATE_DIR, series[series_id].name,
 203                                     ++seq, series_wrap);
 204     }
 205 
 206     free_xml(converted);
 207 }
 208 
 209 static gboolean
 210 process_pe_message(xmlNode *msg, xmlNode *xml_data, pcmk__client_t *sender)
     /* [previous][next][first][last][top][bottom][index][help] */
 211 {
 212     const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO);
 213     const char *op = crm_element_value(msg, F_CRM_TASK);
 214     const char *ref = crm_element_value(msg, F_CRM_REFERENCE);
 215 
 216     crm_trace("Processing %s op (ref=%s)...", op, ref);
 217 
 218     if (op == NULL) {
 219         /* error */
 220 
 221     } else if (strcasecmp(op, CRM_OP_HELLO) == 0) {
 222         /* ignore */
 223 
 224     } else if (pcmk__str_eq(crm_element_value(msg, F_CRM_MSG_TYPE), XML_ATTR_RESPONSE, pcmk__str_casei)) {
 225         /* ignore */
 226 
 227     } else if (sys_to == NULL || strcasecmp(sys_to, CRM_SYSTEM_PENGINE) != 0) {
 228         crm_trace("Bad sys-to %s", crm_str(sys_to));
 229         return FALSE;
 230 
 231     } else if (strcasecmp(op, CRM_OP_PECALC) == 0) {
 232         handle_pecalc_op(msg, xml_data, sender);
 233     }
 234 
 235     return TRUE;
 236 }
 237 
 238 static int32_t
 239 pe_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 240 {
 241     crm_trace("Connection %p", c);
 242     if (pcmk__new_client(c, uid, gid) == NULL) {
 243         return -EIO;
 244     }
 245     return 0;
 246 }
 247 
 248 gboolean process_pe_message(xmlNode *msg, xmlNode *xml_data,
 249                             pcmk__client_t *sender);
 250 
 251 static int32_t
 252 pe_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 253 {
 254     uint32_t id = 0;
 255     uint32_t flags = 0;
 256     pcmk__client_t *c = pcmk__find_client(qbc);
 257     xmlNode *msg = pcmk__client_data2xml(c, data, &id, &flags);
 258 
 259     pcmk__ipc_send_ack(c, id, flags, "ack", CRM_EX_INDETERMINATE);
 260     if (msg != NULL) {
 261         xmlNode *data_xml = get_message_xml(msg, F_CRM_DATA);
 262 
 263         process_pe_message(msg, data_xml, c);
 264         free_xml(msg);
 265     }
 266     return 0;
 267 }
 268 
 269 /* Error code means? */
 270 static int32_t
 271 pe_ipc_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273     pcmk__client_t *client = pcmk__find_client(c);
 274 
 275     if (client == NULL) {
 276         return 0;
 277     }
 278     crm_trace("Connection %p", c);
 279     pcmk__free_client(client);
 280     return 0;
 281 }
 282 
 283 static void
 284 pe_ipc_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 285 {
 286     crm_trace("Connection %p", c);
 287     pe_ipc_closed(c);
 288 }
 289 
 290 struct qb_ipcs_service_handlers ipc_callbacks = {
 291     .connection_accept = pe_ipc_accept,
 292     .connection_created = NULL,
 293     .msg_process = pe_ipc_dispatch,
 294     .connection_closed = pe_ipc_closed,
 295     .connection_destroyed = pe_ipc_destroy
 296 };
 297 
 298 static pcmk__cli_option_t long_options[] = {
 299     // long option, argument type, storage, short option, description, flags
 300     {
 301         "help", no_argument, NULL, '?',
 302         "\tThis text", pcmk__option_default
 303     },
 304     {
 305         "verbose", no_argument, NULL, 'V',
 306         "\tIncrease debug output", pcmk__option_default
 307     },
 308     { 0, 0, 0, 0 }
 309 };
 310 
 311 int
 312 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 313 {
 314     int flag;
 315     int index = 0;
 316     int argerr = 0;
 317     int rc = pcmk_rc_ok;
 318 
 319     crm_log_preinit(NULL, argc, argv);
 320     pcmk__set_cli_options(NULL, "[options]", long_options,
 321                           "daemon for calculating a Pacemaker cluster's "
 322                           "response to events");
 323 
 324     mainloop_add_signal(SIGTERM, pengine_shutdown);
 325 
 326     while (1) {
 327         flag = pcmk__next_cli_option(argc, argv, &index, NULL);
 328         if (flag == -1)
 329             break;
 330 
 331         switch (flag) {
 332             case 'V':
 333                 crm_bump_log_level(argc, argv);
 334                 break;
 335             case 'h':          /* Help message */
 336                 pcmk__cli_help('?', CRM_EX_OK);
 337                 break;
 338             default:
 339                 ++argerr;
 340                 break;
 341         }
 342     }
 343 
 344     if (argc - optind == 1 && pcmk__str_eq("metadata", argv[optind], pcmk__str_casei)) {
 345         pe_metadata();
 346         return CRM_EX_OK;
 347     }
 348 
 349     if (optind > argc) {
 350         ++argerr;
 351     }
 352 
 353     if (argerr) {
 354         pcmk__cli_help('?', CRM_EX_USAGE);
 355     }
 356 
 357     crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
 358     crm_notice("Starting Pacemaker scheduler");
 359 
 360     if (pcmk__daemon_can_write(PE_STATE_DIR, NULL) == FALSE) {
 361         crm_err("Terminating due to bad permissions on " PE_STATE_DIR);
 362         fprintf(stderr,
 363                 "ERROR: Bad permissions on " PE_STATE_DIR " (see logs for details)\n");
 364         fflush(stderr);
 365         return CRM_EX_FATAL;
 366     }
 367 
 368     ipcs = mainloop_add_ipc_server(CRM_SYSTEM_PENGINE, QB_IPC_SHM, &ipc_callbacks);
 369     if (ipcs == NULL) {
 370         crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
 371         crm_exit(CRM_EX_FATAL);
 372     }
 373 
 374     pcmk__register_formats(NULL, formats);
 375     rc = pcmk__output_new(&out, "log", NULL, argv);
 376     if ((rc != pcmk_rc_ok) || (out == NULL)) {
 377         crm_err("Can't log resource details due to internal error: %s\n",
 378                 pcmk_rc_str(rc));
 379         crm_exit(CRM_EX_FATAL);
 380     }
 381 
 382     pe__register_messages(out);
 383     pcmk__register_lib_messages(out);
 384 
 385     pcmk__output_set_log_level(out, LOG_TRACE);
 386 
 387     /* Create the mainloop and run it... */
 388     mainloop = g_main_loop_new(NULL, FALSE);
 389     crm_notice("Pacemaker scheduler successfully started and accepting connections");
 390     g_main_loop_run(mainloop);
 391     pengine_shutdown(0);
 392 }
 393 
 394 void
 395 pengine_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 396 {
 397     mainloop_del_ipc_server(ipcs);
 398     ipcs = NULL;
 399 
 400     pe_free_working_set(sched_data_set);
 401     sched_data_set = NULL;
 402 
 403     pcmk__unregister_formats();
 404     if (out != NULL) {
 405         out->finish(out, CRM_EX_OK, true, NULL);
 406         pcmk__output_free(out);
 407         out = NULL;
 408     }
 409 
 410     crm_exit(CRM_EX_OK);
 411 }

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