Coverage Report

Created: 2026-03-31 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/pacemaker/lib/cib/cib_client.c
Line
Count
Source
1
/*
2
 * Copyright 2004-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
#include <unistd.h>
12
#include <stdbool.h>
13
#include <stdlib.h>
14
#include <stdio.h>
15
#include <stdarg.h>
16
#include <string.h>
17
#include <pwd.h>
18
19
#include <sys/stat.h>
20
#include <sys/types.h>
21
22
#include <glib.h>
23
24
#include <crm/crm.h>
25
#include <crm/cib/internal.h>
26
#include <crm/common/xml.h>
27
28
static GHashTable *cib_op_callback_table = NULL;
29
30
static gint
31
ciblib_GCompareFunc(gconstpointer a, gconstpointer b)
32
0
{
33
0
    int rc = 0;
34
0
    const cib_notify_client_t *a_client = a;
35
0
    const cib_notify_client_t *b_client = b;
36
37
0
    CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
38
0
    rc = strcmp(a_client->event, b_client->event);
39
0
    if (rc == 0) {
40
0
        if (a_client->callback == b_client->callback) {
41
0
            return 0;
42
0
        } else if (((long)a_client->callback) < ((long)b_client->callback)) {
43
0
            pcmk__trace("callbacks for %s are not equal: %p < %p",
44
0
                        a_client->event, a_client->callback, b_client->callback);
45
0
            return -1;
46
0
        }
47
0
        pcmk__trace("callbacks for %s are not equal: %p > %p", a_client->event,
48
0
                    a_client->callback, b_client->callback);
49
0
        return 1;
50
0
    }
51
0
    return rc;
52
0
}
53
54
static int
55
cib_client_add_notify_callback(cib_t * cib, const char *event,
56
                               void (*callback) (const char *event,
57
                                                 xmlNode * msg))
58
0
{
59
0
    GList *list_item = NULL;
60
0
    cib_notify_client_t *new_client = NULL;
61
62
0
    if ((cib->variant != cib_native) && (cib->variant != cib_remote)) {
63
0
        return -EPROTONOSUPPORT;
64
0
    }
65
66
0
    pcmk__trace("Adding callback for %s events (%u)", event,
67
0
                g_list_length(cib->notify_list));
68
69
0
    new_client = pcmk__assert_alloc(1, sizeof(cib_notify_client_t));
70
0
    new_client->event = event;
71
0
    new_client->callback = callback;
72
73
0
    list_item = g_list_find_custom(cib->notify_list, new_client,
74
0
                                   ciblib_GCompareFunc);
75
76
0
    if (list_item != NULL) {
77
0
        pcmk__warn("Callback already present");
78
0
        free(new_client);
79
0
        return -EINVAL;
80
81
0
    } else {
82
0
        cib->notify_list = g_list_append(cib->notify_list, new_client);
83
84
0
        cib->cmds->register_notification(cib, event, 1);
85
86
0
        pcmk__trace("Callback added (%d)", g_list_length(cib->notify_list));
87
0
    }
88
0
    return pcmk_ok;
89
0
}
90
91
static int
92
get_notify_list_event_count(cib_t *cib, const char *event)
93
0
{
94
0
    int count = 0;
95
96
0
    for (GList *iter = g_list_first(cib->notify_list); iter != NULL;
97
0
         iter = iter->next) {
98
0
        cib_notify_client_t *client = (cib_notify_client_t *) iter->data;
99
100
0
        if (strcmp(client->event, event) == 0) {
101
0
            count++;
102
0
        }
103
0
    }
104
0
    pcmk__trace("event(%s) count : %d", event, count);
105
0
    return count;
106
0
}
107
108
static int
109
cib_client_del_notify_callback(cib_t *cib, const char *event,
110
                               void (*callback) (const char *event,
111
                                                 xmlNode *msg))
112
0
{
113
0
    GList *list_item = NULL;
114
0
    cib_notify_client_t *new_client = NULL;
115
116
0
    if (cib->variant != cib_native && cib->variant != cib_remote) {
117
0
        return -EPROTONOSUPPORT;
118
0
    }
119
120
0
    if (get_notify_list_event_count(cib, event) == 0) {
121
0
        pcmk__debug("The callback of the event does not exist(%s)", event);
122
0
        return pcmk_ok;
123
0
    }
124
125
0
    pcmk__debug("Removing callback for %s events", event);
126
127
0
    new_client = pcmk__assert_alloc(1, sizeof(cib_notify_client_t));
128
0
    new_client->event = event;
129
0
    new_client->callback = callback;
130
131
0
    list_item = g_list_find_custom(cib->notify_list, new_client, ciblib_GCompareFunc);
132
133
0
    if (list_item != NULL) {
134
0
        cib_notify_client_t *list_client = list_item->data;
135
136
0
        cib->notify_list = g_list_remove(cib->notify_list, list_client);
137
0
        free(list_client);
138
139
0
        pcmk__trace("Removed callback");
140
141
0
    } else {
142
0
        pcmk__trace("Callback not present");
143
0
    }
144
145
0
    if (get_notify_list_event_count(cib, event) == 0) {
146
        /* When there is not the registration of the event, the processing turns off a notice. */
