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

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