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. cib_proxy_accept_rw
  7. cib_proxy_accept_ro
  8. ipc_proxy_forward_client
  9. ipc_proxy_dispatch
  10. ipc_proxy_shutdown_req
  11. ipc_proxy_closed
  12. ipc_proxy_destroy
  13. ipc_proxy_add_provider
  14. ipc_proxy_remove_provider
  15. ipc_proxy_init
  16. ipc_proxy_cleanup

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

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