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

   1 /*
   2  * Copyright 2012-2025 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/common/xml.h>
  18 #include <crm/services.h>
  19 #include <crm/common/cmdline_internal.h>
  20 #include <crm/common/ipc.h>
  21 #include <crm/common/ipc_internal.h>
  22 #include <crm/common/mainloop.h>
  23 #include <crm/common/output_internal.h>
  24 #include <crm/common/remote_internal.h>
  25 #include <crm/fencing/internal.h>           // stonith__api_new()
  26 #include <crm/lrmd_internal.h>
  27 
  28 #include "pacemaker-execd.h"
  29 
  30 #ifdef PCMK__COMPILE_REMOTE
  31 #  define EXECD_TYPE "remote"
  32 #  define EXECD_NAME PCMK__SERVER_REMOTED
  33 #  define SUMMARY "resource agent executor daemon for Pacemaker Remote nodes"
  34 #else
  35 #  define EXECD_TYPE "local"
  36 #  define EXECD_NAME PCMK__SERVER_EXECD
  37 #  define SUMMARY "resource agent executor daemon for Pacemaker cluster nodes"
  38 #endif
  39 
  40 static GMainLoop *mainloop = NULL;
  41 static qb_ipcs_service_t *ipcs = NULL;
  42 static stonith_t *stonith_api = NULL;
  43 int lrmd_call_id = 0;
  44 time_t start_time;
  45 
  46 static struct {
  47     gchar **log_files;
  48 #ifdef PCMK__COMPILE_REMOTE
  49     gchar *port;
  50 #endif  // PCMK__COMPILE_REMOTE
  51 } options;
  52 
  53 #ifdef PCMK__COMPILE_REMOTE
  54 /* whether shutdown request has been sent */
  55 static gboolean shutting_down = FALSE;
  56 #endif
  57 
  58 static void exit_executor(void);
  59 
  60 static void
  61 stonith_connection_destroy_cb(stonith_t * st, stonith_event_t * e)
     /* [previous][next][first][last][top][bottom][index][help] */
  62 {
  63     stonith_api->state = stonith_disconnected;
  64     stonith_connection_failed();
  65 }
  66 
  67 stonith_t *
  68 get_stonith_connection(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  69 {
  70     if (stonith_api && stonith_api->state == stonith_disconnected) {
  71         stonith__api_free(stonith_api);
  72         stonith_api = NULL;
  73     }
  74 
  75     if (stonith_api == NULL) {
  76         int rc = pcmk_ok;
  77 
  78         stonith_api = stonith__api_new();
  79         if (stonith_api == NULL) {
  80             crm_err("Could not connect to fencer: API memory allocation failed");
  81             return NULL;
  82         }
  83         rc = stonith__api_connect_retry(stonith_api, crm_system_name, 10);
  84         if (rc != pcmk_rc_ok) {
  85             crm_err("Could not connect to fencer in 10 attempts: %s "
  86                     QB_XS " rc=%d", pcmk_rc_str(rc), rc);
  87             stonith__api_free(stonith_api);
  88             stonith_api = NULL;
  89         } else {
  90             stonith_api_operations_t *cmds = stonith_api->cmds;
  91 
  92             cmds->register_notification(stonith_api,
  93                                         PCMK__VALUE_ST_NOTIFY_DISCONNECT,
  94                                         stonith_connection_destroy_cb);
  95         }
  96     }
  97     return stonith_api;
  98 }
  99 
 100 static int32_t
 101 lrmd_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103     crm_trace("Connection %p", c);
 104     if (pcmk__new_client(c, uid, gid) == NULL) {
 105         return -ENOMEM;
 106     }
 107     return 0;
 108 }
 109 
 110 static void
 111 lrmd_ipc_created(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 112 {
 113     pcmk__client_t *new_client = pcmk__find_client(c);
 114 
 115     crm_trace("Connection %p", c);
 116     pcmk__assert(new_client != NULL);
 117     /* Now that the connection is offically established, alert
 118      * the other clients a new connection exists. */
 119 
 120     notify_of_new_client(new_client);
 121 }
 122 
 123 static int32_t
 124 lrmd_ipc_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 125 {
 126     int rc = pcmk_rc_ok;
 127     uint32_t id = 0;
 128     uint32_t flags = 0;
 129     pcmk__client_t *client = pcmk__find_client(c);
 130     xmlNode *request = NULL;
 131 
 132     CRM_CHECK(client != NULL, crm_err("Invalid client");
 133               return FALSE);
 134     CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p", client);
 135               return FALSE);
 136 
 137     rc = pcmk__ipc_msg_append(&client->buffer, data);
 138 
 139     if (rc == pcmk_rc_ipc_more) {
 140         /* We haven't read the complete message yet, so just return. */
 141         return 0;
 142 
 143     } else if (rc == pcmk_rc_ok) {
 144         /* We've read the complete message and there's already a header on
 145          * the front.  Pass it off for processing.
 146          */
 147         request = pcmk__client_data2xml(client, &id, &flags);
 148         g_byte_array_free(client->buffer, TRUE);
 149         client->buffer = NULL;
 150 
 151     } else {
 152         /* Some sort of error occurred reassembling the message.  All we can
 153          * do is clean up, log an error and return.
 154          */
 155         crm_err("Error when reading IPC message: %s", pcmk_rc_str(rc));
 156 
 157         if (client->buffer != NULL) {
 158             g_byte_array_free(client->buffer, TRUE);
 159             client->buffer = NULL;
 160         }
 161 
 162         return 0;
 163     }
 164 
 165     CRM_CHECK(flags & crm_ipc_client_response, crm_err("Invalid client request: %p", client);
 166               return FALSE);
 167 
 168     if (!request) {
 169         return 0;
 170     }
 171 
 172     /* @TODO functionize some of this to reduce duplication with
 173      * lrmd_remote_client_msg()
 174      */
 175 
 176     if (!client->name) {
 177         const char *value = crm_element_value(request,
 178                                               PCMK__XA_LRMD_CLIENTNAME);
 179 
 180         if (value == NULL) {
 181             client->name = pcmk__itoa(pcmk__client_pid(c));
 182         } else {
 183             client->name = pcmk__str_copy(value);
 184         }
 185     }
 186 
 187     lrmd_call_id++;
 188     if (lrmd_call_id < 1) {
 189         lrmd_call_id = 1;
 190     }
 191 
 192     crm_xml_add(request, PCMK__XA_LRMD_CLIENTID, client->id);
 193     crm_xml_add(request, PCMK__XA_LRMD_CLIENTNAME, client->name);
 194     crm_xml_add_int(request, PCMK__XA_LRMD_CALLID, lrmd_call_id);
 195 
 196     process_lrmd_message(client, id, request);
 197 
 198     pcmk__xml_free(request);
 199     return 0;
 200 }
 201 
 202 /*!
 203  * \internal
 204  * \brief Free a client connection, and exit if appropriate
 205  *
 206  * \param[in,out] client  Client connection to free
 207  */
 208 void
 209 lrmd_client_destroy(pcmk__client_t *client)
     /* [previous][next][first][last][top][bottom][index][help] */
 210 {
 211     pcmk__free_client(client);
 212 
 213 #ifdef PCMK__COMPILE_REMOTE
 214     /* If we were waiting to shut down, we can now safely do so
 215      * if there are no more proxied IPC providers
 216      */
 217     if (shutting_down && (ipc_proxy_get_provider() == NULL)) {
 218         exit_executor();
 219     }
 220 #endif
 221 }
 222 
 223 static int32_t
 224 lrmd_ipc_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 225 {
 226     pcmk__client_t *client = pcmk__find_client(c);
 227 
 228     if (client == NULL) {
 229         return 0;
 230     }
 231 
 232     crm_trace("Connection %p", c);
 233     client_disconnect_cleanup(client->id);
 234 #ifdef PCMK__COMPILE_REMOTE
 235     ipc_proxy_remove_provider(client);
 236 #endif
 237     lrmd_client_destroy(client);
 238     return 0;
 239 }
 240 
 241 static void
 242 lrmd_ipc_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 243 {
 244     lrmd_ipc_closed(c);
 245     crm_trace("Connection %p", c);
 246 }
 247 
 248 static struct qb_ipcs_service_handlers lrmd_ipc_callbacks = {
 249     .connection_accept = lrmd_ipc_accept,
 250     .connection_created = lrmd_ipc_created,
 251     .msg_process = lrmd_ipc_dispatch,
 252     .connection_closed = lrmd_ipc_closed,
 253     .connection_destroyed = lrmd_ipc_destroy
 254 };
 255 
 256 // \return Standard Pacemaker return code
 257 int
 258 lrmd_server_send_reply(pcmk__client_t *client, uint32_t id, xmlNode *reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 259 {
 260     crm_trace("Sending reply (%d) to client (%s)", id, client->id);
 261     switch (PCMK__CLIENT_TYPE(client)) {
 262         case pcmk__client_ipc:
 263             return pcmk__ipc_send_xml(client, id, reply, crm_ipc_flags_none);
 264 #ifdef PCMK__COMPILE_REMOTE
 265         case pcmk__client_tls:
 266             return lrmd__remote_send_xml(client->remote, reply, id, "reply");
 267 #endif
 268         default:
 269             crm_err("Could not send reply: unknown type for client %s "
 270                     QB_XS " flags=%#llx",
 271                     pcmk__client_name(client), client->flags);
 272     }
 273     return ENOTCONN;
 274 }
 275 
 276 // \return Standard Pacemaker return code
 277 int
 278 lrmd_server_send_notify(pcmk__client_t *client, xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 279 {
 280     crm_trace("Sending notification to client (%s)", client->id);
 281     switch (PCMK__CLIENT_TYPE(client)) {
 282         case pcmk__client_ipc:
 283             if (client->ipcs == NULL) {
 284                 crm_trace("Could not notify local client: disconnected");
 285                 return ENOTCONN;
 286             }
 287             return pcmk__ipc_send_xml(client, 0, msg, crm_ipc_server_event);
 288 #ifdef PCMK__COMPILE_REMOTE
 289         case pcmk__client_tls:
 290             if (client->remote == NULL) {
 291                 crm_trace("Could not notify remote client: disconnected");
 292                 return ENOTCONN;
 293             } else {
 294                 return lrmd__remote_send_xml(client->remote, msg, 0, "notify");
 295             }
 296 #endif
 297         default:
 298             crm_err("Could not notify client %s with unknown transport "
 299                     QB_XS " flags=%#llx",
 300                     pcmk__client_name(client), client->flags);
 301     }
 302     return ENOTCONN;
 303 }
 304 
 305 /*!
 306  * \internal
 307  * \brief Clean up and exit immediately
 308  */
 309 static void
 310 exit_executor(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 311 {
 312     const guint nclients = pcmk__ipc_client_count();
 313 
 314     crm_info("Terminating with %d client%s",
 315              nclients, pcmk__plural_s(nclients));
 316     stonith__api_free(stonith_api);
 317     if (ipcs) {
 318         mainloop_del_ipc_server(ipcs);
 319     }
 320 
 321 #ifdef PCMK__COMPILE_REMOTE
 322     execd_stop_tls_server();
 323     ipc_proxy_cleanup();
 324 #endif
 325 
 326     pcmk__client_cleanup();
 327 
 328     if (mainloop) {
 329         lrmd_drain_alerts(mainloop);
 330     }
 331 
 332     g_hash_table_destroy(rsc_list);
 333 
 334     // @TODO End mainloop instead so all cleanup is done
 335     crm_exit(CRM_EX_OK);
 336 }
 337 
 338 /*!
 339  * \internal
 340  * \brief Request cluster shutdown if appropriate, otherwise exit immediately
 341  *
 342  * \param[in] nsig  Signal that caused invocation (ignored)
 343  */
 344 static void
 345 lrmd_shutdown(int nsig)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347 #ifdef PCMK__COMPILE_REMOTE
 348     pcmk__client_t *ipc_proxy = ipc_proxy_get_provider();
 349 
 350     /* If there are active proxied IPC providers, then we may be running
 351      * resources, so notify the cluster that we wish to shut down.
 352      */
 353     if (ipc_proxy) {
 354         if (shutting_down) {
 355             crm_notice("Waiting for cluster to stop resources before exiting");
 356             return;
 357         }
 358 
 359         crm_info("Sending shutdown request to cluster");
 360         if (ipc_proxy_shutdown_req(ipc_proxy) < 0) {
 361             crm_crit("Shutdown request failed, exiting immediately");
 362 
 363         } else {
 364             /* We requested a shutdown. Now, we need to wait for an
 365              * acknowledgement from the proxy host, then wait for all proxy
 366              * hosts to disconnect (which ensures that all resources have been
 367              * stopped).
 368              */
 369             shutting_down = TRUE;
 370 
 371             /* Stop accepting new proxy connections */
 372             execd_stop_tls_server();
 373 
 374             /* Currently, we let the OS kill us if the clients don't disconnect
 375              * in a reasonable time. We could instead set a long timer here
 376              * (shorter than what the OS is likely to use) and exit immediately
 377              * if it pops.
 378              */
 379             return;
 380         }
 381     }
 382 #endif
 383     exit_executor();
 384 }
 385 
 386 /*!
 387  * \internal
 388  * \brief Log a shutdown acknowledgment
 389  */
 390 void
 391 handle_shutdown_ack(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 392 {
 393 #ifdef PCMK__COMPILE_REMOTE
 394     if (shutting_down) {
 395         crm_info("IPC proxy provider acknowledged shutdown request");
 396         return;
 397     }
 398 #endif
 399     crm_debug("Ignoring unexpected shutdown acknowledgment "
 400               "from IPC proxy provider");
 401 }
 402 
 403 /*!
 404  * \internal
 405  * \brief Handle rejection of shutdown request
 406  */
 407 void
 408 handle_shutdown_nack(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 409 {
 410 #ifdef PCMK__COMPILE_REMOTE
 411     if (shutting_down) {
 412         crm_info("Exiting immediately after IPC proxy provider "
 413                  "indicated no resources will be stopped");
 414         exit_executor();
 415         return;
 416     }
 417 #endif
 418     crm_debug("Ignoring unexpected shutdown rejection from IPC proxy provider");
 419 }
 420 
 421 static GOptionEntry entries[] = {
 422     { "logfile", 'l', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME_ARRAY,
 423       &options.log_files, "Send logs to the additional named logfile", NULL },
 424 
 425 #ifdef PCMK__COMPILE_REMOTE
 426 
 427     { "port", 'p', G_OPTION_FLAG_NONE, G_OPTION_ARG_STRING, &options.port,
 428       "Port to listen on (defaults to " G_STRINGIFY(DEFAULT_REMOTE_PORT) ")", NULL },
 429 #endif  // PCMK__COMPILE_REMOTE
 430 
 431     { NULL }
 432 };
 433 
 434 static pcmk__supported_format_t formats[] = {
 435     PCMK__SUPPORTED_FORMAT_NONE,
 436     PCMK__SUPPORTED_FORMAT_TEXT,
 437     PCMK__SUPPORTED_FORMAT_XML,
 438     { NULL, NULL, NULL }
 439 };
 440 
 441 static GOptionContext *
 442 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
     /* [previous][next][first][last][top][bottom][index][help] */
 443 {
 444     GOptionContext *context = NULL;
 445 
 446     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 447     pcmk__add_main_args(context, entries);
 448     return context;
 449 }
 450 
 451 int
 452 main(int argc, char **argv, char **envp)
     /* [previous][next][first][last][top][bottom][index][help] */
 453 {
 454     int rc = pcmk_rc_ok;
 455     crm_exit_t exit_code = CRM_EX_OK;
 456 
 457     const char *option = NULL;
 458 
 459     pcmk__output_t *out = NULL;
 460 
 461     GError *error = NULL;
 462 
 463     GOptionGroup *output_group = NULL;
 464     pcmk__common_args_t *args = NULL;
 465     gchar **processed_args = NULL;
 466     GOptionContext *context = NULL;
 467 
 468 #ifdef PCMK__COMPILE_REMOTE
 469     // If necessary, create PID 1 now before any file descriptors are opened
 470     remoted_spawn_pidone(argc, argv, envp);
 471 #endif
 472 
 473     args = pcmk__new_common_args(SUMMARY);
 474 #ifdef PCMK__COMPILE_REMOTE
 475     processed_args = pcmk__cmdline_preproc(argv, "lp");
 476 #else
 477     processed_args = pcmk__cmdline_preproc(argv, "l");
 478 #endif  // PCMK__COMPILE_REMOTE
 479     context = build_arg_context(args, &output_group);
 480 
 481     crm_log_preinit(EXECD_NAME, argc, argv);
 482 
 483     pcmk__register_formats(output_group, formats);
 484     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 485         exit_code = CRM_EX_USAGE;
 486         goto done;
 487     }
 488 
 489     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 490     if (rc != pcmk_rc_ok) {
 491         exit_code = CRM_EX_ERROR;
 492         g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
 493                     "Error creating output format %s: %s",
 494                     args->output_ty, pcmk_rc_str(rc));
 495         goto done;
 496     }
 497 
 498     if (args->version) {
 499         out->version(out, false);
 500         goto done;
 501     }
 502 
 503     // Open additional log files
 504     if (options.log_files != NULL) {
 505         for (gchar **fname = options.log_files; *fname != NULL; fname++) {
 506             rc = pcmk__add_logfile(*fname);
 507 
 508             if (rc != pcmk_rc_ok) {
 509                 out->err(out, "Logging to %s is disabled: %s",
 510                          *fname, pcmk_rc_str(rc));
 511             }
 512         }
 513     }
 514 
 515     pcmk__cli_init_logging(EXECD_NAME, args->verbosity);
 516     crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
 517 
 518     // ocf_log() (in resource-agents) uses the capitalized env options below
 519     option = pcmk__env_option(PCMK__ENV_LOGFACILITY);
 520     if (!pcmk__str_eq(option, PCMK_VALUE_NONE,
 521                       pcmk__str_casei|pcmk__str_null_matches)
 522         && !pcmk__str_eq(option, "/dev/null", pcmk__str_none)) {
 523 
 524         pcmk__set_env_option("LOGFACILITY", option, true);
 525     }
 526 
 527     option = pcmk__env_option(PCMK__ENV_LOGFILE);
 528     if (!pcmk__str_eq(option, PCMK_VALUE_NONE,
 529                       pcmk__str_casei|pcmk__str_null_matches)) {
 530         pcmk__set_env_option("LOGFILE", option, true);
 531 
 532         if (pcmk__env_option_enabled(crm_system_name, PCMK__ENV_DEBUG)) {
 533             pcmk__set_env_option("DEBUGLOG", option, true);
 534         }
 535     }
 536 
 537 #ifdef PCMK__COMPILE_REMOTE
 538     if (options.port != NULL) {
 539         pcmk__set_env_option(PCMK__ENV_REMOTE_PORT, options.port, false);
 540     }
 541 #endif  // PCMK__COMPILE_REMOTE
 542 
 543     start_time = time(NULL);
 544 
 545     crm_notice("Starting Pacemaker " EXECD_TYPE " executor");
 546 
 547     /* The presence of this variable allegedly controls whether child
 548      * processes like httpd will try and use Systemd's sd_notify
 549      * API
 550      */
 551     unsetenv("NOTIFY_SOCKET");
 552 
 553     {
 554         // Temporary directory for resource agent use (leave owned by root)
 555         int rc = pcmk__build_path(PCMK__OCF_TMP_DIR, 0755);
 556 
 557         if (rc != pcmk_rc_ok) {
 558             crm_warn("Could not create resource agent temporary directory "
 559                      PCMK__OCF_TMP_DIR ": %s", pcmk_rc_str(rc));
 560         }
 561     }
 562 
 563     rsc_list = pcmk__strkey_table(NULL, free_rsc);
 564     ipcs = mainloop_add_ipc_server(CRM_SYSTEM_LRMD, QB_IPC_SHM, &lrmd_ipc_callbacks);
 565     if (ipcs == NULL) {
 566         crm_err("Failed to create IPC server: shutting down and inhibiting respawn");
 567         exit_code = CRM_EX_FATAL;
 568         goto done;
 569     }
 570 
 571 #ifdef PCMK__COMPILE_REMOTE
 572     if (lrmd_init_remote_tls_server() < 0) {
 573         crm_err("Failed to create TLS listener: shutting down and staying down");
 574         exit_code = CRM_EX_FATAL;
 575         goto done;
 576     }
 577     ipc_proxy_init();
 578 #endif
 579 
 580     mainloop_add_signal(SIGTERM, lrmd_shutdown);
 581     mainloop = g_main_loop_new(NULL, FALSE);
 582     crm_notice("Pacemaker " EXECD_TYPE " executor successfully started and accepting connections");
 583     crm_notice("OCF resource agent search path is %s", PCMK__OCF_RA_PATH);
 584     g_main_loop_run(mainloop);
 585 
 586     /* should never get here */
 587     exit_executor();
 588 
 589 done:
 590     g_strfreev(options.log_files);
 591 #ifdef PCMK__COMPILE_REMOTE
 592     g_free(options.port);
 593 #endif  // PCMK__COMPILE_REMOTE
 594 
 595     g_strfreev(processed_args);
 596     pcmk__free_arg_context(context);
 597 
 598     pcmk__output_and_clear_error(&error, out);
 599 
 600     if (out != NULL) {
 601         out->finish(out, exit_code, true, NULL);
 602         pcmk__output_free(out);
 603     }
 604     pcmk__unregister_formats();
 605     crm_exit(exit_code);
 606 }

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