root/lib/lrmd/lrmd_client.c

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

DEFINITIONS

This source file includes following definitions.
  1. lrmd_list_add
  2. lrmd_list_freeall
  3. lrmd_key_value_add
  4. lrmd_key_value_freeall
  5. lrmd_new_event
  6. lrmd_copy_event
  7. lrmd_free_event
  8. lrmd_dispatch_internal
  9. lrmd_ipc_dispatch
  10. lrmd_free_xml
  11. remote_executor_connected
  12. handle_remote_msg
  13. process_pending_notifies
  14. lrmd_tls_dispatch
  15. lrmd_poll
  16. lrmd_dispatch
  17. lrmd_create_op
  18. lrmd_ipc_connection_destroy
  19. lrmd_tls_connection_destroy
  20. lrmd__remote_send_xml
  21. read_remote_reply
  22. send_remote_message
  23. lrmd_tls_send_recv
  24. lrmd_send_xml
  25. lrmd_send_xml_no_reply
  26. lrmd_api_is_connected
  27. lrmd_send_command
  28. lrmd_api_poke_connection
  29. lrmd__validate_remote_settings
  30. lrmd_handshake_hello_msg
  31. process_lrmd_handshake_reply
  32. lrmd_handshake
  33. lrmd_handshake_async
  34. lrmd_ipc_connect
  35. copy_gnutls_datum
  36. clear_gnutls_datum
  37. read_gnutls_key
  38. key_is_cached
  39. key_cache_expired
  40. clear_key_cache
  41. get_cached_key
  42. cache_key
  43. get_remote_key
  44. lrmd__init_remote_key
  45. report_async_connection_result
  46. tls_handshake_failed
  47. tls_handshake_succeeded
  48. tls_client_handshake
  49. add_tls_to_mainloop
  50. try_handshake_cb
  51. lrmd_tcp_connect_cb
  52. lrmd_tls_connect_async
  53. lrmd_tls_connect
  54. lrmd_api_connect
  55. lrmd_api_connect_async
  56. lrmd_ipc_disconnect
  57. lrmd_tls_disconnect
  58. lrmd_api_disconnect
  59. lrmd_api_register_rsc
  60. lrmd_api_unregister_rsc
  61. lrmd_new_rsc_info
  62. lrmd_copy_rsc_info
  63. lrmd_free_rsc_info
  64. lrmd_api_get_rsc_info
  65. lrmd_free_op_info
  66. lrmd_api_get_recurring_ops
  67. lrmd_api_set_callback
  68. lrmd_internal_set_proxy_callback
  69. lrmd_internal_proxy_dispatch
  70. lrmd_internal_proxy_send
  71. stonith_get_metadata
  72. lrmd_api_get_metadata
  73. lrmd_api_get_metadata_params
  74. lrmd_api_exec
  75. lrmd_api_exec_alert
  76. lrmd_api_cancel
  77. list_stonith_agents
  78. lrmd_api_list_agents
  79. does_provider_have_agent
  80. lrmd_api_list_ocf_providers
  81. lrmd_api_list_standards
  82. lrmd__new
  83. lrmd_api_new
  84. lrmd_remote_api_new
  85. lrmd_api_delete
  86. metadata_complete
  87. lrmd__metadata_async
  88. lrmd__set_result
  89. lrmd__reset_result
  90. lrmd__uptime
  91. lrmd__node_start_state

   1 /*
   2  * Copyright 2012-2025 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 #include <unistd.h>
  13 #include <stdlib.h>
  14 #include <stdio.h>
  15 #include <stdint.h>         // uint32_t, uint64_t
  16 #include <stdarg.h>
  17 #include <string.h>
  18 #include <ctype.h>
  19 #include <errno.h>
  20 
  21 #include <sys/types.h>
  22 #include <sys/wait.h>
  23 
  24 #include <glib.h>
  25 #include <dirent.h>
  26 
  27 #include <crm/crm.h>
  28 #include <crm/lrmd.h>
  29 #include <crm/lrmd_internal.h>
  30 #include <crm/services.h>
  31 #include <crm/services_internal.h>
  32 #include <crm/common/mainloop.h>
  33 #include <crm/common/ipc_internal.h>
  34 #include <crm/common/remote_internal.h>
  35 #include <crm/common/tls_internal.h>
  36 #include <crm/common/xml.h>
  37 
  38 #include <crm/stonith-ng.h>
  39 #include <crm/fencing/internal.h>   // stonith__*
  40 
  41 #include <gnutls/gnutls.h>
  42 #include <sys/socket.h>
  43 #include <netinet/in.h>
  44 #include <netinet/ip.h>
  45 #include <arpa/inet.h>
  46 #include <netdb.h>
  47 
  48 #define MAX_TLS_RECV_WAIT 10000
  49 
  50 CRM_TRACE_INIT_DATA(lrmd);
  51 
  52 static int lrmd_api_disconnect(lrmd_t * lrmd);
  53 static int lrmd_api_is_connected(lrmd_t * lrmd);
  54 
  55 /* IPC proxy functions */
  56 int lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg);
  57 static void lrmd_internal_proxy_dispatch(lrmd_t *lrmd, xmlNode *msg);
  58 void lrmd_internal_set_proxy_callback(lrmd_t * lrmd, void *userdata, void (*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg));
  59 
  60 // GnuTLS client handshake timeout in seconds
  61 #define TLS_HANDSHAKE_TIMEOUT 5
  62 
  63 static void lrmd_tls_disconnect(lrmd_t * lrmd);
  64 static int global_remote_msg_id = 0;
  65 static void lrmd_tls_connection_destroy(gpointer userdata);
  66 static int add_tls_to_mainloop(lrmd_t *lrmd, bool do_api_handshake);
  67 
  68 typedef struct lrmd_private_s {
  69     uint64_t type;
  70     char *token;
  71     mainloop_io_t *source;
  72 
  73     /* IPC parameters */
  74     crm_ipc_t *ipc;
  75 
  76     pcmk__remote_t *remote;
  77 
  78     /* Extra TLS parameters */
  79     char *remote_nodename;
  80     char *server;
  81     int port;
  82     pcmk__tls_t *tls;
  83 
  84     /* while the async connection is occurring, this is the id
  85      * of the connection timeout timer. */
  86     int async_timer;
  87     int sock;
  88     /* since tls requires a round trip across the network for a
  89      * request/reply, there are times where we just want to be able
  90      * to send a request from the client and not wait around (or even care
  91      * about) what the reply is. */
  92     int expected_late_replies;
  93     GList *pending_notify;
  94     crm_trigger_t *process_notify;
  95     crm_trigger_t *handshake_trigger;
  96 
  97     lrmd_event_callback callback;
  98 
  99     /* Internal IPC proxy msg passing for remote guests */
 100     void (*proxy_callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg);
 101     void *proxy_callback_userdata;
 102     char *peer_version;
 103 } lrmd_private_t;
 104 
 105 static int process_lrmd_handshake_reply(xmlNode *reply, lrmd_private_t *native);
 106 static void report_async_connection_result(lrmd_t * lrmd, int rc);
 107 
 108 static lrmd_list_t *
 109 lrmd_list_add(lrmd_list_t * head, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 110 {
 111     lrmd_list_t *p, *end;
 112 
 113     p = pcmk__assert_alloc(1, sizeof(lrmd_list_t));
 114     p->val = strdup(value);
 115 
 116     end = head;
 117     while (end && end->next) {
 118         end = end->next;
 119     }
 120 
 121     if (end) {
 122         end->next = p;
 123     } else {
 124         head = p;
 125     }
 126 
 127     return head;
 128 }
 129 
 130 void
 131 lrmd_list_freeall(lrmd_list_t * head)
     /* [previous][next][first][last][top][bottom][index][help] */
 132 {
 133     lrmd_list_t *p;
 134 
 135     while (head) {
 136         char *val = (char *)head->val;
 137 
 138         p = head->next;
 139         free(val);
 140         free(head);
 141         head = p;
 142     }
 143 }
 144 
 145 lrmd_key_value_t *
 146 lrmd_key_value_add(lrmd_key_value_t * head, const char *key, const char *value)
     /* [previous][next][first][last][top][bottom][index][help] */
 147 {
 148     lrmd_key_value_t *p, *end;
 149 
 150     p = pcmk__assert_alloc(1, sizeof(lrmd_key_value_t));
 151     p->key = strdup(key);
 152     p->value = strdup(value);
 153 
 154     end = head;
 155     while (end && end->next) {
 156         end = end->next;
 157     }
 158 
 159     if (end) {
 160         end->next = p;
 161     } else {
 162         head = p;
 163     }
 164 
 165     return head;
 166 }
 167 
 168 void
 169 lrmd_key_value_freeall(lrmd_key_value_t * head)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171     lrmd_key_value_t *p;
 172 
 173     while (head) {
 174         p = head->next;
 175         free(head->key);
 176         free(head->value);
 177         free(head);
 178         head = p;
 179     }
 180 }
 181 
 182 /*!
 183  * \brief Create a new lrmd_event_data_t object
 184  *
 185  * \param[in] rsc_id       ID of resource involved in event
 186  * \param[in] task         Action name
 187  * \param[in] interval_ms  Action interval
 188  *
 189  * \return Newly allocated and initialized lrmd_event_data_t
 190  * \note This functions asserts on memory errors, so the return value is
 191  *       guaranteed to be non-NULL. The caller is responsible for freeing the
 192  *       result with lrmd_free_event().
 193  */
 194 lrmd_event_data_t *
 195 lrmd_new_event(const char *rsc_id, const char *task, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
 196 {
 197     lrmd_event_data_t *event = pcmk__assert_alloc(1, sizeof(lrmd_event_data_t));
 198 
 199     // lrmd_event_data_t has (const char *) members that lrmd_free_event() frees
 200     event->rsc_id = pcmk__str_copy(rsc_id);
 201     event->op_type = pcmk__str_copy(task);
 202     event->interval_ms = interval_ms;
 203     return event;
 204 }
 205 
 206 lrmd_event_data_t *
 207 lrmd_copy_event(lrmd_event_data_t * event)
     /* [previous][next][first][last][top][bottom][index][help] */
 208 {
 209     lrmd_event_data_t *copy = NULL;
 210 
 211     copy = pcmk__assert_alloc(1, sizeof(lrmd_event_data_t));
 212 
 213     copy->type = event->type;
 214 
 215     // lrmd_event_data_t has (const char *) members that lrmd_free_event() frees
 216     copy->rsc_id = pcmk__str_copy(event->rsc_id);
 217     copy->op_type = pcmk__str_copy(event->op_type);
 218     copy->user_data = pcmk__str_copy(event->user_data);
 219     copy->output = pcmk__str_copy(event->output);
 220     copy->remote_nodename = pcmk__str_copy(event->remote_nodename);
 221     copy->exit_reason = pcmk__str_copy(event->exit_reason);
 222 
 223     copy->call_id = event->call_id;
 224     copy->timeout = event->timeout;
 225     copy->interval_ms = event->interval_ms;
 226     copy->start_delay = event->start_delay;
 227     copy->rsc_deleted = event->rsc_deleted;
 228     copy->rc = event->rc;
 229     copy->op_status = event->op_status;
 230     copy->t_run = event->t_run;
 231     copy->t_rcchange = event->t_rcchange;
 232     copy->exec_time = event->exec_time;
 233     copy->queue_time = event->queue_time;
 234     copy->connection_rc = event->connection_rc;
 235     copy->params = pcmk__str_table_dup(event->params);
 236 
 237     return copy;
 238 }
 239 
 240 /*!
 241  * \brief Free an executor event
 242  *
 243  * \param[in,out]  Executor event object to free
 244  */
 245 void
 246 lrmd_free_event(lrmd_event_data_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
 247 {
 248     if (event == NULL) {
 249         return;
 250     }
 251     // @TODO Why are these const char *?
 252     free((void *) event->rsc_id);
 253     free((void *) event->op_type);
 254     free((void *) event->user_data);
 255     free((void *) event->remote_nodename);
 256     lrmd__reset_result(event);
 257     if (event->params != NULL) {
 258         g_hash_table_destroy(event->params);
 259     }
 260     free(event);
 261 }
 262 
 263 static void
 264 lrmd_dispatch_internal(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 265 {
 266     xmlNode *msg = data;
 267     lrmd_t *lrmd = user_data;
 268 
 269     const char *type;
 270     const char *proxy_session = crm_element_value(msg,
 271                                                   PCMK__XA_LRMD_IPC_SESSION);
 272     lrmd_private_t *native = lrmd->lrmd_private;
 273     lrmd_event_data_t event = { 0, };
 274 
 275     if (proxy_session != NULL) {
 276         /* this is proxy business */
 277         lrmd_internal_proxy_dispatch(lrmd, msg);
 278         return;
 279     } else if (!native->callback) {
 280         /* no callback set */
 281         crm_trace("notify event received but client has not set callback");
 282         return;
 283     }
 284 
 285     event.remote_nodename = native->remote_nodename;
 286     type = crm_element_value(msg, PCMK__XA_LRMD_OP);
 287     crm_element_value_int(msg, PCMK__XA_LRMD_CALLID, &event.call_id);
 288     event.rsc_id = crm_element_value(msg, PCMK__XA_LRMD_RSC_ID);
 289 
 290     if (pcmk__str_eq(type, LRMD_OP_RSC_REG, pcmk__str_none)) {
 291         event.type = lrmd_event_register;
 292     } else if (pcmk__str_eq(type, LRMD_OP_RSC_UNREG, pcmk__str_none)) {
 293         event.type = lrmd_event_unregister;
 294     } else if (pcmk__str_eq(type, LRMD_OP_RSC_EXEC, pcmk__str_none)) {
 295         int rc = 0;
 296         int exec_time = 0;
 297         int queue_time = 0;
 298         time_t epoch = 0;
 299 
 300         crm_element_value_int(msg, PCMK__XA_LRMD_TIMEOUT, &event.timeout);
 301         crm_element_value_ms(msg, PCMK__XA_LRMD_RSC_INTERVAL,
 302                              &event.interval_ms);
 303         crm_element_value_int(msg, PCMK__XA_LRMD_RSC_START_DELAY,
 304                               &event.start_delay);
 305 
 306         crm_element_value_int(msg, PCMK__XA_LRMD_EXEC_RC, &rc);
 307         event.rc = (enum ocf_exitcode) rc;
 308 
 309         crm_element_value_int(msg, PCMK__XA_LRMD_EXEC_OP_STATUS,
 310                               &event.op_status);
 311         crm_element_value_int(msg, PCMK__XA_LRMD_RSC_DELETED,
 312                               &event.rsc_deleted);
 313 
 314         crm_element_value_epoch(msg, PCMK__XA_LRMD_RUN_TIME, &epoch);
 315         event.t_run = epoch;
 316 
 317         crm_element_value_epoch(msg, PCMK__XA_LRMD_RCCHANGE_TIME, &epoch);
 318         event.t_rcchange = epoch;
 319 
 320         crm_element_value_int(msg, PCMK__XA_LRMD_EXEC_TIME, &exec_time);
 321         CRM_LOG_ASSERT(exec_time >= 0);
 322         event.exec_time = QB_MAX(0, exec_time);
 323 
 324         crm_element_value_int(msg, PCMK__XA_LRMD_QUEUE_TIME, &queue_time);
 325         CRM_LOG_ASSERT(queue_time >= 0);
 326         event.queue_time = QB_MAX(0, queue_time);
 327 
 328         event.op_type = crm_element_value(msg, PCMK__XA_LRMD_RSC_ACTION);
 329         event.user_data = crm_element_value(msg,
 330                                             PCMK__XA_LRMD_RSC_USERDATA_STR);
 331         event.type = lrmd_event_exec_complete;
 332 
 333         /* output and exit_reason may be freed by a callback */
 334         event.output = crm_element_value_copy(msg, PCMK__XA_LRMD_RSC_OUTPUT);
 335         lrmd__set_result(&event, event.rc, event.op_status,
 336                          crm_element_value(msg, PCMK__XA_LRMD_RSC_EXIT_REASON));
 337 
 338         event.params = xml2list(msg);
 339     } else if (pcmk__str_eq(type, LRMD_OP_NEW_CLIENT, pcmk__str_none)) {
 340         event.type = lrmd_event_new_client;
 341     } else if (pcmk__str_eq(type, LRMD_OP_POKE, pcmk__str_none)) {
 342         event.type = lrmd_event_poke;
 343     } else {
 344         return;
 345     }
 346 
 347     crm_trace("op %s notify event received", type);
 348     native->callback(&event);
 349 
 350     if (event.params) {
 351         g_hash_table_destroy(event.params);
 352     }
 353     lrmd__reset_result(&event);
 354 }
 355 
 356 // \return Always 0, to indicate that IPC mainloop source should be kept
 357 static int
 358 lrmd_ipc_dispatch(const char *buffer, ssize_t length, gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 359 {
 360     lrmd_t *lrmd = userdata;
 361     lrmd_private_t *native = lrmd->lrmd_private;
 362 
 363     if (native->callback != NULL) {
 364         xmlNode *msg = pcmk__xml_parse(buffer);
 365 
 366         lrmd_dispatch_internal(msg, lrmd);
 367         pcmk__xml_free(msg);
 368     }
 369     return 0;
 370 }
 371 
 372 static void
 373 lrmd_free_xml(gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 374 {
 375     pcmk__xml_free((xmlNode *) userdata);
 376 }
 377 
 378 static bool
 379 remote_executor_connected(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 380 {
 381     lrmd_private_t *native = lrmd->lrmd_private;
 382 
 383     return (native->remote->tls_session != NULL);
 384 }
 385 
 386 static void
 387 handle_remote_msg(xmlNode *xml, lrmd_t *lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 388 {
 389     lrmd_private_t *native = lrmd->lrmd_private;
 390     const char *msg_type = NULL;
 391 
 392     msg_type = crm_element_value(xml, PCMK__XA_LRMD_REMOTE_MSG_TYPE);
 393     if (pcmk__str_eq(msg_type, "notify", pcmk__str_casei)) {
 394         lrmd_dispatch_internal(xml, lrmd);
 395     } else if (pcmk__str_eq(msg_type, "reply", pcmk__str_casei)) {
 396         const char *op = crm_element_value(xml, PCMK__XA_LRMD_OP);
 397 
 398         if (native->expected_late_replies > 0) {
 399             native->expected_late_replies--;
 400 
 401             /* The register op message we get as a response to lrmd_handshake_async
 402              * is a reply, so we have to handle that here.
 403              */
 404             if (pcmk__str_eq(op, "register", pcmk__str_casei)) {
 405                 int rc = process_lrmd_handshake_reply(xml, native);
 406                 report_async_connection_result(lrmd, pcmk_rc2legacy(rc));
 407             }
 408         } else {
 409             int reply_id = 0;
 410             crm_element_value_int(xml, PCMK__XA_LRMD_CALLID, &reply_id);
 411             /* if this happens, we want to know about it */
 412             crm_err("Got outdated Pacemaker Remote reply %d", reply_id);
 413         }
 414     }
 415 }
 416 
 417 /*!
 418  * \internal
 419  * \brief Notify trigger handler
 420  *
 421  * \param[in,out] userdata API connection
 422  *
 423  * \return Always return G_SOURCE_CONTINUE to leave this trigger handler in the
 424  *         mainloop
 425  */
 426 static int
 427 process_pending_notifies(gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 428 {
 429     lrmd_t *lrmd = userdata;
 430     lrmd_private_t *native = lrmd->lrmd_private;
 431 
 432     if (native->pending_notify == NULL) {
 433         return G_SOURCE_CONTINUE;
 434     }
 435 
 436     crm_trace("Processing pending notifies");
 437     g_list_foreach(native->pending_notify, lrmd_dispatch_internal, lrmd);
 438     g_list_free_full(native->pending_notify, lrmd_free_xml);
 439     native->pending_notify = NULL;
 440     return G_SOURCE_CONTINUE;
 441 }
 442 
 443 /*!
 444  * \internal
 445  * \brief TLS dispatch function for file descriptor sources
 446  *
 447  * \param[in,out] userdata  API connection
 448  *
 449  * \return -1 on error to remove the source from the mainloop, or 0 otherwise
 450  *         to leave it in the mainloop
 451  */
 452 static int
 453 lrmd_tls_dispatch(gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 454 {
 455     lrmd_t *lrmd = userdata;
 456     lrmd_private_t *native = lrmd->lrmd_private;
 457     xmlNode *xml = NULL;
 458     int rc = pcmk_rc_ok;
 459 
 460     if (!remote_executor_connected(lrmd)) {
 461         crm_trace("TLS dispatch triggered after disconnect");
 462         return -1;
 463     }
 464 
 465     crm_trace("TLS dispatch triggered");
 466 
 467     rc = pcmk__remote_ready(native->remote, 0);
 468     if (rc == pcmk_rc_ok) {
 469         rc = pcmk__read_remote_message(native->remote, -1);
 470     }
 471 
 472     if (rc != pcmk_rc_ok && rc != ETIME) {
 473         crm_info("Lost %s executor connection while reading data",
 474                  (native->remote_nodename? native->remote_nodename : "local"));
 475         lrmd_tls_disconnect(lrmd);
 476         return -1;
 477     }
 478 
 479     /* If rc is ETIME, there was nothing to read but we may already have a
 480      * full message in the buffer
 481      */
 482     xml = pcmk__remote_message_xml(native->remote);
 483 
 484     if (xml == NULL) {
 485         return 0;
 486     }
 487 
 488     handle_remote_msg(xml, lrmd);
 489     pcmk__xml_free(xml);
 490     return 0;
 491 }
 492 
 493 /* Not used with mainloop */
 494 int
 495 lrmd_poll(lrmd_t * lrmd, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
 496 {
 497     lrmd_private_t *native = lrmd->lrmd_private;
 498 
 499     switch (native->type) {
 500         case pcmk__client_ipc:
 501             return crm_ipc_ready(native->ipc);
 502 
 503         case pcmk__client_tls:
 504             if (native->pending_notify) {
 505                 return 1;
 506             } else {
 507                 int rc = pcmk__remote_ready(native->remote, 0);
 508 
 509                 switch (rc) {
 510                     case pcmk_rc_ok:
 511                         return 1;
 512                     case ETIME:
 513                         return 0;
 514                     default:
 515                         return pcmk_rc2legacy(rc);
 516                 }
 517             }
 518         default:
 519             crm_err("Unsupported executor connection type (bug?): %d",
 520                     native->type);
 521             return -EPROTONOSUPPORT;
 522     }
 523 }
 524 
 525 /* Not used with mainloop */
 526 bool
 527 lrmd_dispatch(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 528 {
 529     lrmd_private_t *private = NULL;
 530 
 531     pcmk__assert(lrmd != NULL);
 532 
 533     private = lrmd->lrmd_private;
 534     switch (private->type) {
 535         case pcmk__client_ipc:
 536             while (crm_ipc_ready(private->ipc)) {
 537                 if (crm_ipc_read(private->ipc) > 0) {
 538                     const char *msg = crm_ipc_buffer(private->ipc);
 539 
 540                     lrmd_ipc_dispatch(msg, strlen(msg), lrmd);
 541                     pcmk__ipc_free_client_buffer(private->ipc);
 542                 }
 543             }
 544             break;
 545         case pcmk__client_tls:
 546             lrmd_tls_dispatch(lrmd);
 547             break;
 548         default:
 549             crm_err("Unsupported executor connection type (bug?): %d",
 550                     private->type);
 551     }
 552 
 553     if (lrmd_api_is_connected(lrmd) == FALSE) {
 554         crm_err("Connection closed");
 555         return FALSE;
 556     }
 557 
 558     return TRUE;
 559 }
 560 
 561 static xmlNode *
 562 lrmd_create_op(const char *token, const char *op, xmlNode *data, int timeout,
     /* [previous][next][first][last][top][bottom][index][help] */
 563                enum lrmd_call_options options)
 564 {
 565     xmlNode *op_msg = NULL;
 566 
 567     CRM_CHECK(token != NULL, return NULL);
 568 
 569     op_msg = pcmk__xe_create(NULL, PCMK__XE_LRMD_COMMAND);
 570     crm_xml_add(op_msg, PCMK__XA_T, PCMK__VALUE_LRMD);
 571     crm_xml_add(op_msg, PCMK__XA_LRMD_OP, op);
 572     crm_xml_add_int(op_msg, PCMK__XA_LRMD_TIMEOUT, timeout);
 573     crm_xml_add_int(op_msg, PCMK__XA_LRMD_CALLOPT, options);
 574 
 575     if (data != NULL) {
 576         xmlNode *wrapper = pcmk__xe_create(op_msg, PCMK__XE_LRMD_CALLDATA);
 577 
 578         pcmk__xml_copy(wrapper, data);
 579     }
 580 
 581     crm_trace("Created executor %s command with call options %.8lx (%d)",
 582               op, (long)options, options);
 583     return op_msg;
 584 }
 585 
 586 static void
 587 lrmd_ipc_connection_destroy(gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 588 {
 589     lrmd_t *lrmd = userdata;
 590     lrmd_private_t *native = lrmd->lrmd_private;
 591 
 592     switch (native->type) {
 593         case pcmk__client_ipc:
 594             crm_info("Disconnected from local executor");
 595             break;
 596         case pcmk__client_tls:
 597             crm_info("Disconnected from remote executor on %s",
 598                      native->remote_nodename);
 599             break;
 600         default:
 601             crm_err("Unsupported executor connection type %d (bug?)",
 602                     native->type);
 603     }
 604 
 605     /* Prevent these from being cleaned up in lrmd_api_disconnect() */
 606     native->ipc = NULL;
 607     native->source = NULL;
 608 
 609     if (native->callback) {
 610         lrmd_event_data_t event = { 0, };
 611         event.type = lrmd_event_disconnect;
 612         event.remote_nodename = native->remote_nodename;
 613         native->callback(&event);
 614     }
 615 }
 616 
 617 static void
 618 lrmd_tls_connection_destroy(gpointer userdata)
     /* [previous][next][first][last][top][bottom][index][help] */
 619 {
 620     lrmd_t *lrmd = userdata;
 621     lrmd_private_t *native = lrmd->lrmd_private;
 622 
 623     crm_info("TLS connection destroyed");
 624 
 625     if (native->remote->tls_session) {
 626         gnutls_bye(native->remote->tls_session, GNUTLS_SHUT_RDWR);
 627         gnutls_deinit(native->remote->tls_session);
 628         native->remote->tls_session = NULL;
 629     }
 630     if (native->tls) {
 631         pcmk__free_tls(native->tls);
 632         native->tls = NULL;
 633     }
 634     if (native->sock >= 0) {
 635         close(native->sock);
 636     }
 637     if (native->process_notify) {
 638         mainloop_destroy_trigger(native->process_notify);
 639         native->process_notify = NULL;
 640     }
 641     if (native->pending_notify) {
 642         g_list_free_full(native->pending_notify, lrmd_free_xml);
 643         native->pending_notify = NULL;
 644     }
 645     if (native->handshake_trigger != NULL) {
 646         mainloop_destroy_trigger(native->handshake_trigger);
 647         native->handshake_trigger = NULL;
 648     }
 649 
 650     free(native->remote->buffer);
 651     free(native->remote->start_state);
 652     native->remote->buffer = NULL;
 653     native->remote->start_state = NULL;
 654     native->source = 0;
 655     native->sock = -1;
 656 
 657     if (native->callback) {
 658         lrmd_event_data_t event = { 0, };
 659         event.remote_nodename = native->remote_nodename;
 660         event.type = lrmd_event_disconnect;
 661         native->callback(&event);
 662     }
 663     return;
 664 }
 665 
 666 // \return Standard Pacemaker return code
 667 int
 668 lrmd__remote_send_xml(pcmk__remote_t *session, xmlNode *msg, uint32_t id,
     /* [previous][next][first][last][top][bottom][index][help] */
 669                       const char *msg_type)
 670 {
 671     crm_xml_add_int(msg, PCMK__XA_LRMD_REMOTE_MSG_ID, id);
 672     crm_xml_add(msg, PCMK__XA_LRMD_REMOTE_MSG_TYPE, msg_type);
 673     return pcmk__remote_send_xml(session, msg);
 674 }
 675 
 676 // \return Standard Pacemaker return code
 677 static int
 678 read_remote_reply(lrmd_t *lrmd, int total_timeout, int expected_reply_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 679                   xmlNode **reply)
 680 {
 681     lrmd_private_t *native = lrmd->lrmd_private;
 682     time_t start = time(NULL);
 683     const char *msg_type = NULL;
 684     int reply_id = 0;
 685     int remaining_timeout = 0;
 686     int rc = pcmk_rc_ok;
 687 
 688     /* A timeout of 0 here makes no sense.  We have to wait a period of time
 689      * for the response to come back.  If -1 or 0, default to 10 seconds. */
 690     if (total_timeout <= 0 || total_timeout > MAX_TLS_RECV_WAIT) {
 691         total_timeout = MAX_TLS_RECV_WAIT;
 692     }
 693 
 694     for (*reply = NULL; *reply == NULL; ) {
 695 
 696         *reply = pcmk__remote_message_xml(native->remote);
 697         if (*reply == NULL) {
 698             /* read some more off the tls buffer if we still have time left. */
 699             if (remaining_timeout) {
 700                 remaining_timeout = total_timeout - ((time(NULL) - start) * 1000);
 701             } else {
 702                 remaining_timeout = total_timeout;
 703             }
 704             if (remaining_timeout <= 0) {
 705                 return ETIME;
 706             }
 707 
 708             rc = pcmk__read_remote_message(native->remote, remaining_timeout);
 709             if (rc != pcmk_rc_ok) {
 710                 return rc;
 711             }
 712 
 713             *reply = pcmk__remote_message_xml(native->remote);
 714             if (*reply == NULL) {
 715                 return ENOMSG;
 716             }
 717         }
 718 
 719         crm_element_value_int(*reply, PCMK__XA_LRMD_REMOTE_MSG_ID, &reply_id);
 720         msg_type = crm_element_value(*reply, PCMK__XA_LRMD_REMOTE_MSG_TYPE);
 721 
 722         if (!msg_type) {
 723             crm_err("Empty msg type received while waiting for reply");
 724             pcmk__xml_free(*reply);
 725             *reply = NULL;
 726         } else if (pcmk__str_eq(msg_type, "notify", pcmk__str_casei)) {
 727             /* got a notify while waiting for reply, trigger the notify to be processed later */
 728             crm_info("queueing notify");
 729             native->pending_notify = g_list_append(native->pending_notify, *reply);
 730             if (native->process_notify) {
 731                 crm_info("notify trigger set.");
 732                 mainloop_set_trigger(native->process_notify);
 733             }
 734             *reply = NULL;
 735         } else if (!pcmk__str_eq(msg_type, "reply", pcmk__str_casei)) {
 736             /* msg isn't a reply, make some noise */
 737             crm_err("Expected a reply, got %s", msg_type);
 738             pcmk__xml_free(*reply);
 739             *reply = NULL;
 740         } else if (reply_id != expected_reply_id) {
 741             if (native->expected_late_replies > 0) {
 742                 native->expected_late_replies--;
 743             } else {
 744                 crm_err("Got outdated reply, expected id %d got id %d", expected_reply_id, reply_id);
 745             }
 746             pcmk__xml_free(*reply);
 747             *reply = NULL;
 748         }
 749     }
 750 
 751     if (native->remote->buffer && native->process_notify) {
 752         mainloop_set_trigger(native->process_notify);
 753     }
 754 
 755     return rc;
 756 }
 757 
 758 // \return Standard Pacemaker return code
 759 static int
 760 send_remote_message(lrmd_t *lrmd, xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 761 {
 762     int rc = pcmk_rc_ok;
 763     lrmd_private_t *native = lrmd->lrmd_private;
 764 
 765     global_remote_msg_id++;
 766     if (global_remote_msg_id <= 0) {
 767         global_remote_msg_id = 1;
 768     }
 769 
 770     rc = lrmd__remote_send_xml(native->remote, msg, global_remote_msg_id,
 771                                "request");
 772     if (rc != pcmk_rc_ok) {
 773         crm_err("Disconnecting because TLS message could not be sent to "
 774                 "Pacemaker Remote: %s", pcmk_rc_str(rc));
 775         lrmd_tls_disconnect(lrmd);
 776     }
 777     return rc;
 778 }
 779 
 780 static int
 781 lrmd_tls_send_recv(lrmd_t * lrmd, xmlNode * msg, int timeout, xmlNode ** reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 782 {
 783     int rc = 0;
 784     xmlNode *xml = NULL;
 785 
 786     if (!remote_executor_connected(lrmd)) {
 787         return -ENOTCONN;
 788     }
 789 
 790     rc = send_remote_message(lrmd, msg);
 791     if (rc != pcmk_rc_ok) {
 792         return pcmk_rc2legacy(rc);
 793     }
 794 
 795     rc = read_remote_reply(lrmd, timeout, global_remote_msg_id, &xml);
 796     if (rc != pcmk_rc_ok) {
 797         crm_err("Disconnecting remote after request %d reply not received: %s "
 798                 QB_XS " rc=%d timeout=%dms",
 799                 global_remote_msg_id, pcmk_rc_str(rc), rc, timeout);
 800         lrmd_tls_disconnect(lrmd);
 801     }
 802 
 803     if (reply) {
 804         *reply = xml;
 805     } else {
 806         pcmk__xml_free(xml);
 807     }
 808 
 809     return pcmk_rc2legacy(rc);
 810 }
 811 
 812 static int
 813 lrmd_send_xml(lrmd_t * lrmd, xmlNode * msg, int timeout, xmlNode ** reply)
     /* [previous][next][first][last][top][bottom][index][help] */
 814 {
 815     int rc = pcmk_ok;
 816     lrmd_private_t *native = lrmd->lrmd_private;
 817 
 818     switch (native->type) {
 819         case pcmk__client_ipc:
 820             rc = crm_ipc_send(native->ipc, msg, crm_ipc_client_response, timeout, reply);
 821             break;
 822         case pcmk__client_tls:
 823             rc = lrmd_tls_send_recv(lrmd, msg, timeout, reply);
 824             break;
 825         default:
 826             crm_err("Unsupported executor connection type (bug?): %d",
 827                     native->type);
 828             rc = -EPROTONOSUPPORT;
 829     }
 830 
 831     return rc;
 832 }
 833 
 834 static int
 835 lrmd_send_xml_no_reply(lrmd_t * lrmd, xmlNode * msg)
     /* [previous][next][first][last][top][bottom][index][help] */
 836 {
 837     int rc = pcmk_ok;
 838     lrmd_private_t *native = lrmd->lrmd_private;
 839 
 840     switch (native->type) {
 841         case pcmk__client_ipc:
 842             rc = crm_ipc_send(native->ipc, msg, crm_ipc_flags_none, 0, NULL);
 843             break;
 844         case pcmk__client_tls:
 845             rc = send_remote_message(lrmd, msg);
 846             if (rc == pcmk_rc_ok) {
 847                 /* we don't want to wait around for the reply, but
 848                  * since the request/reply protocol needs to behave the same
 849                  * as libqb, a reply will eventually come later anyway. */
 850                 native->expected_late_replies++;
 851             }
 852             rc = pcmk_rc2legacy(rc);
 853             break;
 854         default:
 855             crm_err("Unsupported executor connection type (bug?): %d",
 856                     native->type);
 857             rc = -EPROTONOSUPPORT;
 858     }
 859 
 860     return rc;
 861 }
 862 
 863 static int
 864 lrmd_api_is_connected(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 865 {
 866     lrmd_private_t *native = lrmd->lrmd_private;
 867 
 868     switch (native->type) {
 869         case pcmk__client_ipc:
 870             return crm_ipc_connected(native->ipc);
 871         case pcmk__client_tls:
 872             return remote_executor_connected(lrmd);
 873         default:
 874             crm_err("Unsupported executor connection type (bug?): %d",
 875                     native->type);
 876             return 0;
 877     }
 878 }
 879 
 880 /*!
 881  * \internal
 882  * \brief Send a prepared API command to the executor
 883  *
 884  * \param[in,out] lrmd          Existing connection to the executor
 885  * \param[in]     op            Name of API command to send
 886  * \param[in]     data          Command data XML to add to the sent command
 887  * \param[out]    output_data   If expecting a reply, it will be stored here
 888  * \param[in]     timeout       Timeout in milliseconds (if 0, defaults to
 889  *                              a sensible value per the type of connection,
 890  *                              standard vs. pacemaker remote);
 891  *                              also propagated to the command XML
 892  * \param[in]     call_options  Call options to pass to server when sending
 893  * \param[in]     expect_reply  If true, wait for a reply from the server;
 894  *                              must be true for IPC (as opposed to TLS) clients
 895  *
 896  * \return pcmk_ok on success, -errno on error
 897  */
 898 static int
 899 lrmd_send_command(lrmd_t *lrmd, const char *op, xmlNode *data,
     /* [previous][next][first][last][top][bottom][index][help] */
 900                   xmlNode **output_data, int timeout,
 901                   enum lrmd_call_options options, bool expect_reply)
 902 {
 903     int rc = pcmk_ok;
 904     lrmd_private_t *native = lrmd->lrmd_private;
 905     xmlNode *op_msg = NULL;
 906     xmlNode *op_reply = NULL;
 907 
 908     if (!lrmd_api_is_connected(lrmd)) {
 909         return -ENOTCONN;
 910     }
 911 
 912     if (op == NULL) {
 913         crm_err("No operation specified");
 914         return -EINVAL;
 915     }
 916 
 917     CRM_LOG_ASSERT(native->token != NULL);
 918     crm_trace("Sending %s op to executor", op);
 919 
 920     op_msg = lrmd_create_op(native->token, op, data, timeout, options);
 921 
 922     if (op_msg == NULL) {
 923         return -EINVAL;
 924     }
 925 
 926     if (expect_reply) {
 927         rc = lrmd_send_xml(lrmd, op_msg, timeout, &op_reply);
 928     } else {
 929         rc = lrmd_send_xml_no_reply(lrmd, op_msg);
 930         goto done;
 931     }
 932 
 933     if (rc < 0) {
 934         crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%d): %d", op, timeout, rc);
 935         goto done;
 936 
 937     } else if (op_reply == NULL) {
 938         rc = -ENOMSG;
 939         goto done;
 940     }
 941 
 942     rc = pcmk_ok;
 943     crm_trace("%s op reply received", op);
 944     if (crm_element_value_int(op_reply, PCMK__XA_LRMD_RC, &rc) != 0) {
 945         rc = -ENOMSG;
 946         goto done;
 947     }
 948 
 949     crm_log_xml_trace(op_reply, "Reply");
 950 
 951     if (output_data) {
 952         *output_data = op_reply;
 953         op_reply = NULL;        /* Prevent subsequent free */
 954     }
 955 
 956   done:
 957     if (lrmd_api_is_connected(lrmd) == FALSE) {
 958         crm_err("Executor disconnected");
 959     }
 960 
 961     pcmk__xml_free(op_msg);
 962     pcmk__xml_free(op_reply);
 963     return rc;
 964 }
 965 
 966 static int
 967 lrmd_api_poke_connection(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 968 {
 969     int rc;
 970     lrmd_private_t *native = lrmd->lrmd_private;
 971     xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC);
 972 
 973     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
 974     rc = lrmd_send_command(lrmd, LRMD_OP_POKE, data, NULL, 0, 0,
 975                            (native->type == pcmk__client_ipc));
 976     pcmk__xml_free(data);
 977 
 978     return rc < 0 ? rc : pcmk_ok;
 979 }
 980 
 981 // \return Standard Pacemaker return code
 982 int
 983 lrmd__validate_remote_settings(lrmd_t *lrmd, GHashTable *hash)
     /* [previous][next][first][last][top][bottom][index][help] */
 984 {
 985     int rc = pcmk_rc_ok;
 986     const char *value;
 987     lrmd_private_t *native = lrmd->lrmd_private;
 988     xmlNode *data = pcmk__xe_create(NULL, PCMK__XA_LRMD_OP);
 989 
 990     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
 991 
 992     value = g_hash_table_lookup(hash, PCMK_OPT_STONITH_WATCHDOG_TIMEOUT);
 993     if ((value) &&
 994         (stonith__watchdog_fencing_enabled_for_node(native->remote_nodename))) {
 995        crm_xml_add(data, PCMK__XA_LRMD_WATCHDOG, value);
 996     }
 997 
 998     rc = lrmd_send_command(lrmd, LRMD_OP_CHECK, data, NULL, 0, 0,
 999                            (native->type == pcmk__client_ipc));
1000     pcmk__xml_free(data);
1001     return (rc < 0)? pcmk_legacy2rc(rc) : pcmk_rc_ok;
1002 }
1003 
1004 static xmlNode *
1005 lrmd_handshake_hello_msg(const char *name, bool is_proxy)
     /* [previous][next][first][last][top][bottom][index][help] */
1006 {
1007     xmlNode *hello = pcmk__xe_create(NULL, PCMK__XE_LRMD_COMMAND);
1008 
1009     crm_xml_add(hello, PCMK__XA_T, PCMK__VALUE_LRMD);
1010     crm_xml_add(hello, PCMK__XA_LRMD_OP, CRM_OP_REGISTER);
1011     crm_xml_add(hello, PCMK__XA_LRMD_CLIENTNAME, name);
1012     crm_xml_add(hello, PCMK__XA_LRMD_PROTOCOL_VERSION, LRMD_PROTOCOL_VERSION);
1013 
1014     /* advertise that we are a proxy provider */
1015     if (is_proxy) {
1016         pcmk__xe_set_bool_attr(hello, PCMK__XA_LRMD_IS_IPC_PROVIDER, true);
1017     }
1018 
1019     return hello;
1020 }
1021 
1022 static int
1023 process_lrmd_handshake_reply(xmlNode *reply, lrmd_private_t *native)
     /* [previous][next][first][last][top][bottom][index][help] */
1024 {
1025     int rc = pcmk_rc_ok;
1026     const char *version = crm_element_value(reply, PCMK__XA_LRMD_PROTOCOL_VERSION);
1027     const char *msg_type = crm_element_value(reply, PCMK__XA_LRMD_OP);
1028     const char *tmp_ticket = crm_element_value(reply, PCMK__XA_LRMD_CLIENTID);
1029     const char *start_state = crm_element_value(reply, PCMK__XA_NODE_START_STATE);
1030     long long uptime = -1;
1031 
1032     crm_element_value_int(reply, PCMK__XA_LRMD_RC, &rc);
1033     rc = pcmk_legacy2rc(rc);
1034 
1035     /* The remote executor may add its uptime to the XML reply, which is useful
1036      * in handling transient attributes when the connection to the remote node
1037      * unexpectedly drops.  If no parameter is given, just default to -1.
1038      */
1039     crm_element_value_ll(reply, PCMK__XA_UPTIME, &uptime);
1040     native->remote->uptime = uptime;
1041 
1042     if (start_state) {
1043         native->remote->start_state = strdup(start_state);
1044     }
1045 
1046     if (rc == EPROTO) {
1047         crm_err("Executor protocol version mismatch between client (%s) and server (%s)",
1048                 LRMD_PROTOCOL_VERSION, version);
1049         crm_log_xml_err(reply, "Protocol Error");
1050     } else if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
1051         crm_err("Invalid registration message: %s", msg_type);
1052         crm_log_xml_err(reply, "Bad reply");
1053         rc = EPROTO;
1054     } else if (tmp_ticket == NULL) {
1055         crm_err("No registration token provided");
1056         crm_log_xml_err(reply, "Bad reply");
1057         rc = EPROTO;
1058     } else {
1059         crm_trace("Obtained registration token: %s", tmp_ticket);
1060         native->token = strdup(tmp_ticket);
1061         native->peer_version = strdup(version?version:"1.0"); /* Included since 1.1 */
1062         rc = pcmk_rc_ok;
1063     }
1064 
1065     return rc;
1066 }
1067 
1068 static int
1069 lrmd_handshake(lrmd_t * lrmd, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
1070 {
1071     int rc = pcmk_rc_ok;
1072     lrmd_private_t *native = lrmd->lrmd_private;
1073     xmlNode *reply = NULL;
1074     xmlNode *hello = lrmd_handshake_hello_msg(name, native->proxy_callback != NULL);
1075 
1076     rc = lrmd_send_xml(lrmd, hello, -1, &reply);
1077 
1078     if (rc < 0) {
1079         crm_perror(LOG_DEBUG, "Couldn't complete registration with the executor API: %d", rc);
1080         rc = ECOMM;
1081     } else if (reply == NULL) {
1082         crm_err("Did not receive registration reply");
1083         rc = EPROTO;
1084     } else {
1085         rc = process_lrmd_handshake_reply(reply, native);
1086     }
1087 
1088     pcmk__xml_free(reply);
1089     pcmk__xml_free(hello);
1090 
1091     if (rc != pcmk_rc_ok) {
1092         lrmd_api_disconnect(lrmd);
1093     }
1094 
1095     return rc;
1096 }
1097 
1098 static int
1099 lrmd_handshake_async(lrmd_t * lrmd, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
1100 {
1101     int rc = pcmk_rc_ok;
1102     lrmd_private_t *native = lrmd->lrmd_private;
1103     xmlNode *hello = lrmd_handshake_hello_msg(name, native->proxy_callback != NULL);
1104 
1105     rc = send_remote_message(lrmd, hello);
1106 
1107     if (rc == pcmk_rc_ok) {
1108         native->expected_late_replies++;
1109     } else {
1110         lrmd_api_disconnect(lrmd);
1111     }
1112 
1113     pcmk__xml_free(hello);
1114     return rc;
1115 }
1116 
1117 static int
1118 lrmd_ipc_connect(lrmd_t * lrmd, int *fd)
     /* [previous][next][first][last][top][bottom][index][help] */
1119 {
1120     int rc = pcmk_ok;
1121     lrmd_private_t *native = lrmd->lrmd_private;
1122 
1123     struct ipc_client_callbacks lrmd_callbacks = {
1124         .dispatch = lrmd_ipc_dispatch,
1125         .destroy = lrmd_ipc_connection_destroy
1126     };
1127 
1128     crm_info("Connecting to executor");
1129 
1130     if (fd) {
1131         /* No mainloop */
1132         native->ipc = crm_ipc_new(CRM_SYSTEM_LRMD, 0);
1133         if (native->ipc != NULL) {
1134             rc = pcmk__connect_generic_ipc(native->ipc);
1135             if (rc == pcmk_rc_ok) {
1136                 rc = pcmk__ipc_fd(native->ipc, fd);
1137             }
1138             if (rc != pcmk_rc_ok) {
1139                 crm_err("Connection to executor failed: %s", pcmk_rc_str(rc));
1140                 rc = -ENOTCONN;
1141             }
1142         }
1143     } else {
1144         native->source = mainloop_add_ipc_client(CRM_SYSTEM_LRMD, G_PRIORITY_HIGH, 0, lrmd, &lrmd_callbacks);
1145         native->ipc = mainloop_get_ipc_client(native->source);
1146     }
1147 
1148     if (native->ipc == NULL) {
1149         crm_debug("Could not connect to the executor API");
1150         rc = -ENOTCONN;
1151     }
1152 
1153     return rc;
1154 }
1155 
1156 static void
1157 copy_gnutls_datum(gnutls_datum_t *dest, gnutls_datum_t *source)
     /* [previous][next][first][last][top][bottom][index][help] */
1158 {
1159     pcmk__assert((dest != NULL) && (source != NULL) && (source->data != NULL));
1160 
1161     dest->data = gnutls_malloc(source->size);
1162     pcmk__mem_assert(dest->data);
1163 
1164     memcpy(dest->data, source->data, source->size);
1165     dest->size = source->size;
1166 }
1167 
1168 static void
1169 clear_gnutls_datum(gnutls_datum_t *datum)
     /* [previous][next][first][last][top][bottom][index][help] */
1170 {
1171     gnutls_free(datum->data);
1172     datum->data = NULL;
1173     datum->size = 0;
1174 }
1175 
1176 #define KEY_READ_LEN 256    // Chunk size for reading key from file
1177 
1178 // \return Standard Pacemaker return code
1179 static int
1180 read_gnutls_key(const char *location, gnutls_datum_t *key)
     /* [previous][next][first][last][top][bottom][index][help] */
1181 {
1182     FILE *stream = NULL;
1183     size_t buf_len = KEY_READ_LEN;
1184 
1185     if ((location == NULL) || (key == NULL)) {
1186         return EINVAL;
1187     }
1188 
1189     stream = fopen(location, "r");
1190     if (stream == NULL) {
1191         return errno;
1192     }
1193 
1194     key->data = gnutls_malloc(buf_len);
1195     key->size = 0;
1196     while (!feof(stream)) {
1197         int next = fgetc(stream);
1198 
1199         if (next == EOF) {
1200             if (!feof(stream)) {
1201                 crm_warn("Pacemaker Remote key read was partially successful "
1202                          "(copy in memory may be corrupted)");
1203             }
1204             break;
1205         }
1206         if (key->size == buf_len) {
1207             buf_len = key->size + KEY_READ_LEN;
1208             key->data = gnutls_realloc(key->data, buf_len);
1209             pcmk__assert(key->data);
1210         }
1211         key->data[key->size++] = (unsigned char) next;
1212     }
1213     fclose(stream);
1214 
1215     if (key->size == 0) {
1216         clear_gnutls_datum(key);
1217         return ENOKEY;
1218     }
1219     return pcmk_rc_ok;
1220 }
1221 
1222 // Cache the most recently used Pacemaker Remote authentication key
1223 
1224 struct key_cache_s {
1225     time_t updated;         // When cached key was read (valid for 1 minute)
1226     const char *location;   // Where cached key was read from
1227     gnutls_datum_t key;     // Cached key
1228 };
1229 
1230 static bool
1231 key_is_cached(struct key_cache_s *key_cache)
     /* [previous][next][first][last][top][bottom][index][help] */
1232 {
1233     return key_cache->updated != 0;
1234 }
1235 
1236 static bool
1237 key_cache_expired(struct key_cache_s *key_cache)
     /* [previous][next][first][last][top][bottom][index][help] */
1238 {
1239     return (time(NULL) - key_cache->updated) >= 60;
1240 }
1241 
1242 static void
1243 clear_key_cache(struct key_cache_s *key_cache)
     /* [previous][next][first][last][top][bottom][index][help] */
1244 {
1245     clear_gnutls_datum(&(key_cache->key));
1246     if ((key_cache->updated != 0) || (key_cache->location != NULL)) {
1247         key_cache->updated = 0;
1248         key_cache->location = NULL;
1249         crm_debug("Cleared Pacemaker Remote key cache");
1250     }
1251 }
1252 
1253 static void
1254 get_cached_key(struct key_cache_s *key_cache, gnutls_datum_t *key)
     /* [previous][next][first][last][top][bottom][index][help] */
1255 {
1256     copy_gnutls_datum(key, &(key_cache->key));
1257     crm_debug("Using cached Pacemaker Remote key from %s",
1258               pcmk__s(key_cache->location, "unknown location"));
1259 }
1260 
1261 static void
1262 cache_key(struct key_cache_s *key_cache, gnutls_datum_t *key,
     /* [previous][next][first][last][top][bottom][index][help] */
1263           const char *location)
1264 {
1265     key_cache->updated = time(NULL);
1266     key_cache->location = location;
1267     copy_gnutls_datum(&(key_cache->key), key);
1268     crm_debug("Using (and cacheing) Pacemaker Remote key from %s",
1269               pcmk__s(location, "unknown location"));
1270 }
1271 
1272 /*!
1273  * \internal
1274  * \brief Get Pacemaker Remote authentication key from file or cache
1275  *
1276  * \param[in]  location         Path to key file to try (this memory must
1277  *                              persist across all calls of this function)
1278  * \param[out] key              Key from location or cache
1279  *
1280  * \return Standard Pacemaker return code
1281  */
1282 static int
1283 get_remote_key(const char *location, gnutls_datum_t *key)
     /* [previous][next][first][last][top][bottom][index][help] */
1284 {
1285     static struct key_cache_s key_cache = { 0, };
1286     int rc = pcmk_rc_ok;
1287 
1288     if ((location == NULL) || (key == NULL)) {
1289         return EINVAL;
1290     }
1291 
1292     if (key_is_cached(&key_cache)) {
1293         if (key_cache_expired(&key_cache)) {
1294             clear_key_cache(&key_cache);
1295         } else {
1296             get_cached_key(&key_cache, key);
1297             return pcmk_rc_ok;
1298         }
1299     }
1300 
1301     rc = read_gnutls_key(location, key);
1302     if (rc != pcmk_rc_ok) {
1303         return rc;
1304     }
1305     cache_key(&key_cache, key, location);
1306     return pcmk_rc_ok;
1307 }
1308 
1309 /*!
1310  * \internal
1311  * \brief Initialize the Pacemaker Remote authentication key
1312  *
1313  * Try loading the Pacemaker Remote authentication key from cache if available,
1314  * otherwise from these locations, in order of preference:
1315  *
1316  * - The value of the PCMK_authkey_location environment variable, if set
1317  * - The Pacemaker default key file location
1318  *
1319  * \param[out] key  Where to store key
1320  *
1321  * \return Standard Pacemaker return code
1322  */
1323 int
1324 lrmd__init_remote_key(gnutls_datum_t *key)
     /* [previous][next][first][last][top][bottom][index][help] */
1325 {
1326     static const char *env_location = NULL;
1327     static bool need_env = true;
1328 
1329     int rc = pcmk_rc_ok;
1330 
1331     if (need_env) {
1332         env_location = pcmk__env_option(PCMK__ENV_AUTHKEY_LOCATION);
1333         need_env = false;
1334     }
1335 
1336     // Try location in environment variable, if set
1337     if (env_location != NULL) {
1338         rc = get_remote_key(env_location, key);
1339         if (rc == pcmk_rc_ok) {
1340             return pcmk_rc_ok;
1341         }
1342 
1343         crm_warn("Could not read Pacemaker Remote key from %s: %s",
1344                  env_location, pcmk_rc_str(rc));
1345         return ENOKEY;
1346     }
1347 
1348     // Try default location, if environment wasn't explicitly set to it
1349     rc = get_remote_key(DEFAULT_REMOTE_KEY_LOCATION, key);
1350     if (rc == pcmk_rc_ok) {
1351         return pcmk_rc_ok;
1352     }
1353 
1354     crm_warn("Could not read Pacemaker Remote key from default location %s: %s",
1355              DEFAULT_REMOTE_KEY_LOCATION, pcmk_rc_str(rc));
1356     return ENOKEY;
1357 }
1358 
1359 static void
1360 report_async_connection_result(lrmd_t * lrmd, int rc)
     /* [previous][next][first][last][top][bottom][index][help] */
1361 {
1362     lrmd_private_t *native = lrmd->lrmd_private;
1363 
1364     if (native->callback) {
1365         lrmd_event_data_t event = { 0, };
1366         event.type = lrmd_event_connect;
1367         event.remote_nodename = native->remote_nodename;
1368         event.connection_rc = rc;
1369         native->callback(&event);
1370     }
1371 }
1372 
1373 static void
1374 tls_handshake_failed(lrmd_t *lrmd, int tls_rc, int rc)
     /* [previous][next][first][last][top][bottom][index][help] */
1375 {
1376     lrmd_private_t *native = lrmd->lrmd_private;
1377 
1378     crm_warn("Disconnecting after TLS handshake with "
1379              "Pacemaker Remote server %s:%d failed: %s",
1380              native->server, native->port,
1381              (rc == EPROTO)? gnutls_strerror(tls_rc) : pcmk_rc_str(rc));
1382     report_async_connection_result(lrmd, pcmk_rc2legacy(rc));
1383 
1384     gnutls_deinit(native->remote->tls_session);
1385     native->remote->tls_session = NULL;
1386     lrmd_tls_connection_destroy(lrmd);
1387 }
1388 
1389 static void
1390 tls_handshake_succeeded(lrmd_t *lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1391 {
1392     int rc = pcmk_rc_ok;
1393     lrmd_private_t *native = lrmd->lrmd_private;
1394 
1395     /* Now that the handshake is done, see if any client TLS certificate is
1396      * close to its expiration date and log if so.  If a TLS certificate is not
1397      * in use, this function will just return so we don't need to check for the
1398      * session type here.
1399      */
1400     pcmk__tls_check_cert_expiration(native->remote->tls_session);
1401 
1402     crm_info("TLS connection to Pacemaker Remote server %s:%d succeeded",
1403              native->server, native->port);
1404     rc = add_tls_to_mainloop(lrmd, true);
1405 
1406     /* If add_tls_to_mainloop failed, report that right now.  Otherwise, we have
1407      * to wait until we read the async reply to report anything.
1408      */
1409     if (rc != pcmk_rc_ok) {
1410         report_async_connection_result(lrmd, pcmk_rc2legacy(rc));
1411     }
1412 }
1413 
1414 /*!
1415  * \internal
1416  * \brief Perform a TLS client handshake with a Pacemaker Remote server
1417  *
1418  * \param[in] lrmd  Newly established Pacemaker Remote executor connection
1419  *
1420  * \return Standard Pacemaker return code
1421  */
1422 static int
1423 tls_client_handshake(lrmd_t *lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1424 {
1425     lrmd_private_t *native = lrmd->lrmd_private;
1426     int tls_rc = GNUTLS_E_SUCCESS;
1427     int rc = pcmk__tls_client_handshake(native->remote, TLS_HANDSHAKE_TIMEOUT,
1428                                         &tls_rc);
1429 
1430     if (rc != pcmk_rc_ok) {
1431         tls_handshake_failed(lrmd, tls_rc, rc);
1432     }
1433 
1434     return rc;
1435 }
1436 
1437 /*!
1438  * \internal
1439  * \brief Add trigger and file descriptor mainloop sources for TLS
1440  *
1441  * \param[in,out] lrmd              API connection with established TLS session
1442  * \param[in]     do_api_handshake  Whether to perform executor handshake
1443  *
1444  * \return Standard Pacemaker return code
1445  */
1446 static int
1447 add_tls_to_mainloop(lrmd_t *lrmd, bool do_api_handshake)
     /* [previous][next][first][last][top][bottom][index][help] */
1448 {
1449     lrmd_private_t *native = lrmd->lrmd_private;
1450     int rc = pcmk_rc_ok;
1451 
1452     char *name = crm_strdup_printf("pacemaker-remote-%s:%d",
1453                                    native->server, native->port);
1454 
1455     struct mainloop_fd_callbacks tls_fd_callbacks = {
1456         .dispatch = lrmd_tls_dispatch,
1457         .destroy = lrmd_tls_connection_destroy,
1458     };
1459 
1460     native->process_notify = mainloop_add_trigger(G_PRIORITY_HIGH,
1461                                                   process_pending_notifies, lrmd);
1462     native->source = mainloop_add_fd(name, G_PRIORITY_HIGH, native->sock, lrmd,
1463                                      &tls_fd_callbacks);
1464 
1465     /* Async connections lose the client name provided by the API caller, so we
1466      * have to use our generated name here to perform the executor handshake.
1467      *
1468      * @TODO Keep track of the caller-provided name. Perhaps we should be using
1469      * that name in this function instead of generating one anyway.
1470      */
1471     if (do_api_handshake) {
1472         rc = lrmd_handshake_async(lrmd, name);
1473     }
1474     free(name);
1475     return rc;
1476 }
1477 
1478 struct handshake_data_s {
1479     lrmd_t *lrmd;
1480     time_t start_time;
1481     int timeout_sec;
1482 };
1483 
1484 static gboolean
1485 try_handshake_cb(gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1486 {
1487     struct handshake_data_s *hs = user_data;
1488     lrmd_t *lrmd = hs->lrmd;
1489     lrmd_private_t *native = lrmd->lrmd_private;
1490     pcmk__remote_t *remote = native->remote;
1491 
1492     int rc = pcmk_rc_ok;
1493     int tls_rc = GNUTLS_E_SUCCESS;
1494 
1495     if (time(NULL) >= hs->start_time + hs->timeout_sec) {
1496         rc = ETIME;
1497 
1498         tls_handshake_failed(lrmd, GNUTLS_E_TIMEDOUT, rc);
1499         free(hs);
1500         return 0;
1501     }
1502 
1503     rc = pcmk__tls_client_try_handshake(remote, &tls_rc);
1504 
1505     if (rc == pcmk_rc_ok) {
1506         tls_handshake_succeeded(lrmd);
1507         free(hs);
1508         return 0;
1509     } else if (rc == EAGAIN) {
1510         mainloop_set_trigger(native->handshake_trigger);
1511         return 1;
1512     } else {
1513         rc = EKEYREJECTED;
1514         tls_handshake_failed(lrmd, tls_rc, rc);
1515         free(hs);
1516         return 0;
1517     }
1518 }
1519 
1520 static void
1521 lrmd_tcp_connect_cb(void *userdata, int rc, int sock)
     /* [previous][next][first][last][top][bottom][index][help] */
1522 {
1523     lrmd_t *lrmd = userdata;
1524     lrmd_private_t *native = lrmd->lrmd_private;
1525     int tls_rc = GNUTLS_E_SUCCESS;
1526     bool use_cert = pcmk__x509_enabled();
1527 
1528     native->async_timer = 0;
1529 
1530     if (rc != pcmk_rc_ok) {
1531         lrmd_tls_connection_destroy(lrmd);
1532         crm_info("Could not connect to Pacemaker Remote at %s:%d: %s "
1533                  QB_XS " rc=%d",
1534                  native->server, native->port, pcmk_rc_str(rc), rc);
1535         report_async_connection_result(lrmd, pcmk_rc2legacy(rc));
1536         return;
1537     }
1538 
1539     /* The TCP connection was successful, so establish the TLS connection. */
1540 
1541     native->sock = sock;
1542 
1543     if (native->tls == NULL) {
1544         rc = pcmk__init_tls(&native->tls, false, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_PSK);
1545 
1546         if ((rc != pcmk_rc_ok) || (native->tls == NULL)) {
1547             lrmd_tls_connection_destroy(lrmd);
1548             report_async_connection_result(lrmd, pcmk_rc2legacy(rc));
1549             return;
1550         }
1551     }
1552 
1553     if (!use_cert) {
1554         gnutls_datum_t psk_key = { NULL, 0 };
1555 
1556         rc = lrmd__init_remote_key(&psk_key);
1557         if (rc != pcmk_rc_ok) {
1558             crm_info("Could not connect to Pacemaker Remote at %s:%d: %s "
1559                      QB_XS " rc=%d",
1560                      native->server, native->port, pcmk_rc_str(rc), rc);
1561             lrmd_tls_connection_destroy(lrmd);
1562             report_async_connection_result(lrmd, pcmk_rc2legacy(rc));
1563             return;
1564         }
1565 
1566         pcmk__tls_add_psk_key(native->tls, &psk_key);
1567         gnutls_free(psk_key.data);
1568     }
1569 
1570     native->remote->tls_session = pcmk__new_tls_session(native->tls, sock);
1571     if (native->remote->tls_session == NULL) {
1572         lrmd_tls_connection_destroy(lrmd);
1573         report_async_connection_result(lrmd, -EPROTO);
1574         return;
1575     }
1576 
1577     /* If the TLS handshake immediately succeeds or fails, we can handle that
1578      * now without having to deal with mainloops and retries.  Otherwise, add a
1579      * trigger to keep trying until we get a result (or it times out).
1580      */
1581     rc = pcmk__tls_client_try_handshake(native->remote, &tls_rc);
1582     if (rc == EAGAIN) {
1583         struct handshake_data_s *hs = NULL;
1584 
1585         if (native->handshake_trigger != NULL) {
1586             return;
1587         }
1588 
1589         hs = pcmk__assert_alloc(1, sizeof(struct handshake_data_s));
1590         hs->lrmd = lrmd;
1591         hs->start_time = time(NULL);
1592         hs->timeout_sec = TLS_HANDSHAKE_TIMEOUT;
1593 
1594         native->handshake_trigger = mainloop_add_trigger(G_PRIORITY_LOW, try_handshake_cb, hs);
1595         mainloop_set_trigger(native->handshake_trigger);
1596 
1597     } else if (rc == pcmk_rc_ok) {
1598         tls_handshake_succeeded(lrmd);
1599 
1600     } else {
1601         tls_handshake_failed(lrmd, tls_rc, rc);
1602     }
1603 }
1604 
1605 static int
1606 lrmd_tls_connect_async(lrmd_t * lrmd, int timeout /*ms */ )
     /* [previous][next][first][last][top][bottom][index][help] */
1607 {
1608     int rc = pcmk_rc_ok;
1609     int timer_id = 0;
1610     lrmd_private_t *native = lrmd->lrmd_private;
1611 
1612     native->sock = -1;
1613     rc = pcmk__connect_remote(native->server, native->port, timeout, &timer_id,
1614                               &(native->sock), lrmd, lrmd_tcp_connect_cb);
1615     if (rc != pcmk_rc_ok) {
1616         crm_warn("Pacemaker Remote connection to %s:%d failed: %s "
1617                  QB_XS " rc=%d",
1618                  native->server, native->port, pcmk_rc_str(rc), rc);
1619         return rc;
1620     }
1621     native->async_timer = timer_id;
1622     return rc;
1623 }
1624 
1625 static int
1626 lrmd_tls_connect(lrmd_t * lrmd, int *fd)
     /* [previous][next][first][last][top][bottom][index][help] */
1627 {
1628     int rc = pcmk_rc_ok;
1629     bool use_cert = pcmk__x509_enabled();
1630     lrmd_private_t *native = lrmd->lrmd_private;
1631 
1632     native->sock = -1;
1633     rc = pcmk__connect_remote(native->server, native->port, 0, NULL,
1634                               &(native->sock), NULL, NULL);
1635     if (rc != pcmk_rc_ok) {
1636         crm_warn("Pacemaker Remote connection to %s:%d failed: %s "
1637                  QB_XS " rc=%d",
1638                  native->server, native->port, pcmk_rc_str(rc), rc);
1639         lrmd_tls_connection_destroy(lrmd);
1640         return ENOTCONN;
1641     }
1642 
1643     if (native->tls == NULL) {
1644         rc = pcmk__init_tls(&native->tls, false, use_cert ? GNUTLS_CRD_CERTIFICATE : GNUTLS_CRD_PSK);
1645 
1646         if (rc != pcmk_rc_ok) {
1647             lrmd_tls_connection_destroy(lrmd);
1648             return rc;
1649         }
1650     }
1651 
1652     if (!use_cert) {
1653         gnutls_datum_t psk_key = { NULL, 0 };
1654 
1655         rc = lrmd__init_remote_key(&psk_key);
1656         if (rc != pcmk_rc_ok) {
1657             lrmd_tls_connection_destroy(lrmd);
1658             return rc;
1659         }
1660 
1661         pcmk__tls_add_psk_key(native->tls, &psk_key);
1662         gnutls_free(psk_key.data);
1663     }
1664 
1665     native->remote->tls_session = pcmk__new_tls_session(native->tls, native->sock);
1666     if (native->remote->tls_session == NULL) {
1667         lrmd_tls_connection_destroy(lrmd);
1668         return EPROTO;
1669     }
1670 
1671     if (tls_client_handshake(lrmd) != pcmk_rc_ok) {
1672         return EKEYREJECTED;
1673     }
1674 
1675     crm_info("Client TLS connection established with Pacemaker Remote server %s:%d", native->server,
1676              native->port);
1677 
1678     if (fd) {
1679         *fd = native->sock;
1680     } else {
1681         rc = add_tls_to_mainloop(lrmd, false);
1682     }
1683     return rc;
1684 }
1685 
1686 static int
1687 lrmd_api_connect(lrmd_t * lrmd, const char *name, int *fd)
     /* [previous][next][first][last][top][bottom][index][help] */
1688 {
1689     int rc = -ENOTCONN;
1690     lrmd_private_t *native = lrmd->lrmd_private;
1691 
1692     switch (native->type) {
1693         case pcmk__client_ipc:
1694             rc = lrmd_ipc_connect(lrmd, fd);
1695             break;
1696         case pcmk__client_tls:
1697             rc = lrmd_tls_connect(lrmd, fd);
1698             rc = pcmk_rc2legacy(rc);
1699             break;
1700         default:
1701             crm_err("Unsupported executor connection type (bug?): %d",
1702                     native->type);
1703             rc = -EPROTONOSUPPORT;
1704     }
1705 
1706     if (rc == pcmk_ok) {
1707         rc = lrmd_handshake(lrmd, name);
1708         rc = pcmk_rc2legacy(rc);
1709     }
1710 
1711     return rc;
1712 }
1713 
1714 static int
1715 lrmd_api_connect_async(lrmd_t * lrmd, const char *name, int timeout)
     /* [previous][next][first][last][top][bottom][index][help] */
1716 {
1717     int rc = pcmk_ok;
1718     lrmd_private_t *native = lrmd->lrmd_private;
1719 
1720     CRM_CHECK(native && native->callback, return -EINVAL);
1721 
1722     switch (native->type) {
1723         case pcmk__client_ipc:
1724             /* fake async connection with ipc.  it should be fast
1725              * enough that we gain very little from async */
1726             rc = lrmd_api_connect(lrmd, name, NULL);
1727             if (!rc) {
1728                 report_async_connection_result(lrmd, rc);
1729             }
1730             break;
1731         case pcmk__client_tls:
1732             rc = lrmd_tls_connect_async(lrmd, timeout);
1733             rc = pcmk_rc2legacy(rc);
1734             break;
1735         default:
1736             crm_err("Unsupported executor connection type (bug?): %d",
1737                     native->type);
1738             rc = -EPROTONOSUPPORT;
1739     }
1740 
1741     return rc;
1742 }
1743 
1744 static void
1745 lrmd_ipc_disconnect(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1746 {
1747     lrmd_private_t *native = lrmd->lrmd_private;
1748 
1749     if (native->source != NULL) {
1750         /* Attached to mainloop */
1751         mainloop_del_ipc_client(native->source);
1752         native->source = NULL;
1753         native->ipc = NULL;
1754 
1755     } else if (native->ipc) {
1756         /* Not attached to mainloop */
1757         crm_ipc_t *ipc = native->ipc;
1758 
1759         native->ipc = NULL;
1760         crm_ipc_close(ipc);
1761         crm_ipc_destroy(ipc);
1762     }
1763 }
1764 
1765 static void
1766 lrmd_tls_disconnect(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1767 {
1768     lrmd_private_t *native = lrmd->lrmd_private;
1769 
1770     if (native->remote->tls_session) {
1771         gnutls_bye(native->remote->tls_session, GNUTLS_SHUT_RDWR);
1772         gnutls_deinit(native->remote->tls_session);
1773         native->remote->tls_session = NULL;
1774     }
1775 
1776     if (native->async_timer) {
1777         g_source_remove(native->async_timer);
1778         native->async_timer = 0;
1779     }
1780 
1781     if (native->source != NULL) {
1782         /* Attached to mainloop */
1783         mainloop_del_ipc_client(native->source);
1784         native->source = NULL;
1785 
1786     } else if (native->sock >= 0) {
1787         close(native->sock);
1788         native->sock = -1;
1789     }
1790 
1791     if (native->pending_notify) {
1792         g_list_free_full(native->pending_notify, lrmd_free_xml);
1793         native->pending_notify = NULL;
1794     }
1795 }
1796 
1797 static int
1798 lrmd_api_disconnect(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
1799 {
1800     lrmd_private_t *native = lrmd->lrmd_private;
1801     int rc = pcmk_ok;
1802 
1803     switch (native->type) {
1804         case pcmk__client_ipc:
1805             crm_debug("Disconnecting from local executor");
1806             lrmd_ipc_disconnect(lrmd);
1807             break;
1808         case pcmk__client_tls:
1809             crm_debug("Disconnecting from remote executor on %s",
1810                       native->remote_nodename);
1811             lrmd_tls_disconnect(lrmd);
1812             break;
1813         default:
1814             crm_err("Unsupported executor connection type (bug?): %d",
1815                     native->type);
1816             rc = -EPROTONOSUPPORT;
1817     }
1818 
1819     free(native->token);
1820     native->token = NULL;
1821 
1822     free(native->peer_version);
1823     native->peer_version = NULL;
1824     return rc;
1825 }
1826 
1827 static int
1828 lrmd_api_register_rsc(lrmd_t * lrmd,
     /* [previous][next][first][last][top][bottom][index][help] */
1829                       const char *rsc_id,
1830                       const char *class,
1831                       const char *provider, const char *type, enum lrmd_call_options options)
1832 {
1833     int rc = pcmk_ok;
1834     xmlNode *data = NULL;
1835 
1836     if (!class || !type || !rsc_id) {
1837         return -EINVAL;
1838     }
1839     if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)
1840         && (provider == NULL)) {
1841         return -EINVAL;
1842     }
1843 
1844     data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC);
1845 
1846     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
1847     crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id);
1848     crm_xml_add(data, PCMK__XA_LRMD_CLASS, class);
1849     crm_xml_add(data, PCMK__XA_LRMD_PROVIDER, provider);
1850     crm_xml_add(data, PCMK__XA_LRMD_TYPE, type);
1851     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_REG, data, NULL, 0, options, true);
1852     pcmk__xml_free(data);
1853 
1854     return rc;
1855 }
1856 
1857 static int
1858 lrmd_api_unregister_rsc(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
     /* [previous][next][first][last][top][bottom][index][help] */
1859 {
1860     int rc = pcmk_ok;
1861     xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC);
1862 
1863     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
1864     crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id);
1865     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_UNREG, data, NULL, 0, options, true);
1866     pcmk__xml_free(data);
1867 
1868     return rc;
1869 }
1870 
1871 lrmd_rsc_info_t *
1872 lrmd_new_rsc_info(const char *rsc_id, const char *standard,
     /* [previous][next][first][last][top][bottom][index][help] */
1873                   const char *provider, const char *type)
1874 {
1875     lrmd_rsc_info_t *rsc_info = pcmk__assert_alloc(1, sizeof(lrmd_rsc_info_t));
1876 
1877     rsc_info->id = pcmk__str_copy(rsc_id);
1878     rsc_info->standard = pcmk__str_copy(standard);
1879     rsc_info->provider = pcmk__str_copy(provider);
1880     rsc_info->type = pcmk__str_copy(type);
1881     return rsc_info;
1882 }
1883 
1884 lrmd_rsc_info_t *
1885 lrmd_copy_rsc_info(lrmd_rsc_info_t * rsc_info)
     /* [previous][next][first][last][top][bottom][index][help] */
1886 {
1887     return lrmd_new_rsc_info(rsc_info->id, rsc_info->standard,
1888                              rsc_info->provider, rsc_info->type);
1889 }
1890 
1891 void
1892 lrmd_free_rsc_info(lrmd_rsc_info_t * rsc_info)
     /* [previous][next][first][last][top][bottom][index][help] */
1893 {
1894     if (!rsc_info) {
1895         return;
1896     }
1897     free(rsc_info->id);
1898     free(rsc_info->type);
1899     free(rsc_info->standard);
1900     free(rsc_info->provider);
1901     free(rsc_info);
1902 }
1903 
1904 static lrmd_rsc_info_t *
1905 lrmd_api_get_rsc_info(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
     /* [previous][next][first][last][top][bottom][index][help] */
1906 {
1907     lrmd_rsc_info_t *rsc_info = NULL;
1908     xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC);
1909     xmlNode *output = NULL;
1910     const char *class = NULL;
1911     const char *provider = NULL;
1912     const char *type = NULL;
1913 
1914     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
1915     crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id);
1916     lrmd_send_command(lrmd, LRMD_OP_RSC_INFO, data, &output, 0, options, true);
1917     pcmk__xml_free(data);
1918 
1919     if (!output) {
1920         return NULL;
1921     }
1922 
1923     class = crm_element_value(output, PCMK__XA_LRMD_CLASS);
1924     provider = crm_element_value(output, PCMK__XA_LRMD_PROVIDER);
1925     type = crm_element_value(output, PCMK__XA_LRMD_TYPE);
1926 
1927     if (!class || !type) {
1928         pcmk__xml_free(output);
1929         return NULL;
1930     } else if (pcmk_is_set(pcmk_get_ra_caps(class), pcmk_ra_cap_provider)
1931                && !provider) {
1932         pcmk__xml_free(output);
1933         return NULL;
1934     }
1935 
1936     rsc_info = lrmd_new_rsc_info(rsc_id, class, provider, type);
1937     pcmk__xml_free(output);
1938     return rsc_info;
1939 }
1940 
1941 void
1942 lrmd_free_op_info(lrmd_op_info_t *op_info)
     /* [previous][next][first][last][top][bottom][index][help] */
1943 {
1944     if (op_info) {
1945         free(op_info->rsc_id);
1946         free(op_info->action);
1947         free(op_info->interval_ms_s);
1948         free(op_info->timeout_ms_s);
1949         free(op_info);
1950     }
1951 }
1952 
1953 static int
1954 lrmd_api_get_recurring_ops(lrmd_t *lrmd, const char *rsc_id, int timeout_ms,
     /* [previous][next][first][last][top][bottom][index][help] */
1955                            enum lrmd_call_options options, GList **output)
1956 {
1957     xmlNode *data = NULL;
1958     xmlNode *output_xml = NULL;
1959     int rc = pcmk_ok;
1960 
1961     if (output == NULL) {
1962         return -EINVAL;
1963     }
1964     *output = NULL;
1965 
1966     // Send request
1967     if (rsc_id) {
1968         data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC);
1969         crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
1970         crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id);
1971     }
1972     rc = lrmd_send_command(lrmd, LRMD_OP_GET_RECURRING, data, &output_xml,
1973                            timeout_ms, options, true);
1974     if (data) {
1975         pcmk__xml_free(data);
1976     }
1977 
1978     // Process reply
1979     if ((rc != pcmk_ok) || (output_xml == NULL)) {
1980         return rc;
1981     }
1982     for (const xmlNode *rsc_xml = pcmk__xe_first_child(output_xml,
1983                                                        PCMK__XE_LRMD_RSC, NULL,
1984                                                        NULL);
1985          (rsc_xml != NULL) && (rc == pcmk_ok);
1986          rsc_xml = pcmk__xe_next(rsc_xml, PCMK__XE_LRMD_RSC)) {
1987 
1988         rsc_id = crm_element_value(rsc_xml, PCMK__XA_LRMD_RSC_ID);
1989         if (rsc_id == NULL) {
1990             crm_err("Could not parse recurring operation information from executor");
1991             continue;
1992         }
1993         for (const xmlNode *op_xml = pcmk__xe_first_child(rsc_xml,
1994                                                           PCMK__XE_LRMD_RSC_OP,
1995                                                           NULL, NULL);
1996              op_xml != NULL;
1997              op_xml = pcmk__xe_next(op_xml, PCMK__XE_LRMD_RSC_OP)) {
1998 
1999             lrmd_op_info_t *op_info = calloc(1, sizeof(lrmd_op_info_t));
2000 
2001             if (op_info == NULL) {
2002                 rc = -ENOMEM;
2003                 break;
2004             }
2005             op_info->rsc_id = strdup(rsc_id);
2006             op_info->action = crm_element_value_copy(op_xml,
2007                                                      PCMK__XA_LRMD_RSC_ACTION);
2008             op_info->interval_ms_s =
2009                 crm_element_value_copy(op_xml, PCMK__XA_LRMD_RSC_INTERVAL);
2010             op_info->timeout_ms_s =
2011                 crm_element_value_copy(op_xml, PCMK__XA_LRMD_TIMEOUT);
2012             *output = g_list_prepend(*output, op_info);
2013         }
2014     }
2015     pcmk__xml_free(output_xml);
2016     return rc;
2017 }
2018 
2019 
2020 static void
2021 lrmd_api_set_callback(lrmd_t * lrmd, lrmd_event_callback callback)
     /* [previous][next][first][last][top][bottom][index][help] */
2022 {
2023     lrmd_private_t *native = lrmd->lrmd_private;
2024 
2025     native->callback = callback;
2026 }
2027 
2028 void
2029 lrmd_internal_set_proxy_callback(lrmd_t * lrmd, void *userdata, void (*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg))
     /* [previous][next][first][last][top][bottom][index][help] */
2030 {
2031     lrmd_private_t *native = lrmd->lrmd_private;
2032 
2033     native->proxy_callback = callback;
2034     native->proxy_callback_userdata = userdata;
2035 }
2036 
2037 void
2038 lrmd_internal_proxy_dispatch(lrmd_t *lrmd, xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
2039 {
2040     lrmd_private_t *native = lrmd->lrmd_private;
2041 
2042     if (native->proxy_callback) {
2043         crm_log_xml_trace(msg, "PROXY_INBOUND");
2044         native->proxy_callback(lrmd, native->proxy_callback_userdata, msg);
2045     }
2046 }
2047 
2048 int
2049 lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg)
     /* [previous][next][first][last][top][bottom][index][help] */
2050 {
2051     if (lrmd == NULL) {
2052         return -ENOTCONN;
2053     }
2054     crm_xml_add(msg, PCMK__XA_LRMD_OP, CRM_OP_IPC_FWD);
2055 
2056     crm_log_xml_trace(msg, "PROXY_OUTBOUND");
2057     return lrmd_send_xml_no_reply(lrmd, msg);
2058 }
2059 
2060 static int
2061 stonith_get_metadata(const char *type, char **output)
     /* [previous][next][first][last][top][bottom][index][help] */
2062 {
2063     int rc = pcmk_ok;
2064     stonith_t *stonith_api = stonith__api_new();
2065 
2066     if (stonith_api == NULL) {
2067         crm_err("Could not get fence agent meta-data: API memory allocation failed");
2068         return -ENOMEM;
2069     }
2070 
2071     rc = stonith_api->cmds->metadata(stonith_api, st_opt_sync_call, type, NULL,
2072                                      output, 0);
2073     if ((rc == pcmk_ok) && (*output == NULL)) {
2074         rc = -EIO;
2075     }
2076     stonith_api->cmds->free(stonith_api);
2077     return rc;
2078 }
2079 
2080 static int
2081 lrmd_api_get_metadata(lrmd_t *lrmd, const char *standard, const char *provider,
     /* [previous][next][first][last][top][bottom][index][help] */
2082                       const char *type, char **output,
2083                       enum lrmd_call_options options)
2084 {
2085     return lrmd->cmds->get_metadata_params(lrmd, standard, provider, type,
2086                                            output, options, NULL);
2087 }
2088 
2089 static int
2090 lrmd_api_get_metadata_params(lrmd_t *lrmd, const char *standard,
     /* [previous][next][first][last][top][bottom][index][help] */
2091                              const char *provider, const char *type,
2092                              char **output, enum lrmd_call_options options,
2093                              lrmd_key_value_t *params)
2094 {
2095     svc_action_t *action = NULL;
2096     GHashTable *params_table = NULL;
2097 
2098     if (!standard || !type) {
2099         lrmd_key_value_freeall(params);
2100         return -EINVAL;
2101     }
2102 
2103     if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
2104         lrmd_key_value_freeall(params);
2105 
2106         // stonith-class resources don't support a provider
2107         return stonith_get_metadata(type, output);
2108     }
2109 
2110     params_table = pcmk__strkey_table(free, free);
2111     for (const lrmd_key_value_t *param = params; param; param = param->next) {
2112         pcmk__insert_dup(params_table, param->key, param->value);
2113     }
2114     action = services__create_resource_action(type, standard, provider, type,
2115                                               PCMK_ACTION_META_DATA, 0,
2116                                               PCMK_DEFAULT_ACTION_TIMEOUT_MS,
2117                                               params_table, 0);
2118     lrmd_key_value_freeall(params);
2119 
2120     if (action == NULL) {
2121         return -ENOMEM;
2122     }
2123     if (action->rc != PCMK_OCF_UNKNOWN) {
2124         services_action_free(action);
2125         return -EINVAL;
2126     }
2127 
2128     if (!services_action_sync(action)) {
2129         crm_err("Failed to retrieve meta-data for %s:%s:%s",
2130                 standard, provider, type);
2131         services_action_free(action);
2132         return -EIO;
2133     }
2134 
2135     if (!action->stdout_data) {
2136         crm_err("Failed to receive meta-data for %s:%s:%s",
2137                 standard, provider, type);
2138         services_action_free(action);
2139         return -EIO;
2140     }
2141 
2142     *output = strdup(action->stdout_data);
2143     services_action_free(action);
2144 
2145     return pcmk_ok;
2146 }
2147 
2148 static int
2149 lrmd_api_exec(lrmd_t *lrmd, const char *rsc_id, const char *action,
     /* [previous][next][first][last][top][bottom][index][help] */
2150               const char *userdata, guint interval_ms,
2151               int timeout,      /* ms */
2152               int start_delay,  /* ms */
2153               enum lrmd_call_options options, lrmd_key_value_t * params)
2154 {
2155     int rc = pcmk_ok;
2156     xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC);
2157     xmlNode *args = pcmk__xe_create(data, PCMK__XE_ATTRIBUTES);
2158     lrmd_key_value_t *tmp = NULL;
2159 
2160     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
2161     crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id);
2162     crm_xml_add(data, PCMK__XA_LRMD_RSC_ACTION, action);
2163     crm_xml_add(data, PCMK__XA_LRMD_RSC_USERDATA_STR, userdata);
2164     crm_xml_add_ms(data, PCMK__XA_LRMD_RSC_INTERVAL, interval_ms);
2165     crm_xml_add_int(data, PCMK__XA_LRMD_TIMEOUT, timeout);
2166     crm_xml_add_int(data, PCMK__XA_LRMD_RSC_START_DELAY, start_delay);
2167 
2168     for (tmp = params; tmp; tmp = tmp->next) {
2169         hash2smartfield((gpointer) tmp->key, (gpointer) tmp->value, args);
2170     }
2171 
2172     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_EXEC, data, NULL, timeout, options, true);
2173     pcmk__xml_free(data);
2174 
2175     lrmd_key_value_freeall(params);
2176     return rc;
2177 }
2178 
2179 /* timeout is in ms */
2180 static int
2181 lrmd_api_exec_alert(lrmd_t *lrmd, const char *alert_id, const char *alert_path,
     /* [previous][next][first][last][top][bottom][index][help] */
2182                     int timeout, lrmd_key_value_t *params)
2183 {
2184     int rc = pcmk_ok;
2185     xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_ALERT);
2186     xmlNode *args = pcmk__xe_create(data, PCMK__XE_ATTRIBUTES);
2187     lrmd_key_value_t *tmp = NULL;
2188 
2189     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
2190     crm_xml_add(data, PCMK__XA_LRMD_ALERT_ID, alert_id);
2191     crm_xml_add(data, PCMK__XA_LRMD_ALERT_PATH, alert_path);
2192     crm_xml_add_int(data, PCMK__XA_LRMD_TIMEOUT, timeout);
2193 
2194     for (tmp = params; tmp; tmp = tmp->next) {
2195         hash2smartfield((gpointer) tmp->key, (gpointer) tmp->value, args);
2196     }
2197 
2198     rc = lrmd_send_command(lrmd, LRMD_OP_ALERT_EXEC, data, NULL, timeout,
2199                            lrmd_opt_notify_orig_only, true);
2200     pcmk__xml_free(data);
2201 
2202     lrmd_key_value_freeall(params);
2203     return rc;
2204 }
2205 
2206 static int
2207 lrmd_api_cancel(lrmd_t *lrmd, const char *rsc_id, const char *action,
     /* [previous][next][first][last][top][bottom][index][help] */
2208                 guint interval_ms)
2209 {
2210     int rc = pcmk_ok;
2211     xmlNode *data = pcmk__xe_create(NULL, PCMK__XE_LRMD_RSC);
2212 
2213     crm_xml_add(data, PCMK__XA_LRMD_ORIGIN, __func__);
2214     crm_xml_add(data, PCMK__XA_LRMD_RSC_ACTION, action);
2215     crm_xml_add(data, PCMK__XA_LRMD_RSC_ID, rsc_id);
2216     crm_xml_add_ms(data, PCMK__XA_LRMD_RSC_INTERVAL, interval_ms);
2217     rc = lrmd_send_command(lrmd, LRMD_OP_RSC_CANCEL, data, NULL, 0, 0, true);
2218     pcmk__xml_free(data);
2219     return rc;
2220 }
2221 
2222 static int
2223 list_stonith_agents(lrmd_list_t ** resources)
     /* [previous][next][first][last][top][bottom][index][help] */
2224 {
2225     int rc = 0;
2226     stonith_t *stonith_api = stonith__api_new();
2227     stonith_key_value_t *stonith_resources = NULL;
2228     stonith_key_value_t *dIter = NULL;
2229 
2230     if (stonith_api == NULL) {
2231         crm_err("Could not list fence agents: API memory allocation failed");
2232         return -ENOMEM;
2233     }
2234     stonith_api->cmds->list_agents(stonith_api, st_opt_sync_call, NULL,
2235                                    &stonith_resources, 0);
2236     stonith_api->cmds->free(stonith_api);
2237 
2238     for (dIter = stonith_resources; dIter; dIter = dIter->next) {
2239         rc++;
2240         if (resources) {
2241             *resources = lrmd_list_add(*resources, dIter->value);
2242         }
2243     }
2244 
2245     stonith__key_value_freeall(stonith_resources, true, false);
2246     return rc;
2247 }
2248 
2249 static int
2250 lrmd_api_list_agents(lrmd_t * lrmd, lrmd_list_t ** resources, const char *class,
     /* [previous][next][first][last][top][bottom][index][help] */
2251                      const char *provider)
2252 {
2253     int rc = 0;
2254     int stonith_count = 0; // Initially, whether to include stonith devices
2255 
2256     if (pcmk__str_eq(class, PCMK_RESOURCE_CLASS_STONITH, pcmk__str_casei)) {
2257         stonith_count = 1;
2258 
2259     } else {
2260         GList *gIter = NULL;
2261         GList *agents = resources_list_agents(class, provider);
2262 
2263         for (gIter = agents; gIter != NULL; gIter = gIter->next) {
2264             *resources = lrmd_list_add(*resources, (const char *)gIter->data);
2265             rc++;
2266         }
2267         g_list_free_full(agents, free);
2268 
2269         if (!class) {
2270             stonith_count = 1;
2271         }
2272     }
2273 
2274     if (stonith_count) {
2275         // Now, if stonith devices are included, how many there are
2276         stonith_count = list_stonith_agents(resources);
2277         if (stonith_count > 0) {
2278             rc += stonith_count;
2279         }
2280     }
2281     if (rc == 0) {
2282         crm_notice("No agents found for class %s", class);
2283         rc = -EPROTONOSUPPORT;
2284     }
2285     return rc;
2286 }
2287 
2288 static bool
2289 does_provider_have_agent(const char *agent, const char *provider, const char *class)
     /* [previous][next][first][last][top][bottom][index][help] */
2290 {
2291     bool found = false;
2292     GList *agents = NULL;
2293     GList *gIter2 = NULL;
2294 
2295     agents = resources_list_agents(class, provider);
2296     for (gIter2 = agents; gIter2 != NULL; gIter2 = gIter2->next) {
2297         if (pcmk__str_eq(agent, gIter2->data, pcmk__str_casei)) {
2298             found = true;
2299         }
2300     }
2301     g_list_free_full(agents, free);
2302     return found;
2303 }
2304 
2305 static int
2306 lrmd_api_list_ocf_providers(lrmd_t * lrmd, const char *agent, lrmd_list_t ** providers)
     /* [previous][next][first][last][top][bottom][index][help] */
2307 {
2308     int rc = pcmk_ok;
2309     char *provider = NULL;
2310     GList *ocf_providers = NULL;
2311     GList *gIter = NULL;
2312 
2313     ocf_providers = resources_list_providers(PCMK_RESOURCE_CLASS_OCF);
2314 
2315     for (gIter = ocf_providers; gIter != NULL; gIter = gIter->next) {
2316         provider = gIter->data;
2317         if (!agent || does_provider_have_agent(agent, provider,
2318                                                PCMK_RESOURCE_CLASS_OCF)) {
2319             *providers = lrmd_list_add(*providers, (const char *)gIter->data);
2320             rc++;
2321         }
2322     }
2323 
2324     g_list_free_full(ocf_providers, free);
2325     return rc;
2326 }
2327 
2328 static int
2329 lrmd_api_list_standards(lrmd_t * lrmd, lrmd_list_t ** supported)
     /* [previous][next][first][last][top][bottom][index][help] */
2330 {
2331     int rc = 0;
2332     GList *standards = NULL;
2333     GList *gIter = NULL;
2334 
2335     standards = resources_list_standards();
2336 
2337     for (gIter = standards; gIter != NULL; gIter = gIter->next) {
2338         *supported = lrmd_list_add(*supported, (const char *)gIter->data);
2339         rc++;
2340     }
2341 
2342     if (list_stonith_agents(NULL) > 0) {
2343         *supported = lrmd_list_add(*supported, PCMK_RESOURCE_CLASS_STONITH);
2344         rc++;
2345     }
2346 
2347     g_list_free_full(standards, free);
2348     return rc;
2349 }
2350 
2351 /*!
2352  * \internal
2353  * \brief Create an executor API object
2354  *
2355  * \param[out] api       Will be set to newly created API object (it is the
2356  *                       caller's responsibility to free this value with
2357  *                       lrmd_api_delete() if this function succeeds)
2358  * \param[in]  nodename  If the object will be used for a remote connection,
2359  *                       the node name to use in cluster for remote executor
2360  * \param[in]  server    If the object will be used for a remote connection,
2361  *                       the resolvable host name to connect to
2362  * \param[in]  port      If the object will be used for a remote connection,
2363  *                       port number on \p server to connect to
2364  *
2365  * \return Standard Pacemaker return code
2366  * \note If the caller leaves one of \p nodename or \p server NULL, the other's
2367  *       value will be used for both. If the caller leaves both NULL, an API
2368  *       object will be created for a local executor connection.
2369  */
2370 int
2371 lrmd__new(lrmd_t **api, const char *nodename, const char *server, int port)
     /* [previous][next][first][last][top][bottom][index][help] */
2372 {
2373     lrmd_private_t *pvt = NULL;
2374 
2375     if (api == NULL) {
2376         return EINVAL;
2377     }
2378     *api = NULL;
2379 
2380     // Allocate all memory needed
2381 
2382     *api = calloc(1, sizeof(lrmd_t));
2383     if (*api == NULL) {
2384         return ENOMEM;
2385     }
2386 
2387     pvt = calloc(1, sizeof(lrmd_private_t));
2388     if (pvt == NULL) {
2389         lrmd_api_delete(*api);
2390         *api = NULL;
2391         return ENOMEM;
2392     }
2393     (*api)->lrmd_private = pvt;
2394 
2395     // @TODO Do we need to do this for local connections?
2396     pvt->remote = calloc(1, sizeof(pcmk__remote_t));
2397 
2398     (*api)->cmds = calloc(1, sizeof(lrmd_api_operations_t));
2399 
2400     if ((pvt->remote == NULL) || ((*api)->cmds == NULL)) {
2401         lrmd_api_delete(*api);
2402         *api = NULL;
2403         return ENOMEM;
2404     }
2405 
2406     // Set methods
2407     (*api)->cmds->connect = lrmd_api_connect;
2408     (*api)->cmds->connect_async = lrmd_api_connect_async;
2409     (*api)->cmds->is_connected = lrmd_api_is_connected;
2410     (*api)->cmds->poke_connection = lrmd_api_poke_connection;
2411     (*api)->cmds->disconnect = lrmd_api_disconnect;
2412     (*api)->cmds->register_rsc = lrmd_api_register_rsc;
2413     (*api)->cmds->unregister_rsc = lrmd_api_unregister_rsc;
2414     (*api)->cmds->get_rsc_info = lrmd_api_get_rsc_info;
2415     (*api)->cmds->get_recurring_ops = lrmd_api_get_recurring_ops;
2416     (*api)->cmds->set_callback = lrmd_api_set_callback;
2417     (*api)->cmds->get_metadata = lrmd_api_get_metadata;
2418     (*api)->cmds->exec = lrmd_api_exec;
2419     (*api)->cmds->cancel = lrmd_api_cancel;
2420     (*api)->cmds->list_agents = lrmd_api_list_agents;
2421     (*api)->cmds->list_ocf_providers = lrmd_api_list_ocf_providers;
2422     (*api)->cmds->list_standards = lrmd_api_list_standards;
2423     (*api)->cmds->exec_alert = lrmd_api_exec_alert;
2424     (*api)->cmds->get_metadata_params = lrmd_api_get_metadata_params;
2425 
2426     if ((nodename == NULL) && (server == NULL)) {
2427         pvt->type = pcmk__client_ipc;
2428     } else {
2429         if (nodename == NULL) {
2430             nodename = server;
2431         } else if (server == NULL) {
2432             server = nodename;
2433         }
2434         pvt->type = pcmk__client_tls;
2435         pvt->remote_nodename = strdup(nodename);
2436         pvt->server = strdup(server);
2437         if ((pvt->remote_nodename == NULL) || (pvt->server == NULL)) {
2438             lrmd_api_delete(*api);
2439             *api = NULL;
2440             return ENOMEM;
2441         }
2442         pvt->port = port;
2443         if (pvt->port == 0) {
2444             pvt->port = crm_default_remote_port();
2445         }
2446     }
2447     return pcmk_rc_ok;
2448 }
2449 
2450 lrmd_t *
2451 lrmd_api_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
2452 {
2453     lrmd_t *api = NULL;
2454 
2455     pcmk__assert(lrmd__new(&api, NULL, NULL, 0) == pcmk_rc_ok);
2456     return api;
2457 }
2458 
2459 lrmd_t *
2460 lrmd_remote_api_new(const char *nodename, const char *server, int port)
     /* [previous][next][first][last][top][bottom][index][help] */
2461 {
2462     lrmd_t *api = NULL;
2463 
2464     pcmk__assert(lrmd__new(&api, nodename, server, port) == pcmk_rc_ok);
2465     return api;
2466 }
2467 
2468 void
2469 lrmd_api_delete(lrmd_t * lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
2470 {
2471     if (lrmd == NULL) {
2472         return;
2473     }
2474     if (lrmd->cmds != NULL) { // Never NULL, but make static analysis happy
2475         if (lrmd->cmds->disconnect != NULL) { // Also never really NULL
2476             lrmd->cmds->disconnect(lrmd); // No-op if already disconnected
2477         }
2478         free(lrmd->cmds);
2479     }
2480     if (lrmd->lrmd_private != NULL) {
2481         lrmd_private_t *native = lrmd->lrmd_private;
2482 
2483         free(native->server);
2484         free(native->remote_nodename);
2485         free(native->remote);
2486         free(native->token);
2487         free(native->peer_version);
2488         free(lrmd->lrmd_private);
2489     }
2490     free(lrmd);
2491 }
2492 
2493 struct metadata_cb {
2494      void (*callback)(int pid, const pcmk__action_result_t *result,
2495                       void *user_data);
2496      void *user_data;
2497 };
2498 
2499 /*!
2500  * \internal
2501  * \brief Process asynchronous metadata completion
2502  *
2503  * \param[in,out] action  Metadata action that completed
2504  */
2505 static void
2506 metadata_complete(svc_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
2507 {
2508     struct metadata_cb *metadata_cb = (struct metadata_cb *) action->cb_data;
2509     pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
2510 
2511     services__copy_result(action, &result);
2512     pcmk__set_result_output(&result, action->stdout_data, action->stderr_data);
2513 
2514     metadata_cb->callback(0, &result, metadata_cb->user_data);
2515     result.action_stdout = NULL; // Prevent free, because action owns it
2516     result.action_stderr = NULL; // Prevent free, because action owns it
2517     pcmk__reset_result(&result);
2518     free(metadata_cb);
2519 }
2520 
2521 /*!
2522  * \internal
2523  * \brief Retrieve agent metadata asynchronously
2524  *
2525  * \param[in]     rsc        Resource agent specification
2526  * \param[in]     callback   Function to call with result (this will always be
2527  *                           called, whether by this function directly or later
2528  *                           via the main loop, and on success the metadata will
2529  *                           be in its result argument's action_stdout)
2530  * \param[in,out] user_data  User data to pass to callback
2531  *
2532  * \return Standard Pacemaker return code
2533  * \note This function is not a lrmd_api_operations_t method because it does not
2534  *       need an lrmd_t object and does not go through the executor, but
2535  *       executes the agent directly.
2536  */
2537 int
2538 lrmd__metadata_async(const lrmd_rsc_info_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
2539                      void (*callback)(int pid,
2540                                       const pcmk__action_result_t *result,
2541                                       void *user_data),
2542                      void *user_data)
2543 {
2544     svc_action_t *action = NULL;
2545     struct metadata_cb *metadata_cb = NULL;
2546     pcmk__action_result_t result = PCMK__UNKNOWN_RESULT;
2547 
2548     CRM_CHECK(callback != NULL, return EINVAL);
2549 
2550     if ((rsc == NULL) || (rsc->standard == NULL) || (rsc->type == NULL)) {
2551         pcmk__set_result(&result, PCMK_OCF_NOT_CONFIGURED,
2552                          PCMK_EXEC_ERROR_FATAL,
2553                          "Invalid resource specification");
2554         callback(0, &result, user_data);
2555         pcmk__reset_result(&result);
2556         return EINVAL;
2557     }
2558 
2559     if (strcmp(rsc->standard, PCMK_RESOURCE_CLASS_STONITH) == 0) {
2560         return stonith__metadata_async(rsc->type,
2561                                        pcmk__timeout_ms2s(PCMK_DEFAULT_ACTION_TIMEOUT_MS),
2562                                        callback, user_data);
2563     }
2564 
2565     action = services__create_resource_action(pcmk__s(rsc->id, rsc->type),
2566                                               rsc->standard, rsc->provider,
2567                                               rsc->type,
2568                                               PCMK_ACTION_META_DATA, 0,
2569                                               PCMK_DEFAULT_ACTION_TIMEOUT_MS,
2570                                               NULL, 0);
2571     if (action == NULL) {
2572         pcmk__set_result(&result, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
2573                          "Out of memory");
2574         callback(0, &result, user_data);
2575         pcmk__reset_result(&result);
2576         return ENOMEM;
2577     }
2578     if (action->rc != PCMK_OCF_UNKNOWN) {
2579         services__copy_result(action, &result);
2580         callback(0, &result, user_data);
2581         pcmk__reset_result(&result);
2582         services_action_free(action);
2583         return EINVAL;
2584     }
2585 
2586     action->cb_data = calloc(1, sizeof(struct metadata_cb));
2587     if (action->cb_data == NULL) {
2588         services_action_free(action);
2589         pcmk__set_result(&result, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
2590                          "Out of memory");
2591         callback(0, &result, user_data);
2592         pcmk__reset_result(&result);
2593         return ENOMEM;
2594     }
2595 
2596     metadata_cb = (struct metadata_cb *) action->cb_data;
2597     metadata_cb->callback = callback;
2598     metadata_cb->user_data = user_data;
2599     if (!services_action_async(action, metadata_complete)) {
2600         services_action_free(action);
2601         return pcmk_rc_error; // @TODO Derive from action->rc and ->status
2602     }
2603 
2604     // The services library has taken responsibility for action
2605     return pcmk_rc_ok;
2606 }
2607 
2608 /*!
2609  * \internal
2610  * \brief Set the result of an executor event
2611  *
2612  * \param[in,out] event        Executor event to set
2613  * \param[in]     rc           OCF exit status of event
2614  * \param[in]     op_status    Executor status of event
2615  * \param[in]     exit_reason  Human-friendly description of event
2616  */
2617 void
2618 lrmd__set_result(lrmd_event_data_t *event, enum ocf_exitcode rc, int op_status,
     /* [previous][next][first][last][top][bottom][index][help] */
2619                  const char *exit_reason)
2620 {
2621     if (event == NULL) {
2622         return;
2623     }
2624 
2625     event->rc = rc;
2626     event->op_status = op_status;
2627 
2628     // lrmd_event_data_t has (const char *) members that lrmd_free_event() frees
2629     pcmk__str_update((char **) &event->exit_reason, exit_reason);
2630 }
2631 
2632 /*!
2633  * \internal
2634  * \brief Clear an executor event's exit reason, output, and error output
2635  *
2636  * \param[in,out] event  Executor event to reset
2637  */
2638 void
2639 lrmd__reset_result(lrmd_event_data_t *event)
     /* [previous][next][first][last][top][bottom][index][help] */
2640 {
2641     if (event == NULL) {
2642         return;
2643     }
2644 
2645     free((void *) event->exit_reason);
2646     event->exit_reason = NULL;
2647 
2648     free((void *) event->output);
2649     event->output = NULL;
2650 }
2651 
2652 /*!
2653  * \internal
2654  * \brief Get the uptime of a remote resource connection
2655  *
2656  * When the cluster connects to a remote resource, part of that resource's
2657  * handshake includes the uptime of the remote resource's connection.  This
2658  * uptime is stored in the lrmd_t object.
2659  *
2660  * \return The connection's uptime, or -1 if unknown
2661  */
2662 time_t
2663 lrmd__uptime(lrmd_t *lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
2664 {
2665     lrmd_private_t *native = lrmd->lrmd_private;
2666 
2667     if (native->remote == NULL) {
2668         return -1;
2669     } else {
2670         return native->remote->uptime;
2671     }
2672 }
2673 
2674 const char *
2675 lrmd__node_start_state(lrmd_t *lrmd)
     /* [previous][next][first][last][top][bottom][index][help] */
2676 {
2677     lrmd_private_t *native = lrmd->lrmd_private;
2678 
2679     if (native->remote == NULL) {
2680         return NULL;
2681     } else {
2682         return native->remote->start_state;
2683     }
2684 }

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