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_controld.c
Line
Count
Source
1
/*
2
 * Copyright 2020-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>                      // EINVAL, errno
13
#include <stdbool.h>                    // bool, false, true
14
#include <stdint.h>                     // uint32_t
15
#include <stdlib.h>                     // NULL, calloc, free
16
#include <string.h>                     // strcmp
17
#include <unistd.h>                     // getpid
18
19
#include <glib.h>                       // g_list_*
20
#include <libxml/tree.h>                // xmlNode
21
#include <qb/qblog.h>                   // QB_XS
22
23
#include <crm/common/ipc.h>             // pcmk_ipc_*
24
#include <crm/common/ipc_controld.h>    // pcmk_controld_api_*
25
#include <crm/common/nvpair.h>          // crm_meta_name
26
#include <crm/common/options.h>         // PCMK_META_TIMEOUT
27
#include <crm/common/results.h>         // CRM_EX_*, pcmk_rc_*
28
#include <crm/common/xml.h>
29
#include <crm/crm.h>                    // CRM_OP_*
30
31
#include "crmcommon_private.h"
32
33
struct controld_api_private_s {
34
    char *client_uuid;
35
    unsigned int replies_expected;
36
};
37
38
static xmlNode *create_hello_message(const char *uuid, const char *client_name,
39
                                     const char *major_version,
40
                                     const char *minor_version);
41
42
/*!
43
 * \internal
44
 * \brief Get a string representation of a controller API reply type
45
 *
46
 * \param[in] reply  Controller API reply type
47
 *
48
 * \return String representation of a controller API reply type
49
 */
