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