root/daemons/execd/remoted_proxy.c

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

DEFINITIONS

This source file includes following definitions.
  1. ipc_proxy_get_provider
  2. ipc_proxy_accept
  3. crmd_proxy_accept
  4. attrd_proxy_accept
  5. stonith_proxy_accept
  6. pacemakerd_proxy_accept
  7. cib_proxy_accept_rw
  8. cib_proxy_accept_ro
  9. ipc_proxy_forward_client
  10. ipc_proxy_dispatch
  11. ipc_proxy_shutdown_req
  12. ipc_proxy_closed
  13. ipc_proxy_destroy
  14. ipc_proxy_add_provider
  15. ipc_proxy_remove_provider
  16. ipc_proxy_init
  17. ipc_proxy_cleanup

   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 <unistd.h>
  14 
  15 #include "pacemaker-execd.h"
  16 #include <crm/crm.h>
  17 #include <crm/common/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/cib/internal.h>
  23 #include <crm/fencing/internal.h>
  24 
  25 static qb_ipcs_service_t *cib_ro = NULL;
  26 static qb_ipcs_service_t *cib_rw = NULL;
  27 static qb_ipcs_service_t *cib_shm = NULL;
  28 
  29 static qb_ipcs_service_t *attrd_ipcs = NULL;
  30 static qb_ipcs_service_t *crmd_ipcs = NULL;
  31 static qb_ipcs_service_t *stonith_ipcs = NULL;
  32 static qb_ipcs_service_t *pacemakerd_ipcs = NULL;
  33 
  34 // An IPC provider is a cluster node controller connecting as a client
  35 static GList *ipc_providers = NULL;
  36 
  37 
  38 /* ipc clients == things like cibadmin, crm_resource, connecting locally
  39  *
  40  * @TODO This should be unnecessary (pcmk__foreach_ipc_client() should be
  41  * sufficient)
  42  */
  43 static GHashTable *ipc_clients = NULL;
  44 
  45 /*!
  46  * \internal
  47  * \brief Get an IPC proxy provider
  48  *
  49  * \return Pointer to a provider if one exists, NULL otherwise
  50  *
  51  * \note Grab the first provider, which is the most recent connection. That way,
  52  *       if we haven't yet timed out an old, failed connection, we don't try to
  53  *       use it.
  54  */
  55 pcmk__client_t *
  56 ipc_proxy_get_provider(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  57 {
  58     return ipc_providers? (pcmk__client_t *) (ipc_providers->data) : NULL;
  59 }
  60 
  61 /*!
  62  * \internal
  63  * \brief Accept a client connection on a proxy IPC server
  64  *
  65  * \param[in] c            Client's IPC connection
  66  * \param[in] uid          Client's user ID
  67  * \param[in] gid          Client's group ID
  68  * \param[in] ipc_channel  Name of IPC server to proxy
  69  *
  70  * \return pcmk_ok on success, -errno on error
  71  */
  72 static int32_t
  73 ipc_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid, const char *ipc_channel)
     /* [previous][next][first][last][top][bottom][index][help] */
  74 {
  75     pcmk__client_t *client;
  76     pcmk__client_t *ipc_proxy = ipc_proxy_get_provider();
  77     xmlNode *msg;
  78 
  79     if (ipc_proxy == NULL) {
  80         crm_warn("Cannot proxy IPC connection from uid %d gid %d to %s "
  81                  "because not connected to cluster", uid, gid, ipc_channel);
  82         return -EREMOTEIO;
  83     }
  84 
  85     /* This new client is a local IPC client on a Pacemaker Remote controlled
  86      * node, needing to access cluster node IPC services.
  87      */
  88     client = pcmk__new_client(c, uid, gid);
  89     if (client == NULL) {
  90         return -ENOMEM;
  91     }
  92 
  93     /* This ipc client is bound to a single ipc provider. If the
  94      * provider goes away, this client is disconnected */
  95     client->userdata = pcmk__str_copy(ipc_proxy->id);
  96     client->name = crm_strdup_printf("proxy-%s-%d-%.8s", ipc_channel, client->pid, client->id);
  97 
  98     /* Allow remote executor to distinguish between proxied local clients and
  99      * actual executor API clients
 100      */
 101     pcmk__set_client_flags(client, pcmk__client_to_proxy);
 102 
 103     g_hash_table_insert(ipc_clients, client->id, client);
 104 
 105     msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 106     crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_NEW);
 107     crm_xml_add(msg, PCMK__XA_LRMD_IPC_SERVER, ipc_channel);
 108     crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, client->id);
 109     lrmd_server_send_notify(ipc_proxy, msg);
 110     pcmk__xml_free(msg);
 111     crm_debug("Accepted IPC proxy connection (session ID %s) "
 112               "from uid %d gid %d on channel %s",
 113               client->id, uid, gid, ipc_channel);
 114     return 0;
 115 }
 116 
 117 static int32_t
 118 crmd_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120     return ipc_proxy_accept(c, uid, gid, CRM_SYSTEM_CRMD);
 121 }
 122 
 123 static int32_t
 124 attrd_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 125 {
 126     return ipc_proxy_accept(c, uid, gid, PCMK__VALUE_ATTRD);
 127 }
 128 
 129 static int32_t
 130 stonith_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 131 {
 132     return ipc_proxy_accept(c, uid, gid, "stonith-ng");
 133 }
 134 
 135 static int32_t
 136 pacemakerd_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138     return -EREMOTEIO;
 139 }
 140 
 141 static int32_t
 142 cib_proxy_accept_rw(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144     return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RW);
 145 }
 146 
 147 static int32_t
 148 cib_proxy_accept_ro(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 149 {
 150     return ipc_proxy_accept(c, uid, gid, PCMK__SERVER_BASED_RO);
 151 }
 152 
 153 void
 154 ipc_proxy_forward_client(pcmk__client_t *ipc_proxy, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 155 {
 156     const char *session = crm_element_value(xml, PCMK__XA_LRMD_IPC_SESSION);
 157     const char *msg_type = crm_element_value(xml, PCMK__XA_LRMD_IPC_OP);
 158 
 159     xmlNode *wrapper = pcmk__xe_first_child(xml, PCMK__XE_LRMD_IPC_MSG, NULL,
 160                                             NULL);
 161     xmlNode *msg = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
 162 
 163     pcmk__client_t *ipc_client;
 164     int rc = pcmk_rc_ok;
 165 
 166     if (pcmk__str_eq(msg_type, LRMD_IPC_OP_SHUTDOWN_ACK, pcmk__str_casei)) {
 167         handle_shutdown_ack();
 168         return;
 169     }
 170 
 171     if (pcmk__str_eq(msg_type, LRMD_IPC_OP_SHUTDOWN_NACK, pcmk__str_casei)) {
 172         handle_shutdown_nack();
 173         return;
 174     }
 175 
 176     ipc_client = pcmk__find_client_by_id(session);
 177     if (ipc_client == NULL) {
 178         xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 179         crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY);
 180         crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, session);
 181         lrmd_server_send_notify(ipc_proxy, msg);
 182         pcmk__xml_free(msg);
 183         return;
 184     }
 185 
 186     /* This is an event or response from the ipc provider
 187      * going to the local ipc client.
 188      *
 189      * Looking at the chain of events.
 190      *
 191      * -----remote node----------------|---- cluster node ------
 192      * ipc_client <--1--> this code
 193      *    <--2--> pacemaker-controld:remote_proxy_cb/remote_proxy_relay_event()
 194      *    <--3--> ipc server
 195      *
 196      * This function is receiving a msg from connection 2
 197      * and forwarding it to connection 1.
 198      */
 199 
 200     if (pcmk__str_eq(msg_type, LRMD_IPC_OP_EVENT, pcmk__str_casei)) {
 201         crm_trace("Sending event to %s", ipc_client->id);
 202         rc = pcmk__ipc_send_xml(ipc_client, 0, msg, crm_ipc_server_event);
 203 
 204     } else if (pcmk__str_eq(msg_type, LRMD_IPC_OP_RESPONSE, pcmk__str_casei)) {
 205         int msg_id = 0;
 206 
 207         crm_element_value_int(xml, PCMK__XA_LRMD_IPC_MSG_ID, &msg_id);
 208         crm_trace("Sending response to %d - %s", ipc_client->request_id, ipc_client->id);
 209         rc = pcmk__ipc_send_xml(ipc_client, msg_id, msg, crm_ipc_flags_none);
 210 
 211         CRM_LOG_ASSERT(msg_id == ipc_client->request_id);
 212         ipc_client->request_id = 0;
 213 
 214     } else if (pcmk__str_eq(msg_type, LRMD_IPC_OP_DESTROY, pcmk__str_casei)) {
 215         qb_ipcs_disconnect(ipc_client->ipcs);
 216 
 217     } else {
 218         crm_err("Unknown ipc proxy msg type %s" , msg_type);
 219     }
 220 
 221     if (rc != pcmk_rc_ok) {
 222         crm_warn("Could not proxy IPC to client %s: %s " QB_XS " rc=%d",
 223                  ipc_client->id, pcmk_rc_str(rc), rc);
 224     }
 225 }
 226 
 227 static int32_t
 228 ipc_proxy_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 229 {
 230     int rc = pcmk_rc_ok;
 231     uint32_t id = 0;
 232     uint32_t flags = 0;
 233     pcmk__client_t *client = pcmk__find_client(c);
 234     pcmk__client_t *ipc_proxy = pcmk__find_client_by_id(client->userdata);
 235     xmlNode *wrapper = NULL;
 236     xmlNode *request = NULL;
 237     xmlNode *msg = NULL;
 238 
 239     if (!ipc_proxy) {
 240         qb_ipcs_disconnect(client->ipcs);
 241         return 0;
 242     }
 243 
 244     /* This is a request from the local ipc client going
 245      * to the ipc provider.
 246      *
 247      * Looking at the chain of events.
 248      *
 249      * -----remote node----------------|---- cluster node ------
 250      * ipc_client <--1--> this code
 251      *     <--2--> pacemaker-controld:remote_proxy_dispatch_internal()
 252      *     <--3--> ipc server
 253      *
 254      * This function is receiving a request from connection
 255      * 1 and forwarding it to connection 2.
 256      */
 257     rc = pcmk__ipc_msg_append(&client->buffer, data);
 258 
 259     if (rc == pcmk_rc_ipc_more) {
 260         /* We haven't read the complete message yet, so just return. */
 261         return 0;
 262 
 263     } else if (rc == pcmk_rc_ok) {
 264         /* We've read the complete message and there's already a header on
 265          * the front.  Pass it off for processing.
 266          */
 267         request = pcmk__client_data2xml(client, &id, &flags);
 268         g_byte_array_free(client->buffer, TRUE);
 269         client->buffer = NULL;
 270 
 271     } else {
 272         /* Some sort of error occurred reassembling the message.  All we can
 273          * do is clean up, log an error and return.
 274          */
 275         crm_err("Error when reading IPC message: %s", pcmk_rc_str(rc));
 276 
 277         if (client->buffer != NULL) {
 278             g_byte_array_free(client->buffer, TRUE);
 279             client->buffer = NULL;
 280         }
 281 
 282         return 0;
 283     }
 284 
 285     if (!request) {
 286         return 0;
 287     }
 288 
 289     CRM_CHECK(client != NULL, crm_err("Invalid client");
 290               pcmk__xml_free(request); return FALSE);
 291     CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p", client);
 292               pcmk__xml_free(request); return FALSE);
 293 
 294     /* This ensures that synced request/responses happen over the event channel
 295      * in the controller, allowing the controller to process the messages async.
 296      */
 297     pcmk__set_ipc_flags(flags, pcmk__client_name(client), crm_ipc_proxied);
 298     client->request_id = id;
 299 
 300     msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 301     crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_REQUEST);
 302     crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, client->id);
 303     crm_xml_add(msg, PCMK__XA_LRMD_IPC_CLIENT, pcmk__client_name(client));
 304     crm_xml_add(msg, PCMK__XA_LRMD_IPC_USER, client->user);
 305     crm_xml_add_int(msg, PCMK__XA_LRMD_IPC_MSG_ID, id);
 306     crm_xml_add_int(msg, PCMK__XA_LRMD_IPC_MSG_FLAGS, flags);
 307 
 308     wrapper = pcmk__xe_create(msg, PCMK__XE_LRMD_IPC_MSG);
 309 
 310     pcmk__xml_copy(wrapper, request);
 311 
 312     lrmd_server_send_notify(ipc_proxy, msg);
 313 
 314     pcmk__xml_free(request);
 315     pcmk__xml_free(msg);
 316     return 0;
 317 }
 318 
 319 /*!
 320  * \internal
 321  * \brief Notify a proxy provider that we wish to shut down
 322  *
 323  * \param[in,out] ipc_proxy  IPC client connection to proxy provider
 324  *
 325  * \return 0 on success, -1 on error
 326  */
 327 int
 328 ipc_proxy_shutdown_req(pcmk__client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 329 {
 330     xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 331     int rc;
 332 
 333     crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_REQ);
 334 
 335     /* We don't really have a session, but the controller needs this attribute
 336      * to recognize this as proxy communication.
 337      */
 338     crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, "0");
 339 
 340     rc = (lrmd_server_send_notify(ipc_proxy, msg) != pcmk_rc_ok)? -1 : 0;
 341     pcmk__xml_free(msg);
 342     return rc;
 343 }
 344 
 345 static int32_t
 346 ipc_proxy_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 347 {
 348     pcmk__client_t *client = pcmk__find_client(c);
 349     pcmk__client_t *ipc_proxy;
 350 
 351     if (client == NULL) {
 352         return 0;
 353     }
 354 
 355     ipc_proxy = pcmk__find_client_by_id(client->userdata);
 356 
 357     crm_trace("Connection %p", c);
 358 
 359     if (ipc_proxy) {
 360         xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 361         crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY);
 362         crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, client->id);
 363         lrmd_server_send_notify(ipc_proxy, msg);
 364         pcmk__xml_free(msg);
 365     }
 366 
 367     g_hash_table_remove(ipc_clients, client->id);
 368 
 369     free(client->userdata);
 370     client->userdata = NULL;
 371     pcmk__free_client(client);
 372     return 0;
 373 }
 374 
 375 static void
 376 ipc_proxy_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 377 {
 378     crm_trace("Connection %p", c);
 379     ipc_proxy_closed(c);
 380 }
 381 
 382 static struct qb_ipcs_service_handlers crmd_proxy_callbacks = {
 383     .connection_accept = crmd_proxy_accept,
 384     .connection_created = NULL,
 385     .msg_process = ipc_proxy_dispatch,
 386     .connection_closed = ipc_proxy_closed,
 387     .connection_destroyed = ipc_proxy_destroy
 388 };
 389 
 390 static struct qb_ipcs_service_handlers attrd_proxy_callbacks = {
 391     .connection_accept = attrd_proxy_accept,
 392     .connection_created = NULL,
 393     .msg_process = ipc_proxy_dispatch,
 394     .connection_closed = ipc_proxy_closed,
 395     .connection_destroyed = ipc_proxy_destroy
 396 };
 397 
 398 static struct qb_ipcs_service_handlers stonith_proxy_callbacks = {
 399     .connection_accept = stonith_proxy_accept,
 400     .connection_created = NULL,
 401     .msg_process = ipc_proxy_dispatch,
 402     .connection_closed = ipc_proxy_closed,
 403     .connection_destroyed = ipc_proxy_destroy
 404 };
 405 
 406 static struct qb_ipcs_service_handlers pacemakerd_proxy_callbacks = {
 407     .connection_accept = pacemakerd_proxy_accept,
 408     .connection_created = NULL,
 409     .msg_process = NULL,
 410     .connection_closed = NULL,
 411     .connection_destroyed = NULL
 412 };
 413 
 414 static struct qb_ipcs_service_handlers cib_proxy_callbacks_ro = {
 415     .connection_accept = cib_proxy_accept_ro,
 416     .connection_created = NULL,
 417     .msg_process = ipc_proxy_dispatch,
 418     .connection_closed = ipc_proxy_closed,
 419     .connection_destroyed = ipc_proxy_destroy
 420 };
 421 
 422 static struct qb_ipcs_service_handlers cib_proxy_callbacks_rw = {
 423     .connection_accept = cib_proxy_accept_rw,
 424     .connection_created = NULL,
 425     .msg_process = ipc_proxy_dispatch,
 426     .connection_closed = ipc_proxy_closed,
 427     .connection_destroyed = ipc_proxy_destroy
 428 };
 429 
 430 void
 431 ipc_proxy_add_provider(pcmk__client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 432 {
 433     // Prepending ensures the most recent connection is always first
 434     ipc_providers = g_list_prepend(ipc_providers, ipc_proxy);
 435 }
 436 
 437 void
 438 ipc_proxy_remove_provider(pcmk__client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 439 {
 440     GHashTableIter iter;
 441     pcmk__client_t *ipc_client = NULL;
 442     char *key = NULL;
 443     GList *remove_these = NULL;
 444     GList *gIter = NULL;
 445 
 446     ipc_providers = g_list_remove(ipc_providers, ipc_proxy);
 447 
 448     g_hash_table_iter_init(&iter, ipc_clients);
 449     while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & ipc_client)) {
 450         const char *proxy_id = ipc_client->userdata;
 451         if (pcmk__str_eq(proxy_id, ipc_proxy->id, pcmk__str_casei)) {
 452             crm_info("ipc proxy connection for client %s pid %d destroyed because cluster node disconnected.",
 453                 ipc_client->id, ipc_client->pid);
 454             /* we can't remove during the iteration, so copy items
 455              * to a list we can destroy later */
 456             remove_these = g_list_append(remove_these, ipc_client);
 457         }
 458     }
 459 
 460     for (gIter = remove_these; gIter != NULL; gIter = gIter->next) {
 461         ipc_client = gIter->data;
 462 
 463         // Disconnection callback will free the client here
 464         qb_ipcs_disconnect(ipc_client->ipcs);
 465     }
 466 
 467     /* just frees the list, not the elements in the list */
 468     g_list_free(remove_these);
 469 }
 470 
 471 void
 472 ipc_proxy_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474     ipc_clients = pcmk__strkey_table(NULL, NULL);
 475 
 476     pcmk__serve_based_ipc(&cib_ro, &cib_rw, &cib_shm, &cib_proxy_callbacks_ro,
 477                           &cib_proxy_callbacks_rw);
 478     pcmk__serve_attrd_ipc(&attrd_ipcs, &attrd_proxy_callbacks);
 479     pcmk__serve_fenced_ipc(&stonith_ipcs, &stonith_proxy_callbacks);
 480     pcmk__serve_pacemakerd_ipc(&pacemakerd_ipcs, &pacemakerd_proxy_callbacks);
 481     crmd_ipcs = pcmk__serve_controld_ipc(&crmd_proxy_callbacks);
 482     if (crmd_ipcs == NULL) {
 483         crm_err("Failed to create controller: exiting and inhibiting respawn");
 484         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled");
 485         crm_exit(CRM_EX_FATAL);
 486     }
 487 }
 488 
 489 void
 490 ipc_proxy_cleanup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 491 {
 492     if (ipc_providers) {
 493         g_list_free(ipc_providers);
 494         ipc_providers = NULL;
 495     }
 496     if (ipc_clients) {
 497         g_hash_table_destroy(ipc_clients);
 498         ipc_clients = NULL;
 499     }
 500     pcmk__stop_based_ipc(cib_ro, cib_rw, cib_shm);
 501     qb_ipcs_destroy(attrd_ipcs);
 502     qb_ipcs_destroy(stonith_ipcs);
 503     qb_ipcs_destroy(pacemakerd_ipcs);
 504     qb_ipcs_destroy(crmd_ipcs);
 505     cib_ro = NULL;
 506     cib_rw = NULL;
 507     cib_shm = NULL;
 508 }

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