root/lib/cib/cib_client.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cib_client_noop
  2. cib_client_ping
  3. cib_client_query
  4. cib_client_query_from
  5. cib_client_is_master
  6. cib_client_set_slave
  7. cib_client_set_slave_all
  8. cib_client_set_master
  9. cib_client_bump_epoch
  10. cib_client_upgrade
  11. cib_client_sync
  12. cib_client_sync_from
  13. cib_client_create
  14. cib_client_modify
  15. cib_client_update
  16. cib_client_replace
  17. cib_client_delete
  18. cib_client_delete_absolute
  19. cib_client_erase
  20. cib_destroy_op_callback
  21. destroy_op_callback_table
  22. get_shadow_file
  23. cib_shadow_new
  24. cib_new_no_shadow
  25. cib_new
  26. cib_new_variant
  27. cib_free_callbacks
  28. cib_delete
  29. cib_client_set_op_callback
  30. cib_client_add_notify_callback
  31. get_notify_list_event_count
  32. cib_client_del_notify_callback
  33. ciblib_GCompareFunc
  34. cib_async_timeout_handler
  35. cib_client_register_callback
  36. cib_client_register_callback_full
  37. remove_cib_op_callback
  38. num_cib_op_callbacks
  39. cib_dump_pending_op
  40. cib_dump_pending_callbacks

   1 /*
   2  * Copyright 2004-2020 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 GHashTable *cib_op_callback_table = NULL;
  29 
  30 int cib_client_set_op_callback(cib_t * cib, void (*callback) (const xmlNode * msg, int call_id,
  31                                                               int rc, xmlNode * output));
  32 
  33 int cib_client_add_notify_callback(cib_t * cib, const char *event,
  34                                    void (*callback) (const char *event, xmlNode * msg));
  35 
  36 int cib_client_del_notify_callback(cib_t * cib, const char *event,
  37                                    void (*callback) (const char *event, xmlNode * msg));
  38 
  39 gint ciblib_GCompareFunc(gconstpointer a, gconstpointer b);
  40 
  41 #define op_common(cib) do {                                             \
  42         if(cib == NULL) {                                               \
  43             return -EINVAL;                                             \
  44         } else if(cib->delegate_fn == NULL) {                           \
  45             return -EPROTONOSUPPORT;                                    \
  46         }                                                               \
  47     } while(0)
  48 
  49 static int
  50 cib_client_noop(cib_t * cib, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
  51 {
  52     op_common(cib);
  53     return cib_internal_op(cib, CRM_OP_NOOP, NULL, NULL, NULL, NULL, call_options, NULL);
  54 }
  55 
  56 static int
  57 cib_client_ping(cib_t * cib, xmlNode ** output_data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
  58 {
  59     op_common(cib);
  60     return cib_internal_op(cib, CRM_OP_PING, NULL, NULL, NULL, output_data, call_options, NULL);
  61 }
  62 
  63 static int
  64 cib_client_query(cib_t * cib, const char *section, xmlNode ** output_data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
  65 {
  66     return cib->cmds->query_from(cib, NULL, section, output_data, call_options);
  67 }
  68 
  69 static int
  70 cib_client_query_from(cib_t * cib, const char *host, const char *section,
     /* [previous][next][first][last][top][bottom][index][help] */
  71                       xmlNode ** output_data, int call_options)
  72 {
  73     op_common(cib);
  74     return cib_internal_op(cib, CIB_OP_QUERY, host, section, NULL, output_data, call_options, NULL);
  75 }
  76 
  77 static int
  78 cib_client_is_master(cib_t * cib)
     /* [previous][next][first][last][top][bottom][index][help] */
  79 {
  80     op_common(cib);
  81     return cib_internal_op(cib, CIB_OP_ISMASTER, NULL, NULL, NULL, NULL,
  82                            cib_scope_local | cib_sync_call, NULL);
  83 }
  84 
  85 static int
  86 cib_client_set_slave(cib_t * cib, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
  87 {
  88     op_common(cib);
  89     return cib_internal_op(cib, CIB_OP_SLAVE, NULL, NULL, NULL, NULL, call_options, NULL);
  90 }
  91 
  92 static int
  93 cib_client_set_slave_all(cib_t * cib, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
  94 {
  95     return -EPROTONOSUPPORT;
  96 }
  97 
  98 static int
  99 cib_client_set_master(cib_t * cib, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 100 {
 101     op_common(cib);
 102     crm_trace("Adding cib_scope_local to options");
 103     return cib_internal_op(cib, CIB_OP_MASTER, NULL, NULL, NULL, NULL,
 104                            call_options | cib_scope_local, NULL);
 105 }
 106 
 107 static int
 108 cib_client_bump_epoch(cib_t * cib, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110     op_common(cib);
 111     return cib_internal_op(cib, CIB_OP_BUMP, NULL, NULL, NULL, NULL, call_options, NULL);
 112 }
 113 
 114 static int
 115 cib_client_upgrade(cib_t * cib, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117     op_common(cib);
 118     return cib_internal_op(cib, CIB_OP_UPGRADE, NULL, NULL, NULL, NULL, call_options, NULL);
 119 }
 120 
 121 static int
 122 cib_client_sync(cib_t * cib, const char *section, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     return cib->cmds->sync_from(cib, NULL, section, call_options);
 125 }
 126 
 127 static int
 128 cib_client_sync_from(cib_t * cib, const char *host, const char *section, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 129 {
 130     op_common(cib);
 131     return cib_internal_op(cib, CIB_OP_SYNC, host, section, NULL, NULL, call_options, NULL);
 132 }
 133 
 134 static int
 135 cib_client_create(cib_t * cib, const char *section, xmlNode * data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 136 {
 137     op_common(cib);
 138     return cib_internal_op(cib, CIB_OP_CREATE, NULL, section, data, NULL, call_options, NULL);
 139 }
 140 
 141 static int
 142 cib_client_modify(cib_t * cib, const char *section, xmlNode * data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 143 {
 144     op_common(cib);
 145     return cib_internal_op(cib, CIB_OP_MODIFY, NULL, section, data, NULL, call_options, NULL);
 146 }
 147 
 148 static int
 149 cib_client_update(cib_t * cib, const char *section, xmlNode * data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 150 {
 151     op_common(cib);
 152     return cib_internal_op(cib, CIB_OP_MODIFY, NULL, section, data, NULL, call_options, NULL);
 153 }
 154 
 155 static int
 156 cib_client_replace(cib_t * cib, const char *section, xmlNode * data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158     op_common(cib);
 159     return cib_internal_op(cib, CIB_OP_REPLACE, NULL, section, data, NULL, call_options, NULL);
 160 }
 161 
 162 static int
 163 cib_client_delete(cib_t * cib, const char *section, xmlNode * data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 164 {
 165     op_common(cib);
 166     return cib_internal_op(cib, CIB_OP_DELETE, NULL, section, data, NULL, call_options, NULL);
 167 }
 168 
 169 static int
 170 cib_client_delete_absolute(cib_t * cib, const char *section, xmlNode * data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 171 {
 172     op_common(cib);
 173     return cib_internal_op(cib, CIB_OP_DELETE_ALT, NULL, section, data, NULL, call_options, NULL);
 174 }
 175 
 176 static int
 177 cib_client_erase(cib_t * cib, xmlNode ** output_data, int call_options)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179     op_common(cib);
 180     return cib_internal_op(cib, CIB_OP_ERASE, NULL, NULL, NULL, output_data, call_options, NULL);
 181 }
 182 
 183 static void
 184 cib_destroy_op_callback(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 185 {
 186     cib_callback_client_t *blob = data;
 187 
 188     if (blob->timer && blob->timer->ref > 0) {
 189         g_source_remove(blob->timer->ref);
 190     }
 191     free(blob->timer);
 192 
 193     if (blob->user_data && blob->free_func) {
 194         blob->free_func(blob->user_data);
 195     }
 196 
 197     free(blob);
 198 }
 199 
 200 static void
 201 destroy_op_callback_table(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 202 {
 203     if (cib_op_callback_table != NULL) {
 204         g_hash_table_destroy(cib_op_callback_table);
 205         cib_op_callback_table = NULL;
 206     }
 207 }
 208 
 209 char *
 210 get_shadow_file(const char *suffix)
     /* [previous][next][first][last][top][bottom][index][help] */
 211 {
 212     char *cib_home = NULL;
 213     char *fullname = NULL;
 214     char *name = crm_strdup_printf("shadow.%s", suffix);
 215     const char *dir = getenv("CIB_shadow_dir");
 216 
 217     if (dir == NULL) {
 218         uid_t uid = geteuid();
 219         struct passwd *pwent = getpwuid(uid);
 220         const char *user = NULL;
 221 
 222         if (pwent) {
 223             user = pwent->pw_name;
 224         } else {
 225             user = getenv("USER");
 226             crm_perror(LOG_ERR,
 227                        "Assuming %s because cannot get user details for user ID %d",
 228                        (user? user : "unprivileged user"), uid);
 229         }
 230 
 231         if (pcmk__strcase_any_of(user, "root", CRM_DAEMON_USER, NULL)) {
 232             dir = CRM_CONFIG_DIR;
 233 
 234         } else {
 235             const char *home = NULL;
 236 
 237             if ((home = getenv("HOME")) == NULL) {
 238                 if (pwent) {
 239                     home = pwent->pw_dir;
 240                 }
 241             }
 242 
 243             dir = pcmk__get_tmpdir();
 244             if (home && home[0] == '/') {
 245                 int rc = 0;
 246 
 247                 cib_home = crm_strdup_printf("%s/.cib", home);
 248 
 249                 rc = mkdir(cib_home, 0700);
 250                 if (rc < 0 && errno != EEXIST) {
 251                     crm_perror(LOG_ERR, "Couldn't create user-specific shadow directory: %s",
 252                                cib_home);
 253                     errno = 0;
 254 
 255                 } else {
 256                     dir = cib_home;
 257                 }
 258             }
 259         }
 260     }
 261 
 262     fullname = crm_strdup_printf("%s/%s", dir, name);
 263     free(cib_home);
 264     free(name);
 265 
 266     return fullname;
 267 }
 268 
 269 cib_t *
 270 cib_shadow_new(const char *shadow)
     /* [previous][next][first][last][top][bottom][index][help] */
 271 {
 272     cib_t *new_cib = NULL;
 273     char *shadow_file = NULL;
 274 
 275     CRM_CHECK(shadow != NULL, return NULL);
 276 
 277     shadow_file = get_shadow_file(shadow);
 278     new_cib = cib_file_new(shadow_file);
 279     free(shadow_file);
 280 
 281     return new_cib;
 282 }
 283 
 284 cib_t *
 285 cib_new_no_shadow(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 286 {
 287     unsetenv("CIB_shadow");
 288     return cib_new();
 289 }
 290 
 291 cib_t *
 292 cib_new(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294     const char *value = getenv("CIB_shadow");
 295 
 296     if (value && value[0] != 0) {
 297         return cib_shadow_new(value);
 298     }
 299 
 300     value = getenv("CIB_file");
 301     if (value) {
 302         return cib_file_new(value);
 303     }
 304 
 305     value = getenv("CIB_port");
 306     if (value) {
 307         gboolean encrypted = TRUE;
 308         int port = crm_parse_int(value, NULL);
 309         const char *server = getenv("CIB_server");
 310         const char *user = getenv("CIB_user");
 311         const char *pass = getenv("CIB_passwd");
 312 
 313         value = getenv("CIB_encrypted");
 314         if (value && crm_is_true(value) == FALSE) {
 315             crm_info("Disabling TLS");
 316             encrypted = FALSE;
 317         }
 318 
 319         if (user == NULL) {
 320             user = CRM_DAEMON_USER;
 321             crm_info("Defaulting to user: %s", user);
 322         }
 323 
 324         if (server == NULL) {
 325             server = "localhost";
 326             crm_info("Defaulting to localhost");
 327         }
 328 
 329         return cib_remote_new(server, user, pass, port, encrypted);
 330     }
 331 
 332     return cib_native_new();
 333 }
 334 
 335 /*!
 336  * \internal
 337  * \brief Create a generic CIB connection instance
 338  *
 339  * \return Newly allocated and initialized cib_t instance
 340  *
 341  * \note This is called by each variant's cib_*_new() function before setting
 342  *       variant-specific values.
 343  */
 344 cib_t *
 345 cib_new_variant(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347     cib_t *new_cib = NULL;
 348 
 349     new_cib = calloc(1, sizeof(cib_t));
 350 
 351     remove_cib_op_callback(0, TRUE); /* remove all */
 352 
 353     new_cib->call_id = 1;
 354     new_cib->variant = cib_undefined;
 355 
 356     new_cib->type = cib_no_connection;
 357     new_cib->state = cib_disconnected;
 358 
 359     new_cib->op_callback = NULL;
 360     new_cib->variant_opaque = NULL;
 361     new_cib->notify_list = NULL;
 362 
 363     /* the rest will get filled in by the variant constructor */
 364     new_cib->cmds = calloc(1, sizeof(cib_api_operations_t));
 365 
 366     new_cib->cmds->set_op_callback = cib_client_set_op_callback;
 367     new_cib->cmds->add_notify_callback = cib_client_add_notify_callback;
 368     new_cib->cmds->del_notify_callback = cib_client_del_notify_callback;
 369     new_cib->cmds->register_callback = cib_client_register_callback;
 370     new_cib->cmds->register_callback_full = cib_client_register_callback_full;
 371 
 372     new_cib->cmds->noop = cib_client_noop;
 373     new_cib->cmds->ping = cib_client_ping;
 374     new_cib->cmds->query = cib_client_query;
 375     new_cib->cmds->sync = cib_client_sync;
 376 
 377     new_cib->cmds->query_from = cib_client_query_from;
 378     new_cib->cmds->sync_from = cib_client_sync_from;
 379 
 380     new_cib->cmds->is_master = cib_client_is_master;
 381     new_cib->cmds->set_master = cib_client_set_master;
 382     new_cib->cmds->set_slave = cib_client_set_slave;
 383     new_cib->cmds->set_slave_all = cib_client_set_slave_all;
 384 
 385     new_cib->cmds->upgrade = cib_client_upgrade;
 386     new_cib->cmds->bump_epoch = cib_client_bump_epoch;
 387 
 388     new_cib->cmds->create = cib_client_create;
 389     new_cib->cmds->modify = cib_client_modify;
 390     new_cib->cmds->update = cib_client_update;
 391     new_cib->cmds->replace = cib_client_replace;
 392     new_cib->cmds->remove = cib_client_delete;
 393     new_cib->cmds->erase = cib_client_erase;
 394 
 395     new_cib->cmds->delete_absolute = cib_client_delete_absolute;
 396 
 397     return new_cib;
 398 }
 399 
 400 /*!
 401  * \brief Free all callbacks for a CIB connection
 402  *
 403  * \param[in] cib  CIB connection to clean up
 404  */
 405 void
 406 cib_free_callbacks(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 407 {
 408     if (cib) {
 409         GList *list = cib->notify_list;
 410 
 411         while (list != NULL) {
 412             cib_notify_client_t *client = g_list_nth_data(list, 0);
 413 
 414             list = g_list_remove(list, client);
 415             free(client);
 416         }
 417         cib->notify_list = NULL;
 418     }
 419     destroy_op_callback_table();
 420 }
 421 
 422 /*!
 423  * \brief Free all memory used by CIB connection
 424  *
 425  * \param[in] cib  CIB connection to delete
 426  */
 427 void
 428 cib_delete(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 429 {
 430     cib_free_callbacks(cib);
 431     if (cib) {
 432         cib->cmds->free(cib);
 433     }
 434 }
 435 
 436 int
 437 cib_client_set_op_callback(cib_t * cib, void (*callback) (const xmlNode * msg, int call_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 438                                                           int rc, xmlNode * output))
 439 {
 440     if (callback == NULL) {
 441         crm_info("Un-Setting operation callback");
 442 
 443     } else {
 444         crm_trace("Setting operation callback");
 445     }
 446     cib->op_callback = callback;
 447     return pcmk_ok;
 448 }
 449 
 450 int
 451 cib_client_add_notify_callback(cib_t * cib, const char *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 452                                void (*callback) (const char *event, xmlNode * msg))
 453 {
 454     GList *list_item = NULL;
 455     cib_notify_client_t *new_client = NULL;
 456 
 457     if (cib->variant != cib_native && cib->variant != cib_remote) {
 458         return -EPROTONOSUPPORT;
 459     }
 460 
 461     crm_trace("Adding callback for %s events (%d)", event, g_list_length(cib->notify_list));
 462 
 463     new_client = calloc(1, sizeof(cib_notify_client_t));
 464     new_client->event = event;
 465     new_client->callback = callback;
 466 
 467     list_item = g_list_find_custom(cib->notify_list, new_client, ciblib_GCompareFunc);
 468 
 469     if (list_item != NULL) {
 470         crm_warn("Callback already present");
 471         free(new_client);
 472         return -EINVAL;
 473 
 474     } else {
 475         cib->notify_list = g_list_append(cib->notify_list, new_client);
 476 
 477         cib->cmds->register_notification(cib, event, 1);
 478 
 479         crm_trace("Callback added (%d)", g_list_length(cib->notify_list));
 480     }
 481     return pcmk_ok;
 482 }
 483 
 484 static int 
 485 get_notify_list_event_count(cib_t * cib, const char *event)
     /* [previous][next][first][last][top][bottom][index][help] */
 486 {
 487     GList *l = NULL;
 488     int count = 0;
 489 
 490     for (l = g_list_first(cib->notify_list); l; l = g_list_next(l)) {
 491         cib_notify_client_t *client = (cib_notify_client_t *)l->data;
 492         
 493         if (strcmp(client->event, event) == 0) {
 494             count++;
 495         }
 496     }
 497     crm_trace("event(%s) count : %d", event, count);
 498     return count;
 499 }
 500 
 501 int
 502 cib_client_del_notify_callback(cib_t * cib, const char *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 503                                void (*callback) (const char *event, xmlNode * msg))
 504 {
 505     GList *list_item = NULL;
 506     cib_notify_client_t *new_client = NULL;
 507 
 508     if (cib->variant != cib_native && cib->variant != cib_remote) {
 509         return -EPROTONOSUPPORT;
 510     }
 511 
 512     if (get_notify_list_event_count(cib, event) == 0) {
 513         crm_debug("The callback of the event does not exist(%s)", event);
 514         return pcmk_ok;
 515     }
 516 
 517     crm_debug("Removing callback for %s events", event);
 518 
 519     new_client = calloc(1, sizeof(cib_notify_client_t));
 520     new_client->event = event;
 521     new_client->callback = callback;
 522 
 523     list_item = g_list_find_custom(cib->notify_list, new_client, ciblib_GCompareFunc);
 524 
 525     if (list_item != NULL) {
 526         cib_notify_client_t *list_client = list_item->data;
 527 
 528         cib->notify_list = g_list_remove(cib->notify_list, list_client);
 529         free(list_client);
 530 
 531         crm_trace("Removed callback");
 532 
 533     } else {
 534         crm_trace("Callback not present");
 535     }
 536 
 537     if (get_notify_list_event_count(cib, event) == 0) {
 538         /* When there is not the registration of the event, the processing turns off a notice. */
 539         cib->cmds->register_notification(cib, event, 0);
 540     }
 541 
 542     free(new_client);
 543     return pcmk_ok;
 544 }
 545 
 546 gint
 547 ciblib_GCompareFunc(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 548 {
 549     int rc = 0;
 550     const cib_notify_client_t *a_client = a;
 551     const cib_notify_client_t *b_client = b;
 552 
 553     CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
 554     rc = strcmp(a_client->event, b_client->event);
 555     if (rc == 0) {
 556         if (a_client->callback == b_client->callback) {
 557             return 0;
 558         } else if (((long)a_client->callback) < ((long)b_client->callback)) {
 559             crm_trace("callbacks for %s are not equal: %p < %p",
 560                       a_client->event, a_client->callback, b_client->callback);
 561             return -1;
 562         }
 563         crm_trace("callbacks for %s are not equal: %p > %p",
 564                   a_client->event, a_client->callback, b_client->callback);
 565         return 1;
 566     }
 567     return rc;
 568 }
 569 
 570 static gboolean
 571 cib_async_timeout_handler(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 572 {
 573     struct timer_rec_s *timer = data;
 574 
 575     crm_debug("Async call %d timed out after %ds", timer->call_id, timer->timeout);
 576     cib_native_callback(timer->cib, NULL, timer->call_id, -ETIME);
 577 
 578     /* Always return TRUE, never remove the handler
 579      * We do that in remove_cib_op_callback()
 580      */
 581     return TRUE;
 582 }
 583 
 584 gboolean
 585 cib_client_register_callback(cib_t * cib, int call_id, int timeout, gboolean only_success,
     /* [previous][next][first][last][top][bottom][index][help] */
 586                              void *user_data, const char *callback_name,
 587                              void (*callback) (xmlNode *, int, int, xmlNode *, void *))
 588 {
 589     return cib_client_register_callback_full(cib, call_id, timeout,
 590                                              only_success, user_data,
 591                                              callback_name, callback, NULL);
 592 }
 593 
 594 gboolean
 595 cib_client_register_callback_full(cib_t *cib, int call_id, int timeout,
     /* [previous][next][first][last][top][bottom][index][help] */
 596                                   gboolean only_success, void *user_data,
 597                                   const char *callback_name,
 598                                   void (*callback)(xmlNode *, int, int,
 599                                                    xmlNode *, void *),
 600                                   void (*free_func)(void *))
 601 {
 602     cib_callback_client_t *blob = NULL;
 603 
 604     if (call_id < 0) {
 605         if (only_success == FALSE) {
 606             callback(NULL, call_id, call_id, NULL, user_data);
 607         } else {
 608             crm_warn("CIB call failed: %s", pcmk_strerror(call_id));
 609         }
 610         if (user_data && free_func) {
 611             free_func(user_data);
 612         }
 613         return FALSE;
 614     }
 615 
 616     blob = calloc(1, sizeof(cib_callback_client_t));
 617     blob->id = callback_name;
 618     blob->only_success = only_success;
 619     blob->user_data = user_data;
 620     blob->callback = callback;
 621     blob->free_func = free_func;
 622 
 623     if (timeout > 0) {
 624         struct timer_rec_s *async_timer = NULL;
 625 
 626         async_timer = calloc(1, sizeof(struct timer_rec_s));
 627         blob->timer = async_timer;
 628 
 629         async_timer->cib = cib;
 630         async_timer->call_id = call_id;
 631         async_timer->timeout = timeout * 1000;
 632         async_timer->ref =
 633             g_timeout_add(async_timer->timeout, cib_async_timeout_handler, async_timer);
 634     }
 635 
 636     crm_trace("Adding callback %s for call %d", callback_name, call_id);
 637     g_hash_table_insert(cib_op_callback_table, GINT_TO_POINTER(call_id), blob);
 638 
 639     return TRUE;
 640 }
 641 
 642 void
 643 remove_cib_op_callback(int call_id, gboolean all_callbacks)
     /* [previous][next][first][last][top][bottom][index][help] */
 644 {
 645     if (all_callbacks) {
 646         destroy_op_callback_table();
 647         cib_op_callback_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
 648                                                       NULL, cib_destroy_op_callback);
 649     } else {
 650         g_hash_table_remove(cib_op_callback_table, GINT_TO_POINTER(call_id));
 651     }
 652 }
 653 
 654 int
 655 num_cib_op_callbacks(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 656 {
 657     if (cib_op_callback_table == NULL) {
 658         return 0;
 659     }
 660     return g_hash_table_size(cib_op_callback_table);
 661 }
 662 
 663 static void
 664 cib_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 665 {
 666     int call = GPOINTER_TO_INT(key);
 667     cib_callback_client_t *blob = value;
 668 
 669     crm_debug("Call %d (%s): pending", call, crm_str(blob->id));
 670 }
 671 
 672 void
 673 cib_dump_pending_callbacks(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 674 {
 675     if (cib_op_callback_table == NULL) {
 676         return;
 677     }
 678     return g_hash_table_foreach(cib_op_callback_table, cib_dump_pending_op, NULL);
 679 }

/* [previous][next][first][last][top][bottom][index][help] */