147
0
        cib->cmds->register_notification(cib, event, 0);
148
0
    }
149
150
0
    free(new_client);
151
0
    return pcmk_ok;
152
0
}
153
154
static gboolean
155
cib_async_timeout_handler(gpointer data)
156
0
{
157
0
    struct timer_rec_s *timer = data;
158
159
0
    pcmk__debug("Async call %d timed out after %ds", timer->call_id,
160
0
                timer->timeout);
161
0
    cib_native_callback(timer->cib, NULL, timer->call_id, -ETIME);
162
163
    // We remove the handler in remove_cib_op_callback()
164
0
    return G_SOURCE_CONTINUE;
165
0
}
166
167
static gboolean
168
cib_client_register_callback_full(cib_t *cib, int call_id, int timeout,
169
                                  gboolean only_success, void *user_data,
170
                                  const char *callback_name,
171
                                  void (*callback)(xmlNode *, int, int,
172
                                                   xmlNode *, void *),
173
                                  void (*free_func)(void *))
174
0
{
175
0
    cib_callback_client_t *blob = NULL;
176
177
0
    if (call_id < 0) {
178
0
        if (only_success == FALSE) {
179
0
            callback(NULL, call_id, call_id, NULL, user_data);
180
0
        } else {
181
0
            pcmk__warn("CIB call failed: %s", pcmk_strerror(call_id));
182
0
        }
183
0
        if (user_data && free_func) {
184
0
            free_func(user_data);
185
0
        }
186
0
        return FALSE;
187
0
    }
188
189
0
    blob = pcmk__assert_alloc(1, sizeof(cib_callback_client_t));
190
0
    blob->id = callback_name;
191
0
    blob->only_success = only_success;
192
0
    blob->user_data = user_data;
193
0
    blob->callback = callback;
194
0
    blob->free_func = free_func;
195
196
0
    if (timeout > 0) {
197
0
        struct timer_rec_s *async_timer =
198
0
            pcmk__assert_alloc(1, sizeof(struct timer_rec_s));
199
200
0
        blob->timer = async_timer;
201
202
0
        async_timer->cib = cib;
203
0
        async_timer->call_id = call_id;
204
0
        async_timer->timeout = timeout * 1000;
205
0
        async_timer->ref = pcmk__create_timer(async_timer->timeout,
206
0
                                              cib_async_timeout_handler,
207
0
                                              async_timer);
208
0
    }
209
210
0
    pcmk__trace("Adding callback %s for call %d", callback_name, call_id);
211
0
    pcmk__intkey_table_insert(cib_op_callback_table, call_id, blob);
212
213
0
    return TRUE;
214
0
}
215
216
static gboolean
217
cib_client_register_callback(cib_t *cib, int call_id, int timeout,
218
                             gboolean only_success, void *user_data,
219
                             const char *callback_name,
220
                             void (*callback) (xmlNode *, int, int, xmlNode *,
221
                                               void *))