50
const char *
51
pcmk__controld_api_reply2str(enum pcmk_controld_api_reply reply)
52
0
{
53
0
    switch (reply) {
54
0
        case pcmk_controld_reply_reprobe:
55
0
            return "reprobe";
56
0
        case pcmk_controld_reply_info:
57
0
            return "info";
58
0
        case pcmk_controld_reply_resource:
59
0
            return "resource";
60
0
        case pcmk_controld_reply_ping:
61
0
            return "ping";
62
0
        case pcmk_controld_reply_nodes:
63
0
            return "nodes";
64
0
        default:
65
0
            return "unknown";
66
0
    }
67
0
}
68
69
// \return Standard Pacemaker return code
70
static int
71
new_data(pcmk_ipc_api_t *api)
72
0
{
73
0
    struct controld_api_private_s *private = NULL;
74
75
0
    api->api_data = calloc(1, sizeof(struct controld_api_private_s));
76
77
0
    if (api->api_data == NULL) {
78
0
        return errno;
79
0
    }
80
81
0
    private = api->api_data;
82
83
    /* This is set to the PID because that's how it was always done, but PIDs
84
     * are not unique because clients can be remote. The value appears to be
85
     * unused other than as part of PCMK__XA_CRM_SYS_FROM in IPC requests, which
86
     * is only compared against the internal system names (CRM_SYSTEM_TENGINE,
87
     * etc.), so it shouldn't be a problem.
88
     */
89
0
    private->client_uuid = pcmk__getpid_s();
90
91
    /* @TODO Implement a call ID model similar to the CIB, executor, and fencer
92
     *       IPC APIs, so that requests and replies can be matched, and
93
     *       duplicate replies can be discarded.
94
     */
95
0
    return pcmk_rc_ok;
96
0
}
97
98
static void
99
free_data(void *data)
100
0
{
101
0
    free(((struct controld_api_private_s *) data)->client_uuid);
102
0
    free(data);
103
0
}
104
105
// \return Standard Pacemaker return code
106
static int
107
post_connect(pcmk_ipc_api_t *api)
108
0
{
109
    /* The controller currently requires clients to register via a hello
110
     * request, but does not reply back.
111
     */
112
0
    struct controld_api_private_s *private = api->api_data;
113
0
    const char *client_name = crm_system_name? crm_system_name : "client";
114
0
    xmlNode *hello = NULL;
115
0
    int rc = pcmk_rc_ok;
116
117
0
    hello = create_hello_message(private->client_uuid, client_name,
118
0
                                 PCMK__CONTROLD_API_MAJOR,
119
0
                                 PCMK__CONTROLD_API_MINOR);
120
121
0
    rc = pcmk__send_ipc_request(api, hello);
122
0
    pcmk__xml_free(hello);
123
124
0
    if (rc != pcmk_rc_ok) {
125
0
        pcmk__info("Could not send IPC hello to %s: %s " QB_XS " rc=%s",
126
0
                   pcmk_ipc_name(api, true), pcmk_rc_str(rc), rc);
127
0
    } else {
128
0
        pcmk__debug("Sent IPC hello to %s", pcmk_ipc_name(api, true));
129
0
    }
130
131
0
    return rc;
132
0
}
133
134
static void
135
set_node_info_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
136
0
{
137
0
    if (msg_data == NULL) {
138
0
        return;
139
0
    }
140
141
0
    data->reply_type = pcmk_controld_reply_info;
142
0
    data->data.node_info.have_quorum =
143
0
        pcmk__xe_attr_is_true(msg_data, PCMK_XA_HAVE_QUORUM);
144
0
    data->data.node_info.is_remote =
145
0
        pcmk__xe_attr_is_true(msg_data, PCMK_XA_REMOTE_NODE);
146
147
    /* Integer node_info.id is currently valid only for Corosync nodes.
148
     *
149
     * @TODO: Improve handling after pcmk__node_status_t is refactored to handle
150
     * layer-specific data better.
151
     */
152
0
    pcmk__xe_get_int(msg_data, PCMK_XA_ID, &data->data.node_info.id);
153
154
0
    data->data.node_info.uuid = pcmk__xe_get(msg_data, PCMK_XA_ID);
155
0
    data->data.node_info.uname = pcmk__xe_get(msg_data, PCMK_XA_UNAME);
156
0
    data->data.node_info.state = pcmk__xe_get(msg_data, PCMK_XA_CRMD);
157
0
}
158
159
static void
160
set_ping_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
161
0
{
162
0
    if (msg_data == NULL) {
163
0
        return;
164
0
    }
165
166
0
    data->reply_type = pcmk_controld_reply_ping;
167
0
    data->data.ping.sys_from = pcmk__xe_get(msg_data, PCMK__XA_CRM_SUBSYSTEM);
168
0
    data->data.ping.fsa_state = pcmk__xe_get(msg_data, PCMK__XA_CRMD_STATE);
169
0
    data->data.ping.result = pcmk__xe_get(msg_data, PCMK_XA_RESULT);
170
0
}
171
172
static void
173
set_nodes_data(pcmk_controld_api_reply_t *data, xmlNode *msg_data)
174
0
{
175
0
    pcmk_controld_api_node_t *node_info = NULL;
176
177
0
    data->reply_type = pcmk_controld_reply_nodes;
178
0
    for (xmlNode *node = pcmk__xe_first_child(msg_data, PCMK_XE_NODE, NULL,
179
0
                                              NULL);
180
0
         node != NULL; node = pcmk__xe_next(node, PCMK_XE_NODE)) {
181
182
0
        long long id_ll = 0;
183
184
0
        node_info = pcmk__assert_alloc(1, sizeof(pcmk_controld_api_node_t));
185
186
0
        pcmk__xe_get_ll(node, PCMK_XA_ID, &id_ll);
187
0
        if (id_ll > 0) {
188
0
            node_info->id = id_ll;
189
0
        }
190
191
0
        node_info->uname = pcmk__xe_get(node, PCMK_XA_UNAME);
192
0
        node_info->state = pcmk__xe_get(node, PCMK__XA_IN_CCM);
193
0
        data->data.nodes = g_list_prepend(data->data.nodes, node_info);
194
0
    }
195
0
}
196
197
static bool
198
reply_expected(pcmk_ipc_api_t *api, const xmlNode *request)
199
0
{
200
    // We only need to handle commands that API functions can send
201
0
    return pcmk__str_any_of(pcmk__xe_get(request, PCMK__XA_CRM_TASK),
202
0
                            PCMK__CONTROLD_CMD_NODES,
203
0
                            CRM_OP_LRM_DELETE,
204
0
                            CRM_OP_LRM_FAIL,
205
0
                            CRM_OP_NODE_INFO,
206
0
                            CRM_OP_PING,
207
0
                            CRM_OP_REPROBE,
208
0
                            CRM_OP_RM_NODE_CACHE,
209
0
                            NULL);
210
0
}
211
212
static bool
213
dispatch(pcmk_ipc_api_t *api, xmlNode *reply)
214
0
{
215
0
    struct controld_api_private_s *private = api->api_data;
216
0
    crm_exit_t status = CRM_EX_OK;
217
0
    xmlNode *wrapper = NULL;
218
0
    xmlNode *msg_data = NULL;
219
0
    const char *value = NULL;
220
0
    pcmk_controld_api_reply_t reply_data = {
221
0
        pcmk_controld_reply_unknown, NULL, NULL,
222
0
    };
223
224
0
    if (pcmk__xe_is(reply, PCMK__XE_ACK)) {
225
        /* ACKs are trivial responses that do not count toward expected replies,
226
         * and do not have all the fields that validation requires, so skip that
227
         * processing.
228
         */
229
0
        return private->replies_expected > 0;
230
0
    }
231
232
0
    if (private->replies_expected > 0) {
233
0
        private->replies_expected--;
234
0
    }
235
236
    // Do some basic validation of the reply
237
238
0
    value = pcmk__xe_get(reply, PCMK__XA_SUBT);
239
0
    if (pcmk__str_eq(value, PCMK__VALUE_REQUEST, pcmk__str_none)) {
240
        /* @COMPAT Controllers <3.0.0 set PCMK__XA_SUBT to PCMK__VALUE_REQUEST
241
         * for certain replies. Once we no longer support Pacemaker Remote nodes
242
         * connecting to cluster nodes <3.0.0, or rolling upgrades from <3.0.0,
243
         * we can drop this check.
244
         */
245
0
        pcmk__trace("Received a reply that was marked as a request (bug unless "
246
0
                    "sent by a controller <3.0.0)");
247
248
0
    } else if (!pcmk__str_eq(value, PCMK__VALUE_RESPONSE, pcmk__str_none)) {
249
0
        pcmk__info("Unrecognizable message from controller: invalid message "
250
0
                   "type '%s'",
251
0
                   pcmk__s(value, ""));
252
0
        status = CRM_EX_PROTOCOL;
253
0
        goto done;
254
0
    }
255
256
0
    if (pcmk__str_empty(pcmk__xe_get(reply, PCMK_XA_REFERENCE))) {
257
0
        pcmk__info("Unrecognizable message from controller: no reference");
258
0
        status = CRM_EX_PROTOCOL;
259
0
        goto done;
260
0
    }
261
262
0
    value = pcmk__xe_get(reply, PCMK__XA_CRM_TASK);
263
0
    if (pcmk__str_empty(value)) {
264
0
        pcmk__info("Unrecognizable message from controller: no command name");
265
0
        status = CRM_EX_PROTOCOL;
266
0
        goto done;
267
0
    }
268
269
    // Parse useful info from reply
270
271
0
    reply_data.feature_set = pcmk__xe_get(reply, PCMK_XA_VERSION);
272
0
    reply_data.host_from = pcmk__xe_get(reply, PCMK__XA_SRC);
273
274
0
    wrapper = pcmk__xe_first_child(reply, PCMK__XE_CRM_XML, NULL, NULL);
275
0
    msg_data = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
276
277
0
    if (!strcmp(value, CRM_OP_REPROBE)) {
278
0
        reply_data.reply_type = pcmk_controld_reply_reprobe;
279
280
0
    } else if (!strcmp(value, CRM_OP_NODE_INFO)) {
281
0
        set_node_info_data(&reply_data, msg_data);
282
283
0
    } else if (!strcmp(value, CRM_OP_INVOKE_LRM)) {
284
0
        reply_data.reply_type = pcmk_controld_reply_resource;
285
0
        reply_data.data.resource.node_state = msg_data;
286
287
0
    } else if (!strcmp(value, CRM_OP_PING)) {
288
0
        set_ping_data(&reply_data, msg_data);
289
290
0
    } else if (!strcmp(value, PCMK__CONTROLD_CMD_NODES)) {
291
0
        set_nodes_data(&reply_data, msg_data);
292
293
0
    } else {
294
0
        pcmk__info("Unrecognizable message from controller: unknown command "
295
0
                   "'%s'",
296
0
                   value);
297
0
        status = CRM_EX_PROTOCOL;
298
0
    }
299
300
0
done:
301
0
    pcmk__call_ipc_callback(api, pcmk_ipc_event_reply, status, &reply_data);
302
303
    // Free any reply data that was allocated
304
0
    if (reply_data.reply_type == pcmk_controld_reply_nodes) {
305
0
        g_list_free_full(reply_data.data.nodes, free);
306
0
    }
307
308
0
    return false; // No further replies needed
309
0
}
310
311
pcmk__ipc_methods_t *
312
pcmk__controld_api_methods(void)
313
0
{
314
0
    pcmk__ipc_methods_t *cmds = calloc(1, sizeof(pcmk__ipc_methods_t));
315
316
0
    if (cmds == NULL) {
317
0
        return NULL;
318
0
    }
319
320
0
    cmds->new_data = new_data;
321
0
    cmds->free_data = free_data;
322
0
    cmds->post_connect = post_connect;
323
0
    cmds->reply_expected = reply_expected;
324
0
    cmds->dispatch = dispatch;
325
0
    return cmds;
326
0
}
327
328
/*!
329
 * \internal
330
 * \brief Create XML for a controller IPC request
331
 *
332
 * \param[in] api       Controller connection
333
 * \param[in] op        Controller IPC command name
334
 * \param[in] node      Node name to set as destination host
335
 * \param[in] msg_data  XML to attach to request as message data
336
 *
337
 * \return Newly allocated XML for request
338
 */
