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. build_arg_context
  16. main

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

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