root/daemons/pacemakerd/pcmkd_subdaemons.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmkd_cluster_connected
  2. check_next_subdaemon
  3. escalate_shutdown
  4. pcmk_child_exit
  5. pcmk_process_exit
  6. pcmk_shutdown_worker
  7. start_child
  8. child_liveness
  9. find_and_track_existing_processes
  10. init_children_processes
  11. pcmk_shutdown
  12. restart_cluster_subdaemons
  13. stop_child

   1 /*
   2  * Copyright 2010-2022 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 <errno.h>
  14 #include <grp.h>
  15 #include <signal.h>
  16 #include <stdbool.h>
  17 #include <stdlib.h>
  18 #include <string.h>
  19 #include <sys/types.h>
  20 #include <time.h>
  21 #include <unistd.h>
  22 
  23 #include <crm/cluster.h>
  24 #include <crm/msg_xml.h>
  25 
  26 typedef struct pcmk_child_s {
  27     pid_t pid;
  28     int respawn_count;
  29     bool respawn;
  30     const char *name;
  31     const char *uid;
  32     const char *command;
  33     const char *endpoint;  /* IPC server name */
  34     bool needs_cluster;
  35     int check_count;
  36 
  37     /* Anything below here will be dynamically initialized */
  38     bool needs_retry;
  39     bool active_before_startup;
  40 } pcmk_child_t;
  41 
  42 #define PCMK_PROCESS_CHECK_INTERVAL 1
  43 #define PCMK_PROCESS_CHECK_RETRIES  5
  44 #define SHUTDOWN_ESCALATION_PERIOD  180000  /* 3m */
  45 
  46 /* Index into the array below */
  47 #define PCMK_CHILD_CONTROLD  5
  48 
  49 static pcmk_child_t pcmk_children[] = {
  50     {
  51         0, 0, true,  "pacemaker-based", CRM_DAEMON_USER,
  52         CRM_DAEMON_DIR "/pacemaker-based", PCMK__SERVER_BASED_RO,
  53         true
  54     },
  55     {
  56         0, 0, true, "pacemaker-fenced", NULL,
  57         CRM_DAEMON_DIR "/pacemaker-fenced", "stonith-ng",
  58         true
  59     },
  60     {
  61         0, 0, true,  "pacemaker-execd", NULL,
  62         CRM_DAEMON_DIR "/pacemaker-execd", CRM_SYSTEM_LRMD,
  63         false
  64     },
  65     {
  66         0, 0, true, "pacemaker-attrd", CRM_DAEMON_USER,
  67         CRM_DAEMON_DIR "/pacemaker-attrd", T_ATTRD,
  68         true
  69     },
  70     {
  71         0, 0, true, "pacemaker-schedulerd", CRM_DAEMON_USER,
  72         CRM_DAEMON_DIR "/pacemaker-schedulerd", CRM_SYSTEM_PENGINE,
  73         false
  74     },
  75     {
  76         0, 0, true, "pacemaker-controld", CRM_DAEMON_USER,
  77         CRM_DAEMON_DIR "/pacemaker-controld", CRM_SYSTEM_CRMD,
  78         true
  79     },
  80 };
  81 
  82 static char *opts_default[] = { NULL, NULL };
  83 static char *opts_vgrind[] = { NULL, NULL, NULL, NULL, NULL };
  84 
  85 crm_trigger_t *shutdown_trigger = NULL;
  86 crm_trigger_t *startup_trigger = NULL;
  87 time_t subdaemon_check_progress = 0;
  88 
  89 // Whether we need root group access to talk to cluster layer
  90 static bool need_root_group = true;
  91 
  92 /* When contacted via pacemakerd-api by a client having sbd in
  93  * the name we assume it is sbd-daemon which wants to know
  94  * if pacemakerd shutdown gracefully.
  95  * Thus when everything is shutdown properly pacemakerd
  96  * waits till it has reported the graceful completion of
  97  * shutdown to sbd and just when sbd-client closes the
  98  * connection we can assume that the report has arrived
  99  * properly so that pacemakerd can finally exit.
 100  * Following two variables are used to track that handshake.
 101  */
 102 unsigned int shutdown_complete_state_reported_to = 0;
 103 gboolean shutdown_complete_state_reported_client_closed = FALSE;
 104 
 105 /* state we report when asked via pacemakerd-api status-ping */
 106 const char *pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_INIT;
 107 gboolean running_with_sbd = FALSE; /* local copy */
 108 
 109 GMainLoop *mainloop = NULL;
 110 
 111 static gboolean fatal_error = FALSE;
 112 
 113 static int child_liveness(pcmk_child_t *child);
 114 static gboolean escalate_shutdown(gpointer data);
 115 static int start_child(pcmk_child_t * child);
 116 static void pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode);
 117 static void pcmk_process_exit(pcmk_child_t * child);
 118 static gboolean pcmk_shutdown_worker(gpointer user_data);
 119 static gboolean stop_child(pcmk_child_t * child, int signal);
 120 
 121 static bool
 122 pcmkd_cluster_connected(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124 #if SUPPORT_COROSYNC
 125     return pcmkd_corosync_connected();
 126 #else
 127     return true;
 128 #endif
 129 }
 130 
 131 static gboolean
 132 check_next_subdaemon(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134     static int next_child = 0;
 135     int rc = child_liveness(&pcmk_children[next_child]);
 136 
 137     crm_trace("Checked %s[%lld]: %s (%d)",
 138               pcmk_children[next_child].name,
 139               (long long) PCMK__SPECIAL_PID_AS_0(pcmk_children[next_child].pid),
 140               pcmk_rc_str(rc), rc);
 141 
 142     switch (rc) {
 143         case pcmk_rc_ok:
 144             pcmk_children[next_child].check_count = 0;
 145             subdaemon_check_progress = time(NULL);
 146             break;
 147         case pcmk_rc_ipc_pid_only: // This case: it was previously OK
 148             pcmk_children[next_child].check_count++;
 149             if (pcmk_children[next_child].check_count >= PCMK_PROCESS_CHECK_RETRIES) {
 150                 crm_err("%s[%lld] is unresponsive to ipc after %d tries but "
 151                         "we found the pid so have it killed that we can restart",
 152                         pcmk_children[next_child].name,
 153                         (long long) PCMK__SPECIAL_PID_AS_0(
 154                             pcmk_children[next_child].pid),
 155                         pcmk_children[next_child].check_count);
 156                 stop_child(&pcmk_children[next_child], SIGKILL);
 157                 if (pcmk_children[next_child].respawn) {
 158                     /* as long as the respawn-limit isn't reached
 159                        give it another round of check retries
 160                      */
 161                     pcmk_children[next_child].check_count = 0;
 162                 }
 163             } else {
 164                 crm_notice("%s[%lld] is unresponsive to ipc after %d tries",
 165                         pcmk_children[next_child].name,
 166                         (long long) PCMK__SPECIAL_PID_AS_0(
 167                             pcmk_children[next_child].pid),
 168                         pcmk_children[next_child].check_count);
 169                 if (pcmk_children[next_child].respawn) {
 170                     /* as long as the respawn-limit isn't reached
 171                        and we haven't run out of connect retries
 172                        we account this as progress we are willing
 173                        to tell to sbd
 174                      */
 175                     subdaemon_check_progress = time(NULL);
 176                 }
 177             }
 178             /* go to the next child and see if
 179                we can make progress there
 180              */
 181             break;
 182         case pcmk_rc_ipc_unresponsive:
 183             if (!pcmk_children[next_child].respawn) {
 184                 /* if a subdaemon is down and we don't want it
 185                    to be restarted this is a success during
 186                    shutdown. if it isn't restarted anymore
 187                    due to MAX_RESPAWN it is
 188                    rather no success.
 189                  */
 190                 if (pcmk_children[next_child].respawn_count <= MAX_RESPAWN) {
 191                     subdaemon_check_progress = time(NULL);
 192                 }
 193             }
 194             if (!pcmk_children[next_child].active_before_startup) {
 195                 crm_trace("found %s[%lld] missing - signal-handler "
 196                           "will take care of it",
 197                            pcmk_children[next_child].name,
 198                            (long long) PCMK__SPECIAL_PID_AS_0(
 199                             pcmk_children[next_child].pid));
 200                 break;
 201             }
 202             if (pcmk_children[next_child].respawn) {
 203                 crm_err("%s[%lld] terminated",
 204                         pcmk_children[next_child].name,
 205                         (long long) PCMK__SPECIAL_PID_AS_0(
 206                             pcmk_children[next_child].pid));
 207             } else {
 208                 /* orderly shutdown */
 209                 crm_notice("%s[%lld] terminated",
 210                            pcmk_children[next_child].name,
 211                            (long long) PCMK__SPECIAL_PID_AS_0(
 212                                 pcmk_children[next_child].pid));
 213             }
 214             pcmk_process_exit(&(pcmk_children[next_child]));
 215             break;
 216         default:
 217             crm_exit(CRM_EX_FATAL);
 218             break;  /* static analysis/noreturn */
 219     }
 220 
 221     next_child++;
 222     if (next_child >= PCMK__NELEM(pcmk_children)) {
 223         next_child = 0;
 224     }
 225 
 226     return G_SOURCE_CONTINUE;
 227 }
 228 
 229 static gboolean
 230 escalate_shutdown(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 231 {
 232     pcmk_child_t *child = data;
 233 
 234     if (child->pid == PCMK__SPECIAL_PID) {
 235         pcmk_process_exit(child);
 236 
 237     } else if (child->pid != 0) {
 238         /* Use SIGSEGV instead of SIGKILL to create a core so we can see what it was up to */
 239         crm_err("Child %s not terminating in a timely manner, forcing", child->name);
 240         stop_child(child, SIGSEGV);
 241     }
 242     return FALSE;
 243 }
 244 
 245 static void
 246 pcmk_child_exit(mainloop_child_t * p, pid_t pid, int core, int signo, int exitcode)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248     pcmk_child_t *child = mainloop_child_userdata(p);
 249     const char *name = mainloop_child_name(p);
 250 
 251     if (signo) {
 252         do_crm_log(((signo == SIGKILL)? LOG_WARNING : LOG_ERR),
 253                    "%s[%d] terminated with signal %d (%s)%s",
 254                    name, pid, signo, strsignal(signo),
 255                    (core? " and dumped core" : ""));
 256 
 257     } else {
 258         switch(exitcode) {
 259             case CRM_EX_OK:
 260                 crm_info("%s[%d] exited with status %d (%s)",
 261                          name, pid, exitcode, crm_exit_str(exitcode));
 262                 break;
 263 
 264             case CRM_EX_FATAL:
 265                 crm_warn("Shutting cluster down because %s[%d] had fatal failure",
 266                          name, pid);
 267                 child->respawn = false;
 268                 fatal_error = TRUE;
 269                 pcmk_shutdown(SIGTERM);
 270                 break;
 271 
 272             case CRM_EX_PANIC:
 273                 crm_emerg("%s[%d] instructed the machine to reset", name, pid);
 274                 child->respawn = false;
 275                 fatal_error = TRUE;
 276                 pcmk__panic(__func__);
 277                 pcmk_shutdown(SIGTERM);
 278                 break;
 279 
 280             default:
 281                 crm_err("%s[%d] exited with status %d (%s)",
 282                         name, pid, exitcode, crm_exit_str(exitcode));
 283                 break;
 284         }
 285     }
 286 
 287     pcmk_process_exit(child);
 288 }
 289 
 290 static void
 291 pcmk_process_exit(pcmk_child_t * child)
     /* [previous][next][first][last][top][bottom][index][help] */
 292 {
 293     child->pid = 0;
 294     child->active_before_startup = false;
 295     child->check_count = 0;
 296 
 297     child->respawn_count += 1;
 298     if (child->respawn_count > MAX_RESPAWN) {
 299         crm_err("Child respawn count exceeded by %s", child->name);
 300         child->respawn = false;
 301     }
 302 
 303     if (shutdown_trigger) {
 304         /* resume step-wise shutdown (returned TRUE yields no parallelizing) */
 305         mainloop_set_trigger(shutdown_trigger);
 306 
 307     } else if (!child->respawn) {
 308         /* nothing to do */
 309 
 310     } else if (crm_is_true(getenv("PCMK_fail_fast"))) {
 311         crm_err("Rebooting system because of %s", child->name);
 312         pcmk__panic(__func__);
 313 
 314     } else if (child_liveness(child) == pcmk_rc_ok) {
 315         crm_warn("One-off suppressing strict respawning of a child process %s,"
 316                  " appears alright per %s IPC end-point",
 317                  child->name, child->endpoint);
 318 
 319     } else if (child->needs_cluster && !pcmkd_cluster_connected()) {
 320         crm_notice("Not respawning %s subdaemon until cluster returns",
 321                    child->name);
 322         child->needs_retry = true;
 323 
 324     } else {
 325         crm_notice("Respawning %s subdaemon after unexpected exit",
 326                    child->name);
 327         start_child(child);
 328     }
 329 }
 330 
 331 static gboolean
 332 pcmk_shutdown_worker(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 333 {
 334     static int phase = PCMK__NELEM(pcmk_children) - 1;
 335     static time_t next_log = 0;
 336 
 337     if (phase == PCMK__NELEM(pcmk_children) - 1) {
 338         crm_notice("Shutting down Pacemaker");
 339         pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_SHUTTINGDOWN;
 340     }
 341 
 342     for (; phase >= 0; phase--) {
 343         pcmk_child_t *child = &(pcmk_children[phase]);
 344 
 345         if (child->pid != 0) {
 346             time_t now = time(NULL);
 347 
 348             if (child->respawn) {
 349                 if (child->pid == PCMK__SPECIAL_PID) {
 350                     crm_warn("The process behind %s IPC cannot be"
 351                              " terminated, so either wait the graceful"
 352                              " period of %ld s for its native termination"
 353                              " if it vitally depends on some other daemons"
 354                              " going down in a controlled way already,"
 355                              " or locate and kill the correct %s process"
 356                              " on your own; set PCMK_fail_fast=1 to avoid"
 357                              " this altogether next time around",
 358                              child->name, (long) SHUTDOWN_ESCALATION_PERIOD,
 359                              child->command);
 360                 }
 361                 next_log = now + 30;
 362                 child->respawn = false;
 363                 stop_child(child, SIGTERM);
 364                 if (phase < PCMK_CHILD_CONTROLD) {
 365                     g_timeout_add(SHUTDOWN_ESCALATION_PERIOD,
 366                                   escalate_shutdown, child);
 367                 }
 368 
 369             } else if (now >= next_log) {
 370                 next_log = now + 30;
 371                 crm_notice("Still waiting for %s to terminate "
 372                            CRM_XS " pid=%lld",
 373                            child->name, (long long) child->pid);
 374             }
 375             return TRUE;
 376         }
 377 
 378         /* cleanup */
 379         crm_debug("%s confirmed stopped", child->name);
 380         child->pid = 0;
 381     }
 382 
 383     crm_notice("Shutdown complete");
 384     pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_SHUTDOWNCOMPLETE;
 385     if (!fatal_error && running_with_sbd &&
 386         pcmk__get_sbd_sync_resource_startup() &&
 387         !shutdown_complete_state_reported_client_closed) {
 388         crm_notice("Waiting for SBD to pick up shutdown-complete-state.");
 389         return TRUE;
 390     }
 391 
 392     {
 393         const char *delay = pcmk__env_option(PCMK__ENV_SHUTDOWN_DELAY);
 394         if(delay) {
 395             sync();
 396             pcmk__sleep_ms(crm_get_msec(delay));
 397         }
 398     }
 399 
 400     g_main_loop_quit(mainloop);
 401 
 402     if (fatal_error) {
 403         crm_notice("Shutting down and staying down after fatal error");
 404 #ifdef SUPPORT_COROSYNC
 405         pcmkd_shutdown_corosync();
 406 #endif
 407         crm_exit(CRM_EX_FATAL);
 408     }
 409 
 410     return TRUE;
 411 }
 412 
 413 /* TODO once libqb is taught to juggle with IPC end-points carried over as
 414         bare file descriptor (https://github.com/ClusterLabs/libqb/issues/325)
 415         it shall hand over these descriptors here if/once they are successfully
 416         pre-opened in (presumably) child_liveness(), to avoid any remaining
 417         room for races */
 418  // \return Standard Pacemaker return code
 419 static int
 420 start_child(pcmk_child_t * child)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422     uid_t uid = 0;
 423     gid_t gid = 0;
 424     gboolean use_valgrind = FALSE;
 425     gboolean use_callgrind = FALSE;
 426     const char *env_valgrind = getenv("PCMK_valgrind_enabled");
 427     const char *env_callgrind = getenv("PCMK_callgrind_enabled");
 428 
 429     child->active_before_startup = false;
 430     child->check_count = 0;
 431 
 432     if (child->command == NULL) {
 433         crm_info("Nothing to do for child \"%s\"", child->name);
 434         return pcmk_rc_ok;
 435     }
 436 
 437     if (env_callgrind != NULL && crm_is_true(env_callgrind)) {
 438         use_callgrind = TRUE;
 439         use_valgrind = TRUE;
 440 
 441     } else if (env_callgrind != NULL && strstr(env_callgrind, child->name)) {
 442         use_callgrind = TRUE;
 443         use_valgrind = TRUE;
 444 
 445     } else if (env_valgrind != NULL && crm_is_true(env_valgrind)) {
 446         use_valgrind = TRUE;
 447 
 448     } else if (env_valgrind != NULL && strstr(env_valgrind, child->name)) {
 449         use_valgrind = TRUE;
 450     }
 451 
 452     if (use_valgrind && strlen(VALGRIND_BIN) == 0) {
 453         crm_warn("Cannot enable valgrind for %s:"
 454                  " The location of the valgrind binary is unknown", child->name);
 455         use_valgrind = FALSE;
 456     }
 457 
 458     if (child->uid) {
 459         if (crm_user_lookup(child->uid, &uid, &gid) < 0) {
 460             crm_err("Invalid user (%s) for %s: not found", child->uid, child->name);
 461             return EACCES;
 462         }
 463         crm_info("Using uid=%u and group=%u for process %s", uid, gid, child->name);
 464     }
 465 
 466     child->pid = fork();
 467     CRM_ASSERT(child->pid != -1);
 468 
 469     if (child->pid > 0) {
 470         /* parent */
 471         mainloop_child_add(child->pid, 0, child->name, child, pcmk_child_exit);
 472 
 473         crm_info("Forked child %lld for process %s%s",
 474                  (long long) child->pid, child->name,
 475                  use_valgrind ? " (valgrind enabled: " VALGRIND_BIN ")" : "");
 476         return pcmk_rc_ok;
 477 
 478     } else {
 479         /* Start a new session */
 480         (void)setsid();
 481 
 482         /* Setup the two alternate arg arrays */
 483         opts_vgrind[0] = strdup(VALGRIND_BIN);
 484         if (use_callgrind) {
 485             opts_vgrind[1] = strdup("--tool=callgrind");
 486             opts_vgrind[2] = strdup("--callgrind-out-file=" CRM_STATE_DIR "/callgrind.out.%p");
 487             opts_vgrind[3] = strdup(child->command);
 488             opts_vgrind[4] = NULL;
 489         } else {
 490             opts_vgrind[1] = strdup(child->command);
 491             opts_vgrind[2] = NULL;
 492             opts_vgrind[3] = NULL;
 493             opts_vgrind[4] = NULL;
 494         }
 495         opts_default[0] = strdup(child->command);
 496 
 497         if(gid) {
 498             // Drop root group access if not needed
 499             if (!need_root_group && (setgid(gid) < 0)) {
 500                 crm_warn("Could not set group to %d: %s", gid, strerror(errno));
 501             }
 502 
 503             /* Initialize supplementary groups to only those always granted to
 504              * the user, plus haclient (so we can access IPC).
 505              */
 506             if (initgroups(child->uid, gid) < 0) {
 507                 crm_err("Cannot initialize groups for %s: %s (%d)",
 508                         child->uid, pcmk_rc_str(errno), errno);
 509             }
 510         }
 511 
 512         if (uid && setuid(uid) < 0) {
 513             crm_warn("Could not set user to %s (id %d): %s",
 514                      child->uid, uid, strerror(errno));
 515         }
 516 
 517         pcmk__close_fds_in_child(true);
 518 
 519         pcmk__open_devnull(O_RDONLY);   // stdin (fd 0)
 520         pcmk__open_devnull(O_WRONLY);   // stdout (fd 1)
 521         pcmk__open_devnull(O_WRONLY);   // stderr (fd 2)
 522 
 523         if (use_valgrind) {
 524             (void)execvp(VALGRIND_BIN, opts_vgrind);
 525         } else {
 526             (void)execvp(child->command, opts_default);
 527         }
 528         crm_crit("Could not execute %s: %s", child->command, strerror(errno));
 529         crm_exit(CRM_EX_FATAL);
 530     }
 531     return pcmk_rc_ok;          /* never reached */
 532 }
 533 
 534 /*!
 535  * \internal
 536  * \brief Check the liveness of the child based on IPC name and PID if tracked
 537  *
 538  * \param[in,out] child  Child tracked data
 539  *
 540  * \return Standard Pacemaker return code
 541  *
 542  * \note Return codes of particular interest include pcmk_rc_ipc_unresponsive
 543  *       indicating that no trace of IPC liveness was detected,
 544  *       pcmk_rc_ipc_unauthorized indicating that the IPC endpoint is blocked by
 545  *       an unauthorized process, and pcmk_rc_ipc_pid_only indicating that
 546  *       the child is up by PID but not IPC end-point (possibly starting).
 547  * \note This function doesn't modify any of \p child members but \c pid,
 548  *       and is not actively toying with processes as such but invoking
 549  *       \c stop_child in one particular case (there's for some reason
 550  *       a different authentic holder of the IPC end-point).
 551  */
 552 static int
 553 child_liveness(pcmk_child_t *child)
     /* [previous][next][first][last][top][bottom][index][help] */
 554 {
 555     uid_t cl_uid = 0;
 556     gid_t cl_gid = 0;
 557     const uid_t root_uid = 0;
 558     const gid_t root_gid = 0;
 559     const uid_t *ref_uid;
 560     const gid_t *ref_gid;
 561     int rc = pcmk_rc_ipc_unresponsive;
 562     pid_t ipc_pid = 0;
 563 
 564     if (child->endpoint == NULL
 565             && (child->pid <= 0 || child->pid == PCMK__SPECIAL_PID)) {
 566         crm_err("Cannot track child %s for missing both API end-point and PID",
 567                 child->name);
 568         rc = EINVAL; // Misuse of function when child is not trackable
 569 
 570     } else if (child->endpoint != NULL) {
 571         int legacy_rc = pcmk_ok;
 572 
 573         if (child->uid == NULL) {
 574             ref_uid = &root_uid;
 575             ref_gid = &root_gid;
 576         } else {
 577             ref_uid = &cl_uid;
 578             ref_gid = &cl_gid;
 579             legacy_rc = pcmk_daemon_user(&cl_uid, &cl_gid);
 580         }
 581 
 582         if (legacy_rc < 0) {
 583             rc = pcmk_legacy2rc(legacy_rc);
 584             crm_err("Could not find user and group IDs for user %s: %s "
 585                     CRM_XS " rc=%d", CRM_DAEMON_USER, pcmk_rc_str(rc), rc);
 586         } else {
 587             rc = pcmk__ipc_is_authentic_process_active(child->endpoint,
 588                                                        *ref_uid, *ref_gid,
 589                                                        &ipc_pid);
 590             if ((rc == pcmk_rc_ok) || (rc == pcmk_rc_ipc_unresponsive)) {
 591                 if (child->pid <= 0) {
 592                     /* If rc is pcmk_rc_ok, ipc_pid is nonzero and this
 593                      * initializes a new child. If rc is
 594                      * pcmk_rc_ipc_unresponsive, ipc_pid is zero, and we will
 595                      * investigate further.
 596                      */
 597                     child->pid = ipc_pid;
 598                 } else if ((ipc_pid != 0) && (child->pid != ipc_pid)) {
 599                     /* An unexpected (but authorized) process is responding to
 600                      * IPC. Investigate further.
 601                      */
 602                     rc = pcmk_rc_ipc_unresponsive;
 603                 }
 604             }
 605         }
 606     }
 607 
 608     if (rc == pcmk_rc_ipc_unresponsive) {
 609         /* If we get here, a child without IPC is being tracked, no IPC liveness
 610          * has been detected, or IPC liveness has been detected with an
 611          * unexpected (but authorized) process. This is safe on FreeBSD since
 612          * the only change possible from a proper child's PID into "special" PID
 613          * of 1 behind more loosely related process.
 614          */
 615         int ret = pcmk__pid_active(child->pid, child->name);
 616 
 617         if (ipc_pid && ((ret != pcmk_rc_ok)
 618                         || ipc_pid == PCMK__SPECIAL_PID
 619                         || (pcmk__pid_active(ipc_pid,
 620                                              child->name) == pcmk_rc_ok))) {
 621             /* An unexpected (but authorized) process was detected at the IPC
 622              * endpoint, and either it is active, or the child we're tracking is
 623              * not.
 624              */
 625 
 626             if (ret == pcmk_rc_ok) {
 627                 /* The child we're tracking is active. Kill it, and adopt the
 628                  * detected process. This assumes that our children don't fork
 629                  * (thus getting a different PID owning the IPC), but rather the
 630                  * tracking got out of sync because of some means external to
 631                  * Pacemaker, and adopting the detected process is better than
 632                  * killing it and possibly having to spawn a new child.
 633                  */
 634                 /* not possessing IPC, afterall (what about corosync CPG?) */
 635                 stop_child(child, SIGKILL);
 636             }
 637             rc = pcmk_rc_ok;
 638             child->pid = ipc_pid;
 639         } else if (ret == pcmk_rc_ok) {
 640             // Our tracked child's PID was found active, but not its IPC
 641             rc = pcmk_rc_ipc_pid_only;
 642         } else if ((child->pid == 0) && (ret == EINVAL)) {
 643             // FreeBSD can return EINVAL
 644             rc = pcmk_rc_ipc_unresponsive;
 645         } else {
 646             switch (ret) {
 647                 case EACCES:
 648                     rc = pcmk_rc_ipc_unauthorized;
 649                     break;
 650                 case ESRCH:
 651                     rc = pcmk_rc_ipc_unresponsive;
 652                     break;
 653                 default:
 654                     rc = ret;
 655                     break;
 656             }
 657         }
 658     }
 659     return rc;
 660 }
 661 
 662 /*!
 663  * \internal
 664  * \brief Initial one-off check of the pre-existing "child" processes
 665  *
 666  * With "child" process, we mean the subdaemon that defines an API end-point
 667  * (all of them do as of the comment) -- the possible complement is skipped
 668  * as it is deemed it has no such shared resources to cause conflicts about,
 669  * hence it can presumably be started anew without hesitation.
 670  * If that won't hold true in the future, the concept of a shared resource
 671  * will have to be generalized beyond the API end-point.
 672  *
 673  * For boundary cases that the "child" is still starting (IPC end-point is yet
 674  * to be witnessed), or more rarely (practically FreeBSD only), when there's
 675  * a pre-existing "untrackable" authentic process, we give the situation some
 676  * time to possibly unfold in the right direction, meaning that said socket
 677  * will appear or the unattainable process will disappear per the observable
 678  * IPC, respectively.
 679  *
 680  * \return Standard Pacemaker return code
 681  *
 682  * \note Since this gets run at the very start, \c respawn_count fields
 683  *       for particular children get temporarily overloaded with "rounds
 684  *       of waiting" tracking, restored once we are about to finish with
 685  *       success (i.e. returning value >=0) and will remain unrestored
 686  *       otherwise.  One way to suppress liveness detection logic for
 687  *       particular child is to set the said value to a negative number.
 688  */
 689 #define WAIT_TRIES 4  /* together with interleaved sleeps, worst case ~ 1s */
 690 int
 691 find_and_track_existing_processes(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 692 {
 693     bool wait_in_progress;
 694     int rc;
 695     size_t i, rounds;
 696 
 697     for (rounds = 1; rounds <= WAIT_TRIES; rounds++) {
 698         wait_in_progress = false;
 699         for (i = 0; i < PCMK__NELEM(pcmk_children); i++) {
 700 
 701             if ((pcmk_children[i].endpoint == NULL)
 702                 || (pcmk_children[i].respawn_count < 0)) {
 703                 continue;
 704             }
 705 
 706             rc = child_liveness(&pcmk_children[i]);
 707             if (rc == pcmk_rc_ipc_unresponsive) {
 708                 /* As a speculation, don't give up if there are more rounds to
 709                  * come for other reasons, but don't artificially wait just
 710                  * because of this, since we would preferably start ASAP.
 711                  */
 712                 continue;
 713             }
 714 
 715             pcmk_children[i].respawn_count = rounds;
 716             switch (rc) {
 717                 case pcmk_rc_ok:
 718                     if (pcmk_children[i].pid == PCMK__SPECIAL_PID) {
 719                         if (crm_is_true(getenv("PCMK_fail_fast"))) {
 720                             crm_crit("Cannot reliably track pre-existing"
 721                                      " authentic process behind %s IPC on this"
 722                                      " platform and PCMK_fail_fast requested",
 723                                      pcmk_children[i].endpoint);
 724                             return EOPNOTSUPP;
 725                         } else if (pcmk_children[i].respawn_count == WAIT_TRIES) {
 726                             crm_notice("Assuming pre-existing authentic, though"
 727                                        " on this platform untrackable, process"
 728                                        " behind %s IPC is stable (was in %d"
 729                                        " previous samples) so rather than"
 730                                        " bailing out (PCMK_fail_fast not"
 731                                        " requested), we just switch to a less"
 732                                        " optimal IPC liveness monitoring"
 733                                        " (not very suitable for heavy load)",
 734                                        pcmk_children[i].name, WAIT_TRIES - 1);
 735                             crm_warn("The process behind %s IPC cannot be"
 736                                      " terminated, so the overall shutdown"
 737                                      " will get delayed implicitly (%ld s),"
 738                                      " which serves as a graceful period for"
 739                                      " its native termination if it vitally"
 740                                      " depends on some other daemons going"
 741                                      " down in a controlled way already",
 742                                      pcmk_children[i].name,
 743                                      (long) SHUTDOWN_ESCALATION_PERIOD);
 744                         } else {
 745                             wait_in_progress = true;
 746                             crm_warn("Cannot reliably track pre-existing"
 747                                      " authentic process behind %s IPC on this"
 748                                      " platform, can still disappear in %d"
 749                                      " attempt(s)", pcmk_children[i].endpoint,
 750                                      WAIT_TRIES - pcmk_children[i].respawn_count);
 751                             continue;
 752                         }
 753                     }
 754                     crm_notice("Tracking existing %s process (pid=%lld)",
 755                                pcmk_children[i].name,
 756                                (long long) PCMK__SPECIAL_PID_AS_0(
 757                                                pcmk_children[i].pid));
 758                     pcmk_children[i].respawn_count = -1;  /* 0~keep watching */
 759                     pcmk_children[i].active_before_startup = true;
 760                     break;
 761                 case pcmk_rc_ipc_pid_only:
 762                     if (pcmk_children[i].respawn_count == WAIT_TRIES) {
 763                         crm_crit("%s IPC end-point for existing authentic"
 764                                  " process %lld did not (re)appear",
 765                                  pcmk_children[i].endpoint,
 766                                  (long long) PCMK__SPECIAL_PID_AS_0(
 767                                                  pcmk_children[i].pid));
 768                         return rc;
 769                     }
 770                     wait_in_progress = true;
 771                     crm_warn("Cannot find %s IPC end-point for existing"
 772                              " authentic process %lld, can still (re)appear"
 773                              " in %d attempts (?)",
 774                              pcmk_children[i].endpoint,
 775                              (long long) PCMK__SPECIAL_PID_AS_0(
 776                                              pcmk_children[i].pid),
 777                              WAIT_TRIES - pcmk_children[i].respawn_count);
 778                     continue;
 779                 default:
 780                     crm_crit("Checked liveness of %s: %s " CRM_XS " rc=%d",
 781                              pcmk_children[i].name, pcmk_rc_str(rc), rc);
 782                     return rc;
 783             }
 784         }
 785         if (!wait_in_progress) {
 786             break;
 787         }
 788         pcmk__sleep_ms(250); // Wait a bit for changes to possibly happen
 789     }
 790     for (i = 0; i < PCMK__NELEM(pcmk_children); i++) {
 791         pcmk_children[i].respawn_count = 0;  /* restore pristine state */
 792     }
 793 
 794     g_timeout_add_seconds(PCMK_PROCESS_CHECK_INTERVAL, check_next_subdaemon,
 795                           NULL);
 796     return pcmk_rc_ok;
 797 }
 798 
 799 gboolean
 800 init_children_processes(void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 801 {
 802     if (is_corosync_cluster()) {
 803         /* Corosync clusters can drop root group access, because we set
 804          * uidgid.gid.${gid}=1 via CMAP, which allows these processes to connect
 805          * to corosync.
 806          */
 807         need_root_group = false;
 808     }
 809 
 810     /* start any children that have not been detected */
 811     for (int i = 0; i < PCMK__NELEM(pcmk_children); i++) {
 812         if (pcmk_children[i].pid != 0) {
 813             /* we are already tracking it */
 814             continue;
 815         }
 816 
 817         start_child(&(pcmk_children[i]));
 818     }
 819 
 820     /* From this point on, any daemons being started will be due to
 821      * respawning rather than node start.
 822      *
 823      * This may be useful for the daemons to know
 824      */
 825     setenv("PCMK_respawned", "true", 1);
 826     pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_RUNNING;
 827     return TRUE;
 828 }
 829 
 830 void
 831 pcmk_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 832 {
 833     if (shutdown_trigger == NULL) {
 834         shutdown_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, pcmk_shutdown_worker, NULL);
 835     }
 836     mainloop_set_trigger(shutdown_trigger);
 837 }
 838 
 839 void
 840 restart_cluster_subdaemons(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 841 {
 842     for (int i = 0; i < PCMK__NELEM(pcmk_children); i++) {
 843         if (!pcmk_children[i].needs_retry || pcmk_children[i].pid != 0) {
 844             continue;
 845         }
 846 
 847         crm_notice("Respawning cluster-based subdaemon: %s", pcmk_children[i].name);
 848         if (start_child(&pcmk_children[i])) {
 849             pcmk_children[i].needs_retry = false;
 850         }
 851     }
 852 }
 853 
 854 static gboolean
 855 stop_child(pcmk_child_t * child, int signal)
     /* [previous][next][first][last][top][bottom][index][help] */
 856 {
 857     if (signal == 0) {
 858         signal = SIGTERM;
 859     }
 860 
 861     /* why to skip PID of 1?
 862        - FreeBSD ~ how untrackable process behind IPC is masqueraded as
 863        - elsewhere: how "init" task is designated; in particular, in systemd
 864          arrangement of socket-based activation, this is pretty real */
 865     if (child->command == NULL || child->pid == PCMK__SPECIAL_PID) {
 866         crm_debug("Nothing to do for child \"%s\" (process %lld)",
 867                   child->name, (long long) PCMK__SPECIAL_PID_AS_0(child->pid));
 868         return TRUE;
 869     }
 870 
 871     if (child->pid <= 0) {
 872         crm_trace("Client %s not running", child->name);
 873         return TRUE;
 874     }
 875 
 876     errno = 0;
 877     if (kill(child->pid, signal) == 0) {
 878         crm_notice("Stopping %s "CRM_XS" sent signal %d to process %lld",
 879                    child->name, signal, (long long) child->pid);
 880 
 881     } else {
 882         crm_err("Could not stop %s (process %lld) with signal %d: %s",
 883                 child->name, (long long) child->pid, signal, strerror(errno));
 884     }
 885 
 886     return TRUE;
 887 }
 888 

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