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

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