pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
ipc_client.c
Go to the documentation of this file.
1/*
2 * Copyright 2004-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#if defined(HAVE_UCRED) || defined(HAVE_SOCKPEERCRED)
13#include <sys/socket.h>
14#elif defined(HAVE_GETPEERUCRED)
15#include <ucred.h>
16#endif
17
18#include <stdio.h>
19#include <sys/types.h>
20#include <errno.h>
21#include <bzlib.h>
22
23#include <crm/crm.h> /* indirectly: pcmk_err_generic */
24#include <crm/common/xml.h>
25#include <crm/common/ipc.h>
27#include "crmcommon_private.h"
28
29static int is_ipc_provider_expected(qb_ipcc_connection_t *qb_ipc, int sock,
30 uid_t refuid, gid_t refgid, pid_t *gotpid,
31 uid_t *gotuid, gid_t *gotgid);
32
45int
47{
48 if (api == NULL) {
49 return EINVAL;
50 }
51
52 *api = calloc(1, sizeof(pcmk_ipc_api_t));
53 if (*api == NULL) {
54 return errno;
55 }
56
57 (*api)->server = server;
58 if (pcmk_ipc_name(*api, false) == NULL) {
60 *api = NULL;
61 return EOPNOTSUPP;
62 }
63
64 // Set server methods
65 switch (server) {
66 case pcmk_ipc_attrd:
67 (*api)->cmds = pcmk__attrd_api_methods();
68 break;
69
70 case pcmk_ipc_based:
71 break;
72
74 (*api)->cmds = pcmk__controld_api_methods();
75 break;
76
77 case pcmk_ipc_execd:
78 break;
79
80 case pcmk_ipc_fenced:
81 break;
82
84 (*api)->cmds = pcmk__pacemakerd_api_methods();
85 break;
86
88 (*api)->cmds = pcmk__schedulerd_api_methods();
89 break;
90
91 default: // pcmk_ipc_unknown
93 *api = NULL;
94 return EINVAL;
95 }
96 if ((*api)->cmds == NULL) {
98 *api = NULL;
99 return ENOMEM;
100 }
101
102 (*api)->ipc = crm_ipc_new(pcmk_ipc_name(*api, false), 0);
103 if ((*api)->ipc == NULL) {
104 pcmk_free_ipc_api(*api);
105 *api = NULL;
106 return ENOMEM;
107 }
108
109 // If daemon API has its own data to track, allocate it
110 if ((*api)->cmds->new_data != NULL) {
111 if ((*api)->cmds->new_data(*api) != pcmk_rc_ok) {
112 pcmk_free_ipc_api(*api);
113 *api = NULL;
114 return ENOMEM;
115 }
116 }
117 crm_trace("Created %s API IPC object", pcmk_ipc_name(*api, true));
118 return pcmk_rc_ok;
119}
120
121static void
122free_daemon_specific_data(pcmk_ipc_api_t *api)
123{
124 if ((api != NULL) && (api->cmds != NULL)) {
125 if ((api->cmds->free_data != NULL) && (api->api_data != NULL)) {
126 api->cmds->free_data(api->api_data);
127 api->api_data = NULL;
128 }
129 free(api->cmds);
130 api->cmds = NULL;
131 }
132}
133
143void
145 crm_exit_t status, void *event_data)
146{
147 if ((api != NULL) && (api->cb != NULL)) {
148 api->cb(api, event_type, status, event_data, api->user_data);
149 }
150}
151
160static void
161ipc_post_disconnect(gpointer user_data)
162{
163 pcmk_ipc_api_t *api = user_data;
164
165 crm_info("Disconnected from %s", pcmk_ipc_name(api, true));
166
167 // Perform any daemon-specific handling needed
168 if ((api->cmds != NULL) && (api->cmds->post_disconnect != NULL)) {
169 api->cmds->post_disconnect(api);
170 }
171
172 // Call client's registered event callback
174 NULL);
175
176 /* If this is being called from a running main loop, mainloop_gio_destroy()
177 * will free ipc and mainloop_io immediately after calling this function.
178 * If this is called from a stopped main loop, these will leak, so the best
179 * practice is to close the connection before stopping the main loop.
180 */
181 api->ipc = NULL;
182 api->mainloop_io = NULL;
183
184 if (api->free_on_disconnect) {
185 /* pcmk_free_ipc_api() has already been called, but did not free api
186 * or api->cmds because this function needed them. Do that now.
187 */
188 free_daemon_specific_data(api);
189 crm_trace("Freeing IPC API object after disconnect");
190 free(api);
191 }
192}
193
199void
201{
202 bool free_on_disconnect = false;
203
204 if (api == NULL) {
205 return;
206 }
207 crm_debug("Releasing %s IPC API", pcmk_ipc_name(api, true));
208
209 if (api->ipc != NULL) {
210 if (api->mainloop_io != NULL) {
211 /* We need to keep the api pointer itself around, because it is the
212 * user data for the IPC client destroy callback. That will be
213 * triggered by the pcmk_disconnect_ipc() call below, but it might
214 * happen later in the main loop (if still running).
215 *
216 * This flag tells the destroy callback to free the object. It can't
217 * do that unconditionally, because the application might call this
218 * function after a disconnect that happened by other means.
219 */
220 free_on_disconnect = api->free_on_disconnect = true;
221 }
222 pcmk_disconnect_ipc(api); // Frees api if free_on_disconnect is true
223 }
224 if (!free_on_disconnect) {
225 free_daemon_specific_data(api);
226 crm_trace("Freeing IPC API object");
227 free(api);
228 }
229}
230
240const char *
241pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log)
242{
243 if (api == NULL) {
244 return for_log? "Pacemaker" : NULL;
245 }
246 if (for_log) {
247 const char *name = pcmk__server_log_name(api->server);
248
249 return pcmk__s(name, "Pacemaker");
250 }
251 switch (api->server) {
252 // These servers do not have pcmk_ipc_api_t implementations yet
253 case pcmk_ipc_based:
254 case pcmk_ipc_execd:
255 case pcmk_ipc_fenced:
256 return NULL;
257
258 default:
259 return pcmk__server_ipc_name(api->server);
260 }
261}
262
270bool
272{
273 return (api != NULL) && crm_ipc_connected(api->ipc);
274}
275
287static bool
288call_api_dispatch(pcmk_ipc_api_t *api, xmlNode *message)
289{
290 crm_log_xml_trace(message, "ipc-received");
291 if ((api->cmds != NULL) && (api->cmds->dispatch != NULL)) {
292 return api->cmds->dispatch(api, message);
293 }
294
295 return false;
296}
297
313static int
314dispatch_ipc_data(const char *buffer, pcmk_ipc_api_t *api)
315{
316 bool more = false;
317 xmlNode *msg;
318
319 if (buffer == NULL) {
320 crm_warn("Empty message received from %s IPC",
321 pcmk_ipc_name(api, true));
322 return ENOMSG;
323 }
324
325 msg = pcmk__xml_parse(buffer);
326 if (msg == NULL) {
327 crm_warn("Malformed message received from %s IPC",
328 pcmk_ipc_name(api, true));
329 return EPROTO;
330 }
331
332 more = call_api_dispatch(api, msg);
333 pcmk__xml_free(msg);
334
335 if (more) {
336 return EINPROGRESS;
337 } else {
338 return pcmk_rc_ok;
339 }
340}
341
354static int
355dispatch_ipc_source_data(const char *buffer, ssize_t length, gpointer user_data)
356{
357 pcmk_ipc_api_t *api = user_data;
358
359 CRM_CHECK(api != NULL, return 0);
360 dispatch_ipc_data(buffer, api);
361 return 0;
362}
363
382int
383pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms)
384{
385 int rc;
386 struct pollfd pollfd = { 0, };
387
388 if ((api == NULL) || (api->dispatch_type != pcmk_ipc_dispatch_poll)) {
389 return EINVAL;
390 }
391
392 rc = pcmk__ipc_fd(api->ipc, &(pollfd.fd));
393 if (rc != pcmk_rc_ok) {
394 crm_debug("Could not obtain file descriptor for %s IPC: %s",
395 pcmk_ipc_name(api, true), pcmk_rc_str(rc));
396 return rc;
397 }
398
399 pollfd.events = POLLIN;
400 rc = poll(&pollfd, 1, timeout_ms);
401 if (rc < 0) {
402 /* Some UNIX systems return negative and set EAGAIN for failure to
403 * allocate memory; standardize the return code in that case
404 */
405 return (errno == EAGAIN)? ENOMEM : errno;
406 } else if (rc == 0) {
407 return EAGAIN;
408 }
409 return pcmk_rc_ok;
410}
411
422void
424{
425 if (api == NULL) {
426 return;
427 }
428 while (crm_ipc_ready(api->ipc) > 0) {
429 if (crm_ipc_read(api->ipc) > 0) {
430 dispatch_ipc_data(crm_ipc_buffer(api->ipc), api);
432 }
433 }
434}
435
436// \return Standard Pacemaker return code
437static int
438connect_with_main_loop(pcmk_ipc_api_t *api)
439{
440 int rc;
441
442 struct ipc_client_callbacks callbacks = {
443 .dispatch = dispatch_ipc_source_data,
444 .destroy = ipc_post_disconnect,
445 };
446
447 rc = pcmk__add_mainloop_ipc(api->ipc, G_PRIORITY_DEFAULT, api,
448 &callbacks, &(api->mainloop_io));
449 if (rc != pcmk_rc_ok) {
450 return rc;
451 }
452 crm_debug("Connected to %s IPC (attached to main loop)",
453 pcmk_ipc_name(api, true));
454 /* After this point, api->mainloop_io owns api->ipc, so api->ipc
455 * should not be explicitly freed.
456 */
457 return pcmk_rc_ok;
458}
459
460// \return Standard Pacemaker return code
461static int
462connect_without_main_loop(pcmk_ipc_api_t *api)
463{
464 int rc = pcmk__connect_generic_ipc(api->ipc);
465
466 if (rc != pcmk_rc_ok) {
467 crm_ipc_close(api->ipc);
468 } else {
469 crm_debug("Connected to %s IPC (without main loop)",
470 pcmk_ipc_name(api, true));
471 }
472 return rc;
473}
474
486int
488 enum pcmk_ipc_dispatch dispatch_type,
489 int attempts)
490{
491 int remaining = attempts;
492 int rc = pcmk_rc_ok;
493
494 do {
495 if (rc == ECONNREFUSED) {
496 pcmk__sleep_ms((attempts - remaining) * 500);
497 }
498 rc = pcmk__connect_ipc(api, dispatch_type, remaining);
499 remaining--;
500 } while (rc == ECONNREFUSED && remaining >= 0);
501
502 return rc;
503}
504
505
516int
518 int attempts)
519{
520 int rc = pcmk_rc_ok;
521
522 if ((api == NULL) || (attempts < 1)) {
523 return EINVAL;
524 }
525
526 if (api->ipc == NULL) {
527 api->ipc = crm_ipc_new(pcmk_ipc_name(api, false), 0);
528 if (api->ipc == NULL) {
529 return ENOMEM;
530 }
531 }
532
533 if (crm_ipc_connected(api->ipc)) {
534 crm_trace("Already connected to %s", pcmk_ipc_name(api, true));
535 return pcmk_rc_ok;
536 }
537
538 api->dispatch_type = dispatch_type;
539
540 crm_debug("Attempting connection to %s (up to %d time%s)",
541 pcmk_ipc_name(api, true), attempts, pcmk__plural_s(attempts));
542 for (int remaining = attempts - 1; remaining >= 0; --remaining) {
543 switch (dispatch_type) {
545 rc = connect_with_main_loop(api);
546 break;
547
550 rc = connect_without_main_loop(api);
551 break;
552 }
553
554 if ((remaining == 0) || ((rc != EAGAIN) && (rc != EALREADY))) {
555 break; // Result is final
556 }
557
558 // Retry after soft error (interrupted by signal, etc.)
559 pcmk__sleep_ms((attempts - remaining) * 500);
560 crm_debug("Re-attempting connection to %s (%d attempt%s remaining)",
561 pcmk_ipc_name(api, true), remaining,
562 pcmk__plural_s(remaining));
563 }
564
565 if (rc != pcmk_rc_ok) {
566 return rc;
567 }
568
569 if ((api->cmds != NULL) && (api->cmds->post_connect != NULL)) {
570 rc = api->cmds->post_connect(api);
571 if (rc != pcmk_rc_ok) {
572 crm_ipc_close(api->ipc);
573 }
574 }
575 return rc;
576}
577
586int
588{
589 int rc = pcmk__connect_ipc(api, dispatch_type, 2);
590
591 if (rc != pcmk_rc_ok) {
592 crm_err("Connection to %s failed: %s",
593 pcmk_ipc_name(api, true), pcmk_rc_str(rc));
594 }
595 return rc;
596}
597
609void
611{
612 if ((api == NULL) || (api->ipc == NULL)) {
613 return;
614 }
615 switch (api->dispatch_type) {
617 {
618 mainloop_io_t *mainloop_io = api->mainloop_io;
619
620 // Make sure no code with access to api can use these again
621 api->mainloop_io = NULL;
622 api->ipc = NULL;
623
624 mainloop_del_ipc_client(mainloop_io);
625 // After this point api might have already been freed
626 }
627 break;
628
631 {
632 crm_ipc_t *ipc = api->ipc;
633
634 // Make sure no code with access to api can use ipc again
635 api->ipc = NULL;
636
637 // This should always be the case already, but to be safe
638 api->free_on_disconnect = false;
639
640 crm_ipc_close(ipc);
641 crm_ipc_destroy(ipc);
642 ipc_post_disconnect(api);
643 }
644 break;
645 }
646}
647
660void
662 void *user_data)
663{
664 if (api == NULL) {
665 return;
666 }
667 api->cb = cb;
668 api->user_data = user_data;
669}
670
683int
684pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request)
685{
686 int rc;
687 xmlNode *reply = NULL;
689
690 if ((api == NULL) || (api->ipc == NULL) || (request == NULL)) {
691 return EINVAL;
692 }
693 crm_log_xml_trace(request, "ipc-sent");
694
695 // Synchronous dispatch requires waiting for a reply
697 && (api->cmds != NULL)
698 && (api->cmds->reply_expected != NULL)
699 && (api->cmds->reply_expected(api, request))) {
701 }
702
703 /* The 0 here means a default timeout of 5 seconds
704 *
705 * @TODO Maybe add a timeout_ms member to pcmk_ipc_api_t and a
706 * pcmk_set_ipc_timeout() setter for it, then use it here.
707 */
708 rc = crm_ipc_send(api->ipc, request, flags, 0, &reply);
709
710 if (rc < 0) {
711 return pcmk_legacy2rc(rc);
712 } else if (rc == 0) {
713 return ENODATA;
714 }
715
716 // With synchronous dispatch, we dispatch any reply now
717 if (reply != NULL) {
718 bool more = call_api_dispatch(api, reply);
719
720 pcmk__xml_free(reply);
721
722 while (more) {
723 rc = crm_ipc_read(api->ipc);
724
725 if (rc == -EAGAIN) {
726 continue;
727 } else if (rc == -ENOMSG || rc == pcmk_ok) {
728 return pcmk_rc_ok;
729 } else if (rc < 0) {
730 return -rc;
731 }
732
733 rc = dispatch_ipc_data(crm_ipc_buffer(api->ipc), api);
735
736 if (rc == pcmk_rc_ok) {
737 more = false;
738 } else if (rc == EINPROGRESS) {
739 more = true;
740 } else {
741 continue;
742 }
743 }
744 }
745 return pcmk_rc_ok;
746}
747
770static xmlNode *
771create_purge_node_request(const pcmk_ipc_api_t *api, const char *node_name,
772 uint32_t nodeid)
773{
774 xmlNode *request = NULL;
775 const char *client = crm_system_name? crm_system_name : "client";
776
777 switch (api->server) {
778 case pcmk_ipc_attrd:
779 request = pcmk__xe_create(NULL, __func__);
784 crm_xml_add(request, PCMK__XA_ATTR_HOST, node_name);
785 if (nodeid > 0) {
786 crm_xml_add_int(request, PCMK__XA_ATTR_HOST_ID, nodeid);
787 }
788 break;
789
791 case pcmk_ipc_fenced:
793 request = pcmk__new_request(api->server, client, NULL,
794 pcmk_ipc_name(api, false),
796 if (nodeid > 0) {
797 crm_xml_add_ll(request, PCMK_XA_ID, (long long) nodeid);
798 }
799 crm_xml_add(request, PCMK_XA_UNAME, node_name);
800 break;
801
802 case pcmk_ipc_based:
803 case pcmk_ipc_execd:
805 break;
806
807 default: // pcmk_ipc_unknown (shouldn't be possible)
808 return NULL;
809 }
810 return request;
811}
812
824int
825pcmk_ipc_purge_node(pcmk_ipc_api_t *api, const char *node_name, uint32_t nodeid)
826{
827 int rc = 0;
828 xmlNode *request = NULL;
829
830 if (api == NULL) {
831 return EINVAL;
832 }
833 if ((node_name == NULL) && (nodeid == 0)) {
834 return EINVAL;
835 }
836
837 request = create_purge_node_request(api, node_name, nodeid);
838 if (request == NULL) {
839 return EOPNOTSUPP;
840 }
841 rc = pcmk__send_ipc_request(api, request);
842 pcmk__xml_free(request);
843
844 crm_debug("%s peer cache purge of node %s[%lu]: rc=%d",
845 pcmk_ipc_name(api, true), node_name, (unsigned long) nodeid, rc);
846 return rc;
847}
848
849/*
850 * Generic IPC API (to eventually be deprecated as public API and made internal)
851 */
852
853struct crm_ipc_s {
854 struct pollfd pfd;
855 int need_reply;
856 GByteArray *buffer;
857 char *server_name; // server IPC name being connected to
858 qb_ipcc_connection_t *ipc;
859};
860
876crm_ipc_t *
877crm_ipc_new(const char *name, size_t max_size)
878{
879 crm_ipc_t *client = NULL;
880
881 client = calloc(1, sizeof(crm_ipc_t));
882 if (client == NULL) {
883 crm_err("Could not create IPC connection: %s", strerror(errno));
884 return NULL;
885 }
886
887 client->server_name = strdup(name);
888 if (client->server_name == NULL) {
889 crm_err("Could not create %s IPC connection: %s",
890 name, strerror(errno));
891 free(client);
892 return NULL;
893 }
894
895 client->buffer = NULL;
896 client->pfd.fd = -1;
897 client->pfd.events = POLLIN;
898 client->pfd.revents = 0;
899
900 return client;
901}
902
911int
913{
914 uid_t cl_uid = 0;
915 gid_t cl_gid = 0;
916 pid_t found_pid = 0;
917 uid_t found_uid = 0;
918 gid_t found_gid = 0;
919 int rc = pcmk_rc_ok;
920
921 if (ipc == NULL) {
922 return EINVAL;
923 }
924
925 ipc->need_reply = FALSE;
926 ipc->ipc = qb_ipcc_connect(ipc->server_name, crm_ipc_default_buffer_size());
927 if (ipc->ipc == NULL) {
928 return errno;
929 }
930
931 rc = qb_ipcc_fd_get(ipc->ipc, &ipc->pfd.fd);
932 if (rc < 0) { // -errno
933 crm_ipc_close(ipc);
934 return -rc;
935 }
936
937 rc = pcmk_daemon_user(&cl_uid, &cl_gid);
938 rc = pcmk_legacy2rc(rc);
939 if (rc != pcmk_rc_ok) {
940 crm_ipc_close(ipc);
941 return rc;
942 }
943
944 rc = is_ipc_provider_expected(ipc->ipc, ipc->pfd.fd, cl_uid, cl_gid,
945 &found_pid, &found_uid, &found_gid);
946 if (rc != pcmk_rc_ok) {
947 if (rc == pcmk_rc_ipc_unauthorized) {
948 crm_info("%s IPC provider authentication failed: process %lld has "
949 "uid %lld (expected %lld) and gid %lld (expected %lld)",
950 ipc->server_name,
951 (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
952 (long long) found_uid, (long long) cl_uid,
953 (long long) found_gid, (long long) cl_gid);
954 }
955 crm_ipc_close(ipc);
956 return rc;
957 }
958
959 return pcmk_rc_ok;
960}
961
962void
964{
965 if (client) {
966 if (client->ipc) {
967 qb_ipcc_connection_t *ipc = client->ipc;
968
969 client->ipc = NULL;
970 qb_ipcc_disconnect(ipc);
971 }
972 }
973}
974
975void
977{
978 if (client) {
979 if (client->ipc && qb_ipcc_is_connected(client->ipc)) {
980 crm_notice("Destroying active %s IPC connection",
981 client->server_name);
982 /* The next line is basically unsafe
983 *
984 * If this connection was attached to mainloop and mainloop is active,
985 * the 'disconnected' callback will end up back here and we'll end
986 * up free'ing the memory twice - something that can still happen
987 * even without this if we destroy a connection and it closes before
988 * we call exit
989 */
990 /* crm_ipc_close(client); */
991 } else {
992 crm_trace("Destroying inactive %s IPC connection",
993 client->server_name);
994 }
995
996 if (client->buffer != NULL) {
998 }
999
1000 free(client->server_name);
1001 free(client);
1002 }
1003}
1004
1014int
1016{
1017 if ((ipc == NULL) || (fd == NULL)) {
1018 return EINVAL;
1019 }
1020 if ((ipc->ipc == NULL) || (ipc->pfd.fd < 0)) {
1021 return ENOTCONN;
1022 }
1023 *fd = ipc->pfd.fd;
1024 return pcmk_rc_ok;
1025}
1026
1027int
1029{
1030 int fd = -1;
1031
1032 if (pcmk__ipc_fd(client, &fd) != pcmk_rc_ok) {
1033 crm_err("Could not obtain file descriptor for %s IPC",
1034 ((client == NULL)? "unspecified" : client->server_name));
1035 errno = EINVAL;
1036 return -EINVAL;
1037 }
1038 return fd;
1039}
1040
1041bool
1043{
1044 bool rc = FALSE;
1045
1046 if (client == NULL) {
1047 crm_trace("No client");
1048 return FALSE;
1049
1050 } else if (client->ipc == NULL) {
1051 crm_trace("No connection");
1052 return FALSE;
1053
1054 } else if (client->pfd.fd < 0) {
1055 crm_trace("Bad descriptor");
1056 return FALSE;
1057 }
1058
1059 rc = qb_ipcc_is_connected(client->ipc);
1060 if (rc == FALSE) {
1061 client->pfd.fd = -EINVAL;
1062 }
1063 return rc;
1064}
1065
1073int
1075{
1076 int rc;
1077
1078 pcmk__assert(client != NULL);
1079
1080 if (!crm_ipc_connected(client)) {
1081 return -ENOTCONN;
1082 }
1083
1084 client->pfd.revents = 0;
1085 rc = poll(&(client->pfd), 1, 0);
1086 return (rc < 0)? -errno : rc;
1087}
1088
1089long
1091{
1092 guint8 *buffer = NULL;
1093 long rc = -ENOMSG;
1094
1095 pcmk__assert((client != NULL) && (client->ipc != NULL));
1096 buffer = g_malloc0(crm_ipc_default_buffer_size());
1097
1098 do {
1099 pcmk__ipc_header_t *header = NULL;
1100 ssize_t bytes = qb_ipcc_event_recv(client->ipc, buffer,
1102
1103 header = (pcmk__ipc_header_t *)(void *) buffer;
1104
1105 if (bytes <= 0) {
1106 crm_trace("No message received from %s IPC: %s",
1107 client->server_name, strerror(-bytes));
1108
1109 if (!crm_ipc_connected(client) || bytes == -ENOTCONN) {
1110 crm_err("Connection to %s IPC failed", client->server_name);
1111 rc = -ENOTCONN;
1113 } else if (bytes == -EAGAIN) {
1114 rc = -EAGAIN;
1115 }
1116
1117 goto done;
1118
1119 } else if (bytes != header->size + sizeof(pcmk__ipc_header_t)) {
1120 crm_trace("Message size does not match header");
1121 rc = -EBADMSG;
1123 goto done;
1124 }
1125
1126 crm_trace("Received %s IPC event %" PRId32 " size=%" PRIu32 " rc=%zu",
1127 client->server_name, header->qb.id, header->qb.size,
1128 bytes);
1129
1130 rc = pcmk__ipc_msg_append(&client->buffer, buffer);
1131
1132 if (rc == pcmk_rc_ok) {
1133 break;
1134 } else if (rc == pcmk_rc_ipc_more) {
1135 continue;
1136 } else {
1138 rc = pcmk_rc2legacy(rc);
1139 goto done;
1140 }
1141 } while (true);
1142
1143 if (client->buffer->len > 0) {
1144 /* Data length excluding the header */
1145 rc = client->buffer->len - sizeof(pcmk__ipc_header_t);
1146 }
1147
1148done:
1149 g_free(buffer);
1150 return rc;
1151}
1152
1153void
1155{
1156 pcmk__assert(client != NULL);
1157
1158 if (client->buffer != NULL) {
1159 g_byte_array_free(client->buffer, TRUE);
1160 client->buffer = NULL;
1161 }
1162}
1163
1164const char *
1166{
1167 pcmk__assert(client != NULL);
1168 CRM_CHECK(client->buffer != NULL, return NULL);
1169 return (const char *) (client->buffer->data + sizeof(pcmk__ipc_header_t));
1170}
1171
1172uint32_t
1174{
1175 pcmk__ipc_header_t *header = NULL;
1176
1177 pcmk__assert(client != NULL);
1178 if (client->buffer == NULL) {
1179 return 0;
1180 }
1181
1182 header = (pcmk__ipc_header_t *)(void*) client->buffer->data;
1183 return header->flags;
1184}
1185
1186const char *
1188{
1189 pcmk__assert(client != NULL);
1190 return client->server_name;
1191}
1192
1193// \return Standard Pacemaker return code
1194static int
1195internal_ipc_get_reply(crm_ipc_t *client, int request_id, int ms_timeout,
1196 ssize_t *bytes, xmlNode **reply)
1197{
1198 guint8 *buffer = NULL;
1199 pcmk__ipc_header_t *hdr = NULL;
1200 time_t timeout = 0;
1201 int32_t qb_timeout = -1;
1202 int rc = pcmk_rc_ok;
1203 int reply_id = 0;
1204
1205 if (ms_timeout > 0) {
1206 timeout = time(NULL) + 1 + pcmk__timeout_ms2s(ms_timeout);
1207 qb_timeout = 1000;
1208 }
1209
1210 /* get the reply */
1211 crm_trace("Expecting reply to %s IPC message %d", client->server_name,
1212 request_id);
1213
1214 buffer = g_malloc0(crm_ipc_default_buffer_size());
1215
1216 do {
1217 guint8 *data = NULL;
1218 xmlNode *xml = NULL;
1219
1220 *bytes = qb_ipcc_recv(client->ipc, buffer, crm_ipc_default_buffer_size(),
1221 qb_timeout);
1222
1223 hdr = (pcmk__ipc_header_t *) (void *) buffer;
1224
1225 if (*bytes <= 0) {
1226 if (!crm_ipc_connected(client)) {
1227 crm_err("%s IPC provider disconnected while waiting for message %d",
1228 client->server_name, request_id);
1229 break;
1230 }
1231
1232 continue;
1233
1234 } else if (*bytes != hdr->size + sizeof(pcmk__ipc_header_t)) {
1235 crm_trace("Message size does not match header");
1236 rc = -EBADMSG;
1237 break;
1238 }
1239
1240 reply_id = hdr->qb.id;
1241
1242 if (reply_id == request_id) {
1243 /* Got the reply we were expecting. */
1244 rc = pcmk__ipc_msg_append(&client->buffer, buffer);
1245
1246 if (rc == pcmk_rc_ok) {
1247 break;
1248 } else if (rc == pcmk_rc_ipc_more) {
1249 continue;
1250 } else {
1251 goto done;
1252 }
1253 }
1254
1255 data = buffer + sizeof(pcmk__ipc_header_t);
1256 xml = pcmk__xml_parse((const char *) data);
1257
1258 if (reply_id < request_id) {
1259 crm_err("Discarding old reply %d (need %d)", reply_id, request_id);
1260 crm_log_xml_notice(xml, "OldIpcReply");
1261 } else if (reply_id > request_id) {
1262 crm_err("Discarding newer reply %d (need %d)", reply_id, request_id);
1263 crm_log_xml_notice(xml, "ImpossibleReply");
1264 pcmk__assert(hdr->qb.id <= request_id);
1265 }
1266 } while (time(NULL) < timeout || (timeout == 0 && *bytes == -EAGAIN));
1267
1268 if (client->buffer->len > 0) {
1269 crm_trace("Received %u-byte reply %d to %s IPC %d: %.100s",
1270 client->buffer->len, reply_id, client->server_name,
1271 request_id, crm_ipc_buffer(client));
1272
1273 if (reply != NULL) {
1274 *reply = pcmk__xml_parse(crm_ipc_buffer(client));
1275 }
1276 } else if (*bytes < 0) {
1277 rc = (int) -*bytes; // System errno
1278 crm_trace("No reply to %s IPC %d: %s " QB_XS " rc=%d",
1279 client->server_name, request_id, pcmk_rc_str(rc), rc);
1280 }
1281 /* If bytes == 0, we'll return that to crm_ipc_send which will interpret
1282 * that as pcmk_rc_ok, log that the IPC request failed (since we did not
1283 * give it a valid reply), and return that 0 to its callers. It's up to
1284 * the callers to take appropriate action after that.
1285 */
1286
1287 /* Once we've parsed the client buffer as XML and saved it to reply,
1288 * there's no need to keep the client buffer around anymore. Free it here
1289 * to avoid having to do this anywhere crm_ipc_send is called.
1290 */
1291done:
1293 g_free(buffer);
1294 return rc;
1295}
1296
1297static int
1298discard_old_replies(crm_ipc_t *client, int32_t ms_timeout)
1299{
1300 pcmk__ipc_header_t *header = NULL;
1301 int rc = pcmk_rc_ok;
1302 ssize_t qb_rc = 0;
1304 sizeof(char));
1305
1306 qb_rc = qb_ipcc_recv(client->ipc, buffer, crm_ipc_default_buffer_size(),
1307 ms_timeout);
1308
1309 if (qb_rc < 0) {
1310 crm_warn("Sending %s IPC disabled until pending reply received",
1311 client->server_name);
1312 rc = EALREADY;
1313 goto done;
1314 }
1315
1316 header = (pcmk__ipc_header_t *)(void *) buffer;
1317
1318 if (!pcmk__valid_ipc_header(header)) {
1319 rc = EBADMSG;
1320
1321 } else if (!pcmk_is_set(header->flags, crm_ipc_multipart)
1323 crm_notice("Sending %s IPC re-enabled after pending reply received",
1324 client->server_name);
1325 client->need_reply = FALSE;
1326
1327 } else {
1328 crm_warn("Sending %s IPC disabled until multipart IPC message "
1329 "reply received", client->server_name);
1330 rc = EALREADY;
1331 }
1332
1333done:
1334 free(buffer);
1335 return rc;
1336}
1337
1351int
1352crm_ipc_send(crm_ipc_t *client, const xmlNode *message,
1353 enum crm_ipc_flags flags, int32_t ms_timeout, xmlNode **reply)
1354{
1355 int rc = 0;
1356 ssize_t bytes = 0;
1357 ssize_t sent_bytes = 0;
1358 struct iovec *iov = NULL;
1359 static uint32_t id = 0;
1360 pcmk__ipc_header_t *header;
1361 GString *iov_buffer = NULL;
1362 uint16_t index = 0;
1363
1364 if (client == NULL) {
1365 crm_notice("Can't send IPC request without connection (bug?): %.100s",
1366 message);
1367 return -ENOTCONN;
1368
1369 } else if (!crm_ipc_connected(client)) {
1370 /* Don't even bother */
1371 crm_notice("Can't send %s IPC requests: Connection closed",
1372 client->server_name);
1373 return -ENOTCONN;
1374 }
1375
1376 if (ms_timeout == 0) {
1377 ms_timeout = 5000;
1378 }
1379
1380 /* This block exists only to clear out any old replies that we haven't
1381 * yet read. We don't care about their contents since it's too late to
1382 * do anything with them, so we just read and throw them away.
1383 */
1384 if (client->need_reply) {
1385 int discard_rc = discard_old_replies(client, ms_timeout);
1386
1387 if (discard_rc != pcmk_rc_ok) {
1388 return pcmk_rc2legacy(discard_rc);
1389 }
1390 }
1391
1392 id++;
1393 CRM_LOG_ASSERT(id != 0); /* Crude wrap-around detection */
1394
1395 iov_buffer = g_string_sized_new(1024);
1396 pcmk__xml_string(message, 0, iov_buffer, 0);
1397
1398 do {
1399 ssize_t qb_rc = 0;
1400 time_t timeout = 0;
1401
1402 rc = pcmk__ipc_prepare_iov(id, iov_buffer, index, &iov, &bytes);
1403
1404 if ((rc != pcmk_rc_ok) && (rc != pcmk_rc_ipc_more)) {
1405 crm_warn("Couldn't prepare %s IPC request: %s " QB_XS " rc=%d",
1406 client->server_name, pcmk_rc_str(rc), rc);
1407 g_string_free(iov_buffer, TRUE);
1408 return pcmk_rc2legacy(rc);
1409 }
1410
1411 header = iov[0].iov_base;
1412 pcmk__set_ipc_flags(header->flags, client->server_name, flags);
1413
1415 /* Don't look for a synchronous response */
1417 }
1418
1419 if (pcmk_is_set(header->flags, crm_ipc_multipart)) {
1420 bool is_end = pcmk_is_set(header->flags, crm_ipc_multipart_end);
1421 crm_trace("Sending %s IPC request %" PRId32 " (%spart %" PRIu16 ") of "
1422 "%" PRId32 " bytes using %dms timeout",
1423 client->server_name, header->qb.id, is_end ? "final " : "",
1424 index, header->qb.size, ms_timeout);
1425 crm_trace("Text = %s", (char *) iov[1].iov_base);
1426 } else {
1427 crm_trace("Sending %s IPC request %" PRId32 " of %" PRId32 " bytes "
1428 "using %dms timeout",
1429 client->server_name, header->qb.id, header->qb.size,
1430 ms_timeout);
1431 crm_trace("Text = %s", (char *) iov[1].iov_base);
1432 }
1433
1434 /* Send the IPC request, respecting any timeout we were passed */
1435 if (ms_timeout > 0) {
1436 timeout = time(NULL) + 1 + pcmk__timeout_ms2s(ms_timeout);
1437 }
1438
1439 do {
1440 qb_rc = qb_ipcc_sendv(client->ipc, iov, 2);
1441 } while ((qb_rc == -EAGAIN) && ((timeout == 0) || (time(NULL) < timeout)));
1442
1443 /* An error occurred when sending. */
1444 if (qb_rc <= 0) {
1445 rc = (int) qb_rc; // Negative of system errno
1446 goto send_cleanup;
1447 }
1448
1449 /* Sending succeeded. The next action depends on whether this was a
1450 * multipart IPC message or not.
1451 */
1452 if (rc == pcmk_rc_ok) {
1453 /* This was either a standalone IPC message or the last part of
1454 * a multipart message. Set the return value and break out of
1455 * this processing loop.
1456 */
1457 sent_bytes += qb_rc;
1458 rc = (int) sent_bytes;
1459 break;
1460 } else {
1461 /* There's no way to get here for any value other than rc == pcmk_rc_more
1462 * given the check right after pcmk__ipc_prepare_iov.
1463 *
1464 * This was a multipart message, loop to process the next chunk.
1465 */
1466 sent_bytes += qb_rc;
1467 index++;
1468 }
1469
1471 iov = NULL;
1472 } while (true);
1473
1474 /* If we should not wait for a response, bail now */
1476 crm_trace("Not waiting for reply to %s IPC request %d",
1477 client->server_name, header->qb.id);
1478 goto send_cleanup;
1479 }
1480
1482 rc = internal_ipc_get_reply(client, header->qb.id, ms_timeout, &bytes, reply);
1483 if (rc == pcmk_rc_ok) {
1484 rc = (int) bytes; // Size of reply received
1485 } else {
1486 /* rc is either a positive system errno or a negative standard Pacemaker
1487 * return code. If it's an errno, we need to convert it back to a
1488 * negative number for comparison and return at the end of this function.
1489 */
1490 rc = pcmk_rc2legacy(rc);
1491
1492 if (ms_timeout > 0) {
1493 /* We didn't get the reply in time, so disable future sends for now.
1494 * The only alternative would be to close the connection since we
1495 * don't know how to detect and discard out-of-sequence replies.
1496 *
1497 * @TODO Implement out-of-sequence detection
1498 */
1499 client->need_reply = TRUE;
1500 }
1501 }
1502
1503 send_cleanup:
1504 if (!crm_ipc_connected(client)) {
1505 crm_notice("Couldn't send %s IPC request %d: Connection closed "
1506 QB_XS " rc=%d", client->server_name, header->qb.id, rc);
1507
1508 } else if (rc == -ETIMEDOUT) {
1509 crm_warn("%s IPC request %d failed: %s after %dms " QB_XS " rc=%d",
1510 client->server_name, header->qb.id, pcmk_strerror(rc),
1511 ms_timeout, rc);
1512 crm_write_blackbox(0, NULL);
1513
1514 } else if (rc <= 0) {
1515 crm_warn("%s IPC request %d failed: %s " QB_XS " rc=%d",
1516 client->server_name, header->qb.id,
1517 ((rc == 0)? "No bytes sent" : pcmk_strerror(rc)), rc);
1518 }
1519
1520 g_string_free(iov_buffer, TRUE);
1522 // coverity[return_overflow]
1523 return rc;
1524}
1525
1543static int
1544is_ipc_provider_expected(qb_ipcc_connection_t *qb_ipc, int sock,
1545 uid_t refuid, gid_t refgid,
1546 pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
1547{
1548 int rc = EOPNOTSUPP;
1549 pid_t found_pid = 0;
1550 uid_t found_uid = 0;
1551 gid_t found_gid = 0;
1552
1553#ifdef HAVE_QB_IPCC_AUTH_GET
1554 if (qb_ipc != NULL) {
1555 rc = qb_ipcc_auth_get(qb_ipc, &found_pid, &found_uid, &found_gid);
1556 rc = -rc; // libqb returns 0 or -errno
1557 if (rc == pcmk_rc_ok) {
1558 goto found;
1559 }
1560 }
1561#endif
1562
1563#ifdef HAVE_UCRED
1564 {
1565 struct ucred ucred;
1566 socklen_t ucred_len = sizeof(ucred);
1567
1568 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &ucred, &ucred_len) < 0) {
1569 rc = errno;
1570 } else if (ucred_len != sizeof(ucred)) {
1571 rc = EOPNOTSUPP;
1572 } else {
1573 found_pid = ucred.pid;
1574 found_uid = ucred.uid;
1575 found_gid = ucred.gid;
1576 goto found;
1577 }
1578 }
1579#endif
1580
1581#ifdef HAVE_SOCKPEERCRED
1582 {
1583 struct sockpeercred sockpeercred;
1584 socklen_t sockpeercred_len = sizeof(sockpeercred);
1585
1586 if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED,
1587 &sockpeercred, &sockpeercred_len) < 0) {
1588 rc = errno;
1589 } else if (sockpeercred_len != sizeof(sockpeercred)) {
1590 rc = EOPNOTSUPP;
1591 } else {
1592 found_pid = sockpeercred.pid;
1593 found_uid = sockpeercred.uid;
1594 found_gid = sockpeercred.gid;
1595 goto found;
1596 }
1597 }
1598#endif
1599
1600#ifdef HAVE_GETPEEREID // For example, FreeBSD
1601 if (getpeereid(sock, &found_uid, &found_gid) < 0) {
1602 rc = errno;
1603 } else {
1604 found_pid = PCMK__SPECIAL_PID;
1605 goto found;
1606 }
1607#endif
1608
1609#ifdef HAVE_GETPEERUCRED
1610 {
1611 ucred_t *ucred = NULL;
1612
1613 if (getpeerucred(sock, &ucred) < 0) {
1614 rc = errno;
1615 } else {
1616 found_pid = ucred_getpid(ucred);
1617 found_uid = ucred_geteuid(ucred);
1618 found_gid = ucred_getegid(ucred);
1619 ucred_free(ucred);
1620 goto found;
1621 }
1622 }
1623#endif
1624
1625 return rc; // If we get here, nothing succeeded
1626
1627found:
1628 if (gotpid != NULL) {
1629 *gotpid = found_pid;
1630 }
1631 if (gotuid != NULL) {
1632 *gotuid = found_uid;
1633 }
1634 if (gotgid != NULL) {
1635 *gotgid = found_gid;
1636 }
1637 if ((found_uid != 0) && (found_uid != refuid) && (found_gid != refgid)) {
1639 }
1640 return pcmk_rc_ok;
1641}
1642
1643int
1644crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid,
1645 pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
1646{
1647 int ret = is_ipc_provider_expected(NULL, sock, refuid, refgid,
1648 gotpid, gotuid, gotgid);
1649
1650 /* The old function had some very odd return codes*/
1651 if (ret == 0) {
1652 return 1;
1653 } else if (ret == pcmk_rc_ipc_unauthorized) {
1654 return 0;
1655 } else {
1656 return pcmk_rc2legacy(ret);
1657 }
1658}
1659
1660int
1662 gid_t refgid, pid_t *gotpid)
1663{
1664 static char last_asked_name[PATH_MAX / 2] = ""; /* log spam prevention */
1665 int fd;
1666 int rc = pcmk_rc_ipc_unresponsive;
1667 int auth_rc = 0;
1668 int32_t qb_rc;
1669 pid_t found_pid = 0; uid_t found_uid = 0; gid_t found_gid = 0;
1670 qb_ipcc_connection_t *c;
1671#ifdef HAVE_QB_IPCC_CONNECT_ASYNC
1672 struct pollfd pollfd = { 0, };
1673 int poll_rc;
1674
1675 c = qb_ipcc_connect_async(name, 0,
1676 &(pollfd.fd));
1677#else
1678 c = qb_ipcc_connect(name, 0);
1679#endif
1680 if (c == NULL) {
1681 crm_info("Could not connect to %s IPC: %s", name, strerror(errno));
1683 goto bail;
1684 }
1685#ifdef HAVE_QB_IPCC_CONNECT_ASYNC
1686 pollfd.events = POLLIN;
1687 do {
1688 poll_rc = poll(&pollfd, 1, 2000);
1689 } while ((poll_rc == -1) && (errno == EINTR));
1690
1691 /* If poll() failed, given that disconnect function is not registered yet,
1692 * qb_ipcc_disconnect() won't clean up the socket. In any case, call
1693 * qb_ipcc_connect_continue() here so that it may fail and do the cleanup
1694 * for us.
1695 */
1696 if (qb_ipcc_connect_continue(c) != 0) {
1697 crm_info("Could not connect to %s IPC: %s", name,
1698 (poll_rc == 0)?"timeout":strerror(errno));
1700 c = NULL; // qb_ipcc_connect_continue cleaned up for us
1701 goto bail;
1702 }
1703#endif
1704
1705 qb_rc = qb_ipcc_fd_get(c, &fd);
1706 if (qb_rc != 0) {
1707 rc = (int) -qb_rc; // System errno
1708 crm_err("Could not get fd from %s IPC: %s " QB_XS " rc=%d",
1709 name, pcmk_rc_str(rc), rc);
1710 goto bail;
1711 }
1712
1713 auth_rc = is_ipc_provider_expected(c, fd, refuid, refgid,
1714 &found_pid, &found_uid, &found_gid);
1715 if (auth_rc == pcmk_rc_ipc_unauthorized) {
1716 crm_err("Daemon (IPC %s) effectively blocked with unauthorized"
1717 " process %lld (uid: %lld, gid: %lld)",
1718 name, (long long) PCMK__SPECIAL_PID_AS_0(found_pid),
1719 (long long) found_uid, (long long) found_gid);
1721 goto bail;
1722 }
1723
1724 if (auth_rc != pcmk_rc_ok) {
1725 rc = auth_rc;
1726 crm_err("Could not get peer credentials from %s IPC: %s "
1727 QB_XS " rc=%d", name, pcmk_rc_str(rc), rc);
1728 goto bail;
1729 }
1730
1731 if (gotpid != NULL) {
1732 *gotpid = found_pid;
1733 }
1734
1735 rc = pcmk_rc_ok;
1736 if ((found_uid != refuid || found_gid != refgid)
1737 && strncmp(last_asked_name, name, sizeof(last_asked_name))) {
1738 if ((found_uid == 0) && (refuid != 0)) {
1739 crm_warn("Daemon (IPC %s) runs as root, whereas the expected"
1740 " credentials are %lld:%lld, hazard of violating"
1741 " the least privilege principle",
1742 name, (long long) refuid, (long long) refgid);
1743 } else {
1744 crm_notice("Daemon (IPC %s) runs as %lld:%lld, whereas the"
1745 " expected credentials are %lld:%lld, which may"
1746 " mean a different set of privileges than expected",
1747 name, (long long) found_uid, (long long) found_gid,
1748 (long long) refuid, (long long) refgid);
1749 }
1750 memccpy(last_asked_name, name, '\0', sizeof(last_asked_name));
1751 }
1752
1753bail:
1754 if (c != NULL) {
1755 qb_ipcc_disconnect(c);
1756 }
1757 return rc;
1758}
1759
1760// Deprecated functions kept only for backward API compatibility
1761// LCOV_EXCL_START
1762
1764
1765bool
1767{
1768 int rc = pcmk__connect_generic_ipc(client);
1769
1770 if (rc == pcmk_rc_ok) {
1771 return true;
1772 }
1773 if ((client != NULL) && (client->ipc == NULL)) {
1774 errno = (rc > 0)? rc : ENOTCONN;
1775 crm_debug("Could not establish %s IPC connection: %s (%d)",
1776 client->server_name, pcmk_rc_str(errno), errno);
1777 } else if (rc == pcmk_rc_ipc_unauthorized) {
1778 crm_err("%s IPC provider authentication failed",
1779 (client == NULL)? "Pacemaker" : client->server_name);
1780 errno = ECONNABORTED;
1781 } else {
1782 crm_err("Could not verify authenticity of %s IPC provider",
1783 (client == NULL)? "Pacemaker" : client->server_name);
1784 errno = ENOTCONN;
1785 }
1786 return false;
1787}
1788
1789// LCOV_EXCL_STOP
1790// End deprecated API
const char * name
Definition cib.c:26
int pcmk__add_mainloop_ipc(crm_ipc_t *ipc, int priority, void *userdata, const struct ipc_client_callbacks *callbacks, mainloop_io_t **source)
Connect to IPC and add it as a main loop source.
Definition mainloop.c:867
guint pcmk__timeout_ms2s(guint timeout_ms)
Definition utils.c:429
void pcmk__sleep_ms(unsigned int ms)
Definition utils.c:359
#define pcmk__assert_alloc(nmemb, size)
Definition internal.h:246
uint64_t flags
Definition remote.c:3
int pcmk_daemon_user(uid_t *uid, gid_t *gid)
Get user and group IDs of pacemaker daemon user.
Definition utils.c:143
#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
A dumping ground.
#define CRM_OP_RM_NODE_CACHE
Definition crm.h:131
char * crm_system_name
Definition utils.c:45
#define PCMK__ATTRD_CMD_PEER_REMOVE
struct pcmk__ipc_header_s pcmk__ipc_header_t
G_GNUC_INTERNAL pcmk__ipc_methods_t * pcmk__attrd_api_methods(void)
Definition ipc_attrd.c:116
G_GNUC_INTERNAL pcmk__ipc_methods_t * pcmk__pacemakerd_api_methods(void)
G_GNUC_INTERNAL bool pcmk__valid_ipc_header(const pcmk__ipc_header_t *header)
Definition ipc_common.c:45
G_GNUC_INTERNAL pcmk__ipc_methods_t * pcmk__controld_api_methods(void)
G_GNUC_INTERNAL pcmk__ipc_methods_t * pcmk__schedulerd_api_methods(void)
IPC interface to Pacemaker daemons.
void(* pcmk_ipc_callback_t)(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data, void *user_data)
Callback function type for Pacemaker daemon IPC APIs.
Definition ipc.h:98
unsigned int crm_ipc_default_buffer_size(void)
Return pacemaker's IPC buffer size.
Definition ipc_common.c:31
crm_ipc_flags
Definition ipc.h:135
@ crm_ipc_flags_none
Definition ipc.h:136
@ crm_ipc_multipart
This is a multi-part IPC message.
Definition ipc.h:154
@ crm_ipc_client_response
A response is expected in reply.
Definition ipc.h:142
@ crm_ipc_proxied
ALL replies to proxied connections need to be sent as events
Definition ipc.h:140
@ crm_ipc_multipart_end
This is the end of a multi-part IPC message.
Definition ipc.h:156
void pcmk_free_ipc_event(struct iovec *event)
Free an I/O vector created by pcmk__ipc_prepare_iov()
Definition ipc_server.c:259
pcmk_ipc_event
Possible event types that an IPC event callback can be called for.
Definition ipc.h:61
@ pcmk_ipc_event_disconnect
Termination of IPC connection.
Definition ipc.h:65
pcmk_ipc_server
Available IPC interfaces.
Definition ipc.h:48
@ pcmk_ipc_schedulerd
Scheduler.
Definition ipc.h:56
@ pcmk_ipc_based
CIB manager.
Definition ipc.h:51
@ pcmk_ipc_execd
Executor.
Definition ipc.h:53
@ pcmk_ipc_pacemakerd
Launcher.
Definition ipc.h:55
@ pcmk_ipc_attrd
Attribute manager.
Definition ipc.h:50
@ pcmk_ipc_controld
Controller.
Definition ipc.h:52
@ pcmk_ipc_fenced
Fencer.
Definition ipc.h:54
pcmk_ipc_dispatch
How IPC replies should be dispatched.
Definition ipc.h:74
@ pcmk_ipc_dispatch_sync
Sending a command will wait for any reply.
Definition ipc.h:77
@ pcmk_ipc_dispatch_poll
Caller will poll and dispatch IPC.
Definition ipc.h:76
@ pcmk_ipc_dispatch_main
Attach IPC to GMainLoop for dispatch.
Definition ipc.h:75
struct crm_ipc_s crm_ipc_t
Definition ipc.h:159
int pcmk_poll_ipc(const pcmk_ipc_api_t *api, int timeout_ms)
Check whether an IPC connection has data available (without main loop)
Definition ipc_client.c:383
void crm_ipc_destroy(crm_ipc_t *client)
Definition ipc_client.c:976
int pcmk__connect_generic_ipc(crm_ipc_t *ipc)
Definition ipc_client.c:912
void pcmk_disconnect_ipc(pcmk_ipc_api_t *api)
Disconnect an IPC API instance.
Definition ipc_client.c:610
const char * crm_ipc_name(crm_ipc_t *client)
int pcmk__ipc_fd(crm_ipc_t *ipc, int *fd)
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.
int pcmk_connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type)
Connect to a Pacemaker daemon via IPC.
Definition ipc_client.c:587
long crm_ipc_read(crm_ipc_t *client)
void pcmk__ipc_free_client_buffer(crm_ipc_t *client)
int crm_ipc_get_fd(crm_ipc_t *client)
int pcmk__send_ipc_request(pcmk_ipc_api_t *api, const xmlNode *request)
Definition ipc_client.c:684
int crm_ipc_ready(crm_ipc_t *client)
Check whether an IPC connection is ready to be read.
void pcmk__call_ipc_callback(pcmk_ipc_api_t *api, enum pcmk_ipc_event event_type, crm_exit_t status, void *event_data)
Definition ipc_client.c:144
int pcmk__connect_ipc(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type, int attempts)
Definition ipc_client.c:517
bool crm_ipc_connected(crm_ipc_t *client)
bool crm_ipc_connect(crm_ipc_t *client)
const char * pcmk_ipc_name(const pcmk_ipc_api_t *api, bool for_log)
Get the IPC name used with an IPC API connection.
Definition ipc_client.c:241
int crm_ipc_is_authentic_process(int sock, uid_t refuid, gid_t refgid, pid_t *gotpid, uid_t *gotuid, gid_t *gotgid)
Check the authenticity of the IPC socket peer process (legacy)
void crm_ipc_close(crm_ipc_t *client)
Definition ipc_client.c:963
void pcmk_free_ipc_api(pcmk_ipc_api_t *api)
Free the contents of an IPC API object.
Definition ipc_client.c:200
int pcmk__ipc_is_authentic_process_active(const char *name, uid_t refuid, gid_t refgid, pid_t *gotpid)
int pcmk__connect_ipc_retry_conrefused(pcmk_ipc_api_t *api, enum pcmk_ipc_dispatch dispatch_type, int attempts)
Definition ipc_client.c:487
uint32_t crm_ipc_buffer_flags(crm_ipc_t *client)
int pcmk_new_ipc_api(pcmk_ipc_api_t **api, enum pcmk_ipc_server server)
Create a new object for using Pacemaker daemon IPC.
Definition ipc_client.c:46
const char * crm_ipc_buffer(crm_ipc_t *client)
bool pcmk_ipc_is_connected(pcmk_ipc_api_t *api)
Check whether an IPC API connection is active.
Definition ipc_client.c:271
void pcmk_dispatch_ipc(pcmk_ipc_api_t *api)
Dispatch available messages on an IPC connection (without main loop)
Definition ipc_client.c:423
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_ipc_purge_node(pcmk_ipc_api_t *api, const char *node_name, uint32_t nodeid)
Ask a Pacemaker daemon to purge a node from its peer cache.
Definition ipc_client.c:825
void pcmk_register_ipc_callback(pcmk_ipc_api_t *api, pcmk_ipc_callback_t cb, void *user_data)
Register a callback for IPC API events.
Definition ipc_client.c:661
Deprecated Pacemaker IPC APIs.
#define PCMK__SPECIAL_PID_AS_0(p)
int pcmk__ipc_msg_append(GByteArray **buffer, guint8 *data)
Definition ipc_common.c:99
int pcmk__ipc_prepare_iov(uint32_t request, const GString *message, uint16_t index, struct iovec **result, ssize_t *bytes)
Definition ipc_server.c:595
#define pcmk__set_ipc_flags(ipc_flags, ipc_name, flags_to_set)
#define PCMK__SPECIAL_PID
#define pcmk__clear_ipc_flags(ipc_flags, ipc_name, flags_to_clear)
#define crm_info(fmt, args...)
Definition logging.h:365
void crm_write_blackbox(int nsig, const struct qb_log_callsite *callsite)
Definition logging.c:519
#define crm_warn(fmt, args...)
Definition logging.h:360
#define CRM_LOG_ASSERT(expr)
Definition logging.h:196
#define crm_notice(fmt, args...)
Definition logging.h:363
#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
#define crm_log_xml_notice(xml, text)
Definition logging.h:375
struct mainloop_io_s mainloop_io_t
Definition mainloop.h:41
void mainloop_del_ipc_client(mainloop_io_t *client)
Definition mainloop.c:947
#define pcmk__new_request(server, sender_system, recipient_node, recipient_system, task, data)
#define PCMK__VALUE_ATTRD
unsigned int timeout
Definition pcmk_fence.c:34
#define ENODATA
Definition portability.h:61
const char * pcmk_strerror(int rc)
Definition results.c:257
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition results.c:617
@ CRM_EX_DISCONNECT
Lost connection to something.
Definition results.h:266
@ pcmk_rc_ipc_more
Definition results.h:113
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_ipc_unauthorized
Definition results.h:136
@ pcmk_rc_ipc_unresponsive
Definition results.h:135
#define pcmk_ok
Definition results.h:65
int pcmk_rc2legacy(int rc)
Definition results.c:662
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:675
enum crm_exit_e crm_exit_t
Exit status codes for tools and daemons.
#define pcmk__assert(expr)
const char * pcmk__server_log_name(enum pcmk_ipc_server server)
Definition servers.c:126
const char * pcmk__server_ipc_name(enum pcmk_ipc_server server)
Definition servers.c:144
#define pcmk__plural_s(i)
struct qb_ipc_response_header qb
bool(* reply_expected)(pcmk_ipc_api_t *api, const xmlNode *request)
void(* free_data)(void *api_data)
bool(* dispatch)(pcmk_ipc_api_t *api, xmlNode *msg)
void(* post_disconnect)(pcmk_ipc_api_t *api)
int(* post_connect)(pcmk_ipc_api_t *api)
enum pcmk_ipc_dispatch dispatch_type
mainloop_io_t * mainloop_io
pcmk__ipc_methods_t * cmds
pcmk_ipc_callback_t cb
enum pcmk_ipc_server server
Wrappers for and extensions to libxml2.
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Create an XML attribute with specified name and integer value.
const char * crm_xml_add_ll(xmlNode *node, const char *name, long long value)
Create an XML attribute with specified name and long long int value.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
void pcmk__xe_set_bool_attr(xmlNodePtr node, const char *name, bool value)
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
void pcmk__xml_string(const xmlNode *data, uint32_t options, GString *buffer, int depth)
Definition xml_io.c:370
xmlNode * pcmk__xml_parse(const char *input)
Definition xml_io.c:167
#define PCMK_XA_TASK
Definition xml_names.h:424
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_UNAME
Definition xml_names.h:431
#define PCMK__XA_REAP
#define PCMK__XA_T
#define PCMK__XA_ATTR_HOST_ID
#define PCMK__XA_ATTR_HOST
#define PCMK__XA_SRC