Coverage Report

Created: 2026-09-28 06:52

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