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     free_xml(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 the IPC provider is acknowledging our shutdown request,
 161      * defuse the short exit timer to give the cluster time to
 162      * stop any resources we're running.
 163      */
 164     if (pcmk__str_eq(msg_type, LRMD_IPC_OP_SHUTDOWN_ACK, pcmk__str_casei)) {
 165         handle_shutdown_ack();
 166         return;
 167     }
 168 
 169     if (pcmk__str_eq(msg_type, LRMD_IPC_OP_SHUTDOWN_NACK, pcmk__str_casei)) {
 170         handle_shutdown_nack();
 171         return;
 172     }
 173 
 174     ipc_client = pcmk__find_client_by_id(session);
 175     if (ipc_client == NULL) {
 176         xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 177         crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY);
 178         crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, session);
 179         lrmd_server_send_notify(ipc_proxy, msg);
 180         free_xml(msg);
 181         return;
 182     }
 183 
 184     /* This is an event or response from the ipc provider
 185      * going to the local ipc client.
 186      *
 187      * Looking at the chain of events.
 188      *
 189      * -----remote node----------------|---- cluster node ------
 190      * ipc_client <--1--> this code
 191      *    <--2--> pacemaker-controld:remote_proxy_cb/remote_proxy_relay_event()
 192      *    <--3--> ipc server
 193      *
 194      * This function is receiving a msg from connection 2
 195      * and forwarding it to connection 1.
 196      */
 197 
 198     if (pcmk__str_eq(msg_type, LRMD_IPC_OP_EVENT, pcmk__str_casei)) {
 199         crm_trace("Sending event to %s", ipc_client->id);
 200         rc = pcmk__ipc_send_xml(ipc_client, 0, msg, crm_ipc_server_event);
 201 
 202     } else if (pcmk__str_eq(msg_type, LRMD_IPC_OP_RESPONSE, pcmk__str_casei)) {
 203         int msg_id = 0;
 204 
 205         crm_element_value_int(xml, PCMK__XA_LRMD_IPC_MSG_ID, &msg_id);
 206         crm_trace("Sending response to %d - %s", ipc_client->request_id, ipc_client->id);
 207         rc = pcmk__ipc_send_xml(ipc_client, msg_id, msg, FALSE);
 208 
 209         CRM_LOG_ASSERT(msg_id == ipc_client->request_id);
 210         ipc_client->request_id = 0;
 211 
 212     } else if (pcmk__str_eq(msg_type, LRMD_IPC_OP_DESTROY, pcmk__str_casei)) {
 213         qb_ipcs_disconnect(ipc_client->ipcs);
 214 
 215     } else {
 216         crm_err("Unknown ipc proxy msg type %s" , msg_type);
 217     }
 218 
 219     if (rc != pcmk_rc_ok) {
 220         crm_warn("Could not proxy IPC to client %s: %s " CRM_XS " rc=%d",
 221                  ipc_client->id, pcmk_rc_str(rc), rc);
 222     }
 223 }
 224 
 225 static int32_t
 226 ipc_proxy_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 227 {
 228     uint32_t id = 0;
 229     uint32_t flags = 0;
 230     pcmk__client_t *client = pcmk__find_client(c);
 231     pcmk__client_t *ipc_proxy = pcmk__find_client_by_id(client->userdata);
 232     xmlNode *wrapper = NULL;
 233     xmlNode *request = NULL;
 234     xmlNode *msg = NULL;
 235 
 236     if (!ipc_proxy) {
 237         qb_ipcs_disconnect(client->ipcs);
 238         return 0;
 239     }
 240 
 241     /* This is a request from the local ipc client going
 242      * to the ipc provider.
 243      *
 244      * Looking at the chain of events.
 245      *
 246      * -----remote node----------------|---- cluster node ------
 247      * ipc_client <--1--> this code
 248      *     <--2--> pacemaker-controld:remote_proxy_dispatch_internal()
 249      *     <--3--> ipc server
 250      *
 251      * This function is receiving a request from connection
 252      * 1 and forwarding it to connection 2.
 253      */
 254     request = pcmk__client_data2xml(client, data, &id, &flags);
 255 
 256     if (!request) {
 257         return 0;
 258     }
 259 
 260     CRM_CHECK(client != NULL, crm_err("Invalid client");
 261               free_xml(request); return FALSE);
 262     CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p", client);
 263               free_xml(request); return FALSE);
 264 
 265     /* This ensures that synced request/responses happen over the event channel
 266      * in the controller, allowing the controller to process the messages async.
 267      */
 268     pcmk__set_ipc_flags(flags, pcmk__client_name(client), crm_ipc_proxied);
 269     client->request_id = id;
 270 
 271     msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 272     crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_REQUEST);
 273     crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, client->id);
 274     crm_xml_add(msg, PCMK__XA_LRMD_IPC_CLIENT, pcmk__client_name(client));
 275     crm_xml_add(msg, PCMK__XA_LRMD_IPC_USER, client->user);
 276     crm_xml_add_int(msg, PCMK__XA_LRMD_IPC_MSG_ID, id);
 277     crm_xml_add_int(msg, PCMK__XA_LRMD_IPC_MSG_FLAGS, flags);
 278 
 279     wrapper = pcmk__xe_create(msg, PCMK__XE_LRMD_IPC_MSG);
 280 
 281     pcmk__xml_copy(wrapper, request);
 282 
 283     lrmd_server_send_notify(ipc_proxy, msg);
 284 
 285     free_xml(request);
 286     free_xml(msg);
 287     return 0;
 288 }
 289 
 290 /*!
 291  * \internal
 292  * \brief Notify a proxy provider that we wish to shut down
 293  *
 294  * \param[in,out] ipc_proxy  IPC client connection to proxy provider
 295  *
 296  * \return 0 on success, -1 on error
 297  */
 298 int
 299 ipc_proxy_shutdown_req(pcmk__client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 300 {
 301     xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 302     int rc;
 303 
 304     crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_REQ);
 305 
 306     /* We don't really have a session, but the controller needs this attribute
 307      * to recognize this as proxy communication.
 308      */
 309     crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, "0");
 310 
 311     rc = (lrmd_server_send_notify(ipc_proxy, msg) != pcmk_rc_ok)? -1 : 0;
 312     free_xml(msg);
 313     return rc;
 314 }
 315 
 316 static int32_t
 317 ipc_proxy_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 318 {
 319     pcmk__client_t *client = pcmk__find_client(c);
 320     pcmk__client_t *ipc_proxy;
 321 
 322     if (client == NULL) {
 323         return 0;
 324     }
 325 
 326     ipc_proxy = pcmk__find_client_by_id(client->userdata);
 327 
 328     crm_trace("Connection %p", c);
 329 
 330     if (ipc_proxy) {
 331         xmlNode *msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_IPC_PROXY);
 332         crm_xml_add(msg, PCMK__XA_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY);
 333         crm_xml_add(msg, PCMK__XA_LRMD_IPC_SESSION, client->id);
 334         lrmd_server_send_notify(ipc_proxy, msg);
 335         free_xml(msg);
 336     }
 337 
 338     g_hash_table_remove(ipc_clients, client->id);
 339 
 340     free(client->userdata);
 341     client->userdata = NULL;
 342     pcmk__free_client(client);
 343     return 0;
 344 }
 345 
 346 static void
 347 ipc_proxy_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349     crm_trace("Connection %p", c);
 350     ipc_proxy_closed(c);
 351 }
 352 
 353 static struct qb_ipcs_service_handlers crmd_proxy_callbacks = {
 354     .connection_accept = crmd_proxy_accept,
 355     .connection_created = NULL,
 356     .msg_process = ipc_proxy_dispatch,
 357     .connection_closed = ipc_proxy_closed,
 358     .connection_destroyed = ipc_proxy_destroy
 359 };
 360 
 361 static struct qb_ipcs_service_handlers attrd_proxy_callbacks = {
 362     .connection_accept = attrd_proxy_accept,
 363     .connection_created = NULL,
 364     .msg_process = ipc_proxy_dispatch,
 365     .connection_closed = ipc_proxy_closed,
 366     .connection_destroyed = ipc_proxy_destroy
 367 };
 368 
 369 static struct qb_ipcs_service_handlers stonith_proxy_callbacks = {
 370     .connection_accept = stonith_proxy_accept,
 371     .connection_created = NULL,
 372     .msg_process = ipc_proxy_dispatch,
 373     .connection_closed = ipc_proxy_closed,
 374     .connection_destroyed = ipc_proxy_destroy
 375 };
 376 
 377 static struct qb_ipcs_service_handlers pacemakerd_proxy_callbacks = {
 378     .connection_accept = pacemakerd_proxy_accept,
 379     .connection_created = NULL,
 380     .msg_process = NULL,
 381     .connection_closed = NULL,
 382     .connection_destroyed = NULL
 383 };
 384 
 385 static struct qb_ipcs_service_handlers cib_proxy_callbacks_ro = {
 386     .connection_accept = cib_proxy_accept_ro,
 387     .connection_created = NULL,
 388     .msg_process = ipc_proxy_dispatch,
 389     .connection_closed = ipc_proxy_closed,
 390     .connection_destroyed = ipc_proxy_destroy
 391 };
 392 
 393 static struct qb_ipcs_service_handlers cib_proxy_callbacks_rw = {
 394     .connection_accept = cib_proxy_accept_rw,
 395     .connection_created = NULL,
 396     .msg_process = ipc_proxy_dispatch,
 397     .connection_closed = ipc_proxy_closed,
 398     .connection_destroyed = ipc_proxy_destroy
 399 };
 400 
 401 void
 402 ipc_proxy_add_provider(pcmk__client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 403 {
 404     // Prepending ensures the most recent connection is always first
 405     ipc_providers = g_list_prepend(ipc_providers, ipc_proxy);
 406 }
 407 
 408 void
 409 ipc_proxy_remove_provider(pcmk__client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 410 {
 411     GHashTableIter iter;
 412     pcmk__client_t *ipc_client = NULL;
 413     char *key = NULL;
 414     GList *remove_these = NULL;
 415     GList *gIter = NULL;
 416 
 417     ipc_providers = g_list_remove(ipc_providers, ipc_proxy);
 418 
 419     g_hash_table_iter_init(&iter, ipc_clients);
 420     while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & ipc_client)) {
 421         const char *proxy_id = ipc_client->userdata;
 422         if (pcmk__str_eq(proxy_id, ipc_proxy->id, pcmk__str_casei)) {
 423             crm_info("ipc proxy connection for client %s pid %d destroyed because cluster node disconnected.",
 424                 ipc_client->id, ipc_client->pid);
 425             /* we can't remove during the iteration, so copy items
 426              * to a list we can destroy later */
 427             remove_these = g_list_append(remove_these, ipc_client);
 428         }
 429     }
 430 
 431     for (gIter = remove_these; gIter != NULL; gIter = gIter->next) {
 432         ipc_client = gIter->data;
 433 
 434         // Disconnection callback will free the client here
 435         qb_ipcs_disconnect(ipc_client->ipcs);
 436     }
 437 
 438     /* just frees the list, not the elements in the list */
 439     g_list_free(remove_these);
 440 }
 441 
 442 void
 443 ipc_proxy_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 444 {
 445     ipc_clients = pcmk__strkey_table(NULL, NULL);
 446 
 447     pcmk__serve_based_ipc(&cib_ro, &cib_rw, &cib_shm, &cib_proxy_callbacks_ro,
 448                           &cib_proxy_callbacks_rw);
 449     pcmk__serve_attrd_ipc(&attrd_ipcs, &attrd_proxy_callbacks);
 450     pcmk__serve_fenced_ipc(&stonith_ipcs, &stonith_proxy_callbacks);
 451     pcmk__serve_pacemakerd_ipc(&pacemakerd_ipcs, &pacemakerd_proxy_callbacks);
 452     crmd_ipcs = pcmk__serve_controld_ipc(&crmd_proxy_callbacks);
 453     if (crmd_ipcs == NULL) {
 454         crm_err("Failed to create controller: exiting and inhibiting respawn");
 455         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled");
 456         crm_exit(CRM_EX_FATAL);
 457     }
 458 }
 459 
 460 void
 461 ipc_proxy_cleanup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 462 {
 463     if (ipc_providers) {
 464         g_list_free(ipc_providers);
 465         ipc_providers = NULL;
 466     }
 467     if (ipc_clients) {
 468         g_hash_table_destroy(ipc_clients);
 469         ipc_clients = NULL;
 470     }
 471     pcmk__stop_based_ipc(cib_ro, cib_rw, cib_shm);
 472     qb_ipcs_destroy(attrd_ipcs);
 473     qb_ipcs_destroy(stonith_ipcs);
 474     qb_ipcs_destroy(pacemakerd_ipcs);
 475     qb_ipcs_destroy(crmd_ipcs);
 476     cib_ro = NULL;
 477     cib_rw = NULL;
 478     cib_shm = NULL;
 479 }

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