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