root/daemons/based/pacemaker-based.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib_enable_writes
  2. setup_stand_alone
  3. based_metadata
  4. build_arg_context
  5. main
  6. cib_cs_dispatch
  7. cib_cs_destroy
  8. cib_peer_update_callback
  9. cib_init
  10. startCib

   1 /*
   2  * Copyright 2004-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 
  12 #include <stdio.h>
  13 #include <stdlib.h>
  14 #include <pwd.h>
  15 #include <grp.h>
  16 #include <bzlib.h>
  17 #include <sys/types.h>
  18 
  19 #include <glib.h>
  20 #include <libxml/tree.h>
  21 
  22 #include <crm/crm.h>
  23 #include <crm/cib/internal.h>
  24 #include <crm/cluster/internal.h>
  25 #include <crm/common/cmdline_internal.h>
  26 #include <crm/common/mainloop.h>
  27 #include <crm/common/output_internal.h>
  28 #include <crm/common/xml.h>
  29 
  30 #include <pacemaker-based.h>
  31 
  32 #define SUMMARY "daemon for managing the configuration of a Pacemaker cluster"
  33 
  34 extern int init_remote_listener(int port, gboolean encrypted);
  35 gboolean cib_shutdown_flag = FALSE;
  36 int cib_status = pcmk_ok;
  37 
  38 pcmk_cluster_t *crm_cluster = NULL;
  39 
  40 GMainLoop *mainloop = NULL;
  41 gchar *cib_root = NULL;
  42 static gboolean preserve_status = FALSE;
  43 
  44 gboolean cib_writes_enabled = TRUE;
  45 gboolean stand_alone = FALSE;
  46 
  47 int remote_fd = 0;
  48 int remote_tls_fd = 0;
  49 
  50 GHashTable *config_hash = NULL;
  51 GHashTable *local_notify_queue = NULL;
  52 
  53 static void cib_init(void);
  54 void cib_shutdown(int nsig);
  55 static bool startCib(const char *filename);
  56 extern int write_cib_contents(gpointer p);
  57 
  58 static crm_exit_t exit_code = CRM_EX_OK;
  59 
  60 static void
  61 cib_enable_writes(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63     crm_info("(Re)enabling disk writes");
  64     cib_writes_enabled = TRUE;
  65 }
  66 
  67 /*!
  68  * \internal
  69  * \brief Set up options, users, and groups for stand-alone mode
  70  *
  71  * \param[out] error  GLib error object
  72  *
  73  * \return Standard Pacemaker return code
  74  */
  75 static int
  76 setup_stand_alone(GError **error)
     /* [previous][next][first][last][top][bottom][index][help] */
  77 {
  78     int rc = 0;
  79     struct passwd *pwentry = NULL;
  80 
  81     preserve_status = TRUE;
  82     cib_writes_enabled = FALSE;
  83 
  84     errno = 0;
  85     pwentry = getpwnam(CRM_DAEMON_USER);
  86     if (pwentry == NULL) {
  87         exit_code = CRM_EX_FATAL;
  88         if (errno != 0) {
  89             g_set_error(error, PCMK__EXITC_ERROR, exit_code,
  90                         "Error getting password DB entry for %s: %s",
  91                         CRM_DAEMON_USER, strerror(errno));
  92             return errno;
  93         }
  94         g_set_error(error, PCMK__EXITC_ERROR, exit_code,
  95                     "Password DB entry for '%s' not found", CRM_DAEMON_USER);
  96         return ENXIO;
  97     }
  98 
  99     rc = setgid(pwentry->pw_gid);
 100     if (rc < 0) {
 101         exit_code = CRM_EX_FATAL;
 102         g_set_error(error, PCMK__EXITC_ERROR, exit_code,
 103                     "Could not set group to %d: %s",
 104                     pwentry->pw_gid, strerror(errno));
 105         return errno;
 106     }
 107 
 108     rc = initgroups(CRM_DAEMON_USER, pwentry->pw_gid);
 109     if (rc < 0) {
 110         exit_code = CRM_EX_FATAL;
 111         g_set_error(error, PCMK__EXITC_ERROR, exit_code,
 112                     "Could not setup groups for user %d: %s",
 113                     pwentry->pw_uid, strerror(errno));
 114         return errno;
 115     }
 116 
 117     rc = setuid(pwentry->pw_uid);
 118     if (rc < 0) {
 119         exit_code = CRM_EX_FATAL;
 120         g_set_error(error, PCMK__EXITC_ERROR, exit_code,
 121                     "Could not set user to %d: %s",
 122                     pwentry->pw_uid, strerror(errno));
 123         return errno;
 124     }
 125     return pcmk_rc_ok;
 126 }
 127 
 128 /* @COMPAT Deprecated since 2.1.8. Use pcmk_list_cluster_options() or
 129  * crm_attribute --list-options=cluster instead of querying daemon metadata.
 130  */
 131 static int
 132 based_metadata(pcmk__output_t *out)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134     return pcmk__daemon_metadata(out, "pacemaker-based",
 135                                  "Cluster Information Base manager options",
 136                                  "Cluster options used by Pacemaker's Cluster "
 137                                  "Information Base manager",
 138                                  pcmk__opt_based);
 139 }
 140 
 141 static GOptionEntry entries[] = {
 142     { "stand-alone", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &stand_alone,
 143       "(Advanced use only) Run in stand-alone mode", NULL },
 144 
 145     { "disk-writes", 'w', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE,
 146       &cib_writes_enabled,
 147       "(Advanced use only) Enable disk writes (enabled by default unless in "
 148       "stand-alone mode)", NULL },
 149 
 150     { "cib-root", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &cib_root,
 151       "(Advanced use only) Directory where the CIB XML file should be located "
 152       "(default: " CRM_CONFIG_DIR ")", NULL },
 153 
 154     { NULL }
 155 };
 156 
 157 static pcmk__supported_format_t formats[] = {
 158     PCMK__SUPPORTED_FORMAT_NONE,
 159     PCMK__SUPPORTED_FORMAT_TEXT,
 160     PCMK__SUPPORTED_FORMAT_XML,
 161     { NULL, NULL, NULL }
 162 };
 163 
 164 static GOptionContext *
 165 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
     /* [previous][next][first][last][top][bottom][index][help] */
 166 {
 167     GOptionContext *context = NULL;
 168 
 169     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 170     pcmk__add_main_args(context, entries);
 171     return context;
 172 }
 173 
 174 int
 175 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 176 {
 177     int rc = pcmk_rc_ok;
 178     crm_ipc_t *old_instance = NULL;
 179 
 180     pcmk__output_t *out = NULL;
 181 
 182     GError *error = NULL;
 183 
 184     GOptionGroup *output_group = NULL;
 185     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 186     gchar **processed_args = pcmk__cmdline_preproc(argv, "r");
 187     GOptionContext *context = build_arg_context(args, &output_group);
 188 
 189     crm_log_preinit(NULL, argc, argv);
 190 
 191     pcmk__register_formats(output_group, formats);
 192     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 193         exit_code = CRM_EX_USAGE;
 194         goto done;
 195     }
 196 
 197     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 198     if (rc != pcmk_rc_ok) {
 199         exit_code = CRM_EX_ERROR;
 200         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 201                     "Error creating output format %s: %s",
 202                     args->output_ty, pcmk_rc_str(rc));
 203         goto done;
 204     }
 205 
 206     if (args->version) {
 207         out->version(out, false);
 208         goto done;
 209     }
 210 
 211     mainloop_add_signal(SIGTERM, cib_shutdown);
 212     mainloop_add_signal(SIGPIPE, cib_enable_writes);
 213 
 214     cib_writer = mainloop_add_trigger(G_PRIORITY_LOW, write_cib_contents, NULL);
 215 
 216     if ((g_strv_length(processed_args) >= 2)
 217         && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
 218 
 219         rc = based_metadata(out);
 220         if (rc != pcmk_rc_ok) {
 221             exit_code = CRM_EX_FATAL;
 222             g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 223                         "Unable to display metadata: %s", pcmk_rc_str(rc));
 224         }
 225         goto done;
 226     }
 227 
 228     pcmk__cli_init_logging("pacemaker-based", args->verbosity);
 229     crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
 230     crm_notice("Starting Pacemaker CIB manager");
 231 
 232     old_instance = crm_ipc_new(PCMK__SERVER_BASED_RO, 0);
 233     if (old_instance == NULL) {
 234         /* crm_ipc_new() will have already logged an error message with
 235          * crm_err()
 236          */
 237         exit_code = CRM_EX_FATAL;
 238         goto done;
 239     }
 240 
 241     if (pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok) {
 242         /* IPC end-point already up */
 243         crm_ipc_close(old_instance);
 244         crm_ipc_destroy(old_instance);
 245         crm_err("pacemaker-based is already active, aborting startup");
 246         goto done;
 247     } else {
 248         /* not up or not authentic, we'll proceed either way */
 249         crm_ipc_destroy(old_instance);
 250         old_instance = NULL;
 251     }
 252 
 253     if (stand_alone) {
 254         rc = setup_stand_alone(&error);
 255         if (rc != pcmk_rc_ok) {
 256             goto done;
 257         }
 258     }
 259 
 260     if (cib_root == NULL) {
 261         cib_root = g_strdup(CRM_CONFIG_DIR);
 262     } else {
 263         crm_notice("Using custom config location: %s", cib_root);
 264     }
 265 
 266     if (!pcmk__daemon_can_write(cib_root, NULL)) {
 267         exit_code = CRM_EX_FATAL;
 268         crm_err("Terminating due to bad permissions on %s", cib_root);
 269         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 270                     "Bad permissions on %s (see logs for details)", cib_root);
 271         goto done;
 272     }
 273 
 274     pcmk__cluster_init_node_caches();
 275 
 276     // Read initial CIB, connect to cluster, and start IPC servers
 277     cib_init();
 278 
 279     // Run the main loop
 280     mainloop = g_main_loop_new(NULL, FALSE);
 281     crm_notice("Pacemaker CIB manager successfully started and accepting connections");
 282     g_main_loop_run(mainloop);
 283 
 284     /* If main loop returned, clean up and exit. We disconnect in case
 285      * terminate_cib() was called with fast=-1.
 286      */
 287     pcmk_cluster_disconnect(crm_cluster);
 288     pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
 289 
 290 done:
 291     g_strfreev(processed_args);
 292     pcmk__free_arg_context(context);
 293 
 294     pcmk__cluster_destroy_node_caches();
 295 
 296     if (local_notify_queue != NULL) {
 297         g_hash_table_destroy(local_notify_queue);
 298     }
 299 
 300     if (config_hash != NULL) {
 301         g_hash_table_destroy(config_hash);
 302     }
 303     pcmk__client_cleanup();
 304     pcmk_cluster_free(crm_cluster);
 305     g_free(cib_root);
 306 
 307     pcmk__output_and_clear_error(&error, out);
 308 
 309     if (out != NULL) {
 310         out->finish(out, exit_code, true, NULL);
 311         pcmk__output_free(out);
 312     }
 313     pcmk__unregister_formats();
 314     crm_exit(exit_code);
 315 }
 316 
 317 #if SUPPORT_COROSYNC
 318 static void
 319 cib_cs_dispatch(cpg_handle_t handle,
     /* [previous][next][first][last][top][bottom][index][help] */
 320                  const struct cpg_name *groupName,
 321                  uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
 322 {
 323     uint32_t kind = 0;
 324     xmlNode *xml = NULL;
 325     const char *from = NULL;
 326     char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &kind, &from);
 327 
 328     if(data == NULL) {
 329         return;
 330     }
 331     if (kind == crm_class_cluster) {
 332         xml = pcmk__xml_parse(data);
 333         if (xml == NULL) {
 334             crm_err("Invalid XML: '%.120s'", data);
 335             free(data);
 336             return;
 337         }
 338         crm_xml_add(xml, PCMK__XA_SRC, from);
 339         cib_peer_callback(xml, NULL);
 340     }
 341 
 342     free_xml(xml);
 343     free(data);
 344 }
 345 
 346 static void
 347 cib_cs_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349     if (cib_shutdown_flag) {
 350         crm_info("Corosync disconnection complete");
 351     } else {
 352         crm_crit("Lost connection to cluster layer, shutting down");
 353         terminate_cib(__func__, CRM_EX_DISCONNECT);
 354     }
 355 }
 356 #endif
 357 
 358 static void
 359 cib_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
     /* [previous][next][first][last][top][bottom][index][help] */
 360 {
 361     switch (type) {
 362         case crm_status_processes:
 363             if (cib_legacy_mode()
 364                 && !pcmk_is_set(node->processes, crm_get_cluster_proc())) {
 365 
 366                 uint32_t old = data? *(const uint32_t *)data : 0;
 367 
 368                 if ((node->processes ^ old) & crm_proc_cpg) {
 369                     crm_info("Attempting to disable legacy mode after %s left the cluster",
 370                              node->uname);
 371                     legacy_mode = FALSE;
 372                 }
 373             }
 374             break;
 375 
 376         case crm_status_uname:
 377         case crm_status_nstate:
 378             if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2)
 379                 && (pcmk__ipc_client_count() == 0)) {
 380 
 381                 crm_info("No more peers");
 382                 terminate_cib(__func__, -1);
 383             }
 384             break;
 385     }
 386 }
 387 
 388 static void
 389 cib_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 390 {
 391     crm_cluster = pcmk_cluster_new();
 392 
 393 #if SUPPORT_COROSYNC
 394     if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
 395         pcmk_cluster_set_destroy_fn(crm_cluster, cib_cs_destroy);
 396         pcmk_cpg_set_deliver_fn(crm_cluster, cib_cs_dispatch);
 397         pcmk_cpg_set_confchg_fn(crm_cluster, pcmk__cpg_confchg_cb);
 398     }
 399 #endif // SUPPORT_COROSYNC
 400 
 401     config_hash = pcmk__strkey_table(free, free);
 402 
 403     if (startCib("cib.xml") == FALSE) {
 404         crm_crit("Cannot start CIB... terminating");
 405         crm_exit(CRM_EX_NOINPUT);
 406     }
 407 
 408     if (!stand_alone) {
 409         pcmk__cluster_set_status_callback(&cib_peer_update_callback);
 410 
 411         if (pcmk_cluster_connect(crm_cluster) != pcmk_rc_ok) {
 412             crm_crit("Cannot sign in to the cluster... terminating");
 413             crm_exit(CRM_EX_FATAL);
 414         }
 415     }
 416 
 417     pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks,
 418                           &ipc_rw_callbacks);
 419 
 420     if (stand_alone) {
 421         based_is_primary = true;
 422     }
 423 }
 424 
 425 static bool
 426 startCib(const char *filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 427 {
 428     gboolean active = FALSE;
 429     xmlNode *cib = readCibXmlFile(cib_root, filename, !preserve_status);
 430 
 431     if (activateCibXml(cib, TRUE, "start") == 0) {
 432         int port = 0;
 433 
 434         active = TRUE;
 435 
 436         cib_read_config(config_hash, cib);
 437 
 438         pcmk__scan_port(crm_element_value(cib, PCMK_XA_REMOTE_TLS_PORT), &port);
 439         if (port >= 0) {
 440             remote_tls_fd = init_remote_listener(port, TRUE);
 441         }
 442 
 443         pcmk__scan_port(crm_element_value(cib, PCMK_XA_REMOTE_CLEAR_PORT),
 444                         &port);
 445         if (port >= 0) {
 446             remote_fd = init_remote_listener(port, FALSE);
 447         }
 448     }
 449     return active;
 450 }

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