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_notify
  28. cib_free_callbacks
  29. cib_delete
  30. cib_client_set_op_callback
  31. cib_client_add_notify_callback
  32. get_notify_list_event_count
  33. cib_client_del_notify_callback
  34. ciblib_GCompareFunc
  35. cib_async_timeout_handler
  36. cib_client_register_callback
  37. cib_client_register_callback_full
  38. remove_cib_op_callback
  39. num_cib_op_callbacks
  40. cib_dump_pending_op
  41. cib_dump_pending_callbacks

   1 /*
   2  * Copyright 2004-2021 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     int port;
 296 
 297     if (value && value[0] != 0) {
 298         return cib_shadow_new(value);
 299     }
 300 
 301     value = getenv("CIB_file");
 302     if (value) {
 303         return cib_file_new(value);
 304     }
 305 
 306     value = getenv("CIB_port");
 307     if (value) {
 308         gboolean encrypted = TRUE;
 309         const char *server = getenv("CIB_server");
 310         const char *user = getenv("CIB_user");
 311         const char *pass = getenv("CIB_passwd");
 312 
 313         /* We don't ensure port is valid (>= 0) because cib_new() currently
 314          * can't return NULL in practice, and introducing a NULL return here
 315          * could cause core dumps that would previously just cause signon()
 316          * failures.
 317          */
 318         pcmk__scan_port(value, &port);
 319 
 320         value = getenv("CIB_encrypted");
 321         if (value && crm_is_true(value) == FALSE) {
 322             crm_info("Disabling TLS");
 323             encrypted = FALSE;
 324         }
 325 
 326         if (user == NULL) {
 327             user = CRM_DAEMON_USER;
 328             crm_info("Defaulting to user: %s", user);
 329         }
 330 
 331         if (server == NULL) {
 332             server = "localhost";
 333             crm_info("Defaulting to localhost");
 334         }
 335 
 336         return cib_remote_new(server, user, pass, port, encrypted);
 337     }
 338 
 339     return cib_native_new();
 340 }
 341 
 342 /*!
 343  * \internal
 344  * \brief Create a generic CIB connection instance
 345  *
 346  * \return Newly allocated and initialized cib_t instance
 347  *
 348  * \note This is called by each variant's cib_*_new() function before setting
 349  *       variant-specific values.
 350  */
 351 cib_t *
 352 cib_new_variant(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 353 {
 354     cib_t *new_cib = NULL;
 355 
 356     new_cib = calloc(1, sizeof(cib_t));
 357 
 358     remove_cib_op_callback(0, TRUE); /* remove all */
 359 
 360     new_cib->call_id = 1;
 361     new_cib->variant = cib_undefined;
 362 
 363     new_cib->type = cib_no_connection;
 364     new_cib->state = cib_disconnected;
 365 
 366     new_cib->op_callback = NULL;
 367     new_cib->variant_opaque = NULL;
 368     new_cib->notify_list = NULL;
 369 
 370     /* the rest will get filled in by the variant constructor */
 371     new_cib->cmds = calloc(1, sizeof(cib_api_operations_t));
 372 
 373     new_cib->cmds->set_op_callback = cib_client_set_op_callback;
 374     new_cib->cmds->add_notify_callback = cib_client_add_notify_callback;
 375     new_cib->cmds->del_notify_callback = cib_client_del_notify_callback;
 376     new_cib->cmds->register_callback = cib_client_register_callback;
 377     new_cib->cmds->register_callback_full = cib_client_register_callback_full;
 378 
 379     new_cib->cmds->noop = cib_client_noop;
 380     new_cib->cmds->ping = cib_client_ping;
 381     new_cib->cmds->query = cib_client_query;
 382     new_cib->cmds->sync = cib_client_sync;
 383 
 384     new_cib->cmds->query_from = cib_client_query_from;
 385     new_cib->cmds->sync_from = cib_client_sync_from;
 386 
 387     new_cib->cmds->is_master = cib_client_is_master;
 388     new_cib->cmds->set_master = cib_client_set_master;
 389     new_cib->cmds->set_slave = cib_client_set_slave;
 390     new_cib->cmds->set_slave_all = cib_client_set_slave_all;
 391 
 392     new_cib->cmds->upgrade = cib_client_upgrade;
 393     new_cib->cmds->bump_epoch = cib_client_bump_epoch;
 394 
 395     new_cib->cmds->create = cib_client_create;
 396     new_cib->cmds->modify = cib_client_modify;
 397     new_cib->cmds->update = cib_client_update;
 398     new_cib->cmds->replace = cib_client_replace;
 399     new_cib->cmds->remove = cib_client_delete;
 400     new_cib->cmds->erase = cib_client_erase;
 401 
 402     new_cib->cmds->delete_absolute = cib_client_delete_absolute;
 403 
 404     return new_cib;
 405 }
 406 
 407 void 
 408 cib_free_notify(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 409 {
 410 
 411     if (cib) {
 412         GList *list = cib->notify_list;
 413 
 414         while (list != NULL) {
 415             cib_notify_client_t *client = g_list_nth_data(list, 0);
 416 
 417             list = g_list_remove(list, client);
 418             free(client);
 419         }
 420         cib->notify_list = NULL;
 421     }
 422 }
 423 /*!
 424  * \brief Free all callbacks for a CIB connection
 425  *
 426  * \param[in] cib  CIB connection to clean up
 427  */
 428 void
 429 cib_free_callbacks(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 430 {
 431     cib_free_notify(cib);
 432 
 433     destroy_op_callback_table();
 434 }
 435 
 436 /*!
 437  * \brief Free all memory used by CIB connection
 438  *
 439  * \param[in] cib  CIB connection to delete
 440  */
 441 void
 442 cib_delete(cib_t *cib)
     /* [previous][next][first][last][top][bottom][index][help] */
 443 {
 444     cib_free_callbacks(cib);
 445     if (cib) {
 446         cib->cmds->free(cib);
 447     }
 448 }
 449 
 450 int
 451 cib_client_set_op_callback(cib_t * cib, void (*callback) (const xmlNode * msg, int call_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 452                                                           int rc, xmlNode * output))
 453 {
 454     if (callback == NULL) {
 455         crm_info("Un-Setting operation callback");
 456 
 457     } else {
 458         crm_trace("Setting operation callback");
 459     }
 460     cib->op_callback = callback;
 461     return pcmk_ok;
 462 }
 463 
 464 int
 465 cib_client_add_notify_callback(cib_t * cib, const char *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 466                                void (*callback) (const char *event, xmlNode * msg))
 467 {
 468     GList *list_item = NULL;
 469     cib_notify_client_t *new_client = NULL;
 470 
 471     if (cib->variant != cib_native && cib->variant != cib_remote) {
 472         return -EPROTONOSUPPORT;
 473     }
 474 
 475     crm_trace("Adding callback for %s events (%d)", event, g_list_length(cib->notify_list));
 476 
 477     new_client = calloc(1, sizeof(cib_notify_client_t));
 478     new_client->event = event;
 479     new_client->callback = callback;
 480 
 481     list_item = g_list_find_custom(cib->notify_list, new_client, ciblib_GCompareFunc);
 482 
 483     if (list_item != NULL) {
 484         crm_warn("Callback already present");
 485         free(new_client);
 486         return -EINVAL;
 487 
 488     } else {
 489         cib->notify_list = g_list_append(cib->notify_list, new_client);
 490 
 491         cib->cmds->register_notification(cib, event, 1);
 492 
 493         crm_trace("Callback added (%d)", g_list_length(cib->notify_list));
 494     }
 495     return pcmk_ok;
 496 }
 497 
 498 static int 
 499 get_notify_list_event_count(cib_t * cib, const char *event)
     /* [previous][next][first][last][top][bottom][index][help] */
 500 {
 501     GList *l = NULL;
 502     int count = 0;
 503 
 504     for (l = g_list_first(cib->notify_list); l; l = g_list_next(l)) {
 505         cib_notify_client_t *client = (cib_notify_client_t *)l->data;
 506         
 507         if (strcmp(client->event, event) == 0) {
 508             count++;
 509         }
 510     }
 511     crm_trace("event(%s) count : %d", event, count);
 512     return count;
 513 }
 514 
 515 int
 516 cib_client_del_notify_callback(cib_t * cib, const char *event,
     /* [previous][next][first][last][top][bottom][index][help] */
 517                                void (*callback) (const char *event, xmlNode * msg))
 518 {
 519     GList *list_item = NULL;
 520     cib_notify_client_t *new_client = NULL;
 521 
 522     if (cib->variant != cib_native && cib->variant != cib_remote) {
 523         return -EPROTONOSUPPORT;
 524     }
 525 
 526     if (get_notify_list_event_count(cib, event) == 0) {
 527         crm_debug("The callback of the event does not exist(%s)", event);
 528         return pcmk_ok;
 529     }
 530 
 531     crm_debug("Removing callback for %s events", event);
 532 
 533     new_client = calloc(1, sizeof(cib_notify_client_t));
 534     new_client->event = event;
 535     new_client->callback = callback;
 536 
 537     list_item = g_list_find_custom(cib->notify_list, new_client, ciblib_GCompareFunc);
 538 
 539     if (list_item != NULL) {
 540         cib_notify_client_t *list_client = list_item->data;
 541 
 542         cib->notify_list = g_list_remove(cib->notify_list, list_client);
 543         free(list_client);
 544 
 545         crm_trace("Removed callback");
 546 
 547     } else {
 548         crm_trace("Callback not present");
 549     }
 550 
 551     if (get_notify_list_event_count(cib, event) == 0) {
 552         /* When there is not the registration of the event, the processing turns off a notice. */
 553         cib->cmds->register_notification(cib, event, 0);
 554     }
 555 
 556     free(new_client);
 557     return pcmk_ok;
 558 }
 559 
 560 gint
 561 ciblib_GCompareFunc(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
 562 {
 563     int rc = 0;
 564     const cib_notify_client_t *a_client = a;
 565     const cib_notify_client_t *b_client = b;
 566 
 567     CRM_CHECK(a_client->event != NULL && b_client->event != NULL, return 0);
 568     rc = strcmp(a_client->event, b_client->event);
 569     if (rc == 0) {
 570         if (a_client->callback == b_client->callback) {
 571             return 0;
 572         } else if (((long)a_client->callback) < ((long)b_client->callback)) {
 573             crm_trace("callbacks for %s are not equal: %p < %p",
 574                       a_client->event, a_client->callback, b_client->callback);
 575             return -1;
 576         }
 577         crm_trace("callbacks for %s are not equal: %p > %p",
 578                   a_client->event, a_client->callback, b_client->callback);
 579         return 1;
 580     }
 581     return rc;
 582 }
 583 
 584 static gboolean
 585 cib_async_timeout_handler(gpointer data)
     /* [previous][next][first][last][top][bottom][index][help] */
 586 {
 587     struct timer_rec_s *timer = data;
 588 
 589     crm_debug("Async call %d timed out after %ds", timer->call_id, timer->timeout);
 590     cib_native_callback(timer->cib, NULL, timer->call_id, -ETIME);
 591 
 592     /* Always return TRUE, never remove the handler
 593      * We do that in remove_cib_op_callback()
 594      */
 595     return TRUE;
 596 }
 597 
 598 gboolean
 599 cib_client_register_callback(cib_t * cib, int call_id, int timeout, gboolean only_success,
     /* [previous][next][first][last][top][bottom][index][help] */
 600                              void *user_data, const char *callback_name,
 601                              void (*callback) (xmlNode *, int, int, xmlNode *, void *))
 602 {
 603     return cib_client_register_callback_full(cib, call_id, timeout,
 604                                              only_success, user_data,
 605                                              callback_name, callback, NULL);
 606 }
 607 
 608 gboolean
 609 cib_client_register_callback_full(cib_t *cib, int call_id, int timeout,
     /* [previous][next][first][last][top][bottom][index][help] */
 610                                   gboolean only_success, void *user_data,
 611                                   const char *callback_name,
 612                                   void (*callback)(xmlNode *, int, int,
 613                                                    xmlNode *, void *),
 614                                   void (*free_func)(void *))
 615 {
 616     cib_callback_client_t *blob = NULL;
 617 
 618     if (call_id < 0) {
 619         if (only_success == FALSE) {
 620             callback(NULL, call_id, call_id, NULL, user_data);
 621         } else {
 622             crm_warn("CIB call failed: %s", pcmk_strerror(call_id));
 623         }
 624         if (user_data && free_func) {
 625             free_func(user_data);
 626         }
 627         return FALSE;
 628     }
 629 
 630     blob = calloc(1, sizeof(cib_callback_client_t));
 631     blob->id = callback_name;
 632     blob->only_success = only_success;
 633     blob->user_data = user_data;
 634     blob->callback = callback;
 635     blob->free_func = free_func;
 636 
 637     if (timeout > 0) {
 638         struct timer_rec_s *async_timer = NULL;
 639 
 640         async_timer = calloc(1, sizeof(struct timer_rec_s));
 641         blob->timer = async_timer;
 642 
 643         async_timer->cib = cib;
 644         async_timer->call_id = call_id;
 645         async_timer->timeout = timeout * 1000;
 646         async_timer->ref =
 647             g_timeout_add(async_timer->timeout, cib_async_timeout_handler, async_timer);
 648     }
 649 
 650     crm_trace("Adding callback %s for call %d", callback_name, call_id);
 651     pcmk__intkey_table_insert(cib_op_callback_table, call_id, blob);
 652 
 653     return TRUE;
 654 }
 655 
 656 void
 657 remove_cib_op_callback(int call_id, gboolean all_callbacks)
     /* [previous][next][first][last][top][bottom][index][help] */
 658 {
 659     if (all_callbacks) {
 660         destroy_op_callback_table();
 661         cib_op_callback_table = pcmk__intkey_table(cib_destroy_op_callback);
 662     } else {
 663         pcmk__intkey_table_remove(cib_op_callback_table, call_id);
 664     }
 665 }
 666 
 667 int
 668 num_cib_op_callbacks(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 669 {
 670     if (cib_op_callback_table == NULL) {
 671         return 0;
 672     }
 673     return g_hash_table_size(cib_op_callback_table);
 674 }
 675 
 676 static void
 677 cib_dump_pending_op(gpointer key, gpointer value, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 678 {
 679     int call = GPOINTER_TO_INT(key);
 680     cib_callback_client_t *blob = value;
 681 
 682     crm_debug("Call %d (%s): pending", call, crm_str(blob->id));
 683 }
 684 
 685 void
 686 cib_dump_pending_callbacks(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 687 {
 688     if (cib_op_callback_table == NULL) {
 689         return;
 690     }
 691     return g_hash_table_foreach(cib_op_callback_table, cib_dump_pending_op, NULL);
 692 }

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