root/daemons/execd/pacemaker-execd.c

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

DEFINITIONS

This source file includes following definitions.
  1. stonith_connection_destroy_cb
  2. get_stonith_connection
  3. lrmd_ipc_accept
  4. lrmd_ipc_created
  5. lrmd_ipc_dispatch
  6. lrmd_client_destroy
  7. lrmd_ipc_closed
  8. lrmd_ipc_destroy
  9. lrmd_server_send_reply
  10. lrmd_server_send_notify
  11. lrmd_exit
  12. lrmd_shutdown
  13. handle_shutdown_ack
  14. handle_shutdown_nack
  15. main

   1 /*
   2  * Copyright 2012-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 Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <glib.h>
  13 #include <signal.h>
  14 #include <sys/types.h>
  15 
  16 #include <crm/crm.h>
  17 #include <crm/msg_xml.h>
  18 #include <crm/services.h>
  19 #include <crm/common/mainloop.h>
  20 #include <crm/common/ipc.h>
  21 #include <crm/common/ipc_internal.h>
  22 #include <crm/common/remote_internal.h>
  23 #include <crm/lrmd_internal.h>
  24 
  25 #include "pacemaker-execd.h"
  26 
  27 static GMainLoop *mainloop = NULL;
  28 static qb_ipcs_service_t *ipcs = NULL;
  29 static stonith_t *stonith_api = NULL;
  30 int lrmd_call_id = 0;
  31 
  32 #ifdef PCMK__COMPILE_REMOTE
  33 /* whether shutdown request has been sent */
  34 static sig_atomic_t shutting_down = FALSE;
  35 
  36 /* timer for waiting for acknowledgment of shutdown request */
  37 static guint shutdown_ack_timer = 0;
  38 
  39 static gboolean lrmd_exit(gpointer data);
  40 #endif
  41 
  42 static void
  43 stonith_connection_destroy_cb(stonith_t * st, stonith_event_t * e)
     /* [previous][next][first][last][top][bottom][index][help] */
  44 {
  45     stonith_api->state = stonith_disconnected;
  46     crm_err("Connection to fencer lost");
  47     stonith_connection_failed();
  48 }
  49 
  50 stonith_t *
  51 get_stonith_connection(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  52 {
  53     if (stonith_api && stonith_api->state == stonith_disconnected) {
  54         stonith_api_delete(stonith_api);
  55         stonith_api = NULL;
  56     }
  57 
  58     if (stonith_api == NULL) {
  59         int rc = pcmk_ok;
  60 
  61         stonith_api = stonith_api_new();
  62         if (stonith_api == NULL) {
  63             crm_err("Could not connect to fencer: API memory allocation failed");
  64             return NULL;
  65         }
  66         rc = stonith_api_connect_retry(stonith_api, crm_system_name, 10);
  67         if (rc != pcmk_ok) {
  68             crm_err("Could not connect to fencer in 10 attempts: %s "
  69                     CRM_XS " rc=%d", pcmk_strerror(rc), rc);
  70             stonith_api_delete(stonith_api);
  71             stonith_api = NULL;
  72         } else {
  73             stonith_api->cmds->register_notification(stonith_api,
  74                                                      T_STONITH_NOTIFY_DISCONNECT,
  75                                                      stonith_connection_destroy_cb);
  76         }
  77     }
  78     return stonith_api;
  79 }
  80 
  81 static int32_t
  82 lrmd_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
  83 {
  84     crm_trace("Connection %p", c);
  85     if (pcmk__new_client(c, uid, gid) == NULL) {
  86         return -EIO;
  87     }
  88     return 0;
  89 }
  90 
  91 static void
  92 lrmd_ipc_created(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
  93 {
  94     pcmk__client_t *new_client = pcmk__find_client(c);
  95 
  96     crm_trace("Connection %p", c);
  97     CRM_ASSERT(new_client != NULL);
  98     /* Now that the connection is offically established, alert
  99      * the other clients a new connection exists. */
 100 
 101     notify_of_new_client(new_client);
 102 }
 103 
 104 static int32_t
 105 lrmd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 106 {
 107     uint32_t id = 0;
 108     uint32_t flags = 0;
 109     pcmk__client_t *client = pcmk__find_client(c);
 110     xmlNode *request = pcmk__client_data2xml(client, data, &id, &flags);
 111 
 112     CRM_CHECK(client != NULL, crm_err("Invalid client");
 113               return FALSE);
 114     CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p", client);
 115               return FALSE);
 116 
 117     CRM_CHECK(flags & crm_ipc_client_response, crm_err("Invalid client request: %p", client);
 118               return FALSE);
 119 
 120     if (!request) {
 121         return 0;
 122     }
 123 
 124     if (!client->name) {
 125         const char *value = crm_element_value(request, F_LRMD_CLIENTNAME);
 126 
 127         if (value == NULL) {
 128             client->name = pcmk__itoa(pcmk__client_pid(c));
 129         } else {
 130             client->name = strdup(value);
 131         }
 132     }
 133 
 134     lrmd_call_id++;
 135     if (lrmd_call_id < 1) {
 136         lrmd_call_id = 1;
 137     }
 138 
 139     crm_xml_add(request, F_LRMD_CLIENTID, client->id);
 140     crm_xml_add(request, F_LRMD_CLIENTNAME, client->name);
 141     crm_xml_add_int(request, F_LRMD_CALLID, lrmd_call_id);
 142 
 143     process_lrmd_message(client, id, request);
 144 
 145     free_xml(request);
 146     return 0;
 147 }
 148 
 149 /*!
 150  * \internal
 151  * \brief Free a client connection, and exit if appropriate
 152  *
 153  * \param[in] client  Client connection to free
 154  */
 155 void
 156 lrmd_client_destroy(pcmk__client_t *client)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158     pcmk__free_client(client);
 159 
 160 #ifdef PCMK__COMPILE_REMOTE
 161     /* If we were waiting to shut down, we can now safely do so
 162      * if there are no more proxied IPC providers
 163      */
 164     if (shutting_down && (ipc_proxy_get_provider() == NULL)) {
 165         lrmd_exit(NULL);
 166     }
 167 #endif
 168 }
 169 
 170 static int32_t
 171 lrmd_ipc_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 172 {
 173     pcmk__client_t *client = pcmk__find_client(c);
 174 
 175     if (client == NULL) {
 176         return 0;
 177     }
 178 
 179     crm_trace("Connection %p", c);
 180     client_disconnect_cleanup(client->id);
 181 #ifdef PCMK__COMPILE_REMOTE
 182     ipc_proxy_remove_provider(client);
 183 #endif
 184     lrmd_client_destroy(client);
 185     return 0;
 186 }
 187 
 188 static void
 189 lrmd_ipc_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 190 {
 191     lrmd_ipc_closed(c);
 192     crm_trace("Connection %p", c);
 193 }
 194 
 195 static struct qb_ipcs_service_handlers lrmd_ipc_callbacks = {
 196     .connection_accept = lrmd_ipc_accept,
 197     .connection_created = lrmd_ipc_created,
 198     .msg_process = lrmd_ipc_dispatch,
 199     .connection_closed = lrmd_ipc_closed,
 200     .connection_destroyed = lrmd_ipc_destroy
 201 };
 202 
 203 // \return Standard Pacemaker return code
 204 int
 205 lrmd_server_send_reply(pcmk__client_t *client, uint32_t id, xmlNode *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 206 {
 207     crm_trace("Sending reply (%d) to client (%s)", id, client->id);
 208     switch (PCMK__CLIENT_TYPE(client)) {
 209         case pcmk__client_ipc:
 210             return pcmk__ipc_send_xml(client, id, reply, FALSE);
 211 #ifdef PCMK__COMPILE_REMOTE
 212         case pcmk__client_tls:
 213             return lrmd__remote_send_xml(client->remote, reply, id, "reply");
 214 #endif
 215         default:
 216             crm_err("Could not send reply: unknown type for client %s "
 217                     CRM_XS " flags=%#llx",
 218                     pcmk__client_name(client), client->flags);
 219     }
 220     return ENOTCONN;
 221 }
 222 
 223 // \return Standard Pacemaker return code
 224 int
 225 lrmd_server_send_notify(pcmk__client_t *client, xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 226 {
 227     crm_trace("Sending notification to client (%s)", client->id);
 228     switch (PCMK__CLIENT_TYPE(client)) {
 229         case pcmk__client_ipc:
 230             if (client->ipcs == NULL) {
 231                 crm_trace("Could not notify local client: disconnected");
 232                 return ENOTCONN;
 233             }
 234             return pcmk__ipc_send_xml(client, 0, msg, crm_ipc_server_event);
 235 #ifdef PCMK__COMPILE_REMOTE
 236         case pcmk__client_tls:
 237             if (client->remote == NULL) {
 238                 crm_trace("Could not notify remote client: disconnected");
 239                 return ENOTCONN;
 240             } else {
 241                 return lrmd__remote_send_xml(client->remote, msg, 0, "notify");
 242             }
 243 #endif
 244         default:
 245             crm_err("Could not notify client %s with unknown transport "
 246                     CRM_XS " flags=%#llx",
 247                     pcmk__client_name(client), client->flags);
 248     }
 249     return ENOTCONN;
 250 }
 251 
 252 /*!
 253  * \internal
 254  * \brief Clean up and exit immediately
 255  *
 256  * \param[in] data  Ignored
 257  *
 258  * \return Doesn't return
 259  * \note   This can be used as a timer callback.
 260  */
 261 static gboolean
 262 lrmd_exit(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 263 {
 264     crm_info("Terminating with %d clients", pcmk__ipc_client_count());
 265     if (stonith_api) {
 266         stonith_api->cmds->remove_notification(stonith_api, T_STONITH_NOTIFY_DISCONNECT);
 267         stonith_api->cmds->disconnect(stonith_api);
 268         stonith_api_delete(stonith_api);
 269     }
 270     if (ipcs) {
 271         mainloop_del_ipc_server(ipcs);
 272     }
 273 
 274 #ifdef PCMK__COMPILE_REMOTE
 275     lrmd_tls_server_destroy();
 276     ipc_proxy_cleanup();
 277 #endif
 278 
 279     pcmk__client_cleanup();
 280     g_hash_table_destroy(rsc_list);
 281 
 282     if (mainloop) {
 283         lrmd_drain_alerts(mainloop);
 284     }
 285 
 286     crm_exit(CRM_EX_OK);
 287     return FALSE;
 288 }
 289 
 290 /*!
 291  * \internal
 292  * \brief Request cluster shutdown if appropriate, otherwise exit immediately
 293  *
 294  * \param[in] nsig  Signal that caused invocation (ignored)
 295  */
 296 static void
 297 lrmd_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 298 {
 299 #ifdef PCMK__COMPILE_REMOTE
 300     pcmk__client_t *ipc_proxy = ipc_proxy_get_provider();
 301 
 302     /* If there are active proxied IPC providers, then we may be running
 303      * resources, so notify the cluster that we wish to shut down.
 304      */
 305     if (ipc_proxy) {
 306         if (shutting_down) {
 307             crm_notice("Waiting for cluster to stop resources before exiting");
 308             return;
 309         }
 310 
 311         crm_info("Sending shutdown request to cluster");
 312         if (ipc_proxy_shutdown_req(ipc_proxy) < 0) {
 313             crm_crit("Shutdown request failed, exiting immediately");
 314 
 315         } else {
 316             /* We requested a shutdown. Now, we need to wait for an
 317              * acknowledgement from the proxy host (which ensures the proxy host
 318              * supports shutdown requests), then wait for all proxy hosts to
 319              * disconnect (which ensures that all resources have been stopped).
 320              */
 321             shutting_down = TRUE;
 322 
 323             /* Stop accepting new proxy connections */
 324             lrmd_tls_server_destroy();
 325 
 326             /* Older controller versions will never acknowledge our request, so
 327              * set a fairly short timeout to exit quickly in that case. If we
 328              * get the ack, we'll defuse this timer.
 329              */
 330             shutdown_ack_timer = g_timeout_add_seconds(20, lrmd_exit, NULL);
 331 
 332             /* Currently, we let the OS kill us if the clients don't disconnect
 333              * in a reasonable time. We could instead set a long timer here
 334              * (shorter than what the OS is likely to use) and exit immediately
 335              * if it pops.
 336              */
 337             return;
 338         }
 339     }
 340 #endif
 341     lrmd_exit(NULL);
 342 }
 343 
 344 /*!
 345  * \internal
 346  * \brief Defuse short exit timer if shutting down
 347  */
 348 void handle_shutdown_ack()
     /* [previous][next][first][last][top][bottom][index][help] */
 349 {
 350 #ifdef PCMK__COMPILE_REMOTE
 351     if (shutting_down) {
 352         crm_info("Received shutdown ack");
 353         if (shutdown_ack_timer > 0) {
 354             g_source_remove(shutdown_ack_timer);
 355             shutdown_ack_timer = 0;
 356         }
 357         return;
 358     }
 359 #endif
 360     crm_debug("Ignoring unexpected shutdown ack");
 361 }
 362 
 363 /*!
 364  * \internal
 365  * \brief Make short exit timer fire immediately
 366  */
 367 void handle_shutdown_nack()
     /* [previous][next][first][last][top][bottom][index][help] */
 368 {
 369 #ifdef PCMK__COMPILE_REMOTE
 370     if (shutting_down) {
 371         crm_info("Received shutdown nack");
 372         if (shutdown_ack_timer > 0) {
 373             g_source_remove(shutdown_ack_timer);
 374             shutdown_ack_timer = g_timeout_add(0, lrmd_exit, NULL);
 375         }
 376         return;
 377     }
 378 #endif
 379     crm_debug("Ignoring unexpected shutdown nack");
 380 }
 381 
 382 static pcmk__cli_option_t long_options[] = {
 383     // long option, argument type, storage, short option, description, flags
 384     {
 385         "help", no_argument, NULL, '?',
 386         "\tThis text", pcmk__option_default
 387     },
 388     {
 389         "version", no_argument, NULL, '$',
 390         "\tVersion information", pcmk__option_default
 391     },
 392     {
 393         "verbose", no_argument, NULL, 'V',
 394         "\tIncrease debug output", pcmk__option_default
 395     },
 396     {
 397         "logfile", required_argument, NULL, 'l',
 398         "\tSend logs to the additional named logfile", pcmk__option_default
 399     },
 400 #ifdef PCMK__COMPILE_REMOTE
 401     {
 402         "port", required_argument, NULL, 'p',
 403         "\tPort to listen on", pcmk__option_default
 404     },
 405 #endif
 406     { 0, 0, 0, 0 }
 407 };
 408 
 409 #ifdef PCMK__COMPILE_REMOTE
 410 #  define EXECD_TYPE "remote"
 411 #  define EXECD_NAME "pacemaker-remoted"
 412 #  define EXECD_DESC "resource agent executor daemon for Pacemaker Remote nodes"
 413 #else
 414 #  define EXECD_TYPE "local"
 415 #  define EXECD_NAME "pacemaker-execd"
 416 #  define EXECD_DESC "resource agent executor daemon for Pacemaker cluster nodes"
 417 #endif
 418 
 419 int
 420 main(int argc, char **argv, char **envp)
     /* [previous][next][first][last][top][bottom][index][help] */
 421 {
 422     int flag = 0;
 423     int index = 0;
 424     int bump_log_num = 0;
 425     const char *option = NULL;
 426 
 427 #ifdef PCMK__COMPILE_REMOTE
 428     // If necessary, create PID 1 now before any file descriptors are opened
 429     remoted_spawn_pidone(argc, argv, envp);
 430 #endif
 431 
 432     crm_log_preinit(EXECD_NAME, argc, argv);
 433     pcmk__set_cli_options(NULL, "[options]", long_options, EXECD_DESC);
 434 
 435     while (1) {
 436         flag = pcmk__next_cli_option(argc, argv, &index, NULL);
 437         if (flag == -1) {
 438             break;
 439         }
 440 
 441         switch (flag) {
 442             case 'l':
 443                 {
 444                     int rc = pcmk__add_logfile(optarg);
 445 
 446                     if (rc != pcmk_rc_ok) {
 447                         /* Logging has not yet been initialized, so stderr is
 448                          * the only way to get information out
 449                          */
 450                         fprintf(stderr, "Logging to %s is disabled: %s\n",
 451                                 optarg, pcmk_rc_str(rc));
 452                     }
 453                 }
 454                 break;
 455             case 'p':
 456                 setenv("PCMK_remote_port", optarg, 1);
 457                 break;
 458             case 'V':
 459                 bump_log_num++;
 460                 break;
 461             case '?':
 462             case '$':
 463                 pcmk__cli_help(flag, CRM_EX_OK);
 464                 break;
 465             default:
 466                 pcmk__cli_help('?', CRM_EX_USAGE);
 467                 break;
 468         }
 469     }
 470 
 471     crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
 472 
 473     while (bump_log_num > 0) {
 474         crm_bump_log_level(argc, argv);
 475         bump_log_num--;
 476     }
 477 
 478     option = pcmk__env_option(PCMK__ENV_LOGFACILITY);
 479     if (!pcmk__str_eq(option, PCMK__VALUE_NONE,
 480                       pcmk__str_casei|pcmk__str_null_matches)
 481         && !pcmk__str_eq(option, "/dev/null", pcmk__str_none)) {
 482         setenv("HA_LOGFACILITY", option, 1);  /* Used by the ocf_log/ha_log OCF macro */
 483     }
 484 
 485     option = pcmk__env_option(PCMK__ENV_LOGFILE);
 486     if (!pcmk__str_eq(option, PCMK__VALUE_NONE,
 487                       pcmk__str_casei|pcmk__str_null_matches)) {
 488         setenv("HA_LOGFILE", option, 1);      /* Used by the ocf_log/ha_log OCF macro */
 489 
 490         if (pcmk__env_option_enabled(crm_system_name, PCMK__ENV_DEBUG)) {
 491             setenv("HA_DEBUGLOG", option, 1); /* Used by the ocf_log/ha_debug OCF macro */
 492         }
 493     }
 494 
 495     crm_notice("Starting Pacemaker " EXECD_TYPE " executor");
 496 
 497     /* The presence of this variable allegedly controls whether child
 498      * processes like httpd will try and use Systemd's sd_notify
 499      * API
 500      */
 501     unsetenv("NOTIFY_SOCKET");
 502 
 503     {
 504         // Temporary directory for resource agent use (leave owned by root)
 505         int rc = pcmk__build_path(CRM_RSCTMP_DIR, 0755);
 506 
 507         if (rc != pcmk_rc_ok) {
 508             crm_warn("Could not create resource agent temporary directory "
 509                      CRM_RSCTMP_DIR ": %s", pcmk_rc_str(rc));
 510         }
 511     }
 512 
 513     rsc_list = pcmk__strkey_table(NULL, free_rsc);
 514     ipcs = mainloop_add_ipc_server(CRM_SYSTEM_LRMD, QB_IPC_SHM, &lrmd_ipc_callbacks);
 515     if (ipcs == NULL) {
 516         crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
 517         crm_exit(CRM_EX_FATAL);
 518     }
 519 
 520 #ifdef PCMK__COMPILE_REMOTE
 521     if (lrmd_init_remote_tls_server() < 0) {
 522         crm_err("Failed to create TLS listener: shutting down and staying down");
 523         crm_exit(CRM_EX_FATAL);
 524     }
 525     ipc_proxy_init();
 526 #endif
 527 
 528     mainloop_add_signal(SIGTERM, lrmd_shutdown);
 529     mainloop = g_main_loop_new(NULL, FALSE);
 530     crm_notice("Pacemaker " EXECD_TYPE " executor successfully started and accepting connections");
 531     crm_notice("OCF resource agent search path is %s", OCF_RA_PATH);
 532     g_main_loop_run(mainloop);
 533 
 534     /* should never get here */
 535     lrmd_exit(NULL);
 536     return CRM_EX_OK;
 537 }

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