Coverage Report

Created: 2026-01-02 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/common/ipc_pacemakerd.c
Line
Count
Source
1
/*
2
 * Copyright 2020-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 <stdlib.h>
14
#include <time.h>
15
16
#include <crm/crm.h>
17
#include <crm/common/xml.h>
18
#include <crm/common/ipc.h>
19
#include <crm/common/ipc_internal.h>
20
#include <crm/common/ipc_pacemakerd.h>
21
#include "crmcommon_private.h"
22
23
typedef struct pacemakerd_api_private_s {
24
    enum pcmk_pacemakerd_state state;
25
    char *client_uuid;
26
} pacemakerd_api_private_t;
27
28
static const char *pacemakerd_state_str[] = {
29
    PCMK__VALUE_INIT,
30
    PCMK__VALUE_STARTING_DAEMONS,
31
    PCMK__VALUE_WAIT_FOR_PING,
32
    PCMK__VALUE_RUNNING,
33
    PCMK__VALUE_SHUTTING_DOWN,
34
    PCMK__VALUE_SHUTDOWN_COMPLETE,
35
    PCMK_VALUE_REMOTE,
36
};
37
38
enum pcmk_pacemakerd_state
39
pcmk_pacemakerd_api_daemon_state_text2enum(const char *state)
40
0
{
41
0
    int i;
42
43
0
    if (state == NULL) {
44
0
        return pcmk_pacemakerd_state_invalid;
45
0
    }
46
0
    for (i=pcmk_pacemakerd_state_init; i <= pcmk_pacemakerd_state_max;
47
0
         i++) {
48
0
        if (pcmk__str_eq(state, pacemakerd_state_str[i], pcmk__str_none)) {
49
0
            return i;
50
0
        }
51
0
    }
52
0
    return pcmk_pacemakerd_state_invalid;
53
0
}
54
55
const char *
56
pcmk_pacemakerd_api_daemon_state_enum2text(
57
    enum pcmk_pacemakerd_state state)
58
0
{
59
0
    if ((state >= pcmk_pacemakerd_state_init) &&
60
0
        (state <= pcmk_pacemakerd_state_max)) {
61
0
        return pacemakerd_state_str[state];
62
0
    }
63
0
    return "invalid";
64
0
}
65
66
/*!
67
 * \internal
68
 * \brief Return a friendly string representation of a \p pacemakerd state
69
 *
70
 * \param[in] state  \p pacemakerd state
71
 *
72
 * \return A user-friendly string representation of \p state, or
73
 *         <tt>"Invalid pacemakerd state"</tt>
74
 */
75
const char *
76
pcmk__pcmkd_state_enum2friendly(enum pcmk_pacemakerd_state state)
77
0
{
78
0
    switch (state) {
79
0
        case pcmk_pacemakerd_state_init:
80
0
            return "Initializing pacemaker";
81
0
        case pcmk_pacemakerd_state_starting_daemons:
82
0
            return "Pacemaker daemons are starting";
83
0
        case pcmk_pacemakerd_state_wait_for_ping:
84
0
            return "Waiting for startup trigger from SBD";
85
0
        case pcmk_pacemakerd_state_running:
86
0
            return "Pacemaker is running";
87
0
        case pcmk_pacemakerd_state_shutting_down:
88
0
            return "Pacemaker daemons are shutting down";
89
0
        case pcmk_pacemakerd_state_shutdown_complete:
90
            /* Assuming pacemakerd won't process messages while in
91
             * shutdown_complete state unless reporting to SBD
92
             */
93
0
            return "Pacemaker daemons are shut down (reporting to SBD)";
94
0
        case pcmk_pacemakerd_state_remote:
95
0
            return PCMK__SERVER_REMOTED " is running "
96
0
                   "(on a Pacemaker Remote node)";
97
0
        default:
98
0
            return "Invalid pacemakerd state";
99
0
    }
100
0
}
101
102
/*!
103
 * \internal
104
 * \brief Get a string representation of a \p pacemakerd API reply type
105
 *
106
 * \param[in] reply  \p pacemakerd API reply type
107
 *
108
 * \return String representation of a \p pacemakerd API reply type
109
 */
