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

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