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

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