110
const char *
111
pcmk__pcmkd_api_reply2str(enum pcmk_pacemakerd_api_reply reply)
112
0
{
113
0
    switch (reply) {
114
0
        case pcmk_pacemakerd_reply_ping:
115
0
            return "ping";
116
0
        case pcmk_pacemakerd_reply_shutdown:
117
0
            return "shutdown";
118
0
        default:
119
0
            return "unknown";
120
0
    }
121
0
}
122
123
// \return Standard Pacemaker return code
124
static int
125
new_data(pcmk_ipc_api_t *api)
126
0
{
127
0
    struct pacemakerd_api_private_s *private = NULL;
128
129
0
    api->api_data = calloc(1, sizeof(struct pacemakerd_api_private_s));
130
131
0
    if (api->api_data == NULL) {
132
0
        return errno;
133
0
    }
134
135
0
    private = api->api_data;
136
0
    private->state = pcmk_pacemakerd_state_invalid;
137
    /* other as with cib, controld, ... we are addressing pacemakerd just
138
       from the local node -> pid is unique and thus sufficient as an ID
139
     */
140
0
    private->client_uuid = pcmk__getpid_s();
141
142
0
    return pcmk_rc_ok;
143
0
}
144
145
static void
146
free_data(void *data)
147
0
{
148
0
    free(((struct pacemakerd_api_private_s *) data)->client_uuid);
149
0
    free(data);
150
0
}
151
152
// \return Standard Pacemaker return code
153
static int
154
post_connect(pcmk_ipc_api_t *api)
155
0
{
156
0
    struct pacemakerd_api_private_s *private = NULL;
157
158
0
    if (api->api_data == NULL) {
159
0
        return EINVAL;
160
0
    }
161
0
    private = api->api_data;
162
0
    private->state = pcmk_pacemakerd_state_invalid;
163
164
0
    return pcmk_rc_ok;
165
0
}
166
167
static void
168
post_disconnect(pcmk_ipc_api_t *api)
169
0
{
170
0
    struct pacemakerd_api_private_s *private = NULL;
171
172
0
    if (api->api_data == NULL) {
173
0
        return;
174
0
    }
175
0
    private = api->api_data;
176
0
    private->state = pcmk_pacemakerd_state_invalid;
177
178
0
    return;
179
0
}
180
181
static bool
182
reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
183
0
{
184
0
    const char *command = pcmk__xe_get(request, PCMK__XA_CRM_TASK);
185
186
0
    if (command == NULL) {
187
0
        return false;
188
0
    }
189
190
    // We only need to handle commands that functions in this file can send
191
0
    return pcmk__str_any_of(command, CRM_OP_PING, CRM_OP_QUIT, NULL);
192
0
}
193
194
static bool
195
dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
196
0
{
197
0
    crm_exit_t status = CRM_EX_OK;
198
0
    xmlNode *wrapper = NULL;
199
0
    xmlNode *msg_data = NULL;
200
0
    pcmk_pacemakerd_api_reply_t reply_data = {
201
0
        pcmk_pacemakerd_reply_unknown
202
0
    };
203
0
    const char *value = NULL;
204
205
0
    if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
206
0
        long long int ack_status = 0;
207
0
        const char *status = pcmk__xe_get(reply, PCMK_XA_STATUS);
208
0
        int rc = pcmk__scan_ll(status, &ack_status, CRM_EX_OK);
209
210
0
        if (rc != pcmk_rc_ok) {
211
0
            pcmk__warn("Ack reply from %s has invalid " PCMK_XA_STATUS " '%s' "
212
0
                       "(bug?)",
213
0
                       pcmk_ipc_name(api, true), pcmk__s(status, ""));
214
0
        }
215
0
        return ack_status == CRM_EX_INDETERMINATE;
216
0
    }
217
218
0
    value = pcmk__xe_get(reply, PCMK__XA_T);
219
0
    if (pcmk__parse_server(value) != pcmk_ipc_pacemakerd) {
220
        /* @COMPAT pacemakerd <3.0.0 sets PCMK__VALUE_CRMD as the message type,
221
         * so we can't enforce this check until we no longer support
222
         * Pacemaker Remote nodes connecting to cluster nodes older than that.
223
         */
224
0
        pcmk__trace("Message from %s has unexpected message type '%s' (bug if "
225
0
                    "not from pacemakerd <3.0.0)",
226
0
                    pcmk_ipc_name(api, true), pcmk__s(value, ""));
227
0
    }
228
229
0
    value = pcmk__xe_get(reply, PCMK__XA_SUBT);
230
0
    if (!pcmk__str_eq(value, PCMK__VALUE_RESPONSE, pcmk__str_none)) {
231
0
        pcmk__info("Unrecognizable message from %s: message type '%s' not "
232
0
                   "'" PCMK__VALUE_RESPONSE "'",
233
0
                   pcmk_ipc_name(api, true), pcmk__s(value, ""));
234
0
        status = CRM_EX_PROTOCOL;
235
0
        goto done;
236
0
    }
237
238
0
    if (pcmk__str_empty(pcmk__xe_get(reply, PCMK_XA_REFERENCE))) {
239
0
        pcmk__info("Unrecognizable message from %s: no reference",
240
0
                   pcmk_ipc_name(api, true));
241
0
        status = CRM_EX_PROTOCOL;
242
0
        goto done;
243
0
    }
