root/daemons/pacemakerd/pcmkd_corosync.c

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

DEFINITIONS

This source file includes following definitions.
  1. cfg_shutdown_callback
  2. pcmk_cfg_dispatch
  3. close_cfg
  4. cluster_reconnect_cb
  5. cfg_connection_destroy
  6. cluster_disconnect_cfg
  7. cluster_connect_cfg
  8. pcmkd_shutdown_corosync
  9. pcmkd_corosync_connected
  10. get_config_opt
  11. pacemakerd_read_config

   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 #include "pcmkd_corosync.h"
  13 
  14 #include <sys/utsname.h>
  15 #include <sys/stat.h>           /* for calls to stat() */
  16 #include <libgen.h>             /* For basename() and dirname() */
  17 
  18 #include <sys/types.h>
  19 #include <pwd.h>                /* For getpwname() */
  20 
  21 #include <corosync/hdb.h>
  22 #include <corosync/cfg.h>
  23 #include <corosync/cpg.h>
  24 #include <corosync/cmap.h>
  25 
  26 #include <crm/cluster/internal.h>
  27 #include <crm/common/ipc.h>     /* for crm_ipc_is_authentic_process */
  28 #include <crm/common/mainloop.h>
  29 
  30 #include <crm/common/ipc_internal.h>  /* PCMK__SPECIAL_PID* */
  31 
  32 static corosync_cfg_handle_t cfg_handle = 0;
  33 static mainloop_timer_t *reconnect_timer = NULL;
  34 
  35 /* =::=::=::= CFG - Shutdown stuff =::=::=::= */
  36 
  37 static void
  38 cfg_shutdown_callback(corosync_cfg_handle_t h, corosync_cfg_shutdown_flags_t flags)
     /* [previous][next][first][last][top][bottom][index][help] */
  39 {
  40     crm_info("Corosync wants to shut down: %s",
  41              (flags == COROSYNC_CFG_SHUTDOWN_FLAG_IMMEDIATE) ? "immediate" :
  42              (flags == COROSYNC_CFG_SHUTDOWN_FLAG_REGARDLESS) ? "forced" : "optional");
  43 
  44     /* Never allow corosync to shut down while we're running */
  45     corosync_cfg_replyto_shutdown(h, COROSYNC_CFG_SHUTDOWN_FLAG_NO);
  46 }
  47 
  48 static corosync_cfg_callbacks_t cfg_callbacks = {
  49     .corosync_cfg_shutdown_callback = cfg_shutdown_callback,
  50 };
  51 
  52 static int
  53 pcmk_cfg_dispatch(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  54 {
  55     corosync_cfg_handle_t *handle = (corosync_cfg_handle_t *) user_data;
  56     cs_error_t rc = corosync_cfg_dispatch(*handle, CS_DISPATCH_ALL);
  57 
  58     if (rc != CS_OK) {
  59         return -1;
  60     }
  61     return 0;
  62 }
  63 
  64 static void
  65 close_cfg(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  66 {
  67     if (cfg_handle != 0) {
  68 #ifdef HAVE_COROSYNC_CFG_TRACKSTART
  69         /* Ideally, we would call corosync_cfg_trackstop(cfg_handle) here, but a
  70          * bug in corosync 3.1.1 and 3.1.2 makes it hang forever. Thankfully,
  71          * it's not necessary since we exit immediately after this.
  72          */
  73 #endif
  74         corosync_cfg_finalize(cfg_handle);
  75         cfg_handle = 0;
  76     }
  77 }
  78 
  79 static gboolean
  80 cluster_reconnect_cb(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82     if (cluster_connect_cfg()) {
  83         mainloop_timer_del(reconnect_timer);
  84         reconnect_timer = NULL;
  85         crm_notice("Cluster reconnect succeeded");
  86         pacemakerd_read_config();
  87         restart_cluster_subdaemons();
  88         return G_SOURCE_REMOVE;
  89     } else {
  90         crm_info("Cluster reconnect failed "
  91                  "(connection will be reattempted once per second)");
  92     }
  93     /*
  94      * In theory this will continue forever. In practice the CIB connection from
  95      * attrd will timeout and shut down Pacemaker when it gets bored.
  96      */
  97     return G_SOURCE_CONTINUE;
  98 }
  99 
 100 
 101 static void
 102 cfg_connection_destroy(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 103 {
 104     crm_warn("Lost connection to cluster layer "
 105              "(connection will be reattempted once per second)");
 106     corosync_cfg_finalize(cfg_handle);
 107     cfg_handle = 0;
 108     reconnect_timer = mainloop_timer_add("corosync reconnect", 1000, TRUE, cluster_reconnect_cb, NULL);
 109     mainloop_timer_start(reconnect_timer);
 110 }
 111 
 112 void
 113 cluster_disconnect_cfg(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 114 {
 115     close_cfg();
 116     if (reconnect_timer != NULL) {
 117         /* The mainloop should be gone by this point, so this isn't necessary,
 118          * but cleaning up memory should make valgrind happier.
 119          */
 120         mainloop_timer_del(reconnect_timer);
 121         reconnect_timer = NULL;
 122     }
 123 }
 124 
 125 #define cs_repeat(counter, max, code) do {              \
 126         code;                                           \
 127         if(rc == CS_ERR_TRY_AGAIN || rc == CS_ERR_QUEUE_FULL) {  \
 128             counter++;                                  \
 129             crm_debug("Retrying Corosync operation after %ds", counter);    \
 130             sleep(counter);                             \
 131         } else {                                        \
 132             break;                                      \
 133         }                                               \
 134     } while(counter < max)
 135 
 136 gboolean
 137 cluster_connect_cfg(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 138 {
 139     cs_error_t rc;
 140     int fd = -1, retries = 0, rv;
 141     uid_t found_uid = 0;
 142     gid_t found_gid = 0;
 143     pid_t found_pid = 0;
 144     uint32_t nodeid;
 145 
 146     static struct mainloop_fd_callbacks cfg_fd_callbacks = {
 147         .dispatch = pcmk_cfg_dispatch,
 148         .destroy = cfg_connection_destroy,
 149     };
 150 
 151     cs_repeat(retries, 30, rc = corosync_cfg_initialize(&cfg_handle, &cfg_callbacks));
 152 
 153     if (rc != CS_OK) {
 154         crm_crit("Could not connect to Corosync CFG: %s " CRM_XS " rc=%d",
 155                  cs_strerror(rc), rc);
 156         return FALSE;
 157     }
 158 
 159     rc = corosync_cfg_fd_get(cfg_handle, &fd);
 160     if (rc != CS_OK) {
 161         crm_crit("Could not get Corosync CFG descriptor: %s " CRM_XS " rc=%d",
 162                  cs_strerror(rc), rc);
 163         goto bail;
 164     }
 165 
 166     /* CFG provider run as root (in given user namespace, anyway)? */
 167     if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
 168                                             &found_uid, &found_gid))) {
 169         crm_crit("Rejecting Corosync CFG provider because process %lld "
 170                  "is running as uid %lld gid %lld, not root",
 171                   (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
 172                  (long long) found_uid, (long long) found_gid);
 173         goto bail;
 174     } else if (rv < 0) {
 175         crm_crit("Could not authenticate Corosync CFG provider: %s "
 176                  CRM_XS " rc=%d", strerror(-rv), -rv);
 177         goto bail;
 178     }
 179 
 180     retries = 0;
 181     cs_repeat(retries, 30, rc = corosync_cfg_local_get(cfg_handle, &nodeid));
 182     if (rc != CS_OK) {
 183         crm_crit("Could not get local node ID from Corosync: %s "
 184                  CRM_XS " rc=%d", cs_strerror(rc), rc);
 185         goto bail;
 186     }
 187     crm_debug("Corosync reports local node ID is %lu", (unsigned long) nodeid);
 188 
 189 #ifdef HAVE_COROSYNC_CFG_TRACKSTART
 190     retries = 0;
 191     cs_repeat(retries, 30, rc = corosync_cfg_trackstart(cfg_handle, 0));
 192     if (rc != CS_OK) {
 193         crm_crit("Could not enable Corosync CFG shutdown tracker: %s " CRM_XS " rc=%d",
 194                  cs_strerror(rc), rc);
 195         goto bail;
 196     }
 197 #endif
 198 
 199     mainloop_add_fd("corosync-cfg", G_PRIORITY_DEFAULT, fd, &cfg_handle, &cfg_fd_callbacks);
 200     return TRUE;
 201 
 202   bail:
 203     corosync_cfg_finalize(cfg_handle);
 204     return FALSE;
 205 }
 206 
 207 void
 208 pcmkd_shutdown_corosync(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 209 {
 210     cs_error_t rc;
 211 
 212     if (cfg_handle == 0) {
 213         crm_warn("Unable to shut down Corosync: No connection");
 214         return;
 215     }
 216     crm_info("Asking Corosync to shut down");
 217     rc = corosync_cfg_try_shutdown(cfg_handle,
 218                                     COROSYNC_CFG_SHUTDOWN_FLAG_IMMEDIATE);
 219     if (rc == CS_OK) {
 220         close_cfg();
 221     } else {
 222         crm_warn("Corosync shutdown failed: %s " CRM_XS " rc=%d",
 223                  cs_strerror(rc), rc);
 224     }
 225 }
 226 
 227 bool
 228 pcmkd_corosync_connected(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230     cpg_handle_t local_handle = 0;
 231     cpg_model_v1_data_t cpg_model_info = {CPG_MODEL_V1, NULL, NULL, NULL, 0};
 232     int fd = -1;
 233 
 234     if (cpg_model_initialize(&local_handle, CPG_MODEL_V1, (cpg_model_data_t *) &cpg_model_info, NULL) != CS_OK) {
 235         return false;
 236     }
 237 
 238     if (cpg_fd_get(local_handle, &fd) != CS_OK) {
 239         return false;
 240     }
 241 
 242     cpg_finalize(local_handle);
 243 
 244     return true;
 245 }
 246 
 247 /* =::=::=::= Configuration =::=::=::= */
 248 static int
 249 get_config_opt(uint64_t unused, cmap_handle_t object_handle, const char *key, char **value,
     /* [previous][next][first][last][top][bottom][index][help] */
 250                const char *fallback)
 251 {
 252     int rc = 0, retries = 0;
 253 
 254     cs_repeat(retries, 5, rc = cmap_get_string(object_handle, key, value));
 255     if (rc != CS_OK) {
 256         crm_trace("Search for %s failed %d, defaulting to %s", key, rc, fallback);
 257         pcmk__str_update(value, fallback);
 258     }
 259     crm_trace("%s: %s", key, *value);
 260     return rc;
 261 }
 262 
 263 gboolean
 264 pacemakerd_read_config(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 265 {
 266     cs_error_t rc = CS_OK;
 267     int retries = 0;
 268     cmap_handle_t local_handle;
 269     uint64_t config = 0;
 270     int fd = -1;
 271     uid_t found_uid = 0;
 272     gid_t found_gid = 0;
 273     pid_t found_pid = 0;
 274     int rv;
 275     enum pcmk_cluster_layer cluster_layer = pcmk_cluster_layer_unknown;
 276     const char *cluster_layer_s = NULL;
 277 
 278     // There can be only one possibility
 279     do {
 280         rc = pcmk__init_cmap(&local_handle);
 281         if (rc != CS_OK) {
 282             retries++;
 283             crm_info("Could not connect to Corosync CMAP: %s (retrying in %ds) "
 284                      CRM_XS " rc=%d", cs_strerror(rc), retries, rc);
 285             sleep(retries);
 286 
 287         } else {
 288             break;
 289         }
 290 
 291     } while (retries < 5);
 292 
 293     if (rc != CS_OK) {
 294         crm_crit("Could not connect to Corosync CMAP: %s "
 295                  CRM_XS " rc=%d", cs_strerror(rc), rc);
 296         return FALSE;
 297     }
 298 
 299     rc = cmap_fd_get(local_handle, &fd);
 300     if (rc != CS_OK) {
 301         crm_crit("Could not get Corosync CMAP descriptor: %s " CRM_XS " rc=%d",
 302                  cs_strerror(rc), rc);
 303         cmap_finalize(local_handle);
 304         return FALSE;
 305     }
 306 
 307     /* CMAP provider run as root (in given user namespace, anyway)? */
 308     if (!(rv = crm_ipc_is_authentic_process(fd, (uid_t) 0,(gid_t) 0, &found_pid,
 309                                             &found_uid, &found_gid))) {
 310         crm_crit("Rejecting Corosync CMAP provider because process %lld "
 311                  "is running as uid %lld gid %lld, not root",
 312                  (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
 313                  (long long) found_uid, (long long) found_gid);
 314         cmap_finalize(local_handle);
 315         return FALSE;
 316     } else if (rv < 0) {
 317         crm_crit("Could not authenticate Corosync CMAP provider: %s "
 318                  CRM_XS " rc=%d", strerror(-rv), -rv);
 319         cmap_finalize(local_handle);
 320         return FALSE;
 321     }
 322 
 323     cluster_layer = pcmk_get_cluster_layer();
 324     cluster_layer_s = pcmk_cluster_layer_text(cluster_layer);
 325 
 326     if (cluster_layer != pcmk_cluster_layer_corosync) {
 327         crm_crit("Expected Corosync cluster layer but detected %s "
 328                  CRM_XS " cluster_layer=%d",
 329                  cluster_layer_s, cluster_layer);
 330         return FALSE;
 331     }
 332 
 333     crm_info("Reading configuration for %s cluster layer", cluster_layer_s);
 334     pcmk__set_env_option(PCMK__ENV_CLUSTER_TYPE, PCMK_VALUE_COROSYNC, true);
 335 
 336     // @COMPAT Drop at 3.0.0; added unused in 1.1.9
 337     pcmk__set_env_option(PCMK__ENV_QUORUM_TYPE, PCMK_VALUE_COROSYNC, true);
 338 
 339     // If debug logging is not configured, check whether corosync has it
 340     if (pcmk__env_option(PCMK__ENV_DEBUG) == NULL) {
 341         char *debug_enabled = NULL;
 342 
 343         get_config_opt(config, local_handle, "logging.debug", &debug_enabled, "off");
 344 
 345         if (crm_is_true(debug_enabled)) {
 346             pcmk__set_env_option(PCMK__ENV_DEBUG, "1", true);
 347             if (get_crm_log_level() < LOG_DEBUG) {
 348                 set_crm_log_level(LOG_DEBUG);
 349             }
 350 
 351         } else {
 352             pcmk__set_env_option(PCMK__ENV_DEBUG, "0", true);
 353         }
 354 
 355         free(debug_enabled);
 356     }
 357 
 358     if(local_handle){
 359         gid_t gid = 0;
 360         if (pcmk_daemon_user(NULL, &gid) < 0) {
 361             crm_warn("Could not authorize group with Corosync " CRM_XS
 362                      " No group found for user %s", CRM_DAEMON_USER);
 363 
 364         } else {
 365             char key[PATH_MAX];
 366             snprintf(key, PATH_MAX, "uidgid.gid.%u", gid);
 367             rc = cmap_set_uint8(local_handle, key, 1);
 368             if (rc != CS_OK) {
 369                 crm_warn("Could not authorize group with Corosync: %s " CRM_XS
 370                          " group=%u rc=%d", pcmk__cs_err_str(rc), gid, rc);
 371             }
 372         }
 373     }
 374     cmap_finalize(local_handle);
 375 
 376     return TRUE;
 377 }

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