222
0
{
223
0
    return cib_client_register_callback_full(cib, call_id, timeout,
224
0
                                             only_success, user_data,
225
0
                                             callback_name, callback, NULL);
226
0
}
227
228
static int
229
cib_client_noop(cib_t * cib, int call_options)
230
0
{
231
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_NOOP, NULL, NULL, NULL, NULL,
232
0
                           call_options, cib->user);
233
0
}
234
235
static int
236
cib_client_ping(cib_t * cib, xmlNode ** output_data, int call_options)
237
0
{
238
0
    return cib_internal_op(cib, CRM_OP_PING, NULL, NULL, NULL, output_data,
239
0
                           call_options, cib->user);
240
0
}
241
242
static int
243
cib_client_query(cib_t * cib, const char *section, xmlNode ** output_data, int call_options)
244
0
{
245
0
    return cib->cmds->query_from(cib, NULL, section, output_data, call_options);
246
0
}
247
248
static int
249
cib_client_query_from(cib_t * cib, const char *host, const char *section,
250
                      xmlNode ** output_data, int call_options)
251
0
{
252
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_QUERY, host, section, NULL,
253
0
                           output_data, call_options, cib->user);
254
0
}
255
256
static int
257
set_secondary(cib_t *cib, int call_options)
258
0
{
259
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_SECONDARY, NULL, NULL, NULL,
260
0
                           NULL, call_options, cib->user);
261
0
}
262
263
static int
264
set_primary(cib_t *cib, int call_options)
265
0
{
266
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_PRIMARY, NULL, NULL, NULL,
267
0
                           NULL, call_options, cib->user);
268
0
}
269
270
static int
271
cib_client_bump_epoch(cib_t * cib, int call_options)
272
0
{
273
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_BUMP, NULL, NULL, NULL, NULL,
274
0
                           call_options, cib->user);
275
0
}
276
277
static int
278
cib_client_upgrade(cib_t * cib, int call_options)
279
0
{
280
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_UPGRADE, NULL, NULL, NULL,
281
0
                           NULL, call_options, cib->user);
282
0
}
283
284
static int
285
cib_client_sync(cib_t * cib, const char *section, int call_options)
286
0
{
287
0
    return cib->cmds->sync_from(cib, NULL, section, call_options);
288
0
}
289
290
static int
291
cib_client_sync_from(cib_t * cib, const char *host, const char *section, int call_options)
292
0
{
293
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_SYNC, host, section, NULL,
294
0
                           NULL, call_options, cib->user);
295
0
}
296
297
static int
298
cib_client_create(cib_t * cib, const char *section, xmlNode * data, int call_options)
299
0
{
300
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_CREATE, NULL, section, data,
301
0
                           NULL, call_options, cib->user);
302
0
}
303
304
static int
305
cib_client_modify(cib_t * cib, const char *section, xmlNode * data, int call_options)
306
0
{
307
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_MODIFY, NULL, section, data,
308
0
                           NULL, call_options, cib->user);
309
0
}
310
311
static int
312
cib_client_replace(cib_t * cib, const char *section, xmlNode * data, int call_options)
313
0
{
314
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_REPLACE, NULL, section, data,
315
0
                           NULL, call_options, cib->user);
316
0
}
317
318
static int
319
cib_client_delete(cib_t * cib, const char *section, xmlNode * data, int call_options)
320
0
{
321
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_DELETE, NULL, section, data,
322
0
                           NULL, call_options, cib->user);