244
245
0
    value = pcmk__xe_get(reply, PCMK__XA_CRM_TASK);
246
247
    // Parse useful info from reply
248
0
    wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL);
249
0
    msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
250
251
0
    if (pcmk__str_eq(value, CRM_OP_PING, pcmk__str_none)) {
252
0
        reply_data.reply_type = pcmk_pacemakerd_reply_ping;
253
0
        reply_data.data.ping.state =
254
0
            pcmk_pacemakerd_api_daemon_state_text2enum(
255
0
                pcmk__xe_get(msg_data, PCMK__XA_PACEMAKERD_STATE));
256
0
        reply_data.data.ping.status =
257
0
            pcmk__str_eq(pcmk__xe_get(msg_data, PCMK_XA_RESULT), "ok",
258
0
                         pcmk__str_casei)?pcmk_rc_ok:pcmk_rc_error;
259
260
0
        pcmk__xe_get_time(msg_data, PCMK__XA_CRM_TIMESTAMP,
261
0
                          &reply_data.data.ping.last_good);
262
0
        if (reply_data.data.ping.last_good < 0) {
263
0
            reply_data.data.ping.last_good = 0;
264
0
        }
265
266
0
        reply_data.data.ping.sys_from =
267
0
            pcmk__xe_get(msg_data, PCMK__XA_CRM_SUBSYSTEM);
268
0
    } else if (pcmk__str_eq(value, CRM_OP_QUIT, pcmk__str_none)) {
269
0
        const char *op_status = pcmk__xe_get(msg_data, PCMK__XA_OP_STATUS);
270
271
0
        reply_data.reply_type = pcmk_pacemakerd_reply_shutdown;
272
0
        reply_data.data.shutdown.status = atoi(op_status);
273
0
    } else {
274
0
        pcmk__info("Unrecognizable message from %s: unknown command '%s'",
275
0
                   pcmk_ipc_name(api, true), pcmk__s(value, ""));
276
0
        status = CRM_EX_PROTOCOL;
277
0
        goto done;
278
0
    }
279
280
0
done:
281
0
    pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
282
0
    return false;
283
0
}
284
285
pcmk__ipc_methods_t *
286
pcmk__pacemakerd_api_methods(void)
287
0
{
288
0
    pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
289
290
0
    if (cmds != NULL) {
291
0
        cmds->new_data = new_data;
292
0
        cmds->free_data = free_data;
293
0
        cmds->post_connect = post_connect;
294
0
        cmds->reply_expected = reply_expected;
295
0
        cmds->dispatch = dispatch;
296
0
        cmds->post_disconnect = post_disconnect;
297
0
    }
298
0
    return cmds;
299
0
}
300
301
static int
302
do_pacemakerd_api_call(pcmk_ipc_api_t *api, const char *ipc_name, const char *task)
303
0
{
304
0
    pacemakerd_api_private_t *private;
305
0
    char *sender_system = NULL;
306
0
    xmlNode *cmd;
307
0
    int rc;
308
309
0
    if (api == NULL) {
310
0
        return EINVAL;
311
0
    }
312
313
0
    private = api->api_data;
314
0
    pcmk__assert(private != NULL);
315
316
0
    sender_system = pcmk__assert_asprintf("%s_%s", private->client_uuid,
317
0
                                          pcmk__ipc_sys_name(ipc_name,
318
0
                                                             "client"));
319
0
    cmd = pcmk__new_request(pcmk_ipc_pacemakerd, sender_system, NULL,
320
0
                            CRM_SYSTEM_MCP, task, NULL);
321
0
    free(sender_system);
322
323
0
    if (cmd) {
324
0
        rc = pcmk__send_ipc_request(api, cmd);
325
0
        if (rc != pcmk_rc_ok) {
326
0
            pcmk__debug("Couldn't send request to %s: %s rc=%d",
327
0
                        pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc);
328
0
        }
329
0
        pcmk__xml_free(cmd);
330
0
    } else {
331
0
        rc = ENOMSG;
332
0
    }
333
334
0
    return rc;
335
0
}
336
337
int
338
pcmk_pacemakerd_api_ping(pcmk_ipc_api_t *api, const char *ipc_name)
339
0
{
340
0
    return do_pacemakerd_api_call(api, ipc_name, CRM_OP_PING);
341
0
}
342
343
int
344
pcmk_pacemakerd_api_shutdown(pcmk_ipc_api_t *api, const char *ipc_name)
345
0
{
346
0
    return do_pacemakerd_api_call(api, ipc_name, CRM_OP_QUIT);
347
0
}