339
static xmlNode *
340
create_controller_request(const pcmk_ipc_api_t *api, const char *op,
341
                          const char *node, xmlNode *msg_data)
342
0
{
343
0
    struct controld_api_private_s *private = NULL;
344
0
    const char *sys_to = NULL;
345
0
    char *sender_system = NULL;
346
0
    xmlNode *request = NULL;
347
348
0
    if (api == NULL) {
349
0
        return NULL;
350
0
    }
351
352
0
    private = api->api_data;
353
354
0
    if ((node == NULL) && !strcmp(op, CRM_OP_PING)) {
355
0
        sys_to = CRM_SYSTEM_DC;
356
0
    } else {
357
0
        sys_to = CRM_SYSTEM_CRMD;
358
0
    }
359
360
0
    sender_system = pcmk__assert_asprintf("%s_%s", private->client_uuid,
361
0
                                          pcmk__s(crm_system_name, "client"));
362
0
    request = pcmk__new_request(pcmk_ipc_controld, sender_system, node, sys_to,
363
0
                                op, msg_data);
364
365
0
    free(sender_system);
366
0
    return request;
367
0
}
368
369
// \return Standard Pacemaker return code
370
static int
371
send_controller_request(pcmk_ipc_api_t *api, const xmlNode *request,
372
                        bool reply_is_expected)
