Coverage Report

Created: 2026-04-04 06:09

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