root/lib/common/ipc_client.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk_new_ipc_api
  2. free_daemon_specific_data
  3. pcmk__call_ipc_callback
  4. ipc_post_disconnect
  5. pcmk_free_ipc_api
  6. pcmk_ipc_name
  7. pcmk_ipc_is_connected
  8. call_api_dispatch
  9. dispatch_ipc_data
  10. pcmk_poll_ipc
  11. pcmk_dispatch_ipc
  12. connect_with_main_loop
  13. connect_without_main_loop
  14. pcmk_connect_ipc
  15. pcmk_disconnect_ipc
  16. pcmk_register_ipc_callback
  17. pcmk__send_ipc_request
  18. create_purge_node_request
  19. pcmk_ipc_purge_node
  20. crm_ipc_new
  21. crm_ipc_connect
  22. crm_ipc_close
  23. crm_ipc_destroy
  24. crm_ipc_get_fd
  25. crm_ipc_connected
  26. crm_ipc_ready
  27. crm_ipc_decompress
  28. crm_ipc_read
  29. crm_ipc_buffer
  30. crm_ipc_buffer_flags
  31. crm_ipc_name
  32. internal_ipc_get_reply
  33. crm_ipc_send
  34. pcmk__crm_ipc_is_authentic_process
  35. crm_ipc_is_authentic_process
  36. pcmk__ipc_is_authentic_process_active

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

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