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

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