root/lib/cib/cib_native.c

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

DEFINITIONS

This source file includes following definitions.
  1. cib_native_perform_op_delegate
  2. cib_native_dispatch_internal
  3. cib_native_destroy
  4. cib_native_signoff
  5. cib_native_signon_raw
  6. cib_native_signon
  7. cib_native_free
  8. cib_native_register_notification
  9. cib_native_set_connection_dnotify
  10. cib_native_client_id
  11. cib_native_new

   1 /*
   2  * Copyright 2004 International Business Machines
   3  * Later changes copyright 2004-2023 the Pacemaker project contributors
   4  *
   5  * The version control history for this file may have further details.
   6  *
   7  * This source code is licensed under the GNU Lesser General Public License
   8  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   9  */
  10 
  11 #include <crm_internal.h>
  12 
  13 #ifndef _GNU_SOURCE
  14 #  define _GNU_SOURCE
  15 #endif
  16 
  17 #include <errno.h>
  18 #include <crm_internal.h>
  19 #include <unistd.h>
  20 #include <stdlib.h>
  21 #include <stdio.h>
  22 #include <stdarg.h>
  23 #include <string.h>
  24 
  25 #include <glib.h>
  26 
  27 #include <crm/crm.h>
  28 #include <crm/cib/internal.h>
  29 
  30 #include <crm/msg_xml.h>
  31 #include <crm/common/mainloop.h>
  32 
  33 typedef struct cib_native_opaque_s {
  34     char *token;
  35     crm_ipc_t *ipc;
  36     void (*dnotify_fn) (gpointer user_data);
  37     mainloop_io_t *source;
  38 } cib_native_opaque_t;
  39 
  40 static int
  41 cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host,
     /* [previous][next][first][last][top][bottom][index][help] */
  42                                const char *section, xmlNode *data,
  43                                xmlNode **output_data, int call_options,
  44                                const char *user_name)
  45 {
  46     int rc = pcmk_ok;
  47     int reply_id = 0;
  48     enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
  49 
  50     xmlNode *op_msg = NULL;
  51     xmlNode *op_reply = NULL;
  52 
  53     cib_native_opaque_t *native = cib->variant_opaque;
  54 
  55     if (cib->state == cib_disconnected) {
  56         return -ENOTCONN;
  57     }
  58 
  59     if (output_data != NULL) {
  60         *output_data = NULL;
  61     }
  62 
  63     if (op == NULL) {
  64         crm_err("No operation specified");
  65         return -EINVAL;
  66     }
  67 
  68     if (call_options & cib_sync_call) {
  69         pcmk__set_ipc_flags(ipc_flags, "client", crm_ipc_client_response);
  70     }
  71 
  72     cib->call_id++;
  73     if (cib->call_id < 1) {
  74         cib->call_id = 1;
  75     }
  76 
  77     op_msg = cib_create_op(cib->call_id, op, host, section, data, call_options,
  78                            user_name);
  79     if (op_msg == NULL) {
  80         return -EPROTO;
  81     }
  82 
  83     crm_trace("Sending %s message to the CIB manager (timeout=%ds)", op, cib->call_timeout);
  84     rc = crm_ipc_send(native->ipc, op_msg, ipc_flags, cib->call_timeout * 1000, &op_reply);
  85     free_xml(op_msg);
  86 
  87     if (rc < 0) {
  88         crm_err("Couldn't perform %s operation (timeout=%ds): %s (%d)", op,
  89                 cib->call_timeout, pcmk_strerror(rc), rc);
  90         rc = -ECOMM;
  91         goto done;
  92     }
  93 
  94     crm_log_xml_trace(op_reply, "Reply");
  95 
  96     if (!(call_options & cib_sync_call)) {
  97         crm_trace("Async call, returning %d", cib->call_id);
  98         CRM_CHECK(cib->call_id != 0, return -ENOMSG);
  99         free_xml(op_reply);
 100         return cib->call_id;
 101     }
 102 
 103     rc = pcmk_ok;
 104     crm_element_value_int(op_reply, F_CIB_CALLID, &reply_id);
 105     if (reply_id == cib->call_id) {
 106         xmlNode *tmp = get_message_xml(op_reply, F_CIB_CALLDATA);
 107 
 108         crm_trace("Synchronous reply %d received", reply_id);
 109         if (crm_element_value_int(op_reply, F_CIB_RC, &rc) != 0) {
 110             rc = -EPROTO;
 111         }
 112 
 113         if (output_data == NULL || (call_options & cib_discard_reply)) {
 114             crm_trace("Discarding reply");
 115 
 116         } else if (tmp != NULL) {
 117             *output_data = copy_xml(tmp);
 118         }
 119 
 120     } else if (reply_id <= 0) {
 121         crm_err("Received bad reply: No id set");
 122         crm_log_xml_err(op_reply, "Bad reply");
 123         rc = -ENOMSG;
 124         goto done;
 125 
 126     } else {
 127         crm_err("Received bad reply: %d (wanted %d)", reply_id, cib->call_id);
 128         crm_log_xml_err(op_reply, "Old reply");
 129         rc = -ENOMSG;
 130         goto done;
 131     }
 132 
 133     if (op_reply == NULL && cib->state == cib_disconnected) {
 134         rc = -ENOTCONN;
 135 
 136     } else if (rc == pcmk_ok && op_reply == NULL) {
 137         rc = -ETIME;
 138     }
 139 
 140     switch (rc) {
 141         case pcmk_ok:
 142         case -EPERM:
 143             break;
 144 
 145             /* This is an internal value that clients do not and should not care about */
 146         case -pcmk_err_diff_resync:
 147             rc = pcmk_ok;
 148             break;
 149 
 150             /* These indicate internal problems */
 151         case -EPROTO:
 152         case -ENOMSG:
 153             crm_err("Call failed: %s", pcmk_strerror(rc));
 154             if (op_reply) {
 155                 crm_log_xml_err(op_reply, "Invalid reply");
 156             }
 157             break;
 158 
 159         default:
 160             if (!pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
 161                 crm_warn("Call failed: %s", pcmk_strerror(rc));
 162             }
 163     }
 164 
 165   done:
 166     if (!crm_ipc_connected(native->ipc)) {
 167         crm_err("The CIB manager disconnected");
 168         cib->state = cib_disconnected;
 169     }
 170 
 171     free_xml(op_reply);
 172     return rc;
 173 }
 174 
 175 static int
 176 cib_native_dispatch_internal(const char *buffer, ssize_t length,
     /* [previous][next][first][last][top][bottom][index][help] */
 177                              gpointer userdata)
 178 {
 179     const char *type = NULL;
 180     xmlNode *msg = NULL;
 181 
 182     cib_t *cib = userdata;
 183 
 184     crm_trace("dispatching %p", userdata);
 185 
 186     if (cib == NULL) {
 187         crm_err("No CIB!");
 188         return 0;
 189     }
 190 
 191     msg = string2xml(buffer);
 192 
 193     if (msg == NULL) {
 194         crm_warn("Received a NULL message from the CIB manager");
 195         return 0;
 196     }
 197 
 198     /* do callbacks */
 199     type = crm_element_value(msg, F_TYPE);
 200     crm_trace("Activating %s callbacks...", type);
 201     crm_log_xml_explicit(msg, "cib-reply");
 202 
 203     if (pcmk__str_eq(type, T_CIB, pcmk__str_casei)) {
 204         cib_native_callback(cib, msg, 0, 0);
 205 
 206     } else if (pcmk__str_eq(type, T_CIB_NOTIFY, pcmk__str_casei)) {
 207         g_list_foreach(cib->notify_list, cib_native_notify, msg);
 208 
 209     } else {
 210         crm_err("Unknown message type: %s", type);
 211     }
 212 
 213     free_xml(msg);
 214     return 0;
 215 }
 216 
 217 static void
 218 cib_native_destroy(void *userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 219 {
 220     cib_t *cib = userdata;
 221     cib_native_opaque_t *native = cib->variant_opaque;
 222 
 223     crm_trace("destroying %p", userdata);
 224     cib->state = cib_disconnected;
 225     native->source = NULL;
 226     native->ipc = NULL;
 227 
 228     if (native->dnotify_fn) {
 229         native->dnotify_fn(userdata);
 230     }
 231 }
 232 
 233 static int
 234 cib_native_signoff(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 235 {
 236     cib_native_opaque_t *native = cib->variant_opaque;
 237 
 238     crm_debug("Disconnecting from the CIB manager");
 239 
 240     cib_free_notify(cib);
 241     remove_cib_op_callback(0, TRUE);
 242 
 243     if (native->source != NULL) {
 244         /* Attached to mainloop */
 245         mainloop_del_ipc_client(native->source);
 246         native->source = NULL;
 247         native->ipc = NULL;
 248 
 249     } else if (native->ipc) {
 250         /* Not attached to mainloop */
 251         crm_ipc_t *ipc = native->ipc;
 252 
 253         native->ipc = NULL;
 254         crm_ipc_close(ipc);
 255         crm_ipc_destroy(ipc);
 256     }
 257 
 258     cib->state = cib_disconnected;
 259     cib->type = cib_no_connection;
 260 
 261     return pcmk_ok;
 262 }
 263 
 264 static int
 265 cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type,
     /* [previous][next][first][last][top][bottom][index][help] */
 266                       int *async_fd)
 267 {
 268     int rc = pcmk_ok;
 269     const char *channel = NULL;
 270     cib_native_opaque_t *native = cib->variant_opaque;
 271 
 272     struct ipc_client_callbacks cib_callbacks = {
 273         .dispatch = cib_native_dispatch_internal,
 274         .destroy = cib_native_destroy
 275     };
 276 
 277     cib->call_timeout = PCMK__IPC_TIMEOUT;
 278 
 279     if (type == cib_command) {
 280         cib->state = cib_connected_command;
 281         channel = PCMK__SERVER_BASED_RW;
 282 
 283     } else if (type == cib_command_nonblocking) {
 284         cib->state = cib_connected_command;
 285         channel = PCMK__SERVER_BASED_SHM;
 286 
 287     } else if (type == cib_query) {
 288         cib->state = cib_connected_query;
 289         channel = PCMK__SERVER_BASED_RO;
 290 
 291     } else {
 292         return -ENOTCONN;
 293     }
 294 
 295     crm_trace("Connecting %s channel", channel);
 296 
 297     if (async_fd != NULL) {
 298         native->ipc = crm_ipc_new(channel, 0);
 299 
 300         if (native->ipc && crm_ipc_connect(native->ipc)) {
 301             *async_fd = crm_ipc_get_fd(native->ipc);
 302 
 303         } else if (native->ipc) {
 304             rc = -ENOTCONN;
 305         }
 306 
 307     } else {
 308         native->source =
 309             mainloop_add_ipc_client(channel, G_PRIORITY_HIGH, 512 * 1024 /* 512k */ , cib,
 310                                     &cib_callbacks);
 311         native->ipc = mainloop_get_ipc_client(native->source);
 312     }
 313 
 314     if (rc != pcmk_ok || native->ipc == NULL || !crm_ipc_connected(native->ipc)) {
 315         crm_info("Could not connect to CIB manager for %s", name);
 316         rc = -ENOTCONN;
 317     }
 318 
 319     if (rc == pcmk_ok) {
 320         xmlNode *reply = NULL;
 321         xmlNode *hello = create_xml_node(NULL, "cib_command");
 322 
 323         crm_xml_add(hello, F_TYPE, T_CIB);
 324         crm_xml_add(hello, F_CIB_OPERATION, CRM_OP_REGISTER);
 325         crm_xml_add(hello, F_CIB_CLIENTNAME, name);
 326         crm_xml_add_int(hello, F_CIB_CALLOPTS, cib_sync_call);
 327 
 328         if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1, &reply) > 0) {
 329             const char *msg_type = crm_element_value(reply, F_CIB_OPERATION);
 330 
 331             rc = pcmk_ok;
 332             crm_log_xml_trace(reply, "reg-reply");
 333 
 334             if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
 335                 crm_info("Reply to CIB registration message has "
 336                          "unknown type '%s'", msg_type);
 337                 rc = -EPROTO;
 338 
 339             } else {
 340                 native->token = crm_element_value_copy(reply, F_CIB_CLIENTID);
 341                 if (native->token == NULL) {
 342                     rc = -EPROTO;
 343                 }
 344             }
 345             free_xml(reply);
 346 
 347         } else {
 348             rc = -ECOMM;
 349         }
 350 
 351         free_xml(hello);
 352     }
 353 
 354     if (rc == pcmk_ok) {
 355         crm_info("Successfully connected to CIB manager for %s", name);
 356         return pcmk_ok;
 357     }
 358 
 359     crm_info("Connection to CIB manager for %s failed: %s",
 360              name, pcmk_strerror(rc));
 361     cib_native_signoff(cib);
 362     return rc;
 363 }
 364 
 365 static int
 366 cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type)
     /* [previous][next][first][last][top][bottom][index][help] */
 367 {
 368     return cib_native_signon_raw(cib, name, type, NULL);
 369 }
 370 
 371 static int
 372 cib_native_free(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 373 {
 374     int rc = pcmk_ok;
 375 
 376     if (cib->state != cib_disconnected) {
 377         rc = cib_native_signoff(cib);
 378     }
 379 
 380     if (cib->state == cib_disconnected) {
 381         cib_native_opaque_t *native = cib->variant_opaque;
 382 
 383         free(native->token);
 384         free(cib->variant_opaque);
 385         free(cib->cmds);
 386         free(cib);
 387     }
 388 
 389     return rc;
 390 }
 391 
 392 static int
 393 cib_native_register_notification(cib_t *cib, const char *callback, int enabled)
     /* [previous][next][first][last][top][bottom][index][help] */
 394 {
 395     int rc = pcmk_ok;
 396     xmlNode *notify_msg = create_xml_node(NULL, "cib-callback");
 397     cib_native_opaque_t *native = cib->variant_opaque;
 398 
 399     if (cib->state != cib_disconnected) {
 400         crm_xml_add(notify_msg, F_CIB_OPERATION, T_CIB_NOTIFY);
 401         crm_xml_add(notify_msg, F_CIB_NOTIFY_TYPE, callback);
 402         crm_xml_add_int(notify_msg, F_CIB_NOTIFY_ACTIVATE, enabled);
 403         rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response,
 404                           1000 * cib->call_timeout, NULL);
 405         if (rc <= 0) {
 406             crm_trace("Notification not registered: %d", rc);
 407             rc = -ECOMM;
 408         }
 409     }
 410 
 411     free_xml(notify_msg);
 412     return rc;
 413 }
 414 
 415 static int
 416 cib_native_set_connection_dnotify(cib_t *cib,
     /* [previous][next][first][last][top][bottom][index][help] */
 417                                   void (*dnotify) (gpointer user_data))
 418 {
 419     cib_native_opaque_t *native = NULL;
 420 
 421     if (cib == NULL) {
 422         crm_err("No CIB!");
 423         return FALSE;
 424     }
 425 
 426     native = cib->variant_opaque;
 427     native->dnotify_fn = dnotify;
 428 
 429     return pcmk_ok;
 430 }
 431 
 432 /*!
 433  * \internal
 434  * \brief Get the given CIB connection's unique client identifier
 435  *
 436  * These can be used to check whether this client requested the action that
 437  * triggered a CIB notification.
 438  *
 439  * \param[in]  cib       CIB connection
 440  * \param[out] async_id  If not \p NULL, where to store asynchronous client ID
 441  * \param[out] sync_id   If not \p NULL, where to store synchronous client ID
 442  *
 443  * \return Legacy Pacemaker return code (specifically, \p pcmk_ok)
 444  *
 445  * \note This is the \p cib_native variant implementation of
 446  *       \p cib_api_operations_t:client_id().
 447  * \note For \p cib_native objects, \p async_id and \p sync_id are the same.
 448  * \note The client ID is assigned during CIB sign-on.
 449  */
 450 static int
 451 cib_native_client_id(const cib_t *cib, const char **async_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 452                      const char **sync_id)
 453 {
 454     cib_native_opaque_t *native = cib->variant_opaque;
 455 
 456     if (async_id != NULL) {
 457         *async_id = native->token;
 458     }
 459     if (sync_id != NULL) {
 460         *sync_id = native->token;
 461     }
 462     return pcmk_ok;
 463 }
 464 
 465 cib_t *
 466 cib_native_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 467 {
 468     cib_native_opaque_t *native = NULL;
 469     cib_t *cib = cib_new_variant();
 470 
 471     if (cib == NULL) {
 472         return NULL;
 473     }
 474 
 475     native = calloc(1, sizeof(cib_native_opaque_t));
 476 
 477     if (native == NULL) {
 478         free(cib);
 479         return NULL;
 480     }
 481 
 482     cib->variant = cib_native;
 483     cib->variant_opaque = native;
 484 
 485     native->ipc = NULL;
 486     native->source = NULL;
 487     native->dnotify_fn = NULL;
 488 
 489     /* assign variant specific ops */
 490     cib->delegate_fn = cib_native_perform_op_delegate;
 491     cib->cmds->signon = cib_native_signon;
 492     cib->cmds->signon_raw = cib_native_signon_raw;
 493     cib->cmds->signoff = cib_native_signoff;
 494     cib->cmds->free = cib_native_free;
 495 
 496     cib->cmds->register_notification = cib_native_register_notification;
 497     cib->cmds->set_connection_dnotify = cib_native_set_connection_dnotify;
 498 
 499     cib->cmds->client_id = cib_native_client_id;
 500 
 501     return cib;
 502 }

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