373
0
{
374
0
    if (pcmk__xe_get(request, PCMK_XA_REFERENCE) == NULL) {
375
0
        return EINVAL;
376
0
    }
377
378
0
    if (reply_is_expected) {
379
0
        struct controld_api_private_s *private = api->api_data;
380
381
0
        private->replies_expected++;
382
0
    }
383
384
0
    return pcmk__send_ipc_request(api, request);
385
0
}
386
387
static xmlNode *
388
create_reprobe_message_data(const char *target_node, const char *router_node)
389
0
{
390
0
    xmlNode *msg_data = NULL;
391
392
0
    msg_data = pcmk__xe_create(NULL, "data_for_" CRM_OP_REPROBE);
393
0
    pcmk__xe_set(msg_data, PCMK__META_ON_NODE, target_node);
394
395
0
    if ((router_node != NULL) && !pcmk__str_eq(router_node, target_node, pcmk__str_casei)) {
396
0
        pcmk__xe_set(msg_data, PCMK__XA_ROUTER_NODE, router_node);
397
0
    }
398
399
0
    return msg_data;
400
0
}
401
402
/*!
403
 * \brief Send a reprobe controller operation
404
 *
405
 * \param[in,out] api          Controller connection
406
 * \param[in]     target_node  Name of node to reprobe
407
 * \param[in]     router_node  Router node for host
408
 *
409
 * \return Standard Pacemaker return code
410
 * \note Event callback will get a reply of type pcmk_controld_reply_reprobe.
411
 */
412
int
413
pcmk_controld_api_reprobe(pcmk_ipc_api_t *api, const char *target_node,
414
                          const char *router_node)
