root/daemons/pacemakerd/pacemakerd.c

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

DEFINITIONS

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

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

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