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