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 : }
|