Coverage Report

Created: 2026-01-03 06:24

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