Coverage Report

Created: 2025-12-31 06:13

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