415
0
{
416
0
    xmlNode *request = NULL;
417
0
    xmlNode *msg_data = NULL;
418
0
    int rc = pcmk_rc_ok;
419
420
0
    if (api == NULL) {
421
0
        return EINVAL;
422
0
    }
423
424
0
    if (router_node == NULL) {
425
0
        router_node = target_node;
426
0
    }
427
428
0
    pcmk__debug("Sending %s IPC request to reprobe %s via %s",
429
0
                pcmk_ipc_name(api, true), pcmk__s(target_node, "local node"),
430
0
                pcmk__s(router_node, "local node"));
431
432
0
    msg_data = create_reprobe_message_data(target_node, router_node);
433
0
    request = create_controller_request(api, CRM_OP_REPROBE, router_node,
434
0
                                        msg_data);
435
0
    rc = send_controller_request(api, request, true);
436
437
0
    pcmk__xml_free(msg_data);
438
0
    pcmk__xml_free(request);
439
0
    return rc;
440
0
}
441
442
/*!
443
 * \brief Send a "node info" controller operation
444
 *
445
 * \param[in,out] api     Controller connection
446
 * \param[in]     nodeid  ID of node to get info for (or 0 for local node)
447
 *
448
 * \return Standard Pacemaker return code
449
 * \note Event callback will get a reply of type pcmk_controld_reply_info.
450
 */
451
int
452
pcmk_controld_api_node_info(pcmk_ipc_api_t *api, uint32_t nodeid)
453
0
{
454
0
    xmlNode *request = NULL;
455
0
    int rc = pcmk_rc_ok;
456
457
0
    request = create_controller_request(api, CRM_OP_NODE_INFO, NULL, NULL);
458
0
    if (request == NULL) {
459
0
        return EINVAL;
460
0
    }
461
462
0
    if (nodeid > 0) {
463
0
        pcmk__xe_set_ll(request, PCMK_XA_ID, nodeid);
464
0
    }
465
466
0
    rc = send_controller_request(api, request, true);
467
0
    pcmk__xml_free(request);
468
0
    return rc;
469
0
}
470
471
/*!
472
 * \brief Ask the controller for status
473
 *
474
 * \param[in,out] api        Controller connection
475
 * \param[in]     node_name  Name of node whose status is desired (NULL for DC)
476
 *
477
 * \return Standard Pacemaker return code
478
 * \note Event callback will get a reply of type pcmk_controld_reply_ping.
479
 */
480
int
481
pcmk_controld_api_ping(pcmk_ipc_api_t *api, const char *node_name)
482
0
{
483
0
    xmlNode *request = NULL;
484
0
    int rc = pcmk_rc_ok;
485
486
0
    request = create_controller_request(api, CRM_OP_PING, node_name, NULL);
487
0
    if (request == NULL) {
488
0
        return EINVAL;
489
0
    }
490
491
0
    rc = send_controller_request(api, request, true);
492
0
    pcmk__xml_free(request);
493
0
    return rc;
494
0
}
495
496
/*!
497
 * \brief Ask the controller for cluster information
498
 *
499
 * \param[in,out] api  Controller connection
500
 *
501
 * \return Standard Pacemaker return code
502
 * \note Event callback will get a reply of type pcmk_controld_reply_nodes.
503
 */
504
int
505
pcmk_controld_api_list_nodes(pcmk_ipc_api_t *api)
506
0
{
507
0
    xmlNode *request = NULL;
508
0
    int rc = EINVAL;
509
510
0
    request = create_controller_request(api, PCMK__CONTROLD_CMD_NODES, NULL,
511
0
                                        NULL);
512
0
    if (request != NULL) {
513
0
        rc = send_controller_request(api, request, true);
514
0
        pcmk__xml_free(request);
515
0
    }
516
517
0
    return rc;
518
0
}
519
520
// \return Standard Pacemaker return code
521
static int
522
controller_resource_op(pcmk_ipc_api_t *api, const char *op,
523
                       const char *target_node, const char *router_node,
524
                       bool cib_only, const char *rsc_id,
525
                       const char *rsc_long_id, const char *standard,
526
                       const char *provider, const char *type)