323
0
}
324
325
static int
326
cib_client_erase(cib_t * cib, xmlNode ** output_data, int call_options)
327
0
{
328
0
    return cib_internal_op(cib, PCMK__CIB_REQUEST_ERASE, NULL, NULL, NULL,
329
0
                           output_data, call_options, cib->user);
330
0
}
331
332
static int
333
cib_client_init_transaction(cib_t *cib)
334
0
{
335
0
    int rc = pcmk_rc_ok;
336
337
0
    if (cib == NULL) {
338
0
        return -EINVAL;
339
0
    }
340
341
0
    if (cib->transaction != NULL) {
342
        // A client can have at most one transaction at a time
343
0
        rc = pcmk_rc_already;
344
0
    }
345
346
0
    if (rc == pcmk_rc_ok) {
347
0
        cib->transaction = pcmk__xe_create(NULL, PCMK__XE_CIB_TRANSACTION);
348
0
    }
349
350
0
    if (rc != pcmk_rc_ok) {
351
0
        const char *client_id = NULL;
352
353
0
        cib->cmds->client_id(cib, NULL, &client_id);
354
0
        pcmk__err("Failed to initialize CIB transaction for client %s: %s",
355
0
                  client_id, pcmk_rc_str(rc));
356
0
    }
357
0
    return pcmk_rc2legacy(rc);
358
0
}
359
360
static int
361
cib_client_end_transaction(cib_t *cib, bool commit, int call_options)
362
0
{
363
0
    const char *client_id = NULL;
364
0
    int rc = pcmk_ok;
365
366
0
    if (cib == NULL) {
367
0
        return -EINVAL;
368
0
    }
369
370
0
    cib->cmds->client_id(cib, NULL, &client_id);
371
0
    client_id = pcmk__s(client_id, "(unidentified)");
372
373
0
    if (commit) {
374
0
        if (cib->transaction == NULL) {
375
0
            rc = pcmk_rc_no_transaction;
376
377
0
            pcmk__err("Failed to commit transaction for CIB client %s: %s",
378
0
                      client_id, pcmk_rc_str(rc));
379
0
            return pcmk_rc2legacy(rc);
380
0
        }
381
0
        rc = cib_internal_op(cib, PCMK__CIB_REQUEST_COMMIT_TRANSACT, NULL, NULL,
382
0
                             cib->transaction, NULL, call_options, cib->user);
383
384
0
    } else {
385
        // Discard always succeeds
386
0
        if (cib->transaction != NULL) {
387
0
            pcmk__trace("Discarded transaction for CIB client %s", client_id);
388
0
        } else {
389
0
            pcmk__trace("No transaction found for CIB client %s", client_id);
390
0
        }
391
0
    }
392
0
    pcmk__xml_free(cib->transaction);
393
0
    cib->transaction = NULL;
394
0
    return rc;
395
0
}
396
397
static int
398
cib_client_fetch_schemas(cib_t *cib, xmlNode **output_data, const char *after_ver,
399
                         int call_options)
