root/daemons/pacemakerd/pacemakerd.c

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

DEFINITIONS

This source file includes following definitions.
  1. PCMK__OUTPUT_ARGS
  2. PCMK__OUTPUT_ARGS
  3. pid_cb
  4. standby_cb
  5. pcmk_ignore
  6. pcmk_sigquit
  7. pacemakerd_chown
  8. create_pcmk_dirs
  9. remove_core_file_limit
  10. pacemakerd_event_cb
  11. build_arg_context
  12. main

   1 /*
   2  * Copyright 2010-2024 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 #include "pacemakerd.h"
  12 
  13 #if SUPPORT_COROSYNC
  14 #include "pcmkd_corosync.h"
  15 #endif
  16 
  17 #include <pwd.h>
  18 #include <errno.h>
  19 #include <unistd.h>
  20 #include <stdio.h>
  21 #include <stdbool.h>
  22 #include <sys/stat.h>
  23 #include <sys/types.h>
  24 #include <sys/time.h>
  25 #include <sys/resource.h>
  26 
  27 #include <crm/crm.h>  /* indirectly: CRM_EX_* */
  28 #include <crm/common/mainloop.h>
  29 #include <crm/common/xml.h>
  30 #include <crm/common/cmdline_internal.h>
  31 #include <crm/common/ipc_pacemakerd.h>
  32 #include <crm/common/output_internal.h>
  33 #include <crm/cluster/internal.h>
  34 #include <crm/cluster.h>
  35 
  36 #define SUMMARY "pacemakerd - primary Pacemaker daemon that launches and monitors all subsidiary Pacemaker daemons"
  37 
  38 struct {
  39     gboolean features;
  40     gboolean foreground;
  41     gboolean shutdown;
  42     gboolean standby;
  43 } options;
  44 
  45 static pcmk__output_t *out = NULL;
  46 
  47 static pcmk__supported_format_t formats[] = {
  48     PCMK__SUPPORTED_FORMAT_NONE,
  49     PCMK__SUPPORTED_FORMAT_TEXT,
  50     PCMK__SUPPORTED_FORMAT_XML,
  51     { NULL, NULL, NULL }
  52 };
  53 
  54 PCMK__OUTPUT_ARGS("features")
     /* [previous][next][first][last][top][bottom][index][help] */
  55 static int
  56 pacemakerd_features(pcmk__output_t *out, va_list args) {
  57     out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
  58               BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
  59     return pcmk_rc_ok;
  60 }
  61 
  62 PCMK__OUTPUT_ARGS("features")
     /* [previous][next][first][last][top][bottom][index][help] */
  63 static int
  64 pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
  65     gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
  66 
  67     pcmk__output_xml_create_parent(out, PCMK_XE_PACEMAKERD,
  68                                    PCMK_XA_VERSION, PACEMAKER_VERSION,
  69                                    PCMK_XA_BUILD, BUILD_VERSION,
  70                                    PCMK_XA_FEATURE_SET, CRM_FEATURE_SET,
  71                                    NULL);
  72     out->begin_list(out, NULL, NULL, PCMK_XE_FEATURES);
  73 
  74     for (char **s = feature_list; *s != NULL; s++) {
  75         pcmk__output_create_xml_text_node(out, PCMK_XE_FEATURE, *s);
  76     }
  77 
  78     out->end_list(out);
  79 
  80     pcmk__output_xml_pop_parent(out);
  81 
  82     g_strfreev(feature_list);
  83     return pcmk_rc_ok;
  84 }
  85 
  86 static pcmk__message_entry_t fmt_functions[] = {
  87     { "features", "default", pacemakerd_features },
  88     { "features", "xml", pacemakerd_features_xml },
  89 
  90     { NULL, NULL, NULL }
  91 };
  92 
  93 static gboolean
  94 pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
  95     return TRUE;
  96 }
  97 
  98 static gboolean
  99 standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
     /* [previous][next][first][last][top][bottom][index][help] */
 100     options.standby = TRUE;
 101     pcmk__set_env_option(PCMK__ENV_NODE_START_STATE, PCMK_VALUE_STANDBY, false);
 102     return TRUE;
 103 }
 104 
 105 static GOptionEntry entries[] = {
 106     { "features", 'F', 0, G_OPTION_ARG_NONE, &options.features,
 107       "Display full version and list of features Pacemaker was built with",
 108       NULL },
 109     { "foreground", 'f', 0, G_OPTION_ARG_NONE, &options.foreground,
 110       "(Ignored) Pacemaker always runs in the foreground",
 111       NULL },
 112     { "pid-file", 'p', 0, G_OPTION_ARG_CALLBACK, pid_cb,
 113       "(Ignored) Daemon pid file location",
 114       "FILE" },
 115     { "shutdown", 'S', 0, G_OPTION_ARG_NONE, &options.shutdown,
 116       "Instruct Pacemaker to shutdown on this machine",
 117       NULL },
 118     { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, standby_cb,
 119       "Start node in standby state",
 120       NULL },
 121 
 122     { NULL }
 123 };
 124 
 125 static void
 126 pcmk_ignore(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128     crm_info("Ignoring signal %s (%d)", strsignal(nsig), nsig);
 129 }
 130 
 131 static void
 132 pcmk_sigquit(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134     pcmk__panic(__func__);
 135 }
 136 
 137 static void
 138 pacemakerd_chown(const char *path, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140     int rc = chown(path, uid, gid);
 141 
 142     if (rc < 0) {
 143         crm_warn("Cannot change the ownership of %s to user %s and gid %d: %s",
 144                  path, CRM_DAEMON_USER, gid, pcmk_rc_str(errno));
 145     }
 146 }
 147 
 148 static void
 149 create_pcmk_dirs(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 150 {
 151     uid_t pcmk_uid = 0;
 152     gid_t pcmk_gid = 0;
 153 
 154     const char *dirs[] = {
 155         CRM_PACEMAKER_DIR, // core/blackbox/scheduler/CIB files
 156         CRM_CORE_DIR,      // core files
 157         CRM_BLACKBOX_DIR,  // blackbox dumps
 158         PE_STATE_DIR,      // scheduler inputs
 159         CRM_CONFIG_DIR,    // the Cluster Information Base (CIB)
 160         // Don't build CRM_RSCTMP_DIR, pacemaker-execd will do it
 161         NULL
 162     };
 163 
 164     if (pcmk_daemon_user(&pcmk_uid, &pcmk_gid) < 0) {
 165         crm_err("Cluster user %s does not exist, aborting Pacemaker startup",
 166                 CRM_DAEMON_USER);
 167         crm_exit(CRM_EX_NOUSER);
 168     }
 169 
 170     // Used by some resource agents
 171     if ((mkdir(CRM_STATE_DIR, 0750) < 0) && (errno != EEXIST)) {
 172         crm_warn("Could not create directory " CRM_STATE_DIR ": %s",
 173                  pcmk_rc_str(errno));
 174     } else {
 175         pacemakerd_chown(CRM_STATE_DIR, pcmk_uid, pcmk_gid);
 176     }
 177 
 178     for (int i = 0; dirs[i] != NULL; ++i) {
 179         int rc = pcmk__build_path(dirs[i], 0750);
 180 
 181         if (rc != pcmk_rc_ok) {
 182             crm_warn("Could not create directory %s: %s",
 183                      dirs[i], pcmk_rc_str(rc));
 184         } else {
 185             pacemakerd_chown(dirs[i], pcmk_uid, pcmk_gid);
 186         }
 187     }
 188 }
 189 
 190 static void
 191 remove_core_file_limit(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 192 {
 193     struct rlimit cores;
 194 
 195     // Get current limits
 196     if (getrlimit(RLIMIT_CORE, &cores) < 0) {
 197         crm_notice("Unable to check system core file limits "
 198                    "(consider ensuring the size is unlimited): %s",
 199                    strerror(errno));
 200         return;
 201     }
 202 
 203     // Check whether core dumps are disabled
 204     if (cores.rlim_max == 0) {
 205         if (geteuid() != 0) { // Yes, and there's nothing we can do about it
 206             crm_notice("Core dumps are disabled (consider enabling them)");
 207             return;
 208         }
 209         cores.rlim_max = RLIM_INFINITY; // Yes, but we're root, so enable them
 210     }
 211 
 212     // Raise soft limit to hard limit (if not already done)
 213     if (cores.rlim_cur != cores.rlim_max) {
 214         cores.rlim_cur = cores.rlim_max;
 215         if (setrlimit(RLIMIT_CORE, &cores) < 0) {
 216             crm_notice("Unable to raise system limit on core file size "
 217                        "(consider doing so manually): %s",
 218                        strerror(errno));
 219             return;
 220         }
 221     }
 222 
 223     if (cores.rlim_cur == RLIM_INFINITY) {
 224         crm_trace("Core file size is unlimited");
 225     } else {
 226         crm_trace("Core file size is limited to %llu bytes",
 227                   (unsigned long long) cores.rlim_cur);
 228     }
 229 }
 230 
 231 static void
 232 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
     /* [previous][next][first][last][top][bottom][index][help] */
 233                     enum pcmk_ipc_event event_type, crm_exit_t status,
 234                     void *event_data, void *user_data)
 235 {
 236     pcmk_pacemakerd_api_reply_t *reply = event_data;
 237 
 238     switch (event_type) {
 239         case pcmk_ipc_event_reply:
 240             break;
 241 
 242         default:
 243             return;
 244     }
 245 
 246     if (status != CRM_EX_OK) {
 247         out->err(out, "Bad reply from pacemakerd: %s", crm_exit_str(status));
 248         return;
 249     }
 250 
 251     if (reply->reply_type != pcmk_pacemakerd_reply_shutdown) {
 252         out->err(out, "Unknown reply type %d from pacemakerd",
 253                  reply->reply_type);
 254     }
 255 }
 256 
 257 static GOptionContext *
 258 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 259     GOptionContext *context = NULL;
 260 
 261     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 262     pcmk__add_main_args(context, entries);
 263     return context;
 264 }
 265 
 266 int
 267 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 268 {
 269     int rc = pcmk_rc_ok;
 270     crm_exit_t exit_code = CRM_EX_OK;
 271 
 272     GError *error = NULL;
 273 
 274     GOptionGroup *output_group = NULL;
 275     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 276     gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
 277     GOptionContext *context = build_arg_context(args, &output_group);
 278 
 279     bool old_instance_connected = false;
 280 
 281     pcmk_ipc_api_t *old_instance = NULL;
 282     qb_ipcs_service_t *ipcs = NULL;
 283 
 284     subdaemon_check_progress = time(NULL);
 285 
 286     setenv("LC_ALL", "C", 1); // Ensure logs are in a common language
 287 
 288     crm_log_preinit(NULL, argc, argv);
 289     mainloop_add_signal(SIGHUP, pcmk_ignore);
 290     mainloop_add_signal(SIGQUIT, pcmk_sigquit);
 291 
 292     pcmk__register_formats(output_group, formats);
 293     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 294         exit_code = CRM_EX_USAGE;
 295         goto done;
 296     }
 297 
 298     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 299     if ((rc != pcmk_rc_ok) || (out == NULL)) {
 300         exit_code = CRM_EX_ERROR;
 301         g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
 302                     args->output_ty, pcmk_rc_str(rc));
 303         goto done;
 304     }
 305 
 306     pcmk__register_messages(out, fmt_functions);
 307 
 308     if (options.features) {
 309         out->message(out, "features");
 310         exit_code = CRM_EX_OK;
 311         goto done;
 312     }
 313 
 314     if (args->version) {
 315         out->version(out, false);
 316         goto done;
 317     }
 318 
 319     // @COMPAT Drop at 3.0.0; likely last used in 1.1.24
 320     pcmk__set_env_option(PCMK__ENV_MCP, PCMK_VALUE_TRUE, true);
 321 
 322     if (options.shutdown) {
 323         pcmk__cli_init_logging("pacemakerd", args->verbosity);
 324     } else {
 325         crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
 326     }
 327 
 328     crm_debug("Checking for existing Pacemaker instance");
 329 
 330     rc = pcmk_new_ipc_api(&old_instance, pcmk_ipc_pacemakerd);
 331     if (old_instance == NULL) {
 332         out->err(out, "Could not check for existing pacemakerd: %s", pcmk_rc_str(rc));
 333         exit_code = pcmk_rc2exitc(rc);
 334         goto done;
 335     }
 336 
 337     pcmk_register_ipc_callback(old_instance, pacemakerd_event_cb, NULL);
 338     rc = pcmk__connect_ipc(old_instance, pcmk_ipc_dispatch_sync, 2);
 339     if (rc != pcmk_rc_ok) {
 340         crm_debug("No existing %s instance found: %s",
 341                   pcmk_ipc_name(old_instance, true), pcmk_rc_str(rc));
 342     }
 343     old_instance_connected = pcmk_ipc_is_connected(old_instance);
 344 
 345     if (options.shutdown) {
 346         if (old_instance_connected) {
 347             rc = pcmk_pacemakerd_api_shutdown(old_instance, crm_system_name);
 348             pcmk_dispatch_ipc(old_instance);
 349 
 350             exit_code = pcmk_rc2exitc(rc);
 351 
 352             if (exit_code != CRM_EX_OK) {
 353                 pcmk_free_ipc_api(old_instance);
 354                 goto done;
 355             }
 356 
 357             /* We get the ACK immediately, and the response right after that,
 358              * but it might take a while for pacemakerd to get around to
 359              * shutting down.  Wait for that to happen (with 30-minute timeout).
 360              */
 361             for (int i = 0; i < 900; i++) {
 362                 if (!pcmk_ipc_is_connected(old_instance)) {
 363                     exit_code = CRM_EX_OK;
 364                     pcmk_free_ipc_api(old_instance);
 365                     goto done;
 366                 }
 367 
 368                 sleep(2);
 369             }
 370 
 371             exit_code = CRM_EX_TIMEOUT;
 372             pcmk_free_ipc_api(old_instance);
 373             goto done;
 374 
 375         } else {
 376             out->err(out, "Could not request shutdown "
 377                      "of existing Pacemaker instance: %s", pcmk_rc_str(rc));
 378             pcmk_free_ipc_api(old_instance);
 379             exit_code = CRM_EX_DISCONNECT;
 380             goto done;
 381         }
 382 
 383     } else if (old_instance_connected) {
 384         pcmk_free_ipc_api(old_instance);
 385         crm_err("Aborting start-up because active Pacemaker instance found");
 386         exit_code = CRM_EX_FATAL;
 387         goto done;
 388     }
 389 
 390     pcmk_free_ipc_api(old_instance);
 391 
 392     /* Don't allow any accidental output after this point. */
 393     if (out != NULL) {
 394         out->finish(out, exit_code, true, NULL);
 395         pcmk__output_free(out);
 396         out = NULL;
 397     }
 398 
 399 #ifdef SUPPORT_COROSYNC
 400     if (pacemakerd_read_config() == FALSE) {
 401         crm_exit(CRM_EX_UNAVAILABLE);
 402     }
 403 #endif
 404 
 405     // OCF shell functions and cluster-glue need facility under different name
 406     {
 407         const char *facility = pcmk__env_option(PCMK__ENV_LOGFACILITY);
 408 
 409         if (!pcmk__str_eq(facility, PCMK_VALUE_NONE,
 410                           pcmk__str_casei|pcmk__str_null_matches)) {
 411             pcmk__set_env_option("LOGFACILITY", facility, true);
 412         }
 413     }
 414 
 415     crm_notice("Starting Pacemaker %s "CRM_XS" build=%s features:%s",
 416                PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
 417     mainloop = g_main_loop_new(NULL, FALSE);
 418 
 419     remove_core_file_limit();
 420     create_pcmk_dirs();
 421     pcmk__serve_pacemakerd_ipc(&ipcs, &pacemakerd_ipc_callbacks);
 422 
 423 #ifdef SUPPORT_COROSYNC
 424     /* Allows us to block shutdown */
 425     if (!cluster_connect_cfg()) {
 426         exit_code = CRM_EX_PROTOCOL;
 427         goto done;
 428     }
 429 #endif
 430 
 431     if (pcmk__locate_sbd() > 0) {
 432         running_with_sbd = TRUE;
 433     }
 434 
 435     switch (find_and_track_existing_processes()) {
 436         case pcmk_rc_ok:
 437             break;
 438         case pcmk_rc_ipc_unauthorized:
 439             exit_code = CRM_EX_CANTCREAT;
 440             goto done;
 441         default:
 442             exit_code = CRM_EX_FATAL;
 443             goto done;
 444     };
 445 
 446     mainloop_add_signal(SIGTERM, pcmk_shutdown);
 447     mainloop_add_signal(SIGINT, pcmk_shutdown);
 448 
 449     if ((running_with_sbd) && pcmk__get_sbd_sync_resource_startup()) {
 450         crm_notice("Waiting for startup-trigger from SBD.");
 451         pacemakerd_state = PCMK__VALUE_WAIT_FOR_PING;
 452         startup_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, init_children_processes, NULL);
 453     } else {
 454         if (running_with_sbd) {
 455             crm_warn("Enabling SBD_SYNC_RESOURCE_STARTUP would (if supported "
 456                      "by your SBD version) improve reliability of "
 457                      "interworking between SBD & pacemaker.");
 458         }
 459         pacemakerd_state = PCMK__VALUE_STARTING_DAEMONS;
 460         init_children_processes(NULL);
 461     }
 462 
 463     crm_notice("Pacemaker daemon successfully started and accepting connections");
 464     g_main_loop_run(mainloop);
 465 
 466     if (ipcs) {
 467         crm_trace("Closing IPC server");
 468         mainloop_del_ipc_server(ipcs);
 469         ipcs = NULL;
 470     }
 471 
 472     g_main_loop_unref(mainloop);
 473 #ifdef SUPPORT_COROSYNC
 474     cluster_disconnect_cfg();
 475 #endif
 476 
 477 done:
 478     g_strfreev(processed_args);
 479     pcmk__free_arg_context(context);
 480 
 481     pcmk__output_and_clear_error(&error, out);
 482 
 483     if (out != NULL) {
 484         out->finish(out, exit_code, true, NULL);
 485         pcmk__output_free(out);
 486     }
 487     pcmk__unregister_formats();
 488     crm_exit(exit_code);
 489 }

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