527
0
{
528
0
    int rc = pcmk_rc_ok;
529
0
    char *key = NULL;
530
0
    xmlNode *request = NULL;
531
0
    xmlNode *msg_data = NULL;
532
0
    xmlNode *xml_rsc = NULL;
533
0
    xmlNode *params = NULL;
534
535
0
    if (api == NULL) {
536
0
        return EINVAL;
537
0
    }
538
539
0
    if (router_node == NULL) {
540
0
        router_node = target_node;
541
0
    }
542
543
0
    msg_data = pcmk__xe_create(NULL, PCMK__XE_RSC_OP);
544
545
    /* The controller logs the transition key from resource op requests, so we
546
     * need to have *something* for it.
547
     * @TODO don't use "crm-resource"
548
     */
549
0
    key = pcmk__transition_key(0, getpid(), 0,
550
0
                               "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx");
551
0
    pcmk__xe_set(msg_data, PCMK__XA_TRANSITION_KEY, key);
552
0
    free(key);
553
554
0
    pcmk__xe_set(msg_data, PCMK__META_ON_NODE, target_node);
555
0
    if (!pcmk__str_eq(router_node, target_node, pcmk__str_casei)) {
556
0
        pcmk__xe_set(msg_data, PCMK__XA_ROUTER_NODE, router_node);
557
0
    }
558
559
0
    if (cib_only) {
560
        // Indicate that only the CIB needs to be cleaned
561
0
        pcmk__xe_set(msg_data, PCMK__XA_MODE, PCMK__VALUE_CIB);
562
0
    }
563
564
0
    xml_rsc = pcmk__xe_create(msg_data, PCMK_XE_PRIMITIVE);
565
0
    pcmk__xe_set(xml_rsc, PCMK_XA_ID, rsc_id);
566
0
    pcmk__xe_set(xml_rsc, PCMK__XA_LONG_ID, rsc_long_id);
567
0
    pcmk__xe_set(xml_rsc, PCMK_XA_CLASS, standard);
568
0
    pcmk__xe_set(xml_rsc, PCMK_XA_PROVIDER, provider);
569
0
    pcmk__xe_set(xml_rsc, PCMK_XA_TYPE, type);
570
571
0
    params = pcmk__xe_create(msg_data, PCMK__XE_ATTRIBUTES);
572
0
    pcmk__xe_set(params, PCMK_XA_CRM_FEATURE_SET, CRM_FEATURE_SET);
573
574
    // The controller parses the timeout from the request
575
0
    key = crm_meta_name(PCMK_META_TIMEOUT);
576
0
    pcmk__xe_set(params, key, "60000");  /* 1 minute */ //@TODO pass as arg
577
0
    free(key);
578
579
0
    request = create_controller_request(api, op, router_node, msg_data);
580
0
    rc = send_controller_request(api, request, true);
581
0
    pcmk__xml_free(msg_data);
582
0
    pcmk__xml_free(request);
583
0
    return rc;
584
0
}
585
586
/*!
587
 * \brief Ask the controller to fail a resource
588
 *
589
 * \param[in,out] api          Controller connection
590
 * \param[in]     target_node  Name of node resource is on
591
 * \param[in]     router_node  Router node for target
592
 * \param[in]     rsc_id       ID of resource to fail
593
 * \param[in]     rsc_long_id  Long ID of resource (if any)
594
 * \param[in]     standard     Standard of resource
595
 * \param[in]     provider     Provider of resource (if any)
596
 * \param[in]     type         Type of resource to fail
597
 *
598
 * \return Standard Pacemaker return code
599
 * \note Event callback will get a reply of type pcmk_controld_reply_resource.
600
 */
601
int
602
pcmk_controld_api_fail(pcmk_ipc_api_t *api,
603
                       const char *target_node, const char *router_node,
604
                       const char *rsc_id, const char *rsc_long_id,
605
                       const char *standard, const char *provider,
606
                       const char *type)
607
0
{
608
0
    pcmk__debug("Sending %s IPC request to fail %s (a.k.a. %s) on %s via %s",
609
0
                pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"),
610
0
                pcmk__s(rsc_long_id, "no other names"),
611
0
                pcmk__s(target_node, "unspecified node"),
612
0
                pcmk__s(router_node, "unspecified node"));
613
0
    return controller_resource_op(api, CRM_OP_LRM_FAIL, target_node,
614
0
                                  router_node, false, rsc_id, rsc_long_id,
615
0
                                  standard, provider, type);
616
0
}
617
618
/*!
619
 * \brief Ask the controller to refresh a resource
620
 *
621
 * \param[in,out] api          Controller connection
622
 * \param[in]     target_node  Name of node resource is on
623
 * \param[in]     router_node  Router node for target
624
 * \param[in]     rsc_id       ID of resource to refresh
625
 * \param[in]     rsc_long_id  Long ID of resource (if any)
626
 * \param[in]     standard     Standard of resource
627
 * \param[in]     provider     Provider of resource (if any)
628
 * \param[in]     type         Type of resource
629
 * \param[in]     cib_only     If true, clean resource from CIB only
630
 *
631
 * \return Standard Pacemaker return code
632
 * \note Event callback will get a reply of type pcmk_controld_reply_resource.
633
 */