400
0
{
401
0
    xmlNode *data = pcmk__xe_create(NULL, PCMK__XA_SCHEMA);
402
0
    int rc = pcmk_ok;
403
404
0
    pcmk__xe_set(data, PCMK_XA_VERSION, after_ver);
405
406
0
    rc = cib_internal_op(cib, PCMK__CIB_REQUEST_SCHEMAS, NULL, NULL, data,
407
0
                         output_data, call_options, NULL);
408
0
    pcmk__xml_free(data);
409
0
    return rc;
410
0
}
411
412
static void
413
cib_client_set_user(cib_t *cib, const char *user)
414
0
{
415
0
    pcmk__str_update(&(cib->user), user);
416
0
}
417
418
static void
419
cib_destroy_op_callback(gpointer data)
420
0
{
421
0
    cib_callback_client_t *blob = data;
422
423
0
    if (blob->timer && blob->timer->ref > 0) {
424
0
        g_source_remove(blob->timer->ref);
425
0
    }
426
0
    free(blob->timer);
427
428
0
    if (blob->user_data && blob->free_func) {
429
0
        blob->free_func(blob->user_data);
430
0
    }
431
432
0
    free(blob);
433
0
}
434
435
static void
436
destroy_op_callback_table(void)
437
46
{
438
46
    if (cib_op_callback_table != NULL) {
439
23
        g_hash_table_destroy(cib_op_callback_table);
440
23
        cib_op_callback_table = NULL;
441
23
    }
442
46
}
443
444
char *
445
get_shadow_file(const char *suffix)
446
0
{
447
0
    char *cib_home = NULL;
448
0
    char *fullname = NULL;
449
0
    char *name = pcmk__assert_asprintf("shadow.%s", suffix);
450
0
    const char *dir = getenv("CIB_shadow_dir");
451
452
0
    if (dir == NULL) {
453
        /* @TODO This basically duplicates pcmk__uid2username(), but we need the
454
         * password database entry, not just the user name from it. We should
455
         * reduce the duplication.
456
         */
457
0
        struct passwd *pwent = NULL;
458
0
        const char *user = NULL;
459
460
0
        errno = 0;
461
0
        pwent = getpwuid(geteuid());
462
463
0
        if (pwent) {
464
0
            user = pwent->pw_name;
465
466
0
        } else {
467
            // Save errno before getenv()
468
0
            int rc = errno;
469
470
0
            user = getenv("USER");
471
0
            pcmk__warn("Could not get password database entry for effective "
472
0
                       "user ID %lld: %s. Assuming user is %s.",
473
0
                       (long long) geteuid(),
474
0
                       ((rc != 0)? strerror(rc) : "No matching entry found"),
475
0
                       pcmk__s(user, "unprivileged user"));
476
0
        }
477
478
0
        if (pcmk__strcase_any_of(user, "root", CRM_DAEMON_USER, NULL)) {
479
0
            dir = CRM_CONFIG_DIR;
480
481
0
        } else {
482
0
            const char *home = NULL;
483
484
0
            if ((home = getenv("HOME")) == NULL) {
485
0
                if (pwent) {
486
0
                    home = pwent->pw_dir;
487
0
                }
488
0
            }
489
490
0
            dir = pcmk__get_tmpdir();
491
0
            if (home && home[0] == '/') {
492
0
                int rc = 0;
493
494
0
                cib_home = pcmk__assert_asprintf("%s/.cib", home);
495
496
0
                rc = mkdir(cib_home, 0700);
497
0
                if (rc < 0 && errno != EEXIST) {
498
0
                    pcmk__err("Couldn't create user-specific shadow directory "
499
0
                              "%s: %s",
500
0
                              cib_home, strerror(errno));
501
502
0
                } else {
503
0
                    dir = cib_home;
504
0
                }
505
0
            }
506
0
        }
507
0
    }
508
509
0
    fullname = pcmk__assert_asprintf("%s/%s", dir, name);
510
0
    free(cib_home);
511
0
    free(name);
512
513
0
    return fullname;
514
0
}
515
516
cib_t *
517
cib_shadow_new(const char *shadow)
518
0
{
519
0
    cib_t *new_cib = NULL;
520
0
    char *shadow_file = NULL;
521
522
0
    CRM_CHECK(shadow != NULL, return NULL);
523
524
0
    shadow_file = get_shadow_file(shadow);
525
0
    new_cib = cib_file_new(shadow_file);
526
0
    free(shadow_file);
527
528
0
    return new_cib;
529
0
}
530
531
/*!
532
 * \brief Create a new CIB connection object
533
 *
534
 * Create a new live, remote, file, or shadow file CIB connection object based
535
 * on the values of CIB-related environment variables (CIB_shadow, CIB_file,
536
 * CIB_port, CIB_server, CIB_user, and CIB_passwd). The object will not be
537
 * connected.
538
 *
539
 * \return Newly allocated CIB connection object
540
 * \note The CIB API does not fully support opening multiple CIB connection
541
 *       objects simultaneously, so the returned object should be treated as a
542
 *       singleton.
543
 */
544
/* @TODO Ensure all APIs support multiple simultaneous CIB connection objects
545
 * (at least cib_free_callbacks() currently does not).
546
 */
