LCOV - code coverage report
Current view: top level - cib - cib_native.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 251 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 11 0.0 %

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

Generated by: LCOV version 1.14