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