547
cib_t *
548
cib_new(void)
549
0
{
550
0
    const char *value = getenv("CIB_shadow");
551
0
    const char *server = NULL;
552
0
    const char *user = NULL;
553
0
    const char *pass = NULL;
554
0
    gboolean encrypted = TRUE;
555
0
    int port;
556
557
0
    if (!pcmk__str_empty(value)) {
558
0
        return cib_shadow_new(value);
559
0
    }
560
561
0
    value = getenv("CIB_file");
562
0
    if (!pcmk__str_empty(value)) {
563
0
        return cib_file_new(value);
564
0
    }
565
566
0
    value = getenv("CIB_port");
567
0
    if (pcmk__str_empty(value)) {
568
0
        return cib_native_new();
569
0
    }
570
571
    /* We don't ensure port is valid (>= 0) because cib_new() currently can't
572
     * return NULL in practice, and introducing a NULL return here could cause
573
     * core dumps that would previously just cause signon() failures.
574
     */
575
0
    pcmk__scan_port(value, &port);
576
577
0
    if (!pcmk__is_true(getenv("CIB_encrypted"))) {
578
0
        encrypted = FALSE;
579
0
    }
580
581
0
    server = getenv("CIB_server");
582
0
    user = getenv("CIB_user");
583
0
    pass = getenv("CIB_passwd");
584
585
0
    if (pcmk__str_empty(user)) {
586
0
        user = CRM_DAEMON_USER;
587
0
    }
588
589
0
    if (pcmk__str_empty(server)) {
590
0
        server = "localhost";
591
0
    }
592
593
0
    pcmk__debug("Initializing %s remote CIB access to %s:%d as user %s",
594
0
                (encrypted? "encrypted" : "plain-text"), server, port, user);
595
0
    return cib_remote_new(server, user, pass, port, encrypted);
596
0
}
597
598
/*!
599
 * \internal
600
 * \brief Create a generic CIB connection instance
601
 *
602
 * \return Newly allocated and initialized cib_t instance
603
 *
604
 * \note This is called by each variant's cib_*_new() function before setting
605
 *       variant-specific values.
606
 */
607
cib_t *
608
cib_new_variant(void)
609
23
{
610
23
    cib_t *new_cib = NULL;
611
612
23
    new_cib = calloc(1, sizeof(cib_t));
613
614
23
    if (new_cib == NULL) {
615
0
        return NULL;
616
0
    }
617
618
23
    remove_cib_op_callback(0, TRUE); /* remove all */
619
620
23
    new_cib->call_id = 1;
621
23
    new_cib->variant = cib_undefined;
622
623
23
    new_cib->type = cib_no_connection;
624
23
    new_cib->state = cib_disconnected;
625
23
    new_cib->variant_opaque = NULL;
626
23
    new_cib->notify_list = NULL;
627
628
    /* the rest will get filled in by the variant constructor */
629
23
    new_cib->cmds = calloc(1, sizeof(cib_api_operations_t));
630
631
23
    if (new_cib->cmds == NULL) {
632
0
        free(new_cib);
633
0
        return NULL;
634
0
    }
635
636
23
    new_cib->cmds->add_notify_callback = cib_client_add_notify_callback;
637
23
    new_cib->cmds->del_notify_callback = cib_client_del_notify_callback;
638
23
    new_cib->cmds->register_callback = cib_client_register_callback;
639
23
    new_cib->cmds->register_callback_full = cib_client_register_callback_full;
640
641
23
    new_cib->cmds->noop = cib_client_noop; // Deprecated method
642
23
    new_cib->cmds->ping = cib_client_ping;
643
23
    new_cib->cmds->query = cib_client_query;
644
23
    new_cib->cmds->sync = cib_client_sync;
645
646
23
    new_cib->cmds->query_from = cib_client_query_from;
647
23
    new_cib->cmds->sync_from = cib_client_sync_from;
648
649
23
    new_cib->cmds->set_primary = set_primary;
650
23
    new_cib->cmds->set_secondary = set_secondary;
651
652
23
    new_cib->cmds->upgrade = cib_client_upgrade;
653
23
    new_cib->cmds->bump_epoch = cib_client_bump_epoch;
654
655
23
    new_cib->cmds->create = cib_client_create;
656
23
    new_cib->cmds->modify = cib_client_modify;
657
23
    new_cib->cmds->replace = cib_client_replace;
658
23
    new_cib->cmds->remove = cib_client_delete;
659
23
    new_cib->cmds->erase = cib_client_erase;
660
661
23
    new_cib->cmds->init_transaction = cib_client_init_transaction;
662
23
    new_cib->cmds->end_transaction = cib_client_end_transaction;
663
664
23
    new_cib->cmds->set_user = cib_client_set_user;
665
666
23
    new_cib->cmds->fetch_schemas = cib_client_fetch_schemas;
667
668
23
    return new_cib;
669
23
}
670
671
void 
672
cib_free_notify(cib_t *cib)
673
23
{
674
675
23
    if (cib) {
676
23
        GList *list = cib->notify_list;
677
678
23
        while (list != NULL) {
679
0
            cib_notify_client_t *client = g_list_nth_data(list, 0);
680
681
0
            list = g_list_remove(list, client);
682
0
            free(client);
683
0
        }
684
23
        cib->notify_list = NULL;
685
23
    }
686
23
}
687
688
/*!
689
 * \brief Free all callbacks for a CIB connection
690
 *
691
 * \param[in,out] cib  CIB connection to clean up
692
 */
