root/lrmd/ipc_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_created
  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 (c) 2012 David Vossel <davidvossel@gmail.com>
   3  *
   4  * This library is free software; you can redistribute it and/or
   5  * modify it under the terms of the GNU Lesser General Public
   6  * License as published by the Free Software Foundation; either
   7  * version 2.1 of the License, or (at your option) any later version.
   8  * 
   9  * This library is distributed in the hope that it will be useful,
  10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  12  * Lesser General Public License for more details.
  13  * 
  14  * You should have received a copy of the GNU Lesser General Public
  15  * License along with this library; if not, write to the Free Software
  16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  17  *
  18  */
  19 
  20 
  21 #include <crm_internal.h>
  22 #include <lrmd_private.h>
  23 
  24 #include <glib.h>
  25 #include <unistd.h>
  26 
  27 #include <crm/crm.h>
  28 #include <crm/msg_xml.h>
  29 #include <crm/services.h>
  30 #include <crm/common/mainloop.h>
  31 #include <crm/common/ipc.h>
  32 #include <crm/common/ipcs.h>
  33 #include <crm/cib/internal.h>
  34 #include <crm/fencing/internal.h>
  35 
  36 static qb_ipcs_service_t *cib_ro = NULL;
  37 static qb_ipcs_service_t *cib_rw = NULL;
  38 static qb_ipcs_service_t *cib_shm = NULL;
  39 
  40 static qb_ipcs_service_t *attrd_ipcs = NULL;
  41 static qb_ipcs_service_t *crmd_ipcs = NULL;
  42 static qb_ipcs_service_t *stonith_ipcs = NULL;
  43 
  44 /* ipc providers == crmd clients connecting from cluster nodes */
  45 static GHashTable *ipc_providers = NULL;
  46 /* ipc clients == things like cibadmin, crm_resource, connecting locally */
  47 static GHashTable *ipc_clients = NULL;
  48 
  49 /*!
  50  * \internal
  51  * \brief Get an IPC proxy provider
  52  *
  53  * \return Pointer to a provider if one exists, NULL otherwise
  54  *
  55  * \note Grab the first provider available; any provider will work, and usually
  56  *       there will be only one. These are client connections originating from a
  57  *       cluster node's crmd.
  58  */
  59 crm_client_t *
  60 ipc_proxy_get_provider()
     /* [previous][next][first][last][top][bottom][index][help] */
  61 {
  62     if (ipc_providers) {
  63         GHashTableIter iter;
  64         gpointer key = NULL;
  65         gpointer value = NULL;
  66 
  67         g_hash_table_iter_init(&iter, ipc_providers);
  68         if (g_hash_table_iter_next(&iter, &key, &value)) {
  69             return (crm_client_t*)value;
  70         }
  71     }
  72     return NULL;
  73 }
  74 
  75 static int32_t
  76 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] */
  77 {
  78     crm_client_t *client;
  79     crm_client_t *ipc_proxy = ipc_proxy_get_provider();
  80     xmlNode *msg;
  81 
  82     crm_trace("Connection %p on channel %s", c, ipc_channel);
  83 
  84     if (ipc_proxy == NULL) {
  85         crm_err("No ipc providers available for uid %d gid %d", uid, gid);
  86         return -EREMOTEIO;
  87     }
  88 
  89     /* this new client is a local ipc client on a remote
  90      * guest wanting to access the ipc on any available cluster nodes */
  91     client = crm_client_new(c, uid, gid);
  92     if (client == NULL) {
  93         return -EREMOTEIO;
  94     }
  95 
  96     /* This ipc client is bound to a single ipc provider. If the
  97      * provider goes away, this client is disconnected */
  98     client->userdata = strdup(ipc_proxy->id);
  99     client->name = crm_strdup_printf("proxy-%s-%d-%.8s", ipc_channel, client->pid, client->id);
 100 
 101     g_hash_table_insert(ipc_clients, client->id, client);
 102 
 103     msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
 104     crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_NEW);
 105     crm_xml_add(msg, F_LRMD_IPC_IPC_SERVER, ipc_channel);
 106     crm_xml_add(msg, F_LRMD_IPC_SESSION, client->id);
 107     lrmd_server_send_notify(ipc_proxy, msg);
 108     free_xml(msg);
 109     crm_debug("created new ipc proxy with session id %s", client->id);
 110     return 0;
 111 }
 112 
 113 static int32_t
 114 crmd_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 115 {
 116     return ipc_proxy_accept(c, uid, gid, CRM_SYSTEM_CRMD);
 117 }
 118 
 119 static int32_t
 120 attrd_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 121 {
 122     return ipc_proxy_accept(c, uid, gid, T_ATTRD);
 123 }
 124 
 125 static int32_t
 126 stonith_proxy_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128     return ipc_proxy_accept(c, uid, gid, "stonith-ng");
 129 }
 130 
 131 static int32_t
 132 cib_proxy_accept_rw(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 133 {
 134     return ipc_proxy_accept(c, uid, gid, cib_channel_rw);
 135 }
 136 
 137 static int32_t
 138 cib_proxy_accept_ro(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help] */
 139 {
 140     return ipc_proxy_accept(c, uid, gid, cib_channel_ro);
 141 }
 142 
 143 static void
 144 ipc_proxy_created(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 145 {
 146     crm_trace("Connection %p", c);
 147 }
 148 
 149 void
 150 ipc_proxy_forward_client(crm_client_t *ipc_proxy, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 151 {
 152     const char *session = crm_element_value(xml, F_LRMD_IPC_SESSION);
 153     const char *msg_type = crm_element_value(xml, F_LRMD_IPC_OP);
 154     xmlNode *msg = get_message_xml(xml, F_LRMD_IPC_MSG);
 155     crm_client_t *ipc_client;
 156     int rc = 0;
 157 
 158     /* If the IPC provider is acknowledging our shutdown request,
 159      * defuse the short exit timer to give the cluster time to
 160      * stop any resources we're running.
 161      */
 162     if (safe_str_eq(msg_type, LRMD_IPC_OP_SHUTDOWN_ACK)) {
 163         handle_shutdown_ack();
 164         return;
 165     }
 166 
 167     if (safe_str_eq(msg_type, LRMD_IPC_OP_SHUTDOWN_NACK)) {
 168         handle_shutdown_nack();
 169         return;
 170     }
 171 
 172     ipc_client = crm_client_get_by_id(session);
 173     if (ipc_client == NULL) {
 174         xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
 175         crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY);
 176         crm_xml_add(msg, F_LRMD_IPC_SESSION, session);
 177         lrmd_server_send_notify(ipc_proxy, msg);
 178         free_xml(msg);
 179         return;
 180     }
 181 
 182     /* This is an event or response from the ipc provider
 183      * going to the local ipc client.
 184      *
 185      * Looking at the chain of events.
 186      *
 187      * -----remote node----------------|---- cluster node ------
 188      * ipc_client <--1--> this code <--2--> crmd:remote_proxy_cb/remote_proxy_relay_event() <----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 (safe_str_eq(msg_type, LRMD_IPC_OP_EVENT)) {
 195         crm_trace("Sending event to %s", ipc_client->id);
 196         rc = crm_ipcs_send(ipc_client, 0, msg, crm_ipc_server_event);
 197 
 198     } else if (safe_str_eq(msg_type, LRMD_IPC_OP_RESPONSE)) {
 199         int msg_id = 0;
 200 
 201         crm_element_value_int(xml, F_LRMD_IPC_MSG_ID, &msg_id);
 202         crm_trace("Sending response to %d - %s", ipc_client->request_id, ipc_client->id);
 203         rc = crm_ipcs_send(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 (safe_str_eq(msg_type, LRMD_IPC_OP_DESTROY)) {
 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 < 0) {
 216         crm_warn("IPC Proxy send to ipc client %s failed, rc = %d", ipc_client->id, rc);
 217     }
 218 }
 219 
 220 static int32_t
 221 ipc_proxy_dispatch(qb_ipcs_connection_t * c, void *data, size_t size)
     /* [previous][next][first][last][top][bottom][index][help] */
 222 {
 223     uint32_t id = 0;
 224     uint32_t flags = 0;
 225     crm_client_t *client = crm_client_get(c);
 226     crm_client_t *ipc_proxy = crm_client_get_by_id(client->userdata);
 227     xmlNode *request = NULL;
 228     xmlNode *msg = NULL;
 229 
 230     if (!ipc_proxy) {
 231         qb_ipcs_disconnect(client->ipcs);
 232         return 0;
 233     }
 234 
 235     /* This is a request from the local ipc client going
 236      * to the ipc provider.
 237      *
 238      * Looking at the chain of events.
 239      *
 240      * -----remote node----------------|---- cluster node ------
 241      * ipc_client <--1--> this code <--2--> crmd:remote_proxy_dispatch_internal() <----3----> ipc server
 242      *
 243      * This function is receiving a request from connection
 244      * 1 and forwarding it to connection 2.
 245      */
 246     request = crm_ipcs_recv(client, data, size, &id, &flags);
 247 
 248     if (!request) {
 249         return 0;
 250     }
 251 
 252     CRM_CHECK(client != NULL, crm_err("Invalid client");
 253               free_xml(request); return FALSE);
 254     CRM_CHECK(client->id != NULL, crm_err("Invalid client: %p", client);
 255               free_xml(request); return FALSE);
 256 
 257     /* this ensures that synced request/responses happen over the event channel
 258      * in the crmd, allowing the crmd to process the messages async */
 259     set_bit(flags, crm_ipc_proxied);
 260     client->request_id = id;
 261 
 262     msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
 263     crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_REQUEST);
 264     crm_xml_add(msg, F_LRMD_IPC_SESSION, client->id);
 265     crm_xml_add(msg, F_LRMD_IPC_CLIENT, crm_client_name(client));
 266     crm_xml_add(msg, F_LRMD_IPC_USER, client->user);
 267     crm_xml_add_int(msg, F_LRMD_IPC_MSG_ID, id);
 268     crm_xml_add_int(msg, F_LRMD_IPC_MSG_FLAGS, flags);
 269     add_message_xml(msg, F_LRMD_IPC_MSG, request);
 270     lrmd_server_send_notify(ipc_proxy, msg);
 271     free_xml(request);
 272     free_xml(msg);
 273 
 274     return 0;
 275 }
 276 
 277 /*!
 278  * \internal
 279  * \brief Notify a proxy provider that we wish to shut down
 280  *
 281  * \return 0 on success, -1 on error
 282  */
 283 int
 284 ipc_proxy_shutdown_req(crm_client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 285 {
 286     xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
 287     int rc;
 288 
 289     crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_SHUTDOWN_REQ);
 290 
 291     /* We don't really have a session, but crmd needs this attribute
 292      * to recognize this as proxy communication.
 293      */
 294     crm_xml_add(msg, F_LRMD_IPC_SESSION, "0");
 295 
 296     rc = (lrmd_server_send_notify(ipc_proxy, msg) < 0)? -1 : 0;
 297     free_xml(msg);
 298     return rc;
 299 }
 300 
 301 static int32_t
 302 ipc_proxy_closed(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 303 {
 304     crm_client_t *client = crm_client_get(c);
 305     crm_client_t *ipc_proxy;
 306 
 307     if (client == NULL) {
 308         return 0;
 309     }
 310 
 311     ipc_proxy = crm_client_get_by_id(client->userdata);
 312 
 313     crm_trace("Connection %p", c);
 314 
 315     if (ipc_proxy) {
 316         xmlNode *msg = create_xml_node(NULL, T_LRMD_IPC_PROXY);
 317         crm_xml_add(msg, F_LRMD_IPC_OP, LRMD_IPC_OP_DESTROY);
 318         crm_xml_add(msg, F_LRMD_IPC_SESSION, client->id);
 319         lrmd_server_send_notify(ipc_proxy, msg);
 320         free_xml(msg);
 321     }
 322 
 323     g_hash_table_remove(ipc_clients, client->id);
 324 
 325     free(client->userdata);
 326     client->userdata = NULL;
 327     crm_client_destroy(client);
 328     return 0;
 329 }
 330 
 331 static void
 332 ipc_proxy_destroy(qb_ipcs_connection_t * c)
     /* [previous][next][first][last][top][bottom][index][help] */
 333 {
 334     crm_trace("Connection %p", c);
 335     ipc_proxy_closed(c);
 336 }
 337 
 338 static struct qb_ipcs_service_handlers crmd_proxy_callbacks = {
 339     .connection_accept = crmd_proxy_accept,
 340     .connection_created = ipc_proxy_created,
 341     .msg_process = ipc_proxy_dispatch,
 342     .connection_closed = ipc_proxy_closed,
 343     .connection_destroyed = ipc_proxy_destroy
 344 };
 345 
 346 static struct qb_ipcs_service_handlers attrd_proxy_callbacks = {
 347     .connection_accept = attrd_proxy_accept,
 348     .connection_created = ipc_proxy_created,
 349     .msg_process = ipc_proxy_dispatch,
 350     .connection_closed = ipc_proxy_closed,
 351     .connection_destroyed = ipc_proxy_destroy
 352 };
 353 
 354 static struct qb_ipcs_service_handlers stonith_proxy_callbacks = {
 355     .connection_accept = stonith_proxy_accept,
 356     .connection_created = ipc_proxy_created,
 357     .msg_process = ipc_proxy_dispatch,
 358     .connection_closed = ipc_proxy_closed,
 359     .connection_destroyed = ipc_proxy_destroy
 360 };
 361 
 362 static struct qb_ipcs_service_handlers cib_proxy_callbacks_ro = {
 363     .connection_accept = cib_proxy_accept_ro,
 364     .connection_created = ipc_proxy_created,
 365     .msg_process = ipc_proxy_dispatch,
 366     .connection_closed = ipc_proxy_closed,
 367     .connection_destroyed = ipc_proxy_destroy
 368 };
 369 
 370 static struct qb_ipcs_service_handlers cib_proxy_callbacks_rw = {
 371     .connection_accept = cib_proxy_accept_rw,
 372     .connection_created = ipc_proxy_created,
 373     .msg_process = ipc_proxy_dispatch,
 374     .connection_closed = ipc_proxy_closed,
 375     .connection_destroyed = ipc_proxy_destroy
 376 };
 377 
 378 void
 379 ipc_proxy_add_provider(crm_client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 380 {
 381     if (ipc_providers == NULL) {
 382         return;
 383     }
 384     g_hash_table_insert(ipc_providers, ipc_proxy->id, ipc_proxy);
 385 }
 386 
 387 void
 388 ipc_proxy_remove_provider(crm_client_t *ipc_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
 389 {
 390     GHashTableIter iter;
 391     crm_client_t *ipc_client = NULL;
 392     char *key = NULL;
 393     GList *remove_these = NULL;
 394     GListPtr gIter = NULL;
 395 
 396     if (ipc_providers == NULL) {
 397         return;
 398     }
 399 
 400     g_hash_table_remove(ipc_providers, ipc_proxy->id);
 401 
 402     g_hash_table_iter_init(&iter, ipc_clients);
 403     while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & ipc_client)) {
 404         const char *proxy_id = ipc_client->userdata;
 405         if (safe_str_eq(proxy_id, ipc_proxy->id)) {
 406             crm_info("ipc proxy connection for client %s pid %d destroyed because cluster node disconnected.",
 407                 ipc_client->id, ipc_client->pid);
 408             /* we can't remove during the iteration, so copy items
 409              * to a list we can destroy later */
 410             remove_these = g_list_append(remove_these, ipc_client);
 411         }
 412     }
 413 
 414     for (gIter = remove_these; gIter != NULL; gIter = gIter->next) {
 415         ipc_client = gIter->data;
 416         qb_ipcs_disconnect(ipc_client->ipcs);
 417     }
 418 
 419     /* just frees the list, not the elements in the list */
 420     g_list_free(remove_these);
 421 }
 422 
 423 void
 424 ipc_proxy_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 425 {
 426     ipc_clients = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, NULL);
 427     ipc_providers = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, NULL);
 428 
 429     cib_ipc_servers_init(&cib_ro,
 430                          &cib_rw,
 431                          &cib_shm,
 432                          &cib_proxy_callbacks_ro,
 433                          &cib_proxy_callbacks_rw);
 434 
 435     attrd_ipc_server_init(&attrd_ipcs, &attrd_proxy_callbacks);
 436     stonith_ipc_server_init(&stonith_ipcs, &stonith_proxy_callbacks);
 437     crmd_ipcs = crmd_ipc_server_init(&crmd_proxy_callbacks);
 438     if (crmd_ipcs == NULL) {
 439         crm_err("Failed to create crmd server: exiting and inhibiting respawn.");
 440         crm_warn("Verify pacemaker and pacemaker_remote are not both enabled.");
 441         crm_exit(DAEMON_RESPAWN_STOP);
 442     }
 443 }
 444 
 445 void
 446 ipc_proxy_cleanup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 447 {
 448     if (ipc_providers) {
 449         g_hash_table_destroy(ipc_providers);
 450     }
 451     if (ipc_clients) {
 452         g_hash_table_destroy(ipc_clients);
 453     }
 454     cib_ipc_servers_destroy(cib_ro, cib_rw, cib_shm);
 455     qb_ipcs_destroy(attrd_ipcs);
 456     qb_ipcs_destroy(stonith_ipcs);
 457     qb_ipcs_destroy(crmd_ipcs);
 458     cib_ro = NULL;
 459     cib_rw = NULL;
 460     cib_shm = NULL;
 461     ipc_providers = NULL;
 462     ipc_clients = NULL;
 463 }

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