Line data Source code
1 : /*
2 : * Copyright 2004 International Business Machines
3 : * Later changes copyright 2004-2024 the Pacemaker project contributors
4 : *
5 : * The version control history for this file may have further details.
6 : *
7 : * This source code is licensed under the GNU Lesser General Public License
8 : * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
9 : */
10 :
11 : #include <crm_internal.h>
12 :
13 : #ifndef _GNU_SOURCE
14 : # define _GNU_SOURCE
15 : #endif
16 :
17 : #include <errno.h>
18 : #include <crm_internal.h>
19 : #include <unistd.h>
20 : #include <stdlib.h>
21 : #include <stdio.h>
22 : #include <stdarg.h>
23 : #include <string.h>
24 :
25 : #include <glib.h>
26 :
27 : #include <crm/crm.h>
28 : #include <crm/cib/internal.h>
29 :
30 : #include <crm/common/mainloop.h>
31 : #include <crm/common/xml.h>
32 :
33 : typedef struct cib_native_opaque_s {
34 : char *token;
35 : crm_ipc_t *ipc;
36 : void (*dnotify_fn) (gpointer user_data);
37 : mainloop_io_t *source;
38 : } cib_native_opaque_t;
39 :
40 : static int
41 0 : cib_native_perform_op_delegate(cib_t *cib, const char *op, const char *host,
42 : const char *section, xmlNode *data,
43 : xmlNode **output_data, int call_options,
44 : const char *user_name)
45 : {
46 0 : int rc = pcmk_ok;
47 0 : int reply_id = 0;
48 0 : enum crm_ipc_flags ipc_flags = crm_ipc_flags_none;
49 :
50 0 : xmlNode *op_msg = NULL;
51 0 : xmlNode *op_reply = NULL;
52 :
53 0 : cib_native_opaque_t *native = cib->variant_opaque;
54 :
55 0 : if (cib->state == cib_disconnected) {
56 0 : return -ENOTCONN;
57 : }
58 :
59 0 : if (output_data != NULL) {
60 0 : *output_data = NULL;
61 : }
62 :
63 0 : if (op == NULL) {
64 0 : crm_err("No operation specified");
65 0 : return -EINVAL;
66 : }
67 :
68 0 : if (call_options & cib_sync_call) {
69 0 : pcmk__set_ipc_flags(ipc_flags, "client", crm_ipc_client_response);
70 : }
71 :
72 0 : rc = cib__create_op(cib, op, host, section, data, call_options, user_name,
73 : NULL, &op_msg);
74 0 : if (rc != pcmk_ok) {
75 0 : return rc;
76 : }
77 :
78 0 : if (pcmk_is_set(call_options, cib_transaction)) {
79 0 : rc = cib__extend_transaction(cib, op_msg);
80 0 : goto done;
81 : }
82 :
83 0 : crm_trace("Sending %s message to the CIB manager (timeout=%ds)", op, cib->call_timeout);
84 0 : rc = crm_ipc_send(native->ipc, op_msg, ipc_flags, cib->call_timeout * 1000, &op_reply);
85 :
86 0 : if (rc < 0) {
87 0 : crm_err("Couldn't perform %s operation (timeout=%ds): %s (%d)", op,
88 : cib->call_timeout, pcmk_strerror(rc), rc);
89 0 : rc = -ECOMM;
90 0 : goto done;
91 : }
92 :
93 0 : crm_log_xml_trace(op_reply, "Reply");
94 :
95 0 : if (!(call_options & cib_sync_call)) {
96 0 : crm_trace("Async call, returning %d", cib->call_id);
97 0 : CRM_CHECK(cib->call_id != 0, return -ENOMSG);
98 0 : free_xml(op_reply);
99 0 : return cib->call_id;
100 : }
101 :
102 0 : rc = pcmk_ok;
103 0 : crm_element_value_int(op_reply, PCMK__XA_CIB_CALLID, &reply_id);
104 0 : if (reply_id == cib->call_id) {
105 0 : xmlNode *wrapper = pcmk__xe_first_child(op_reply, PCMK__XE_CIB_CALLDATA,
106 : NULL, NULL);
107 0 : xmlNode *tmp = pcmk__xe_first_child(wrapper, NULL, NULL, NULL);
108 :
109 0 : crm_trace("Synchronous reply %d received", reply_id);
110 0 : if (crm_element_value_int(op_reply, PCMK__XA_CIB_RC, &rc) != 0) {
111 0 : rc = -EPROTO;
112 : }
113 :
114 0 : if (output_data == NULL || (call_options & cib_discard_reply)) {
115 0 : crm_trace("Discarding reply");
116 : } else {
117 0 : *output_data = pcmk__xml_copy(NULL, tmp);
118 : }
119 :
120 0 : } else if (reply_id <= 0) {
121 0 : crm_err("Received bad reply: No id set");
122 0 : crm_log_xml_err(op_reply, "Bad reply");
123 0 : rc = -ENOMSG;
124 0 : goto done;
125 :
126 : } else {
127 0 : crm_err("Received bad reply: %d (wanted %d)", reply_id, cib->call_id);
128 0 : crm_log_xml_err(op_reply, "Old reply");
129 0 : rc = -ENOMSG;
130 0 : goto done;
131 : }
132 :
133 0 : if (op_reply == NULL && cib->state == cib_disconnected) {
134 0 : rc = -ENOTCONN;
135 :
136 0 : } else if (rc == pcmk_ok && op_reply == NULL) {
137 0 : rc = -ETIME;
138 : }
139 :
140 0 : switch (rc) {
141 0 : case pcmk_ok:
142 : case -EPERM:
143 0 : break;
144 :
145 : /* This is an internal value that clients do not and should not care about */
146 0 : case -pcmk_err_diff_resync:
147 0 : rc = pcmk_ok;
148 0 : break;
149 :
150 : /* These indicate internal problems */
151 0 : case -EPROTO:
152 : case -ENOMSG:
153 0 : crm_err("Call failed: %s", pcmk_strerror(rc));
154 0 : if (op_reply) {
155 0 : crm_log_xml_err(op_reply, "Invalid reply");
156 : }
157 0 : break;
158 :
159 0 : default:
160 0 : if (!pcmk__str_eq(op, PCMK__CIB_REQUEST_QUERY, pcmk__str_none)) {
161 0 : crm_warn("Call failed: %s", pcmk_strerror(rc));
162 : }
163 : }
164 :
165 0 : done:
166 0 : if (!crm_ipc_connected(native->ipc)) {
167 0 : crm_err("The CIB manager disconnected");
168 0 : cib->state = cib_disconnected;
169 : }
170 :
171 0 : free_xml(op_msg);
172 0 : free_xml(op_reply);
173 0 : return rc;
174 : }
175 :
176 : static int
177 0 : cib_native_dispatch_internal(const char *buffer, ssize_t length,
178 : gpointer userdata)
179 : {
180 0 : const char *type = NULL;
181 0 : xmlNode *msg = NULL;
182 :
183 0 : cib_t *cib = userdata;
184 :
185 0 : crm_trace("dispatching %p", userdata);
186 :
187 0 : if (cib == NULL) {
188 0 : crm_err("No CIB!");
189 0 : return 0;
190 : }
191 :
192 0 : msg = pcmk__xml_parse(buffer);
193 :
194 0 : if (msg == NULL) {
195 0 : crm_warn("Received a NULL message from the CIB manager");
196 0 : return 0;
197 : }
198 :
199 : /* do callbacks */
200 0 : type = crm_element_value(msg, PCMK__XA_T);
201 0 : crm_trace("Activating %s callbacks...", type);
202 0 : crm_log_xml_explicit(msg, "cib-reply");
203 :
204 0 : if (pcmk__str_eq(type, PCMK__VALUE_CIB, pcmk__str_none)) {
205 0 : cib_native_callback(cib, msg, 0, 0);
206 :
207 0 : } else if (pcmk__str_eq(type, PCMK__VALUE_CIB_NOTIFY, pcmk__str_none)) {
208 0 : g_list_foreach(cib->notify_list, cib_native_notify, msg);
209 :
210 : } else {
211 0 : crm_err("Unknown message type: %s", type);
212 : }
213 :
214 0 : free_xml(msg);
215 0 : return 0;
216 : }
217 :
218 : static void
219 0 : cib_native_destroy(void *userdata)
220 : {
221 0 : cib_t *cib = userdata;
222 0 : cib_native_opaque_t *native = cib->variant_opaque;
223 :
224 0 : crm_trace("destroying %p", userdata);
225 0 : cib->state = cib_disconnected;
226 0 : native->source = NULL;
227 0 : native->ipc = NULL;
228 :
229 0 : if (native->dnotify_fn) {
230 0 : native->dnotify_fn(userdata);
231 : }
232 0 : }
233 :
234 : static int
235 0 : cib_native_signoff(cib_t *cib)
236 : {
237 0 : cib_native_opaque_t *native = cib->variant_opaque;
238 :
239 0 : crm_debug("Disconnecting from the CIB manager");
240 :
241 0 : cib_free_notify(cib);
242 0 : remove_cib_op_callback(0, TRUE);
243 :
244 0 : if (native->source != NULL) {
245 : /* Attached to mainloop */
246 0 : mainloop_del_ipc_client(native->source);
247 0 : native->source = NULL;
248 0 : native->ipc = NULL;
249 :
250 0 : } else if (native->ipc) {
251 : /* Not attached to mainloop */
252 0 : crm_ipc_t *ipc = native->ipc;
253 :
254 0 : native->ipc = NULL;
255 0 : crm_ipc_close(ipc);
256 0 : crm_ipc_destroy(ipc);
257 : }
258 :
259 0 : cib->cmds->end_transaction(cib, false, cib_none);
260 0 : cib->state = cib_disconnected;
261 0 : cib->type = cib_no_connection;
262 :
263 0 : return pcmk_ok;
264 : }
265 :
266 : static int
267 0 : cib_native_signon_raw(cib_t *cib, const char *name, enum cib_conn_type type,
268 : int *async_fd)
269 : {
270 0 : int rc = pcmk_ok;
271 0 : const char *channel = NULL;
272 0 : cib_native_opaque_t *native = cib->variant_opaque;
273 0 : xmlNode *hello = NULL;
274 :
275 0 : struct ipc_client_callbacks cib_callbacks = {
276 : .dispatch = cib_native_dispatch_internal,
277 : .destroy = cib_native_destroy
278 : };
279 :
280 0 : cib->call_timeout = PCMK__IPC_TIMEOUT;
281 :
282 0 : if (type == cib_command) {
283 0 : cib->state = cib_connected_command;
284 0 : channel = PCMK__SERVER_BASED_RW;
285 :
286 0 : } else if (type == cib_command_nonblocking) {
287 0 : cib->state = cib_connected_command;
288 0 : channel = PCMK__SERVER_BASED_SHM;
289 :
290 0 : } else if (type == cib_query) {
291 0 : cib->state = cib_connected_query;
292 0 : channel = PCMK__SERVER_BASED_RO;
293 :
294 : } else {
295 0 : return -ENOTCONN;
296 : }
297 :
298 0 : crm_trace("Connecting %s channel", channel);
299 :
300 0 : if (async_fd != NULL) {
301 0 : native->ipc = crm_ipc_new(channel, 0);
302 0 : if (native->ipc != NULL) {
303 0 : rc = pcmk__connect_generic_ipc(native->ipc);
304 0 : if (rc == pcmk_rc_ok) {
305 0 : rc = pcmk__ipc_fd(native->ipc, async_fd);
306 0 : if (rc != pcmk_rc_ok) {
307 0 : crm_info("Couldn't get file descriptor for %s IPC",
308 : channel);
309 : }
310 : }
311 0 : rc = pcmk_rc2legacy(rc);
312 : }
313 :
314 : } else {
315 0 : native->source =
316 0 : mainloop_add_ipc_client(channel, G_PRIORITY_HIGH, 512 * 1024 /* 512k */ , cib,
317 : &cib_callbacks);
318 0 : native->ipc = mainloop_get_ipc_client(native->source);
319 : }
320 :
321 0 : if (rc != pcmk_ok || native->ipc == NULL || !crm_ipc_connected(native->ipc)) {
322 0 : crm_info("Could not connect to CIB manager for %s", name);
323 0 : rc = -ENOTCONN;
324 : }
325 :
326 0 : if (rc == pcmk_ok) {
327 0 : rc = cib__create_op(cib, CRM_OP_REGISTER, NULL, NULL, NULL,
328 : cib_sync_call, NULL, name, &hello);
329 : }
330 :
331 0 : if (rc == pcmk_ok) {
332 0 : xmlNode *reply = NULL;
333 :
334 0 : if (crm_ipc_send(native->ipc, hello, crm_ipc_client_response, -1,
335 : &reply) > 0) {
336 0 : const char *msg_type = crm_element_value(reply, PCMK__XA_CIB_OP);
337 :
338 0 : crm_log_xml_trace(reply, "reg-reply");
339 :
340 0 : if (!pcmk__str_eq(msg_type, CRM_OP_REGISTER, pcmk__str_casei)) {
341 0 : crm_info("Reply to CIB registration message has unknown type "
342 : "'%s'",
343 : msg_type);
344 0 : rc = -EPROTO;
345 :
346 : } else {
347 0 : native->token = crm_element_value_copy(reply,
348 : PCMK__XA_CIB_CLIENTID);
349 0 : if (native->token == NULL) {
350 0 : rc = -EPROTO;
351 : }
352 : }
353 0 : free_xml(reply);
354 :
355 : } else {
356 0 : rc = -ECOMM;
357 : }
358 0 : free_xml(hello);
359 : }
360 :
361 0 : if (rc == pcmk_ok) {
362 0 : crm_info("Successfully connected to CIB manager for %s", name);
363 0 : return pcmk_ok;
364 : }
365 :
366 0 : crm_info("Connection to CIB manager for %s failed: %s",
367 : name, pcmk_strerror(rc));
368 0 : cib_native_signoff(cib);
369 0 : return rc;
370 : }
371 :
372 : static int
373 0 : cib_native_signon(cib_t *cib, const char *name, enum cib_conn_type type)
374 : {
375 0 : return cib_native_signon_raw(cib, name, type, NULL);
376 : }
377 :
378 : static int
379 0 : cib_native_free(cib_t *cib)
380 : {
381 0 : int rc = pcmk_ok;
382 :
383 0 : if (cib->state != cib_disconnected) {
384 0 : rc = cib_native_signoff(cib);
385 : }
386 :
387 0 : if (cib->state == cib_disconnected) {
388 0 : cib_native_opaque_t *native = cib->variant_opaque;
389 :
390 0 : free(native->token);
391 0 : free(cib->variant_opaque);
392 0 : free(cib->cmds);
393 0 : free(cib->user);
394 0 : free(cib);
395 : }
396 :
397 0 : return rc;
398 : }
399 :
400 : static int
401 0 : cib_native_register_notification(cib_t *cib, const char *callback, int enabled)
402 : {
403 0 : int rc = pcmk_ok;
404 0 : xmlNode *notify_msg = pcmk__xe_create(NULL, PCMK__XE_CIB_CALLBACK);
405 0 : cib_native_opaque_t *native = cib->variant_opaque;
406 :
407 0 : if (cib->state != cib_disconnected) {
408 0 : crm_xml_add(notify_msg, PCMK__XA_CIB_OP, PCMK__VALUE_CIB_NOTIFY);
409 0 : crm_xml_add(notify_msg, PCMK__XA_CIB_NOTIFY_TYPE, callback);
410 0 : crm_xml_add_int(notify_msg, PCMK__XA_CIB_NOTIFY_ACTIVATE, enabled);
411 0 : rc = crm_ipc_send(native->ipc, notify_msg, crm_ipc_client_response,
412 0 : 1000 * cib->call_timeout, NULL);
413 0 : if (rc <= 0) {
414 0 : crm_trace("Notification not registered: %d", rc);
415 0 : rc = -ECOMM;
416 : }
417 : }
418 :
419 0 : free_xml(notify_msg);
420 0 : return rc;
421 : }
422 :
423 : static int
424 0 : cib_native_set_connection_dnotify(cib_t *cib,
425 : void (*dnotify) (gpointer user_data))
426 : {
427 0 : cib_native_opaque_t *native = NULL;
428 :
429 0 : if (cib == NULL) {
430 0 : crm_err("No CIB!");
431 0 : return FALSE;
432 : }
433 :
434 0 : native = cib->variant_opaque;
435 0 : native->dnotify_fn = dnotify;
436 :
437 0 : return pcmk_ok;
438 : }
439 :
440 : /*!
441 : * \internal
442 : * \brief Get the given CIB connection's unique client identifier
443 : *
444 : * These can be used to check whether this client requested the action that
445 : * triggered a CIB notification.
446 : *
447 : * \param[in] cib CIB connection
448 : * \param[out] async_id If not \p NULL, where to store asynchronous client ID
449 : * \param[out] sync_id If not \p NULL, where to store synchronous client ID
450 : *
451 : * \return Legacy Pacemaker return code (specifically, \p pcmk_ok)
452 : *
453 : * \note This is the \p cib_native variant implementation of
454 : * \p cib_api_operations_t:client_id().
455 : * \note For \p cib_native objects, \p async_id and \p sync_id are the same.
456 : * \note The client ID is assigned during CIB sign-on.
457 : */
458 : static int
459 0 : cib_native_client_id(const cib_t *cib, const char **async_id,
460 : const char **sync_id)
461 : {
462 0 : cib_native_opaque_t *native = cib->variant_opaque;
463 :
464 0 : if (async_id != NULL) {
465 0 : *async_id = native->token;
466 : }
467 0 : if (sync_id != NULL) {
468 0 : *sync_id = native->token;
469 : }
470 0 : return pcmk_ok;
471 : }
472 :
473 : cib_t *
474 0 : cib_native_new(void)
475 : {
476 0 : cib_native_opaque_t *native = NULL;
477 0 : cib_t *cib = cib_new_variant();
478 :
479 0 : if (cib == NULL) {
480 0 : return NULL;
481 : }
482 :
483 0 : native = calloc(1, sizeof(cib_native_opaque_t));
484 :
485 0 : if (native == NULL) {
486 0 : free(cib);
487 0 : return NULL;
488 : }
489 :
490 0 : cib->variant = cib_native;
491 0 : cib->variant_opaque = native;
492 :
493 0 : native->ipc = NULL;
494 0 : native->source = NULL;
495 0 : native->dnotify_fn = NULL;
496 :
497 : /* assign variant specific ops */
498 0 : cib->delegate_fn = cib_native_perform_op_delegate;
499 0 : cib->cmds->signon = cib_native_signon;
500 0 : cib->cmds->signon_raw = cib_native_signon_raw;
501 0 : cib->cmds->signoff = cib_native_signoff;
502 0 : cib->cmds->free = cib_native_free;
503 :
504 0 : cib->cmds->register_notification = cib_native_register_notification;
505 0 : cib->cmds->set_connection_dnotify = cib_native_set_connection_dnotify;
506 :
507 0 : cib->cmds->client_id = cib_native_client_id;
508 :
509 0 : return cib;
510 : }
|