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

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

Generated by: LCOV version 1.14