634
int
635
pcmk_controld_api_refresh(pcmk_ipc_api_t *api, const char *target_node,
636
                          const char *router_node,
637
                          const char *rsc_id, const char *rsc_long_id,
638
                          const char *standard, const char *provider,
639
                          const char *type, bool cib_only)
640
0
{
641
0
    pcmk__debug("Sending %s IPC request to refresh %s (a.k.a. %s) on %s via %s",
642
0
                pcmk_ipc_name(api, true), pcmk__s(rsc_id, "unknown resource"),
643
0
                pcmk__s(rsc_long_id, "no other names"),
644
0
                pcmk__s(target_node, "unspecified node"),
645
0
                pcmk__s(router_node, "unspecified node"));
646
0
    return controller_resource_op(api, CRM_OP_LRM_DELETE, target_node,
647
0
                                  router_node, cib_only, rsc_id, rsc_long_id,
648
0
                                  standard, provider, type);
649
0
}
650
651
/*!
652
 * \brief Get the number of IPC replies currently expected from the controller
653
 *
654
 * \param[in] api  Controller IPC API connection
655
 *
656
 * \return Number of replies expected
657
 */
658
unsigned int
659
pcmk_controld_api_replies_expected(const pcmk_ipc_api_t *api)
660
0
{
661
0
    struct controld_api_private_s *private = NULL;
662
663
0
    CRM_CHECK(api != NULL, return 0);
664
665
0
    private = api->api_data;
666
0
    return private->replies_expected;
667
0
}
668
669
/*!
670
 * \internal
671
 * \brief Create XML for a controller IPC "hello" message
672
 */
673
static xmlNode *
674
create_hello_message(const char *uuid, const char *client_name,
675
                     const char *major_version, const char *minor_version)
676
0
{
677
0
    xmlNode *hello_node = NULL;
678
0
    xmlNode *hello = NULL;
679
0
    char *sender_system = NULL;
680
681
0
    if (pcmk__str_empty(uuid) || pcmk__str_empty(client_name)
682
0
        || pcmk__str_empty(major_version) || pcmk__str_empty(minor_version)) {
683
0
        pcmk__err("Could not create IPC hello message from %s (UUID %s): "
684
0
                  "missing information",
685
0
                  pcmk__s(client_name, "unknown client"),
686
0
                  pcmk__s(uuid, "unknown"));
687
0
        return NULL;
688
0
    }
689
690
0
    hello_node = pcmk__xe_create(NULL, PCMK__XE_OPTIONS);
691
0
    pcmk__xe_set(hello_node, PCMK__XA_MAJOR_VERSION, major_version);
692
0
    pcmk__xe_set(hello_node, PCMK__XA_MINOR_VERSION, minor_version);
693
0
    pcmk__xe_set(hello_node, PCMK__XA_CLIENT_NAME, client_name);
694
695
    // @TODO Nothing uses this. Drop, or keep for debugging?
696
0
    pcmk__xe_set(hello_node, PCMK__XA_CLIENT_UUID, uuid);
697
698
0
    sender_system = pcmk__assert_asprintf("%s_%s", uuid, client_name);
699
0
    hello = pcmk__new_request(pcmk_ipc_controld, sender_system, NULL, NULL,
700
0
                              CRM_OP_HELLO, hello_node);
701
0
    free(sender_system);
702
0
    pcmk__xml_free(hello_node);
703
704
0
    if (hello == NULL) {
705
0
        pcmk__err("Could not create IPC hello message from %s (UUID %s): "
706
0
                  "Request creation failed",
707
0
                  client_name, uuid);
708
0
        return NULL;
709
0
    }
710
711
0
    pcmk__trace("Created hello message from %s (UUID %s)", client_name, uuid);
712
0
    return hello;
713
0
}