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. execd_exit_if_shutting_down
  13. lrmd_shutdown
  14. handle_shutdown_ack
  15. handle_shutdown_nack
  16. main

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

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