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

          Line data    Source code
       1             : /*
       2             :  * Copyright 2004-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             : #if defined(HAVE_UCRED) || defined(HAVE_SOCKPEERCRED)
      13             : #  ifdef HAVE_UCRED
      14             : #    ifndef _GNU_SOURCE
      15             : #      define _GNU_SOURCE
      16             : #    endif
      17             : #  endif
      18             : #  include <sys/socket.h>
      19             : #elif defined(HAVE_GETPEERUCRED)
      20             : #  include <ucred.h>
      21             : #endif
      22             : 
      23             : #include <stdio.h>
      24             : #include <sys/types.h>
      25             : #include <errno.h>
      26             : #include <bzlib.h>
      27             : 
      28             : #include <crm/crm.h>   /* indirectly: pcmk_err_generic */
      29             : #include <crm/common/xml.h>
      30             : #include <crm/common/ipc.h>
      31             : #include <crm/common/ipc_internal.h>
      32             : #include "crmcommon_private.h"
      33             : 
      34             : static int is_ipc_provider_expected(qb_ipcc_connection_t *qb_ipc, int sock,
      35             :                                     uid_t refuid, gid_t refgid, pid_t *gotpid,
      36             :                                     uid_t *gotuid, gid_t *gotgid);
      37             : 
      38             : /*!
      39             :  * \brief Create a new object for using Pacemaker daemon IPC
      40             :  *
      41             :  * \param[out] api     Where to store new IPC object
      42             :  * \param[in]  server  Which Pacemaker daemon the object is for
      43             :  *
      44             :  * \return Standard Pacemaker result code
      45             :  *
      46             :  * \note The caller is responsible for freeing *api using pcmk_free_ipc_api().
      47             :  * \note This is intended to supersede crm_ipc_new() but currently only
      48             :  *       supports the controller, pacemakerd, and schedulerd IPC API.
      49             :  */
      50             : int
      51           0 : pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server)
      52             : {
      53           0 :     if (api == NULL) {
      54           0 :         return EINVAL;
      55             :     }
      56             : 
      57           0 :     *api = calloc(1, sizeof(pcmk_ipc_api_t));
      58           0 :     if (*api == NULL) {
      59           0 :         return errno;
      60             :     }
      61             : 
      62           0 :     (*api)->server = server;
      63           0 :     if (pcmk_ipc_name(*api, false) == NULL) {
      64           0 :         pcmk_free_ipc_api(*api);
      65           0 :         *api = NULL;
      66           0 :         return EOPNOTSUPP;
      67             :     }
      68             : 
      69           0 :     (*api)->ipc_size_max = 0;
      70             : 
      71             :     // Set server methods and max_size (if not default)
      72           0 :     switch (server) {
      73           0 :         case pcmk_ipc_attrd:
      74           0 :             (*api)->cmds = pcmk__attrd_api_methods();
      75           0 :             break;
      76             : 
      77           0 :         case pcmk_ipc_based:
      78           0 :             (*api)->ipc_size_max = 512 * 1024; // 512KB
      79           0 :             break;
      80             : 
      81           0 :         case pcmk_ipc_controld:
      82           0 :             (*api)->cmds = pcmk__controld_api_methods();
      83           0 :             break;
      84             : 
      85           0 :         case pcmk_ipc_execd:
      86           0 :             break;
      87             : 
      88           0 :         case pcmk_ipc_fenced:
      89           0 :             break;
      90             : 
      91           0 :         case pcmk_ipc_pacemakerd:
      92           0 :             (*api)->cmds = pcmk__pacemakerd_api_methods();
      93           0 :             break;
      94             : 
      95           0 :         case pcmk_ipc_schedulerd:
      96           0 :             (*api)->cmds = pcmk__schedulerd_api_methods();
      97             :             // @TODO max_size could vary by client, maybe take as argument?
      98           0 :             (*api)->ipc_size_max = 5 * 1024 * 1024; // 5MB
      99           0 :             break;
     100             :     }
     101           0 :     if ((*api)->cmds == NULL) {
     102           0 :         pcmk_free_ipc_api(*api);
     103           0 :         *api = NULL;
     104           0 :         return ENOMEM;
     105             :     }
     106             : 
     107           0 :     (*api)->ipc = crm_ipc_new(pcmk_ipc_name(*api, false),
     108           0 :                               (*api)->ipc_size_max);
     109           0 :     if ((*api)->ipc == NULL) {
     110           0 :         pcmk_free_ipc_api(*api);
     111           0 :         *api = NULL;
     112           0 :         return ENOMEM;
     113             :     }
     114             : 
     115             :     // If daemon API has its own data to track, allocate it
     116           0 :     if ((*api)->cmds->new_data != NULL) {
     117           0 :         if ((*api)->cmds->new_data(*api) != pcmk_rc_ok) {
     118           0 :             pcmk_free_ipc_api(*api);
     119           0 :             *api = NULL;
     120           0 :             return ENOMEM;
     121             :         }
     122             :     }
     123           0 :     crm_trace("Created %s API IPC object", pcmk_ipc_name(*api, true));
     124           0 :     return pcmk_rc_ok;
     125             : }
     126             : 
     127             : static void
     128           0 : free_daemon_specific_data(pcmk_ipc_api_t *api)
     129             : {
     130           0 :     if ((api != NULL) && (api->cmds != NULL)) {
     131           0 :         if ((api->cmds->free_data != NULL) && (api->api_data != NULL)) {
     132           0 :             api->cmds->free_data(api->api_data);
     133           0 :             api->api_data = NULL;
     134             :         }
     135           0 :         free(api->cmds);
     136           0 :         api->cmds = NULL;
     137             :     }
     138           0 : }
     139             : 
     140             : /*!
     141             :  * \internal
     142             :  * \brief Call an IPC API event callback, if one is registed
     143             :  *
     144             :  * \param[in,out] api         IPC API connection
     145             :  * \param[in]     event_type  The type of event that occurred
     146             :  * \param[in]     status      Event status
     147             :  * \param[in,out] event_data  Event-specific data
     148             :  */
     149             : void
     150           0 : pcmk__call_ipc_callback(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type,
     151             :                         crm_exit_t status, void *event_data)
     152             : {
     153           0 :     if ((api != NULL) && (api->cb != NULL)) {
     154           0 :         api->cb(api, event_type, status, event_data, api->user_data);
     155             :     }
     156           0 : }
     157             : 
     158             : /*!
     159             :  * \internal
     160             :  * \brief Clean up after an IPC disconnect
     161             :  *
     162             :  * \param[in,out] user_data  IPC API connection that disconnected
     163             :  *
     164             :  * \note This function can be used as a main loop IPC destroy callback.
     165             :  */
     166             : static void
     167           0 : ipc_post_disconnect(gpointer user_data)
     168             : {
     169           0 :     pcmk_ipc_api_t *api = user_data;
     170             : 
     171           0 :     crm_info("Disconnected from %s", pcmk_ipc_name(api, true));
     172             : 
     173             :     // Perform any daemon-specific handling needed
     174           0 :     if ((api->cmds != NULL) && (api->cmds->post_disconnect != NULL)) {
     175           0 :         api->cmds->post_disconnect(api);
     176             :     }
     177             : 
     178             :     // Call client's registered event callback
     179           0 :     pcmk__call_ipc_callback(api, pcmk_ipc_event_disconnect, CRM_EX_DISCONNECT,
     180             :                             NULL);
     181             : 
     182             :     /* If this is being called from a running main loop, mainloop_gio_destroy()
     183             :      * will free ipc and mainloop_io immediately after calling this function.
     184             :      * If this is called from a stopped main loop, these will leak, so the best
     185             :      * practice is to close the connection before stopping the main loop.
     186             :      */
     187           0 :     api->ipc = NULL;
     188           0 :     api->mainloop_io = NULL;
     189             : 
     190           0 :     if (api->free_on_disconnect) {
     191             :         /* pcmk_free_ipc_api() has already been called, but did not free api
     192             :          * or api->cmds because this function needed them. Do that now.
     193             :          */
     194           0 :         free_daemon_specific_data(api);
     195           0 :         crm_trace("Freeing IPC API object after disconnect");
     196           0 :         free(api);
     197             :     }
     198           0 : }
     199             : 
     200             : /*!
     201             :  * \brief Free the contents of an IPC API object
     202             :  *
     203             :  * \param[in,out] api  IPC API object to free
     204             :  */
     205             : void
     206           0 : pcmk_free_ipc_api(pcmk_ipc_api_t *api)
     207             : {
     208           0 :     bool free_on_disconnect = false;
     209             : 
     210           0 :     if (api == NULL) {
     211           0 :         return;
     212             :     }
     213           0 :     crm_debug("Releasing %s IPC API", pcmk_ipc_name(api, true));
     214             : 
     215           0 :     if (api->ipc != NULL) {
     216           0 :         if (api->mainloop_io != NULL) {
     217             :             /* We need to keep the api pointer itself around, because it is the
     218             :              * user data for the IPC client destroy callback. That will be
     219             :              * triggered by the pcmk_disconnect_ipc() call below, but it might
     220             :              * happen later in the main loop (if still running).
     221             :              *
     222             :              * This flag tells the destroy callback to free the object. It can't
     223             :              * do that unconditionally, because the application might call this
     224             :              * function after a disconnect that happened by other means.
     225             :              */
     226           0 :             free_on_disconnect = api->free_on_disconnect = true;
     227             :         }
     228           0 :         pcmk_disconnect_ipc(api); // Frees api if free_on_disconnect is true
     229             :     }
     230           0 :     if (!free_on_disconnect) {
     231           0 :         free_daemon_specific_data(api);
     232           0 :         crm_trace("Freeing IPC API object");
     233           0 :         free(api);
     234             :     }
     235             : }
     236             : 
     237             : /*!
     238             :  * \brief Get the IPC name used with an IPC API connection
     239             :  *
     240             :  * \param[in] api      IPC API connection
     241             :  * \param[in] for_log  If true, return human-friendly name instead of IPC name
     242             :  *
     243             :  * \return IPC API's human-friendly or connection name, or if none is available,
     244             :  *         "Pacemaker" if for_log is true and NULL if for_log is false
     245             :  */
     246             : const char *
     247           0 : pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log)
     248             : {
     249           0 :     if (api == NULL) {
     250           0 :         return for_log? "Pacemaker" : NULL;
     251             :     }
     252           0 :     switch (api->server) {
     253           0 :         case pcmk_ipc_attrd:
     254           0 :             return for_log? "attribute manager" : PCMK__VALUE_ATTRD;
     255             : 
     256           0 :         case pcmk_ipc_based:
     257           0 :             return for_log? "CIB manager" : NULL /* PCMK__SERVER_BASED_RW */;
     258             : 
     259           0 :         case pcmk_ipc_controld:
     260           0 :             return for_log? "controller" : CRM_SYSTEM_CRMD;
     261             : 
     262           0 :         case pcmk_ipc_execd:
     263           0 :             return for_log? "executor" : NULL /* CRM_SYSTEM_LRMD */;
     264             : 
     265           0 :         case pcmk_ipc_fenced:
     266           0 :             return for_log? "fencer" : NULL /* "stonith-ng" */;
     267             : 
     268           0 :         case pcmk_ipc_pacemakerd:
     269           0 :             return for_log? "launcher" : CRM_SYSTEM_MCP;
     270             : 
     271           0 :         case pcmk_ipc_schedulerd:
     272           0 :             return for_log? "scheduler" : CRM_SYSTEM_PENGINE;
     273             : 
     274           0 :         default:
     275           0 :             return for_log? "Pacemaker" : NULL;
     276             :     }
     277             : }
     278             : 
     279             : /*!
     280             :  * \brief Check whether an IPC API connection is active
     281             :  *
     282             :  * \param[in,out] api  IPC API connection
     283             :  *
     284             :  * \return true if IPC is connected, false otherwise
     285             :  */
     286             : bool
     287           0 : pcmk_ipc_is_connected(pcmk_ipc_api_t *api)
     288             : {
     289           0 :     return (api != NULL) && crm_ipc_connected(api->ipc);
     290             : }
     291             : 
     292             : /*!
     293             :  * \internal
     294             :  * \brief Call the daemon-specific API's dispatch function
     295             :  *
     296             :  * Perform daemon-specific handling of IPC reply dispatch. It is the daemon
     297             :  * method's responsibility to call the client's registered event callback, as
     298             :  * well as allocate and free any event data.
     299             :  *
     300             :  * \param[in,out] api      IPC API connection
     301             :  * \param[in,out] message  IPC reply XML to dispatch
     302             :  */
     303             : static bool
     304           0 : call_api_dispatch(pcmk_ipc_api_t *api, xmlNode *message)
     305             : {
     306           0 :     crm_log_xml_trace(message, "ipc-received");
     307           0 :     if ((api->cmds != NULL) && (api->cmds->dispatch != NULL)) {
     308           0 :         return api->cmds->dispatch(api, message);
     309             :     }
     310             : 
     311           0 :     return false;
     312             : }
     313             : 
     314             : /*!
     315             :  * \internal
     316             :  * \brief Dispatch previously read IPC data
     317             :  *
     318             :  * \param[in]     buffer  Data read from IPC
     319             :  * \param[in,out] api     IPC object
     320             :  *
     321             :  * \return Standard Pacemaker return code.  In particular:
     322             :  *
     323             :  * pcmk_rc_ok: There are no more messages expected from the server.  Quit
     324             :  *             reading.
     325             :  * EINPROGRESS: There are more messages expected from the server.  Keep reading.
     326             :  *
     327             :  * All other values indicate an error.
     328             :  */
     329             : static int
     330           0 : dispatch_ipc_data(const char *buffer, pcmk_ipc_api_t *api)
     331             : {
     332           0 :     bool more = false;
     333             :     xmlNode *msg;
     334             : 
     335           0 :     if (buffer == NULL) {
     336           0 :         crm_warn("Empty message received from %s IPC",
     337             :                  pcmk_ipc_name(api, true));
     338           0 :         return ENOMSG;
     339             :     }
     340             : 
     341           0 :     msg = pcmk__xml_parse(buffer);
     342           0 :     if (msg == NULL) {
     343           0 :         crm_warn("Malformed message received from %s IPC",
     344             :                  pcmk_ipc_name(api, true));
     345           0 :         return EPROTO;
     346             :     }
     347             : 
     348           0 :     more = call_api_dispatch(api, msg);
     349           0 :     free_xml(msg);
     350             : 
     351           0 :     if (more) {
     352           0 :         return EINPROGRESS;
     353             :     } else {
     354           0 :         return pcmk_rc_ok;
     355             :     }
     356             : }
     357             : 
     358             : /*!
     359             :  * \internal
     360             :  * \brief Dispatch data read from IPC source
     361             :  *
     362             :  * \param[in]     buffer     Data read from IPC
     363             :  * \param[in]     length     Number of bytes of data in buffer (ignored)
     364             :  * \param[in,out] user_data  IPC object
     365             :  *
     366             :  * \return Always 0 (meaning connection is still required)
     367             :  *
     368             :  * \note This function can be used as a main loop IPC dispatch callback.
     369             :  */
     370             : static int
     371           0 : dispatch_ipc_source_data(const char *buffer, ssize_t length, gpointer user_data)
     372             : {
     373           0 :     pcmk_ipc_api_t *api = user_data;
     374             : 
     375           0 :     CRM_CHECK(api != NULL, return 0);
     376           0 :     dispatch_ipc_data(buffer, api);
     377           0 :     return 0;
     378             : }
     379             : 
     380             : /*!
     381             :  * \brief Check whether an IPC connection has data available (without main loop)
     382             :  *
     383             :  * \param[in]  api         IPC API connection
     384             :  * \param[in]  timeout_ms  If less than 0, poll indefinitely; if 0, poll once
     385             :  *                         and return immediately; otherwise, poll for up to
     386             :  *                         this many milliseconds
     387             :  *
     388             :  * \return Standard Pacemaker return code
     389             :  *
     390             :  * \note Callers of pcmk_connect_ipc() using pcmk_ipc_dispatch_poll should call
     391             :  *       this function to check whether IPC data is available. Return values of
     392             :  *       interest include pcmk_rc_ok meaning data is available, and EAGAIN
     393             :  *       meaning no data is available; all other values indicate errors.
     394             :  * \todo This does not allow the caller to poll multiple file descriptors at
     395             :  *       once. If there is demand for that, we could add a wrapper for
     396             :  *       pcmk__ipc_fd(api->ipc), so the caller can call poll() themselves.
     397             :  */
     398             : int
     399           0 : pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms)
     400             : {
     401             :     int rc;
     402           0 :     struct pollfd pollfd = { 0, };
     403             : 
     404           0 :     if ((api == NULL) || (api->dispatch_type != pcmk_ipc_dispatch_poll)) {
     405           0 :         return EINVAL;
     406             :     }
     407             : 
     408           0 :     rc = pcmk__ipc_fd(api->ipc, &(pollfd.fd));
     409           0 :     if (rc != pcmk_rc_ok) {
     410           0 :         crm_debug("Could not obtain file descriptor for %s IPC: %s",
     411             :                   pcmk_ipc_name(api, true), pcmk_rc_str(rc));
     412           0 :         return rc;
     413             :     }
     414             : 
     415           0 :     pollfd.events = POLLIN;
     416           0 :     rc = poll(&pollfd, 1, timeout_ms);
     417           0 :     if (rc < 0) {
     418             :         /* Some UNIX systems return negative and set EAGAIN for failure to
     419             :          * allocate memory; standardize the return code in that case
     420             :          */
     421           0 :         return (errno == EAGAIN)? ENOMEM : errno;
     422           0 :     } else if (rc == 0) {
     423           0 :         return EAGAIN;
     424             :     }
     425           0 :     return pcmk_rc_ok;
     426             : }
     427             : 
     428             : /*!
     429             :  * \brief Dispatch available messages on an IPC connection (without main loop)
     430             :  *
     431             :  * \param[in,out] api  IPC API connection
     432             :  *
     433             :  * \return Standard Pacemaker return code
     434             :  *
     435             :  * \note Callers of pcmk_connect_ipc() using pcmk_ipc_dispatch_poll should call
     436             :  *       this function when IPC data is available.
     437             :  */
     438             : void
     439           0 : pcmk_dispatch_ipc(pcmk_ipc_api_t *api)
     440             : {
     441           0 :     if (api == NULL) {
     442           0 :         return;
     443             :     }
     444           0 :     while (crm_ipc_ready(api->ipc) > 0) {
     445           0 :         if (crm_ipc_read(api->ipc) > 0) {
     446           0 :             dispatch_ipc_data(crm_ipc_buffer(api->ipc), api);
     447             :         }
     448             :     }
     449             : }
     450             : 
     451             : // \return Standard Pacemaker return code
     452             : static int
     453           0 : connect_with_main_loop(pcmk_ipc_api_t *api)
     454             : {
     455             :     int rc;
     456             : 
     457           0 :     struct ipc_client_callbacks callbacks = {
     458             :         .dispatch = dispatch_ipc_source_data,
     459             :         .destroy = ipc_post_disconnect,
     460             :     };
     461             : 
     462           0 :     rc = pcmk__add_mainloop_ipc(api->ipc, G_PRIORITY_DEFAULT, api,
     463             :                                 &callbacks, &(api->mainloop_io));
     464           0 :     if (rc != pcmk_rc_ok) {
     465           0 :         return rc;
     466             :     }
     467           0 :     crm_debug("Connected to %s IPC (attached to main loop)",
     468             :               pcmk_ipc_name(api, true));
     469             :     /* After this point, api->mainloop_io owns api->ipc, so api->ipc
     470             :      * should not be explicitly freed.
     471             :      */
     472           0 :     return pcmk_rc_ok;
     473             : }
     474             : 
     475             : // \return Standard Pacemaker return code
     476             : static int
     477           0 : connect_without_main_loop(pcmk_ipc_api_t *api)
     478             : {
     479           0 :     int rc = pcmk__connect_generic_ipc(api->ipc);
     480             : 
     481           0 :     if (rc != pcmk_rc_ok) {
     482           0 :         crm_ipc_close(api->ipc);
     483             :     } else {
     484           0 :         crm_debug("Connected to %s IPC (without main loop)",
     485             :                   pcmk_ipc_name(api, true));
     486             :     }
     487           0 :     return rc;
     488             : }
     489             : 
     490             : /*!
     491             :  * \internal
     492             :  * \brief Connect to a Pacemaker daemon via IPC (retrying after soft errors)
     493             :  *
     494             :  * \param[in,out] api            IPC API instance
     495             :  * \param[in]     dispatch_type  How IPC replies should be dispatched
     496             :  * \param[in]     attempts       How many times to try (in case of soft error)
     497             :  *
     498             :  * \return Standard Pacemaker return code
     499             :  */
     500             : int
     501           0 : pcmk__connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type,
     502             :                   int attempts)
     503             : {
     504           0 :     int rc = pcmk_rc_ok;
     505             : 
     506           0 :     if ((api == NULL) || (attempts < 1)) {
     507           0 :         return EINVAL;
     508             :     }
     509             : 
     510           0 :     if (api->ipc == NULL) {
     511           0 :         api->ipc = crm_ipc_new(pcmk_ipc_name(api, false), api->ipc_size_max);
     512           0 :         if (api->ipc == NULL) {
     513           0 :             return ENOMEM;
     514             :         }
     515             :     }
     516             : 
     517           0 :     if (crm_ipc_connected(api->ipc)) {
     518           0 :         crm_trace("Already connected to %s", pcmk_ipc_name(api, true));
     519           0 :         return pcmk_rc_ok;
     520             :     }
     521             : 
     522           0 :     api->dispatch_type = dispatch_type;
     523             : 
     524           0 :     crm_debug("Attempting connection to %s (up to %d time%s)",
     525             :               pcmk_ipc_name(api, true), attempts, pcmk__plural_s(attempts));
     526           0 :     for (int remaining = attempts - 1; remaining >= 0; --remaining) {
     527           0 :         switch (dispatch_type) {
     528           0 :             case pcmk_ipc_dispatch_main:
     529           0 :                 rc = connect_with_main_loop(api);
     530           0 :                 break;
     531             : 
     532           0 :             case pcmk_ipc_dispatch_sync:
     533             :             case pcmk_ipc_dispatch_poll:
     534           0 :                 rc = connect_without_main_loop(api);
     535           0 :                 break;
     536             :         }
     537             : 
     538           0 :         if ((remaining == 0) || ((rc != EAGAIN) && (rc != EALREADY))) {
     539             :             break; // Result is final
     540             :         }
     541             : 
     542             :         // Retry after soft error (interrupted by signal, etc.)
     543           0 :         pcmk__sleep_ms((attempts - remaining) * 500);
     544           0 :         crm_debug("Re-attempting connection to %s (%d attempt%s remaining)",
     545             :                   pcmk_ipc_name(api, true), remaining,
     546             :                   pcmk__plural_s(remaining));
     547             :     }
     548             : 
     549           0 :     if (rc != pcmk_rc_ok) {
     550           0 :         return rc;
     551             :     }
     552             : 
     553           0 :     if ((api->cmds != NULL) && (api->cmds->post_connect != NULL)) {
     554           0 :         rc = api->cmds->post_connect(api);
     555           0 :         if (rc != pcmk_rc_ok) {
     556           0 :             crm_ipc_close(api->ipc);
     557             :         }
     558             :     }
     559           0 :     return rc;
     560             : }
     561             : 
     562             : /*!
     563             :  * \brief Connect to a Pacemaker daemon via IPC
     564             :  *
     565             :  * \param[in,out] api            IPC API instance
     566             :  * \param[in]     dispatch_type  How IPC replies should be dispatched
     567             :  *
     568             :  * \return Standard Pacemaker return code
     569             :  */
     570             : int
     571           0 : pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
     572             : {
     573           0 :     int rc = pcmk__connect_ipc(api, dispatch_type, 2);
     574             : 
     575           0 :     if (rc != pcmk_rc_ok) {
     576           0 :         crm_err("Connection to %s failed: %s",
     577             :                 pcmk_ipc_name(api, true), pcmk_rc_str(rc));
     578             :     }
     579           0 :     return rc;
     580             : }
     581             : 
     582             : /*!
     583             :  * \brief Disconnect an IPC API instance
     584             :  *
     585             :  * \param[in,out] api  IPC API connection
     586             :  *
     587             :  * \return Standard Pacemaker return code
     588             :  *
     589             :  * \note If the connection is attached to a main loop, this function should be
     590             :  *       called before quitting the main loop, to ensure that all memory is
     591             :  *       freed.
     592             :  */
     593             : void
     594           0 : pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
     595             : {
     596           0 :     if ((api == NULL) || (api->ipc == NULL)) {
     597           0 :         return;
     598             :     }
     599           0 :     switch (api->dispatch_type) {
     600           0 :         case pcmk_ipc_dispatch_main:
     601             :             {
     602           0 :                 mainloop_io_t *mainloop_io = api->mainloop_io;
     603             : 
     604             :                 // Make sure no code with access to api can use these again
     605           0 :                 api->mainloop_io = NULL;
     606           0 :                 api->ipc = NULL;
     607             : 
     608           0 :                 mainloop_del_ipc_client(mainloop_io);
     609             :                 // After this point api might have already been freed
     610             :             }
     611           0 :             break;
     612             : 
     613           0 :         case pcmk_ipc_dispatch_poll:
     614             :         case pcmk_ipc_dispatch_sync:
     615             :             {
     616           0 :                 crm_ipc_t *ipc = api->ipc;
     617             : 
     618             :                 // Make sure no code with access to api can use ipc again
     619           0 :                 api->ipc = NULL;
     620             : 
     621             :                 // This should always be the case already, but to be safe
     622           0 :                 api->free_on_disconnect = false;
     623             : 
     624           0 :                 crm_ipc_close(ipc);
     625           0 :                 crm_ipc_destroy(ipc);
     626           0 :                 ipc_post_disconnect(api);
     627             :             }
     628           0 :             break;
     629             :     }
     630             : }
     631             : 
     632             : /*!
     633             :  * \brief Register a callback for IPC API events
     634             :  *
     635             :  * \param[in,out] api       IPC API connection
     636             :  * \param[in]     callback  Callback to register
     637             :  * \param[in]     userdata  Caller data to pass to callback
     638             :  *
     639             :  * \note This function may be called multiple times to update the callback
     640             :  *       and/or user data. The caller remains responsible for freeing
     641             :  *       userdata in any case (after the IPC is disconnected, if the
     642             :  *       user data is still registered with the IPC).
     643             :  */
     644             : void
     645           0 : pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb,
     646             :                            void *user_data)
     647             : {
     648           0 :     if (api == NULL) {
     649           0 :         return;
     650             :     }
     651           0 :     api->cb = cb;
     652           0 :     api->user_data = user_data;
     653             : }
     654             : 
     655             : /*!
     656             :  * \internal
     657             :  * \brief Send an XML request across an IPC API connection
     658             :  *
     659             :  * \param[in,out] api      IPC API connection
     660             :  * \param[in]     request  XML request to send
     661             :  *
     662             :  * \return Standard Pacemaker return code
     663             :  *
     664             :  * \note Daemon-specific IPC API functions should call this function to send
     665             :  *       requests, because it handles different dispatch types appropriately.
     666             :  */
     667             : int
     668           0 : pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request)
     669             : {
     670             :     int rc;
     671           0 :     xmlNode *reply = NULL;
     672           0 :     enum crm_ipc_flags flags = crm_ipc_flags_none;
     673             : 
     674           0 :     if ((api == NULL) || (api->ipc == NULL) || (request == NULL)) {
     675           0 :         return EINVAL;
     676             :     }
     677           0 :     crm_log_xml_trace(request, "ipc-sent");
     678             : 
     679             :     // Synchronous dispatch requires waiting for a reply
     680           0 :     if ((api->dispatch_type == pcmk_ipc_dispatch_sync)
     681           0 :         && (api->cmds != NULL)
     682           0 :         && (api->cmds->reply_expected != NULL)
     683           0 :         && (api->cmds->reply_expected(api, request))) {
     684           0 :         flags = crm_ipc_client_response;
     685             :     }
     686             : 
     687             :     // The 0 here means a default timeout of 5 seconds
     688           0 :     rc = crm_ipc_send(api->ipc, request, flags, 0, &reply);
     689             : 
     690           0 :     if (rc < 0) {
     691           0 :         return pcmk_legacy2rc(rc);
     692           0 :     } else if (rc == 0) {
     693           0 :         return ENODATA;
     694             :     }
     695             : 
     696             :     // With synchronous dispatch, we dispatch any reply now
     697           0 :     if (reply != NULL) {
     698           0 :         bool more = call_api_dispatch(api, reply);
     699             : 
     700           0 :         free_xml(reply);
     701             : 
     702           0 :         while (more) {
     703           0 :             rc = crm_ipc_read(api->ipc);
     704             : 
     705           0 :             if (rc == -EAGAIN) {
     706           0 :                 continue;
     707           0 :             } else if (rc == -ENOMSG || rc == pcmk_ok) {
     708           0 :                 return pcmk_rc_ok;
     709           0 :             } else if (rc < 0) {
     710           0 :                 return -rc;
     711             :             }
     712             : 
     713           0 :             rc = dispatch_ipc_data(crm_ipc_buffer(api->ipc), api);
     714             : 
     715           0 :             if (rc == pcmk_rc_ok) {
     716           0 :                 more = false;
     717           0 :             } else if (rc == EINPROGRESS) {
     718           0 :                 more = true;
     719             :             } else {
     720           0 :                 continue;
     721             :             }
     722             :         }
     723             :     }
     724           0 :     return pcmk_rc_ok;
     725             : }
     726             : 
     727             : /*!
     728             :  * \internal
     729             :  * \brief Create the XML for an IPC request to purge a node from the peer cache
     730             :  *
     731             :  * \param[in]  api        IPC API connection
     732             :  * \param[in]  node_name  If not NULL, name of node to purge
     733             :  * \param[in]  nodeid     If not 0, node ID of node to purge
     734             :  *
     735             :  * \return Newly allocated IPC request XML
     736             :  *
     737             :  * \note The controller, fencer, and pacemakerd use the same request syntax, but
     738             :  *       the attribute manager uses a different one. The CIB manager doesn't
     739             :  *       have any syntax for it. The executor and scheduler don't connect to the
     740             :  *       cluster layer and thus don't have or need any syntax for it.
     741             :  *
     742             :  * \todo Modify the attribute manager to accept the common syntax (as well
     743             :  *       as its current one, for compatibility with older clients). Modify
     744             :  *       the CIB manager to accept and honor the common syntax. Modify the
     745             :  *       executor and scheduler to accept the syntax (immediately returning
     746             :  *       success), just for consistency. Modify this function to use the
     747             :  *       common syntax with all daemons if their version supports it.
     748             :  */
     749             : static xmlNode *
     750           0 : create_purge_node_request(const pcmk_ipc_api_t *api, const char *node_name,
     751             :                           uint32_t nodeid)
     752             : {
     753           0 :     xmlNode *request = NULL;
     754           0 :     const char *client = crm_system_name? crm_system_name : "client";
     755             : 
     756           0 :     switch (api->server) {
     757           0 :         case pcmk_ipc_attrd:
     758           0 :             request = pcmk__xe_create(NULL, __func__);
     759           0 :             crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_ATTRD);
     760           0 :             crm_xml_add(request, PCMK__XA_SRC, crm_system_name);
     761           0 :             crm_xml_add(request, PCMK_XA_TASK, PCMK__ATTRD_CMD_PEER_REMOVE);
     762           0 :             pcmk__xe_set_bool_attr(request, PCMK__XA_REAP, true);
     763           0 :             pcmk__xe_add_node(request, node_name, nodeid);
     764           0 :             break;
     765             : 
     766           0 :         case pcmk_ipc_controld:
     767             :         case pcmk_ipc_fenced:
     768             :         case pcmk_ipc_pacemakerd:
     769           0 :             request = create_request(CRM_OP_RM_NODE_CACHE, NULL, NULL,
     770             :                                      pcmk_ipc_name(api, false), client, NULL);
     771           0 :             if (nodeid > 0) {
     772           0 :                 crm_xml_set_id(request, "%lu", (unsigned long) nodeid);
     773             :             }
     774           0 :             crm_xml_add(request, PCMK_XA_UNAME, node_name);
     775           0 :             break;
     776             : 
     777           0 :         case pcmk_ipc_based:
     778             :         case pcmk_ipc_execd:
     779             :         case pcmk_ipc_schedulerd:
     780           0 :             break;
     781             :     }
     782           0 :     return request;
     783             : }
     784             : 
     785             : /*!
     786             :  * \brief Ask a Pacemaker daemon to purge a node from its peer cache
     787             :  *
     788             :  * \param[in,out] api        IPC API connection
     789             :  * \param[in]     node_name  If not NULL, name of node to purge
     790             :  * \param[in]     nodeid     If not 0, node ID of node to purge
     791             :  *
     792             :  * \return Standard Pacemaker return code
     793             :  *
     794             :  * \note At least one of node_name or nodeid must be specified.
     795             :  */
     796             : int
     797           0 : pcmk_ipc_purge_node(pcmk_ipc_api_t *api, const char *node_name, uint32_t nodeid)
     798             : {
     799           0 :     int rc = 0;
     800           0 :     xmlNode *request = NULL;
     801             : 
     802           0 :     if (api == NULL) {
     803           0 :         return EINVAL;
     804             :     }
     805           0 :     if ((node_name == NULL) && (nodeid == 0)) {
     806           0 :         return EINVAL;
     807             :     }
     808             : 
     809           0 :     request = create_purge_node_request(api, node_name, nodeid);
     810           0 :     if (request == NULL) {
     811           0 :         return EOPNOTSUPP;
     812             :     }
     813           0 :     rc = pcmk__send_ipc_request(api, request);
     814           0 :     free_xml(request);
     815             : 
     816           0 :     crm_debug("%s peer cache purge of node %s[%lu]: rc=%d",
     817             :               pcmk_ipc_name(api, true), node_name, (unsigned long) nodeid, rc);
     818           0 :     return rc;
     819             : }
     820             : 
     821             : /*
     822             :  * Generic IPC API (to eventually be deprecated as public API and made internal)
     823             :  */
     824             : 
     825             : struct crm_ipc_s {
     826             :     struct pollfd pfd;
     827             :     unsigned int max_buf_size; // maximum bytes we can send or receive over IPC
     828             :     unsigned int buf_size;     // size of allocated buffer
     829             :     int msg_size;
     830             :     int need_reply;
     831             :     char *buffer;
     832             :     char *server_name;          // server IPC name being connected to
     833             :     qb_ipcc_connection_t *ipc;
     834             : };
     835             : 
     836             : /*!
     837             :  * \brief Create a new (legacy) object for using Pacemaker daemon IPC
     838             :  *
     839             :  * \param[in] name      IPC system name to connect to
     840             :  * \param[in] max_size  Use a maximum IPC buffer size of at least this size
     841             :  *
     842             :  * \return Newly allocated IPC object on success, NULL otherwise
     843             :  *
     844             :  * \note The caller is responsible for freeing the result using
     845             :  *       crm_ipc_destroy().
     846             :  * \note This should be considered deprecated for use with daemons supported by
     847             :  *       pcmk_new_ipc_api().
     848             :  */
     849             : crm_ipc_t *
     850           0 : crm_ipc_new(const char *name, size_t max_size)
     851             : {
     852           0 :     crm_ipc_t *client = NULL;
     853             : 
     854           0 :     client = calloc(1, sizeof(crm_ipc_t));
     855           0 :     if (client == NULL) {
     856           0 :         crm_err("Could not create IPC connection: %s", strerror(errno));
     857           0 :         return NULL;
     858             :     }
     859             : 
     860           0 :     client->server_name = strdup(name);
     861           0 :     if (client->server_name == NULL) {
     862           0 :         crm_err("Could not create %s IPC connection: %s",
     863             :                 name, strerror(errno));
     864           0 :         free(client);
     865           0 :         return NULL;
     866             :     }
     867           0 :     client->buf_size = pcmk__ipc_buffer_size(max_size);
     868           0 :     client->buffer = malloc(client->buf_size);
     869           0 :     if (client->buffer == NULL) {
     870           0 :         crm_err("Could not create %s IPC connection: %s",
     871             :                 name, strerror(errno));
     872           0 :         free(client->server_name);
     873           0 :         free(client);
     874           0 :         return NULL;
     875             :     }
     876             : 
     877             :     /* Clients initiating connection pick the max buf size */
     878           0 :     client->max_buf_size = client->buf_size;
     879             : 
     880           0 :     client->pfd.fd = -1;
     881           0 :     client->pfd.events = POLLIN;
     882           0 :     client->pfd.revents = 0;
     883             : 
     884           0 :     return client;
     885             : }
     886             : 
     887             : /*!
     888             :  * \internal
     889             :  * \brief Connect a generic (not daemon-specific) IPC object
     890             :  *
     891             :  * \param[in,out] ipc  Generic IPC object to connect
     892             :  *
     893             :  * \return Standard Pacemaker return code
     894             :  */
     895             : int
     896           0 : pcmk__connect_generic_ipc(crm_ipc_t *ipc)
     897             : {
     898           0 :     uid_t cl_uid = 0;
     899           0 :     gid_t cl_gid = 0;
     900           0 :     pid_t found_pid = 0;
     901           0 :     uid_t found_uid = 0;
     902           0 :     gid_t found_gid = 0;
     903           0 :     int rc = pcmk_rc_ok;
     904             : 
     905           0 :     if (ipc == NULL) {
     906           0 :         return EINVAL;
     907             :     }
     908             : 
     909           0 :     ipc->need_reply = FALSE;
     910           0 :     ipc->ipc = qb_ipcc_connect(ipc->server_name, ipc->buf_size);
     911           0 :     if (ipc->ipc == NULL) {
     912           0 :         return errno;
     913             :     }
     914             : 
     915           0 :     rc = qb_ipcc_fd_get(ipc->ipc, &ipc->pfd.fd);
     916           0 :     if (rc < 0) { // -errno
     917           0 :         crm_ipc_close(ipc);
     918           0 :         return -rc;
     919             :     }
     920             : 
     921           0 :     rc = pcmk_daemon_user(&cl_uid, &cl_gid);
     922           0 :     rc = pcmk_legacy2rc(rc);
     923           0 :     if (rc != pcmk_rc_ok) {
     924           0 :         crm_ipc_close(ipc);
     925           0 :         return rc;
     926             :     }
     927             : 
     928           0 :     rc = is_ipc_provider_expected(ipc->ipc, ipc->pfd.fd, cl_uid, cl_gid,
     929             :                                   &found_pid, &found_uid, &found_gid);
     930           0 :     if (rc != pcmk_rc_ok) {
     931           0 :         if (rc == pcmk_rc_ipc_unauthorized) {
     932           0 :             crm_info("%s IPC provider authentication failed: process %lld has "
     933             :                      "uid %lld (expected %lld) and gid %lld (expected %lld)",
     934             :                      ipc->server_name,
     935             :                      (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
     936             :                      (long long) found_uid, (long long) cl_uid,
     937             :                      (long long) found_gid, (long long) cl_gid);
     938             :         }
     939           0 :         crm_ipc_close(ipc);
     940           0 :         return rc;
     941             :     }
     942             : 
     943           0 :     ipc->max_buf_size = qb_ipcc_get_buffer_size(ipc->ipc);
     944           0 :     if (ipc->max_buf_size > ipc->buf_size) {
     945           0 :         free(ipc->buffer);
     946           0 :         ipc->buffer = calloc(ipc->max_buf_size, sizeof(char));
     947           0 :         if (ipc->buffer == NULL) {
     948           0 :             rc = errno;
     949           0 :             crm_ipc_close(ipc);
     950           0 :             return rc;
     951             :         }
     952           0 :         ipc->buf_size = ipc->max_buf_size;
     953             :     }
     954             : 
     955           0 :     return pcmk_rc_ok;
     956             : }
     957             : 
     958             : /*!
     959             :  * \brief Establish an IPC connection to a Pacemaker component
     960             :  *
     961             :  * \param[in,out] client  Connection instance obtained from crm_ipc_new()
     962             :  *
     963             :  * \return true on success, false otherwise (in which case errno will be set;
     964             :  *         specifically, in case of discovering the remote side is not
     965             :  *         authentic, its value is set to ECONNABORTED).
     966             :  */
     967             : bool
     968           0 : crm_ipc_connect(crm_ipc_t *client)
     969             : {
     970           0 :     int rc = pcmk__connect_generic_ipc(client);
     971             : 
     972           0 :     if (rc == pcmk_rc_ok) {
     973           0 :         return true;
     974             :     }
     975           0 :     if ((client != NULL) && (client->ipc == NULL)) {
     976           0 :         errno = (rc > 0)? rc : ENOTCONN;
     977           0 :         crm_debug("Could not establish %s IPC connection: %s (%d)",
     978             :                   client->server_name, pcmk_rc_str(errno), errno);
     979           0 :     } else if (rc == pcmk_rc_ipc_unauthorized) {
     980           0 :         crm_err("%s IPC provider authentication failed",
     981             :                 (client == NULL)? "Pacemaker" : client->server_name);
     982           0 :         errno = ECONNABORTED;
     983             :     } else {
     984           0 :         crm_perror(LOG_ERR,
     985             :                    "Could not verify authenticity of %s IPC provider",
     986             :                    (client == NULL)? "Pacemaker" : client->server_name);
     987           0 :         errno = ENOTCONN;
     988             :     }
     989           0 :     return false;
     990             : }
     991             : 
     992             : void
     993           0 : crm_ipc_close(crm_ipc_t * client)
     994             : {
     995           0 :     if (client) {
     996           0 :         if (client->ipc) {
     997           0 :             qb_ipcc_connection_t *ipc = client->ipc;
     998             : 
     999           0 :             client->ipc = NULL;
    1000           0 :             qb_ipcc_disconnect(ipc);
    1001             :         }
    1002             :     }
    1003           0 : }
    1004             : 
    1005             : void
    1006           0 : crm_ipc_destroy(crm_ipc_t * client)
    1007             : {
    1008           0 :     if (client) {
    1009           0 :         if (client->ipc && qb_ipcc_is_connected(client->ipc)) {
    1010           0 :             crm_notice("Destroying active %s IPC connection",
    1011             :                        client->server_name);
    1012             :             /* The next line is basically unsafe
    1013             :              *
    1014             :              * If this connection was attached to mainloop and mainloop is active,
    1015             :              *   the 'disconnected' callback will end up back here and we'll end
    1016             :              *   up free'ing the memory twice - something that can still happen
    1017             :              *   even without this if we destroy a connection and it closes before
    1018             :              *   we call exit
    1019             :              */
    1020             :             /* crm_ipc_close(client); */
    1021             :         } else {
    1022           0 :             crm_trace("Destroying inactive %s IPC connection",
    1023             :                       client->server_name);
    1024             :         }
    1025           0 :         free(client->buffer);
    1026           0 :         free(client->server_name);
    1027           0 :         free(client);
    1028             :     }
    1029           0 : }
    1030             : 
    1031             : /*!
    1032             :  * \internal
    1033             :  * \brief Get the file descriptor for a generic IPC object
    1034             :  *
    1035             :  * \param[in,out] ipc  Generic IPC object to get file descriptor for
    1036             :  * \param[out]    fd   Where to store file descriptor
    1037             :  *
    1038             :  * \return Standard Pacemaker return code
    1039             :  */
    1040             : int
    1041           0 : pcmk__ipc_fd(crm_ipc_t *ipc, int *fd)
    1042             : {
    1043           0 :     if ((ipc == NULL) || (fd == NULL)) {
    1044           0 :         return EINVAL;
    1045             :     }
    1046           0 :     if ((ipc->ipc == NULL) || (ipc->pfd.fd < 0)) {
    1047           0 :         return ENOTCONN;
    1048             :     }
    1049           0 :     *fd = ipc->pfd.fd;
    1050           0 :     return pcmk_rc_ok;
    1051             : }
    1052             : 
    1053             : int
    1054           0 : crm_ipc_get_fd(crm_ipc_t * client)
    1055             : {
    1056           0 :     int fd = -1;
    1057             : 
    1058           0 :     if (pcmk__ipc_fd(client, &fd) != pcmk_rc_ok) {
    1059           0 :         crm_err("Could not obtain file descriptor for %s IPC",
    1060             :                 ((client == NULL)? "unspecified" : client->server_name));
    1061           0 :         errno = EINVAL;
    1062           0 :         return -EINVAL;
    1063             :     }
    1064           0 :     return fd;
    1065             : }
    1066             : 
    1067             : bool
    1068           0 : crm_ipc_connected(crm_ipc_t * client)
    1069             : {
    1070           0 :     bool rc = FALSE;
    1071             : 
    1072           0 :     if (client == NULL) {
    1073           0 :         crm_trace("No client");
    1074           0 :         return FALSE;
    1075             : 
    1076           0 :     } else if (client->ipc == NULL) {
    1077           0 :         crm_trace("No connection");
    1078           0 :         return FALSE;
    1079             : 
    1080           0 :     } else if (client->pfd.fd < 0) {
    1081           0 :         crm_trace("Bad descriptor");
    1082           0 :         return FALSE;
    1083             :     }
    1084             : 
    1085           0 :     rc = qb_ipcc_is_connected(client->ipc);
    1086           0 :     if (rc == FALSE) {
    1087           0 :         client->pfd.fd = -EINVAL;
    1088             :     }
    1089           0 :     return rc;
    1090             : }
    1091             : 
    1092             : /*!
    1093             :  * \brief Check whether an IPC connection is ready to be read
    1094             :  *
    1095             :  * \param[in,out] client  Connection to check
    1096             :  *
    1097             :  * \return Positive value if ready to be read, 0 if not ready, -errno on error
    1098             :  */
    1099             : int
    1100           0 : crm_ipc_ready(crm_ipc_t *client)
    1101             : {
    1102             :     int rc;
    1103             : 
    1104           0 :     CRM_ASSERT(client != NULL);
    1105             : 
    1106           0 :     if (!crm_ipc_connected(client)) {
    1107           0 :         return -ENOTCONN;
    1108             :     }
    1109             : 
    1110           0 :     client->pfd.revents = 0;
    1111           0 :     rc = poll(&(client->pfd), 1, 0);
    1112           0 :     return (rc < 0)? -errno : rc;
    1113             : }
    1114             : 
    1115             : // \return Standard Pacemaker return code
    1116             : static int
    1117           0 : crm_ipc_decompress(crm_ipc_t * client)
    1118             : {
    1119           0 :     pcmk__ipc_header_t *header = (pcmk__ipc_header_t *)(void*)client->buffer;
    1120             : 
    1121           0 :     if (header->size_compressed) {
    1122           0 :         int rc = 0;
    1123           0 :         unsigned int size_u = 1 + header->size_uncompressed;
    1124             :         /* never let buf size fall below our max size required for ipc reads. */
    1125           0 :         unsigned int new_buf_size = QB_MAX((sizeof(pcmk__ipc_header_t) + size_u), client->max_buf_size);
    1126           0 :         char *uncompressed = pcmk__assert_alloc(1, new_buf_size);
    1127             : 
    1128           0 :         crm_trace("Decompressing message data %u bytes into %u bytes",
    1129             :                  header->size_compressed, size_u);
    1130             : 
    1131           0 :         rc = BZ2_bzBuffToBuffDecompress(uncompressed + sizeof(pcmk__ipc_header_t), &size_u,
    1132           0 :                                         client->buffer + sizeof(pcmk__ipc_header_t), header->size_compressed, 1, 0);
    1133           0 :         rc = pcmk__bzlib2rc(rc);
    1134             : 
    1135           0 :         if (rc != pcmk_rc_ok) {
    1136           0 :             crm_err("Decompression failed: %s " CRM_XS " rc=%d",
    1137             :                     pcmk_rc_str(rc), rc);
    1138           0 :             free(uncompressed);
    1139           0 :             return rc;
    1140             :         }
    1141             : 
    1142             :         /*
    1143             :          * This assert no longer holds true.  For an identical msg, some clients may
    1144             :          * require compression, and others may not. If that same msg (event) is sent
    1145             :          * to multiple clients, it could result in some clients receiving a compressed
    1146             :          * msg even though compression was not explicitly required for them.
    1147             :          *
    1148             :          * CRM_ASSERT((header->size_uncompressed + sizeof(pcmk__ipc_header_t)) >= ipc_buffer_max);
    1149             :          */
    1150           0 :         CRM_ASSERT(size_u == header->size_uncompressed);
    1151             : 
    1152           0 :         memcpy(uncompressed, client->buffer, sizeof(pcmk__ipc_header_t));       /* Preserve the header */
    1153           0 :         header = (pcmk__ipc_header_t *)(void*)uncompressed;
    1154             : 
    1155           0 :         free(client->buffer);
    1156           0 :         client->buf_size = new_buf_size;
    1157           0 :         client->buffer = uncompressed;
    1158             :     }
    1159             : 
    1160           0 :     CRM_ASSERT(client->buffer[sizeof(pcmk__ipc_header_t) + header->size_uncompressed - 1] == 0);
    1161           0 :     return pcmk_rc_ok;
    1162             : }
    1163             : 
    1164             : long
    1165           0 : crm_ipc_read(crm_ipc_t * client)
    1166             : {
    1167           0 :     pcmk__ipc_header_t *header = NULL;
    1168             : 
    1169           0 :     CRM_ASSERT(client != NULL);
    1170           0 :     CRM_ASSERT(client->ipc != NULL);
    1171           0 :     CRM_ASSERT(client->buffer != NULL);
    1172             : 
    1173           0 :     client->buffer[0] = 0;
    1174           0 :     client->msg_size = qb_ipcc_event_recv(client->ipc, client->buffer,
    1175           0 :                                           client->buf_size, 0);
    1176           0 :     if (client->msg_size >= 0) {
    1177           0 :         int rc = crm_ipc_decompress(client);
    1178             : 
    1179           0 :         if (rc != pcmk_rc_ok) {
    1180           0 :             return pcmk_rc2legacy(rc);
    1181             :         }
    1182             : 
    1183           0 :         header = (pcmk__ipc_header_t *)(void*)client->buffer;
    1184           0 :         if (!pcmk__valid_ipc_header(header)) {
    1185           0 :             return -EBADMSG;
    1186             :         }
    1187             : 
    1188           0 :         crm_trace("Received %s IPC event %d size=%u rc=%d text='%.100s'",
    1189             :                   client->server_name, header->qb.id, header->qb.size,
    1190             :                   client->msg_size,
    1191             :                   client->buffer + sizeof(pcmk__ipc_header_t));
    1192             : 
    1193             :     } else {
    1194           0 :         crm_trace("No message received from %s IPC: %s",
    1195             :                   client->server_name, pcmk_strerror(client->msg_size));
    1196             : 
    1197           0 :         if (client->msg_size == -EAGAIN) {
    1198           0 :             return -EAGAIN;
    1199             :         }
    1200             :     }
    1201             : 
    1202           0 :     if (!crm_ipc_connected(client) || client->msg_size == -ENOTCONN) {
    1203           0 :         crm_err("Connection to %s IPC failed", client->server_name);
    1204             :     }
    1205             : 
    1206           0 :     if (header) {
    1207             :         /* Data excluding the header */
    1208           0 :         return header->size_uncompressed;
    1209             :     }
    1210           0 :     return -ENOMSG;
    1211             : }
    1212             : 
    1213             : const char *
    1214           0 : crm_ipc_buffer(crm_ipc_t * client)
    1215             : {
    1216           0 :     CRM_ASSERT(client != NULL);
    1217           0 :     return client->buffer + sizeof(pcmk__ipc_header_t);
    1218             : }
    1219             : 
    1220             : uint32_t
    1221           0 : crm_ipc_buffer_flags(crm_ipc_t * client)
    1222             : {
    1223           0 :     pcmk__ipc_header_t *header = NULL;
    1224             : 
    1225           0 :     CRM_ASSERT(client != NULL);
    1226           0 :     if (client->buffer == NULL) {
    1227           0 :         return 0;
    1228             :     }
    1229             : 
    1230           0 :     header = (pcmk__ipc_header_t *)(void*)client->buffer;
    1231           0 :     return header->flags;
    1232             : }
    1233             : 
    1234             : const char *
    1235           0 : crm_ipc_name(crm_ipc_t * client)
    1236             : {
    1237           0 :     CRM_ASSERT(client != NULL);
    1238           0 :     return client->server_name;
    1239             : }
    1240             : 
    1241             : // \return Standard Pacemaker return code
    1242             : static int
    1243           0 : internal_ipc_get_reply(crm_ipc_t *client, int request_id, int ms_timeout,
    1244             :                        ssize_t *bytes)
    1245             : {
    1246           0 :     time_t timeout = time(NULL) + 1 + (ms_timeout / 1000);
    1247           0 :     int rc = pcmk_rc_ok;
    1248             : 
    1249             :     /* get the reply */
    1250           0 :     crm_trace("Waiting on reply to %s IPC message %d",
    1251             :               client->server_name, request_id);
    1252             :     do {
    1253             : 
    1254           0 :         *bytes = qb_ipcc_recv(client->ipc, client->buffer, client->buf_size, 1000);
    1255           0 :         if (*bytes > 0) {
    1256           0 :             pcmk__ipc_header_t *hdr = NULL;
    1257             : 
    1258           0 :             rc = crm_ipc_decompress(client);
    1259           0 :             if (rc != pcmk_rc_ok) {
    1260           0 :                 return rc;
    1261             :             }
    1262             : 
    1263           0 :             hdr = (pcmk__ipc_header_t *)(void*)client->buffer;
    1264           0 :             if (hdr->qb.id == request_id) {
    1265             :                 /* Got it */
    1266           0 :                 break;
    1267           0 :             } else if (hdr->qb.id < request_id) {
    1268           0 :                 xmlNode *bad = pcmk__xml_parse(crm_ipc_buffer(client));
    1269             : 
    1270           0 :                 crm_err("Discarding old reply %d (need %d)", hdr->qb.id, request_id);
    1271           0 :                 crm_log_xml_notice(bad, "OldIpcReply");
    1272             : 
    1273             :             } else {
    1274           0 :                 xmlNode *bad = pcmk__xml_parse(crm_ipc_buffer(client));
    1275             : 
    1276           0 :                 crm_err("Discarding newer reply %d (need %d)", hdr->qb.id, request_id);
    1277           0 :                 crm_log_xml_notice(bad, "ImpossibleReply");
    1278           0 :                 CRM_ASSERT(hdr->qb.id <= request_id);
    1279             :             }
    1280           0 :         } else if (!crm_ipc_connected(client)) {
    1281           0 :             crm_err("%s IPC provider disconnected while waiting for message %d",
    1282             :                     client->server_name, request_id);
    1283           0 :             break;
    1284             :         }
    1285             : 
    1286           0 :     } while (time(NULL) < timeout);
    1287             : 
    1288           0 :     if (*bytes < 0) {
    1289           0 :         rc = (int) -*bytes; // System errno
    1290             :     }
    1291           0 :     return rc;
    1292             : }
    1293             : 
    1294             : /*!
    1295             :  * \brief Send an IPC XML message
    1296             :  *
    1297             :  * \param[in,out] client      Connection to IPC server
    1298             :  * \param[in]     message     XML message to send
    1299             :  * \param[in]     flags       Bitmask of crm_ipc_flags
    1300             :  * \param[in]     ms_timeout  Give up if not sent within this much time
    1301             :  *                            (5 seconds if 0, or no timeout if negative)
    1302             :  * \param[out]    reply       Reply from server (or NULL if none)
    1303             :  *
    1304             :  * \return Negative errno on error, otherwise size of reply received in bytes
    1305             :  *         if reply was needed, otherwise number of bytes sent
    1306             :  */
    1307             : int
    1308           0 : crm_ipc_send(crm_ipc_t *client, const xmlNode *message,
    1309             :              enum crm_ipc_flags flags, int32_t ms_timeout, xmlNode **reply)
    1310             : {
    1311           0 :     int rc = 0;
    1312           0 :     ssize_t qb_rc = 0;
    1313           0 :     ssize_t bytes = 0;
    1314             :     struct iovec *iov;
    1315             :     static uint32_t id = 0;
    1316             :     static int factor = 8;
    1317             :     pcmk__ipc_header_t *header;
    1318             : 
    1319           0 :     if (client == NULL) {
    1320           0 :         crm_notice("Can't send IPC request without connection (bug?): %.100s",
    1321             :                    message);
    1322           0 :         return -ENOTCONN;
    1323             : 
    1324           0 :     } else if (!crm_ipc_connected(client)) {
    1325             :         /* Don't even bother */
    1326           0 :         crm_notice("Can't send %s IPC requests: Connection closed",
    1327             :                    client->server_name);
    1328           0 :         return -ENOTCONN;
    1329             :     }
    1330             : 
    1331           0 :     if (ms_timeout == 0) {
    1332           0 :         ms_timeout = 5000;
    1333             :     }
    1334             : 
    1335           0 :     if (client->need_reply) {
    1336           0 :         qb_rc = qb_ipcc_recv(client->ipc, client->buffer, client->buf_size, ms_timeout);
    1337           0 :         if (qb_rc < 0) {
    1338           0 :             crm_warn("Sending %s IPC disabled until pending reply received",
    1339             :                      client->server_name);
    1340           0 :             return -EALREADY;
    1341             : 
    1342             :         } else {
    1343           0 :             crm_notice("Sending %s IPC re-enabled after pending reply received",
    1344             :                        client->server_name);
    1345           0 :             client->need_reply = FALSE;
    1346             :         }
    1347             :     }
    1348             : 
    1349           0 :     id++;
    1350           0 :     CRM_LOG_ASSERT(id != 0); /* Crude wrap-around detection */
    1351           0 :     rc = pcmk__ipc_prepare_iov(id, message, client->max_buf_size, &iov, &bytes);
    1352           0 :     if (rc != pcmk_rc_ok) {
    1353           0 :         crm_warn("Couldn't prepare %s IPC request: %s " CRM_XS " rc=%d",
    1354             :                  client->server_name, pcmk_rc_str(rc), rc);
    1355           0 :         return pcmk_rc2legacy(rc);
    1356             :     }
    1357             : 
    1358           0 :     header = iov[0].iov_base;
    1359           0 :     pcmk__set_ipc_flags(header->flags, client->server_name, flags);
    1360             : 
    1361           0 :     if (pcmk_is_set(flags, crm_ipc_proxied)) {
    1362             :         /* Don't look for a synchronous response */
    1363           0 :         pcmk__clear_ipc_flags(flags, "client", crm_ipc_client_response);
    1364             :     }
    1365             : 
    1366           0 :     if(header->size_compressed) {
    1367           0 :         if(factor < 10 && (client->max_buf_size / 10) < (bytes / factor)) {
    1368           0 :             crm_notice("Compressed message exceeds %d0%% of configured IPC "
    1369             :                        "limit (%u bytes); consider setting PCMK_ipc_buffer to "
    1370             :                        "%u or higher",
    1371             :                        factor, client->max_buf_size, 2 * client->max_buf_size);
    1372           0 :             factor++;
    1373             :         }
    1374             :     }
    1375             : 
    1376           0 :     crm_trace("Sending %s IPC request %d of %u bytes using %dms timeout",
    1377             :               client->server_name, header->qb.id, header->qb.size, ms_timeout);
    1378             : 
    1379           0 :     if ((ms_timeout > 0) || !pcmk_is_set(flags, crm_ipc_client_response)) {
    1380             : 
    1381           0 :         time_t timeout = time(NULL) + 1 + (ms_timeout / 1000);
    1382             : 
    1383             :         do {
    1384             :             /* @TODO Is this check really needed? Won't qb_ipcc_sendv() return
    1385             :              * an error if it's not connected?
    1386             :              */
    1387           0 :             if (!crm_ipc_connected(client)) {
    1388           0 :                 goto send_cleanup;
    1389             :             }
    1390             : 
    1391           0 :             qb_rc = qb_ipcc_sendv(client->ipc, iov, 2);
    1392           0 :         } while ((qb_rc == -EAGAIN) && (time(NULL) < timeout));
    1393             : 
    1394           0 :         rc = (int) qb_rc; // Negative of system errno, or bytes sent
    1395           0 :         if (qb_rc <= 0) {
    1396           0 :             goto send_cleanup;
    1397             : 
    1398           0 :         } else if (!pcmk_is_set(flags, crm_ipc_client_response)) {
    1399           0 :             crm_trace("Not waiting for reply to %s IPC request %d",
    1400             :                       client->server_name, header->qb.id);
    1401           0 :             goto send_cleanup;
    1402             :         }
    1403             : 
    1404           0 :         rc = internal_ipc_get_reply(client, header->qb.id, ms_timeout, &bytes);
    1405           0 :         if (rc != pcmk_rc_ok) {
    1406             :             /* We didn't get the reply in time, so disable future sends for now.
    1407             :              * The only alternative would be to close the connection since we
    1408             :              * don't know how to detect and discard out-of-sequence replies.
    1409             :              *
    1410             :              * @TODO Implement out-of-sequence detection
    1411             :              */
    1412           0 :             client->need_reply = TRUE;
    1413             :         }
    1414           0 :         rc = (int) bytes; // Negative system errno, or size of reply received
    1415             : 
    1416             :     } else {
    1417             :         // No timeout, and client response needed
    1418             :         do {
    1419           0 :             qb_rc = qb_ipcc_sendv_recv(client->ipc, iov, 2, client->buffer,
    1420           0 :                                        client->buf_size, -1);
    1421           0 :         } while ((qb_rc == -EAGAIN) && crm_ipc_connected(client));
    1422           0 :         rc = (int) qb_rc; // Negative system errno, or size of reply received
    1423             :     }
    1424             : 
    1425           0 :     if (rc > 0) {
    1426           0 :         pcmk__ipc_header_t *hdr = (pcmk__ipc_header_t *)(void*)client->buffer;
    1427             : 
    1428           0 :         crm_trace("Received %d-byte reply %d to %s IPC %d: %.100s",
    1429             :                   rc, hdr->qb.id, client->server_name, header->qb.id,
    1430             :                   crm_ipc_buffer(client));
    1431             : 
    1432           0 :         if (reply) {
    1433           0 :             *reply = pcmk__xml_parse(crm_ipc_buffer(client));
    1434             :         }
    1435             : 
    1436             :     } else {
    1437           0 :         crm_trace("No reply to %s IPC %d: rc=%d",
    1438             :                   client->server_name, header->qb.id, rc);
    1439             :     }
    1440             : 
    1441           0 :   send_cleanup:
    1442           0 :     if (!crm_ipc_connected(client)) {
    1443           0 :         crm_notice("Couldn't send %s IPC request %d: Connection closed "
    1444             :                    CRM_XS " rc=%d", client->server_name, header->qb.id, rc);
    1445             : 
    1446           0 :     } else if (rc == -ETIMEDOUT) {
    1447           0 :         crm_warn("%s IPC request %d failed: %s after %dms " CRM_XS " rc=%d",
    1448             :                  client->server_name, header->qb.id, pcmk_strerror(rc),
    1449             :                  ms_timeout, rc);
    1450           0 :         crm_write_blackbox(0, NULL);
    1451             : 
    1452           0 :     } else if (rc <= 0) {
    1453           0 :         crm_warn("%s IPC request %d failed: %s " CRM_XS " rc=%d",
    1454             :                  client->server_name, header->qb.id,
    1455             :                  ((rc == 0)? "No bytes sent" : pcmk_strerror(rc)), rc);
    1456             :     }
    1457             : 
    1458           0 :     pcmk_free_ipc_event(iov);
    1459           0 :     return rc;
    1460             : }
    1461             : 
    1462             : /*!
    1463             :  * \brief Ensure an IPC provider has expected user or group
    1464             :  *
    1465             :  * \param[in]  qb_ipc  libqb client connection if available
    1466             :  * \param[in]  sock    Connected Unix socket for IPC
    1467             :  * \param[in]  refuid  Expected user ID
    1468             :  * \param[in]  refgid  Expected group ID
    1469             :  * \param[out] gotpid  If not NULL, where to store provider's actual process ID
    1470             :  *                     (or 1 on platforms where ID is not available)
    1471             :  * \param[out] gotuid  If not NULL, where to store provider's actual user ID
    1472             :  * \param[out] gotgid  If not NULL, where to store provider's actual group ID
    1473             :  *
    1474             :  * \return Standard Pacemaker return code
    1475             :  * \note An actual user ID of 0 (root) will always be considered authorized,
    1476             :  *       regardless of the expected values provided. The caller can use the
    1477             :  *       output arguments to be stricter than this function.
    1478             :  */
    1479             : static int
    1480           0 : is_ipc_provider_expected(qb_ipcc_connection_t *qb_ipc, int sock,
    1481             :                          uid_t refuid, gid_t refgid,
    1482             :                          pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
    1483             : {
    1484           0 :     int rc = EOPNOTSUPP;
    1485           0 :     pid_t found_pid = 0;
    1486           0 :     uid_t found_uid = 0;
    1487           0 :     gid_t found_gid = 0;
    1488             : 
    1489             : #ifdef HAVE_QB_IPCC_AUTH_GET
    1490           0 :     if (qb_ipc != NULL) {
    1491           0 :         rc = qb_ipcc_auth_get(qb_ipc, &found_pid, &found_uid, &found_gid);
    1492           0 :         rc = -rc; // libqb returns 0 or -errno
    1493           0 :         if (rc == pcmk_rc_ok) {
    1494           0 :             goto found;
    1495             :         }
    1496             :     }
    1497             : #endif
    1498             : 
    1499             : #ifdef HAVE_UCRED
    1500             :     {
    1501             :         struct ucred ucred;
    1502           0 :         socklen_t ucred_len = sizeof(ucred);
    1503             : 
    1504           0 :         if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &ucred_len) < 0) {
    1505           0 :             rc = errno;
    1506           0 :         } else if (ucred_len != sizeof(ucred)) {
    1507           0 :             rc = EOPNOTSUPP;
    1508             :         } else {
    1509           0 :             found_pid = ucred.pid;
    1510           0 :             found_uid = ucred.uid;
    1511           0 :             found_gid = ucred.gid;
    1512           0 :             goto found;
    1513             :         }
    1514             :     }
    1515             : #endif
    1516             : 
    1517             : #ifdef HAVE_SOCKPEERCRED
    1518             :     {
    1519             :         struct sockpeercred sockpeercred;
    1520             :         socklen_t sockpeercred_len = sizeof(sockpeercred);
    1521             : 
    1522             :         if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
    1523             :                        &sockpeercred, &sockpeercred_len) < 0) {
    1524             :             rc = errno;
    1525             :         } else if (sockpeercred_len != sizeof(sockpeercred)) {
    1526             :             rc = EOPNOTSUPP;
    1527             :         } else {
    1528             :             found_pid = sockpeercred.pid;
    1529             :             found_uid = sockpeercred.uid;
    1530             :             found_gid = sockpeercred.gid;
    1531             :             goto found;
    1532             :         }
    1533             :     }
    1534             : #endif
    1535             : 
    1536             : #ifdef HAVE_GETPEEREID // For example, FreeBSD
    1537             :     if (getpeereid(sock, &found_uid, &found_gid) < 0) {
    1538             :         rc = errno;
    1539             :     } else {
    1540             :         found_pid = PCMK__SPECIAL_PID;
    1541             :         goto found;
    1542             :     }
    1543             : #endif
    1544             : 
    1545             : #ifdef HAVE_GETPEERUCRED
    1546             :     {
    1547             :         ucred_t *ucred = NULL;
    1548             : 
    1549             :         if (getpeerucred(sock, &ucred) < 0) {
    1550             :             rc = errno;
    1551             :         } else {
    1552             :             found_pid = ucred_getpid(ucred);
    1553             :             found_uid = ucred_geteuid(ucred);
    1554             :             found_gid = ucred_getegid(ucred);
    1555             :             ucred_free(ucred);
    1556             :             goto found;
    1557             :         }
    1558             :     }
    1559             : #endif
    1560             : 
    1561           0 :     return rc; // If we get here, nothing succeeded
    1562             : 
    1563           0 : found:
    1564           0 :     if (gotpid != NULL) {
    1565           0 :         *gotpid = found_pid;
    1566             :     }
    1567           0 :     if (gotuid != NULL) {
    1568           0 :         *gotuid = found_uid;
    1569             :     }
    1570           0 :     if (gotgid != NULL) {
    1571           0 :         *gotgid = found_gid;
    1572             :     }
    1573           0 :     if ((found_uid != 0) && (found_uid != refuid) && (found_gid != refgid)) {
    1574           0 :         return pcmk_rc_ipc_unauthorized;
    1575             :     }
    1576           0 :     return pcmk_rc_ok;
    1577             : }
    1578             : 
    1579             : int
    1580           0 : crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid,
    1581             :                              pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
    1582             : {
    1583           0 :     int ret = is_ipc_provider_expected(NULL, sock, refuid, refgid,
    1584             :                                        gotpid, gotuid, gotgid);
    1585             : 
    1586             :     /* The old function had some very odd return codes*/
    1587           0 :     if (ret == 0) {
    1588           0 :         return 1;
    1589           0 :     } else if (ret == pcmk_rc_ipc_unauthorized) {
    1590           0 :         return 0;
    1591             :     } else {
    1592           0 :         return pcmk_rc2legacy(ret);
    1593             :     }
    1594             : }
    1595             : 
    1596             : int
    1597           0 : pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid,
    1598             :                                       gid_t refgid, pid_t *gotpid)
    1599             : {
    1600             :     static char last_asked_name[PATH_MAX / 2] = "";  /* log spam prevention */
    1601             :     int fd;
    1602           0 :     int rc = pcmk_rc_ipc_unresponsive;
    1603           0 :     int auth_rc = 0;
    1604             :     int32_t qb_rc;
    1605           0 :     pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
    1606             :     qb_ipcc_connection_t *c;
    1607             : #ifdef HAVE_QB_IPCC_CONNECT_ASYNC
    1608           0 :     struct pollfd pollfd = { 0, };
    1609             :     int poll_rc;
    1610             : 
    1611           0 :     c = qb_ipcc_connect_async(name, 0,
    1612             :                               &(pollfd.fd));
    1613             : #else
    1614             :     c = qb_ipcc_connect(name, 0);
    1615             : #endif
    1616           0 :     if (c == NULL) {
    1617           0 :         crm_info("Could not connect to %s IPC: %s", name, strerror(errno));
    1618           0 :         rc = pcmk_rc_ipc_unresponsive;
    1619           0 :         goto bail;
    1620             :     }
    1621             : #ifdef HAVE_QB_IPCC_CONNECT_ASYNC
    1622           0 :     pollfd.events = POLLIN;
    1623             :     do {
    1624           0 :         poll_rc = poll(&pollfd, 1, 2000);
    1625           0 :     } while ((poll_rc == -1) && (errno == EINTR));
    1626             : 
    1627             :     /* If poll() failed, given that disconnect function is not registered yet,
    1628             :      * qb_ipcc_disconnect() won't clean up the socket. In any case, call
    1629             :      * qb_ipcc_connect_continue() here so that it may fail and do the cleanup
    1630             :      * for us.
    1631             :      */
    1632           0 :     if (qb_ipcc_connect_continue(c) != 0) {
    1633           0 :         crm_info("Could not connect to %s IPC: %s", name,
    1634             :                  (poll_rc == 0)?"timeout":strerror(errno));
    1635           0 :         rc = pcmk_rc_ipc_unresponsive;
    1636           0 :         c = NULL; // qb_ipcc_connect_continue cleaned up for us
    1637           0 :         goto bail;
    1638             :     }
    1639             : #endif
    1640             : 
    1641           0 :     qb_rc = qb_ipcc_fd_get(c, &fd);
    1642           0 :     if (qb_rc != 0) {
    1643           0 :         rc = (int) -qb_rc; // System errno
    1644           0 :         crm_err("Could not get fd from %s IPC: %s " CRM_XS " rc=%d",
    1645             :                 name, pcmk_rc_str(rc), rc);
    1646           0 :         goto bail;
    1647             :     }
    1648             : 
    1649           0 :     auth_rc = is_ipc_provider_expected(c, fd, refuid, refgid,
    1650             :                                        &found_pid, &found_uid, &found_gid);
    1651           0 :     if (auth_rc == pcmk_rc_ipc_unauthorized) {
    1652           0 :         crm_err("Daemon (IPC %s) effectively blocked with unauthorized"
    1653             :                 " process %lld (uid: %lld, gid: %lld)",
    1654             :                 name, (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
    1655             :                 (long long) found_uid, (long long) found_gid);
    1656           0 :         rc = pcmk_rc_ipc_unauthorized;
    1657           0 :         goto bail;
    1658             :     }
    1659             : 
    1660           0 :     if (auth_rc != pcmk_rc_ok) {
    1661           0 :         rc = auth_rc;
    1662           0 :         crm_err("Could not get peer credentials from %s IPC: %s "
    1663             :                 CRM_XS " rc=%d", name, pcmk_rc_str(rc), rc);
    1664           0 :         goto bail;
    1665             :     }
    1666             : 
    1667           0 :     if (gotpid != NULL) {
    1668           0 :         *gotpid = found_pid;
    1669             :     }
    1670             : 
    1671           0 :     rc = pcmk_rc_ok;
    1672           0 :     if ((found_uid != refuid || found_gid != refgid)
    1673           0 :             && strncmp(last_asked_name, name, sizeof(last_asked_name))) {
    1674           0 :         if ((found_uid == 0) && (refuid != 0)) {
    1675           0 :             crm_warn("Daemon (IPC %s) runs as root, whereas the expected"
    1676             :                      " credentials are %lld:%lld, hazard of violating"
    1677             :                      " the least privilege principle",
    1678             :                      name, (long long) refuid, (long long) refgid);
    1679             :         } else {
    1680           0 :             crm_notice("Daemon (IPC %s) runs as %lld:%lld, whereas the"
    1681             :                        " expected credentials are %lld:%lld, which may"
    1682             :                        " mean a different set of privileges than expected",
    1683             :                        name, (long long) found_uid, (long long) found_gid,
    1684             :                        (long long) refuid, (long long) refgid);
    1685             :         }
    1686           0 :         memccpy(last_asked_name, name, '\0', sizeof(last_asked_name));
    1687             :     }
    1688             : 
    1689           0 : bail:
    1690           0 :     if (c != NULL) {
    1691           0 :         qb_ipcc_disconnect(c);
    1692             :     }
    1693           0 :     return rc;
    1694             : }

Generated by: LCOV version 1.14