693
void
694
cib_free_callbacks(cib_t *cib)
695
23
{
696
23
    cib_free_notify(cib);
697
698
23
    destroy_op_callback_table();
699
23
}
700
701
/*!
702
 * \brief Free all memory used by CIB connection
703
 *
704
 * \param[in,out] cib  CIB connection to delete
705
 */
706
void
707
cib_delete(cib_t *cib)
708
23
{
709
23
    cib_free_callbacks(cib);
710
23
    if (cib) {
711
23
        cib->cmds->free(cib);
712
23
    }
713
23
}
714
715
void
716
remove_cib_op_callback(int call_id, gboolean all_callbacks)
717
23
{
718
23
    if (all_callbacks) {
719
23
        destroy_op_callback_table();
720
23
        cib_op_callback_table = pcmk__intkey_table(cib_destroy_op_callback);
721
23
    } else {
722
0
        pcmk__intkey_table_remove(cib_op_callback_table, call_id);
723
0
    }
724
23
}
725
726
int
727
num_cib_op_callbacks(void)
728
0
{
729
0
    if (cib_op_callback_table == NULL) {
730
0
        return 0;
731
0
    }
732
0
    return g_hash_table_size(cib_op_callback_table);
733
0
}
734
735
static void
736
cib_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
737
0
{
738
0
    int call = GPOINTER_TO_INT(key);
739
0
    cib_callback_client_t *blob = value;
740
741
0
    pcmk__debug("Call %d (%s): pending", call, pcmk__s(blob->id, "without ID"));
742
0
}
743
744
void
745
cib_dump_pending_callbacks(void)
746
0
{
747
0
    if (cib_op_callback_table == NULL) {
748
0
        return;
749
0
    }
750
0
    return g_hash_table_foreach(cib_op_callback_table, cib_dump_pending_op, NULL);
751
0
}
752
753
cib_callback_client_t*
754
cib__lookup_id (int call_id)
755
0
{
756
0
    return pcmk__intkey_table_lookup(cib_op_callback_table, call_id);
757
0
}
758
759
// Deprecated functions kept only for backward API compatibility
760
// LCOV_EXCL_START
761
762
#include <crm/cib_compat.h>
763
764
cib_t *
765
cib_new_no_shadow(void)
766
0
{
767
0
    const char *shadow = getenv("CIB_shadow");
768
0
    cib_t *cib = NULL;
769
770
0
    unsetenv("CIB_shadow");
771
0
    cib = cib_new();
772
773
0
    if (shadow != NULL) {
774
0
        setenv("CIB_shadow", shadow, 1);
775
0
    }
776
0
    return cib;
777
0
}
778
779
// LCOV_EXCL_STOP
780
// End deprecated API