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 #include <stdio.h>
13 #include <stdint.h> // uint64_t
14 #include <sys/types.h>
15
16 #include <crm/common/xml.h>
17 #include "crmcommon_private.h"
18
19 /* The IPC buffer is always 128k. If we are asked to send a message larger
20 * than that size, it will be split into multiple messages that must be
21 * reassembled on the other end.
22 */
23 #define BUFFER_SIZE (128*1024) // 128k
24
25 /*!
26 * \brief Return pacemaker's IPC buffer size
27 *
28 * \return IPC buffer size in bytes
29 */
30 unsigned int
31 crm_ipc_default_buffer_size(void)
/* ![[previous]](../icons/n_left.png)
![[next]](../icons/right.png)
![[first]](../icons/n_first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
32 {
33 return BUFFER_SIZE;
34 }
35
36 /*!
37 * \internal
38 * \brief Check whether an IPC header is valid
39 *
40 * \param[in] header IPC header to check
41 *
42 * \return true if IPC header has a supported version, false otherwise
43 */
44 bool
45 pcmk__valid_ipc_header(const pcmk__ipc_header_t *header)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
46 {
47 if (header == NULL) {
48 crm_err("IPC message without header");
49 return false;
50
51 } else if (header->version > PCMK__IPC_VERSION) {
52 crm_err("Filtering incompatible v%d IPC message (only versions <= %d supported)",
53 header->version, PCMK__IPC_VERSION);
54 return false;
55 }
56 return true;
57 }
58
59 const char *
60 pcmk__client_type_str(uint64_t client_type)
/* ![[previous]](../icons/left.png)
![[next]](../icons/right.png)
![[first]](../icons/first.png)
![[last]](../icons/last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
61 {
62 switch (client_type) {
63 case pcmk__client_ipc:
64 return "IPC";
65 case pcmk__client_tcp:
66 return "TCP";
67 case pcmk__client_tls:
68 return "TLS";
69 default:
70 return "unknown";
71 }
72 }
73
74 /*!
75 * \internal
76 * \brief Add more data to a received partial IPC message
77 *
78 * This function can be called repeatedly to build up a complete IPC message
79 * from smaller parts. It does this by inspecting flags on the message.
80 * Most of the time, IPC messages will be small enough where this function
81 * won't get called more than once, but more complex clusters can end up with
82 * very large IPC messages that don't fit in a single buffer.
83 *
84 * Important return values:
85 *
86 * - EBADMSG - Something was wrong with the data.
87 * - pcmk_rc_ipc_more - \p data was a chunk of a partial message and there is
88 * more to come. The caller should not process the message
89 * yet and should continue reading from the IPC connection.
90 * - pcmk_rc_ok - We have the complete message. The caller should process
91 * it and free the buffer to prepare for the next message.
92 *
93 * \param[in,out] buffer The buffer to add this data to
94 * \param[in] data The received IPC message or message portion.
95 *
96 * \return Standard Pacemaker return code
97 */
98 int
99 pcmk__ipc_msg_append(GByteArray **buffer, guint8 *data)
/* ![[previous]](../icons/left.png)
![[next]](../icons/n_right.png)
![[first]](../icons/first.png)
![[last]](../icons/n_last.png)
![[top]](../icons/top.png)
![[bottom]](../icons/bottom.png)
![[index]](../icons/index.png)
*/
100 {
101 pcmk__ipc_header_t *full_header = NULL;
102 pcmk__ipc_header_t *header = (pcmk__ipc_header_t *) (void *) data;
103 const guint8 *payload = (guint8 *) data + sizeof(pcmk__ipc_header_t);
104 int rc = pcmk_rc_ok;
105
106 if (!pcmk__valid_ipc_header(header)) {
107 return EBADMSG;
108 }
109
110 if (pcmk_is_set(header->flags, crm_ipc_multipart_end)) {
111 full_header = (pcmk__ipc_header_t *) (void *) (*buffer)->data;
112
113 /* This is the end of a multipart IPC message. Add the payload of the
114 * received data (so, don't include the header) to the partial buffer.
115 * Remember that this needs to include the null terminating character.
116 */
117 CRM_CHECK(buffer != NULL && *buffer != NULL && header->part_id != 0,
118 return EINVAL);
119 CRM_CHECK(full_header->qb.id == header->qb.id, return EBADMSG);
120 g_byte_array_append(*buffer, payload, header->size);
121
122 crm_trace("Received IPC message %" PRId32 " (final part %" PRIu16 ") of %"
123 PRId32 " bytes",
124 header->qb.id, header->part_id, header->qb.size);
125
126 } else if (pcmk_is_set(header->flags, crm_ipc_multipart)) {
127 if (header->part_id == 0) {
128 /* This is the first part of a multipart IPC message. Initialize
129 * the buffer with the entire message, including its header. Do
130 * not include the null terminating character.
131 */
132 CRM_CHECK(buffer != NULL && *buffer == NULL, return EINVAL);
133 *buffer = g_byte_array_new();
134
135 /* Clear any multipart flags from the header of the incoming part
136 * so they'll be clear in the fully reassembled message. This
137 * message is passed to pcmk__client_data2xml, which will extract
138 * the header flags and return them. Those flags can then be used
139 * when constructing a reply, including ACKs. We don't want these
140 * specific incoming flags to influence the reply.
141 */
142 pcmk__clear_ipc_flags(header->flags, "server", crm_ipc_multipart);
143
144 g_byte_array_append(*buffer, data,
145 sizeof(pcmk__ipc_header_t) + header->size - 1);
146
147 } else {
148 full_header = (pcmk__ipc_header_t *) (void *) (*buffer)->data;
149
150 /* This is some intermediate part of a multipart message. Add
151 * the payload of the received data (so, don't include the header)
152 * to the partial buffer and return. Do not include the null
153 * terminating character.
154 */
155 CRM_CHECK(buffer != NULL && *buffer != NULL, return EINVAL);
156 CRM_CHECK(full_header->qb.id == header->qb.id, return EBADMSG);
157 g_byte_array_append(*buffer, payload, header->size - 1);
158 }
159
160 crm_trace("Received IPC message %" PRId32 " (part %" PRIu16 ") of %"
161 PRId32 " bytes",
162 header->qb.id, header->part_id, header->qb.size);
163
164 rc = pcmk_rc_ipc_more;
165
166 } else {
167 /* This is a standalone IPC message. For simplicity in the caller,
168 * copy the entire message over into a byte array so it can be handled
169 * the same as a multipart message.
170 */
171 CRM_CHECK(buffer != NULL && *buffer == NULL, return EINVAL);
172 *buffer = g_byte_array_new();
173 g_byte_array_append(*buffer, data,
174 sizeof(pcmk__ipc_header_t) + header->size);
175
176 crm_trace("Received IPC message %" PRId32 " of %" PRId32 " bytes",
177 header->qb.id, header->qb.size);
178 }
179
180 crm_trace("Text = %s", payload);
181 crm_trace("Buffer = %s", (*buffer)->data + sizeof(pcmk__ipc_header_t));
182
183 /* The buffer's header should have a size that matches the full size of
184 * the received message, not just the last chunk of it.
185 */
186 full_header = (pcmk__ipc_header_t *) (void *) (*buffer)->data;
187 full_header->size = (*buffer)->len - sizeof(pcmk__ipc_header_t);
188
189 return rc;
190 }