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