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