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