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