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. is_primary
  6. set_secondary
  7. set_all_secondary
  8. set_primary
  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
  42. cib__lookup_id

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

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