1 /*
2 * Copyright 2014-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
12 #include <stdbool.h>
13
14 #include <crm/crm.h>
15 #include <dbus/dbus.h>
16 #include <pcmk-dbus.h>
17
18 /*
19 * DBus message dispatch
20 */
21
22 // List of DBus connections (DBusConnection*) with messages available
23 static GList *conn_dispatches = NULL;
24
25 /*!
26 * \internal
27 * \brief Save an indication that DBus messages need dispatching
28 *
29 * \param[in] connection DBus connection with messages to dispatch
30 * \param[in] new_status Dispatch status as reported by DBus library
31 * \param[in] data Ignored
32 *
33 * \note This is suitable to be used as a DBus status dispatch function.
34 * As mentioned in the DBus documentation, dbus_connection_dispatch() must
35 * not be called from within this function, and any re-entrancy is a bad
36 * idea. Instead, this should just flag the main loop that messages need
37 * to be dispatched.
38 */
39 static void
40 update_dispatch_status(DBusConnection *connection,
41 DBusDispatchStatus new_status, void *data)
42 {
43 if (new_status == DBUS_DISPATCH_DATA_REMAINS) {
44 pcmk__trace("DBus connection has messages available for dispatch");
45 conn_dispatches = g_list_prepend(conn_dispatches, connection);
46 } else {
47 pcmk__trace("DBus connection has no messages available for dispatch "
48 "(status %d)",
49 new_status);
50 }
51 }
52
53 /*!
54 * \internal
55 * \brief Dispatch available messages on all DBus connections
56 */
57 static void
58 dispatch_messages(void)
59 {
60 for (GList *gIter = conn_dispatches; gIter != NULL; gIter = gIter->next) {
61 DBusConnection *connection = gIter->data;
62
63 while (dbus_connection_get_dispatch_status(connection)
64 == DBUS_DISPATCH_DATA_REMAINS) {
65 pcmk__trace("Dispatching available messages on DBus connection");
66 dbus_connection_dispatch(connection);
67 }
68 }
69
70 g_clear_pointer(&conn_dispatches, g_list_free);
71 }
72
73
74 /*
75 * DBus file descriptor watches
76 *
77 * The DBus library allows the caller to register functions for the library to
78 * use for file descriptor notifications via a main loop.
79 */
80
81 /* Copied from dbus-watch.c */
82 static const char*
83 dbus_watch_flags_to_string(int flags)
84 {
85 const char *watch_type;
86
87 if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) {
88 watch_type = "read/write";
89 } else if (flags & DBUS_WATCH_READABLE) {
90 watch_type = "read";
91 } else if (flags & DBUS_WATCH_WRITABLE) {
92 watch_type = "write";
93 } else {
94 watch_type = "neither read nor write";
95 }
96 return watch_type;
97 }
98
99 /*!
100 * \internal
101 * \brief Dispatch data available on a DBus file descriptor watch
102 *
103 * \param[in,out] userdata Pointer to the DBus watch
104 *
105 * \return Always 0
106 * \note This is suitable for use as a dispatch function in
107 * struct mainloop_fd_callbacks (which means that a negative return value
108 * would indicate the file descriptor is no longer required).
109 */
110 static int
111 dispatch_fd_data(gpointer userdata)
112 {
113 bool oom = FALSE;
114 DBusWatch *watch = userdata;
115 int flags = dbus_watch_get_flags(watch);
116 bool enabled = dbus_watch_get_enabled (watch);
117
118 pcmk__trace("Dispatching DBus watch for file descriptor %d "
119 "with flags %#x (%s)",
120 dbus_watch_get_unix_fd(watch), flags,
121 dbus_watch_flags_to_string(flags));
122
123 if (enabled && (flags & (DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE))) {
124 oom = !dbus_watch_handle(watch, flags);
125
126 } else if (enabled) {
127 oom = !dbus_watch_handle(watch, DBUS_WATCH_ERROR);
128 }
129
130 if (flags != dbus_watch_get_flags(watch)) {
131 flags = dbus_watch_get_flags(watch);
132 pcmk__trace("Dispatched DBus file descriptor watch: now %#x (%s)",
133 flags, dbus_watch_flags_to_string(flags));
134 }
135
136 if (oom) {
137 pcmk__crit("Could not dispatch DBus file descriptor data: Out of "
138 "memory");
139 } else {
140 dispatch_messages();
141 }
142 return 0;
143 }
144
145 static void
146 watch_fd_closed(gpointer userdata)
147 {
148 pcmk__trace("DBus watch for file descriptor %d is now closed",
149 dbus_watch_get_unix_fd((DBusWatch *) userdata));
150 }
151
152 static struct mainloop_fd_callbacks pcmk_dbus_cb = {
153 .dispatch = dispatch_fd_data,
154 .destroy = watch_fd_closed,
155 };
156
157 static dbus_bool_t
158 add_dbus_watch(DBusWatch *watch, void *data)
159 {
160 int fd = dbus_watch_get_unix_fd(watch);
161
162 mainloop_io_t *client = mainloop_add_fd("dbus", G_PRIORITY_DEFAULT, fd,
163 watch, &pcmk_dbus_cb);
164
165 pcmk__trace("Added DBus watch for file descriptor %d", fd);
166 dbus_watch_set_data(watch, client, NULL);
167 return TRUE;
168 }
169
170 static void
171 toggle_dbus_watch(DBusWatch *watch, void *data)
172 {
173 // @TODO Should this do something more?
174 pcmk__debug("DBus watch for file descriptor %d is now %s",
175 dbus_watch_get_unix_fd(watch),
176 (dbus_watch_get_enabled(watch)? "enabled" : "disabled"));
177 }
178
179 static void
180 remove_dbus_watch(DBusWatch *watch, void *data)
181 {
182 pcmk__trace("Removed DBus watch for file descriptor %d",
183 dbus_watch_get_unix_fd(watch));
184 mainloop_del_fd((mainloop_io_t *) dbus_watch_get_data(watch));
185 }
186
187 static void
188 register_watch_functions(DBusConnection *connection)
189 {
190 dbus_connection_set_watch_functions(connection, add_dbus_watch,
191 remove_dbus_watch,
192 toggle_dbus_watch, NULL, NULL);
193 }
194
195 /*
196 * DBus main loop timeouts
197 *
198 * The DBus library allows the caller to register functions for the library to
199 * use for managing timers via a main loop.
200 */
201
202 static gboolean
203 timer_popped(gpointer data)
204 {
205 pcmk__debug("%dms DBus timer expired",
206 dbus_timeout_get_interval((DBusTimeout *) data));
207 dbus_timeout_handle(data);
208 return FALSE;
209 }
210
211 static dbus_bool_t
212 add_dbus_timer(DBusTimeout *timeout, void *data)
213 {
214 int interval_ms = dbus_timeout_get_interval(timeout);
215 guint id = pcmk__create_timer(interval_ms, timer_popped, timeout);
216
217 if (id) {
218 dbus_timeout_set_data(timeout, GUINT_TO_POINTER(id), NULL);
219 }
220 pcmk__trace("Added %dms DBus timer", interval_ms);
221 return TRUE;
222 }
223
224 static void
225 remove_dbus_timer(DBusTimeout *timeout, void *data)
226 {
227 void *vid = dbus_timeout_get_data(timeout);
228 guint id = GPOINTER_TO_UINT(vid);
229
230 pcmk__trace("Removing %dms DBus timer", dbus_timeout_get_interval(timeout));
231 if (id) {
232 g_source_remove(id);
233 dbus_timeout_set_data(timeout, 0, NULL);
234 }
235 }
236
237 static void
238 toggle_dbus_timer(DBusTimeout *timeout, void *data)
239 {
240 bool enabled = dbus_timeout_get_enabled(timeout);
241
242 pcmk__trace("Toggling %dms DBus timer %s",
243 dbus_timeout_get_interval(timeout), (enabled? "off": "on"));
244 if (enabled) {
245 add_dbus_timer(timeout, data);
246 } else {
247 remove_dbus_timer(timeout, data);
248 }
249 }
250
251 static void
252 register_timer_functions(DBusConnection *connection)
253 {
254 dbus_connection_set_timeout_functions(connection, add_dbus_timer,
255 remove_dbus_timer,
256 toggle_dbus_timer, NULL, NULL);
257 }
258
259 /*
260 * General DBus utilities
261 */
262
263 DBusConnection *
264 pcmk_dbus_connect(void)
265 {
266 DBusError err;
267 DBusConnection *connection;
268
269 dbus_error_init(&err);
270 connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
271 if (dbus_error_is_set(&err)) {
272 pcmk__err("Could not connect to DBus: %s", err.message);
273 dbus_error_free(&err);
274 return NULL;
275 }
276 if (connection == NULL) {
277 return NULL;
278 }
279
280 /* Tell libdbus not to exit the process when a disconnect happens. This
281 * defaults to FALSE but is toggled on by the dbus_bus_get() call above.
282 */
283 dbus_connection_set_exit_on_disconnect(connection, FALSE);
284
285 // Set custom handlers for various situations
286 register_timer_functions(connection);
287 register_watch_functions(connection);
288 dbus_connection_set_dispatch_status_function(connection,
289 update_dispatch_status,
290 NULL, NULL);
291
292 // Call the dispatch function to check for any messages waiting already
293 update_dispatch_status(connection,
294 dbus_connection_get_dispatch_status(connection),
295 NULL);
296 return connection;
297 }
298
299 void
300 pcmk_dbus_disconnect(DBusConnection *connection)
301 {
302 /* We acquire our dbus connection with dbus_bus_get(), which makes it a
303 * shared connection. Therefore, we can't close or free it here. The
304 * best we can do is decrement the reference count so dbus knows when
305 * there are no more clients connected to it.
306 */
307 dbus_connection_unref(connection);
308 }
309
310 // Custom DBus error names to use
311 #define ERR_NO_REQUEST "org.clusterlabs.pacemaker.NoRequest"
312 #define ERR_NO_REPLY "org.clusterlabs.pacemaker.NoReply"
313 #define ERR_INVALID_REPLY "org.clusterlabs.pacemaker.InvalidReply"
314 #define ERR_INVALID_REPLY_METHOD "org.clusterlabs.pacemaker.InvalidReply.Method"
315 #define ERR_INVALID_REPLY_SIGNAL "org.clusterlabs.pacemaker.InvalidReply.Signal"
316 #define ERR_INVALID_REPLY_TYPE "org.clusterlabs.pacemaker.InvalidReply.Type"
317 #define ERR_SEND_FAILED "org.clusterlabs.pacemaker.SendFailed"
318
319 /*!
320 * \internal
321 * \brief Check whether a DBus reply indicates an error occurred
322 *
323 * \param[in] pending If non-NULL, indicates that a DBus request was sent
324 * \param[in] reply Reply received from DBus
325 * \param[out] ret If non-NULL, will be set to DBus error, if any
326 *
327 * \return TRUE if an error was found, FALSE otherwise
328 *
329 * \note Following the DBus API convention, a TRUE return is exactly equivalent
330 * to ret being set. If ret is provided and this function returns TRUE,
331 * the caller is responsible for calling dbus_error_free() on ret when
332 * done using it.
333 */
334 bool
335 pcmk_dbus_find_error(const DBusPendingCall *pending, DBusMessage *reply,
336 DBusError *ret)
337 {
338 DBusError error;
339
340 dbus_error_init(&error);
341
342 if (pending == NULL) {
343 dbus_set_error_const(&error, ERR_NO_REQUEST, "No request sent");
344
345 } else if (reply == NULL) {
346 dbus_set_error_const(&error, ERR_NO_REPLY, "No reply");
347
348 } else {
349 DBusMessageIter args;
350 int dtype = dbus_message_get_type(reply);
351
352 switch (dtype) {
353 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
354 dbus_message_iter_init(reply, &args);
355 pcmk__if_tracing(
356 {
357 char *sig = dbus_message_iter_get_signature(&args);
358
359 pcmk__trace("Received DBus reply with argument type "
360 "'%s'",
361 pcmk__s(sig, "(bad signature)"));
362 dbus_free(sig);
363 },
364 {}
365 );
366 break;
367 case DBUS_MESSAGE_TYPE_INVALID:
368 dbus_set_error_const(&error, ERR_INVALID_REPLY,
369 "Invalid reply");
370 break;
371 case DBUS_MESSAGE_TYPE_METHOD_CALL:
372 dbus_set_error_const(&error, ERR_INVALID_REPLY_METHOD,
373 "Invalid reply (method call)");
374 break;
375 case DBUS_MESSAGE_TYPE_SIGNAL:
376 dbus_set_error_const(&error, ERR_INVALID_REPLY_SIGNAL,
377 "Invalid reply (signal)");
378 break;
379 case DBUS_MESSAGE_TYPE_ERROR:
380 dbus_set_error_from_message(&error, reply);
381 break;
382 default:
383 dbus_set_error(&error, ERR_INVALID_REPLY_TYPE,
384 "Unknown reply type %d", dtype);
385 }
386 }
387
388 if (dbus_error_is_set(&error)) {
389 pcmk__trace("DBus reply indicated error '%s' (%s)",
390 error.name, error.message);
391 if (ret) {
392 dbus_error_init(ret);
393 dbus_move_error(&error, ret);
394 } else {
395 dbus_error_free(&error);
396 }
397 return TRUE;
398 }
399
400 return FALSE;
401 }
402
403 /*!
404 * \internal
405 * \brief Send a DBus request and wait for the reply
406 *
407 * \param[in,out] msg DBus request to send
408 * \param[in,out] connection DBus connection to use
409 * \param[out] error If non-NULL, will be set to error, if any
410 * \param[in] timeout Timeout to use for request
411 *
412 * \return DBus reply
413 *
414 * \note If error is non-NULL, it is initialized, so the caller may always use
415 * dbus_error_is_set() to determine whether an error occurred; the caller
416 * is responsible for calling dbus_error_free() in this case.
417 */
418 DBusMessage *
419 pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection,
420 DBusError *error, int timeout)
421 {
422 const char *method = NULL;
423 DBusMessage *reply = NULL;
424 DBusPendingCall* pending = NULL;
425
426 pcmk__assert(dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
427 method = dbus_message_get_member (msg);
428
429 /* Ensure caller can reliably check whether error is set */
430 if (error) {
431 dbus_error_init(error);
432 }
433
434 if (timeout <= 0) {
435 /* DBUS_TIMEOUT_USE_DEFAULT (-1) tells DBus to use a sane default */
436 timeout = DBUS_TIMEOUT_USE_DEFAULT;
437 }
438
439 // send message and get a handle for a reply
440 if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
441 if (error) {
442 dbus_set_error(error, ERR_SEND_FAILED,
443 "Could not queue DBus '%s' request", method);
444 }
445 return NULL;
446 }
447
448 dbus_connection_flush(connection);
449
450 if (pending) {
451 /* block until we receive a reply */
452 dbus_pending_call_block(pending);
453
454 /* get the reply message */
455 reply = dbus_pending_call_steal_reply(pending);
456 }
457
458 (void) pcmk_dbus_find_error(pending, reply, error);
459
460 if (pending) {
461 /* free the pending message handle */
462 dbus_pending_call_unref(pending);
463 }
464
465 return reply;
466 }
467
468 /*!
469 * \internal
470 * \brief Send a DBus message with a callback for the reply
471 *
472 * \param[in,out] msg DBus message to send
473 * \param[in,out] connection DBus connection to send on
474 * \param[in] done Function to call when pending call completes
475 * \param[in] user_data Data to pass to done callback
476 *
477 * \return Handle for reply on success, NULL on error
478 * \note The caller can assume that the done callback is called always and
479 * only when the return value is non-NULL. (This allows the caller to
480 * know where it should free dynamically allocated user_data.)
481 */
482 DBusPendingCall *
483 pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection,
484 void (*done)(DBusPendingCall *pending, void *user_data),
485 void *user_data, int timeout)
486 {
487 const char *method = NULL;
488 DBusPendingCall* pending = NULL;
489
490 pcmk__assert(done != NULL);
491 pcmk__assert(dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
492 method = dbus_message_get_member(msg);
493
494 if (timeout <= 0) {
495 /* DBUS_TIMEOUT_USE_DEFAULT (-1) tells DBus to use a sane default */
496 timeout = DBUS_TIMEOUT_USE_DEFAULT;
497 }
498
499 // send message and get a handle for a reply
500 if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
501 pcmk__err("Could not send DBus %s message: failed", method);
502 return NULL;
503
504 } else if (pending == NULL) {
505 pcmk__err("Could not send DBus %s message: connection may be closed",
506 method);
507 return NULL;
508 }
509
510 if (dbus_pending_call_get_completed(pending)) {
511 pcmk__info("DBus %s message completed too soon", method);
512 /* Calling done() directly in this case instead of setting notify below
513 * breaks things
514 */
515 }
516 if (!dbus_pending_call_set_notify(pending, done, user_data, NULL)) {
517 return NULL;
518 }
519 return pending;
520 }
521
522 bool
523 pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected,
524 const char *function, int line)
525 {
526 int dtype = 0;
527 DBusMessageIter lfield;
528
529 if (field == NULL) {
530 if (dbus_message_iter_init(msg, &lfield)) {
531 field = &lfield;
532 }
533 }
534
535 if (field == NULL) {
536 do_crm_log_alias(LOG_INFO, __FILE__, function, line,
537 "DBus reply has empty parameter list (expected '%c')",
538 expected);
539 return FALSE;
540 }
541
542 dtype = dbus_message_iter_get_arg_type(field);
543
544 if (dtype != expected) {
545 DBusMessageIter args;
546 char *sig;
547
548 dbus_message_iter_init(msg, &args);
549 sig = dbus_message_iter_get_signature(&args);
550 do_crm_log_alias(LOG_INFO, __FILE__, function, line,
551 "DBus reply has unexpected type "
552 "(expected '%c' not '%c' in '%s')",
553 expected, dtype, sig);
554 dbus_free(sig);
555 return FALSE;
556 }
557
558 return TRUE;
559 }
560
561
562 /*
563 * Property queries
564 */
565
566 /* DBus APIs often provide queryable properties that use this standard
567 * interface. See:
568 * https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
569 */
570 #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties"
571
572 // Callback prototype for when a DBus property query result is received
573 typedef void (*property_callback_func)(const char *name, // Property name
574 const char *value, // Property value
575 void *userdata); // Caller-provided data
576
577 // Data needed by DBus property queries
578 struct property_query {
579 char *name; // Property name being queried
580 char *target; // Name of DBus bus that query should be sent to
581 char *object; // DBus object path for object with the property
582 void *userdata; // Caller-provided data to supply to callback
583 property_callback_func callback; // Function to call when result is received
584 };
585
586 static void
587 free_property_query(struct property_query *data)
588 {
589 free(data->target);
590 free(data->object);
591 free(data->name);
592 free(data);
593 }
594
595 static char *
596 handle_query_result(DBusMessage *reply, struct property_query *data)
597 {
598 DBusError error;
599 char *output = NULL;
600 DBusMessageIter args;
601 DBusMessageIter variant_iter;
602 DBusBasicValue value;
603
604 dbus_error_init(&error);
605
606 // First, check if the reply contains an error
607 if (pcmk_dbus_find_error((void*)&error, reply, &error)) {
608 pcmk__err("DBus query for %s property '%s' failed: %s", data->object,
609 data->name, error.message);
610 dbus_error_free(&error);
611 goto cleanup;
612 }
613
614 // The lone output argument should be a DBus variant type
615 dbus_message_iter_init(reply, &args);
616 if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_VARIANT,
617 __func__, __LINE__)) {
618 pcmk__err("DBus query for %s property '%s' failed: Unexpected reply "
619 "type",
620 data->object, data->name);
621 goto cleanup;
622 }
623
624 // The variant should be a string
625 dbus_message_iter_recurse(&args, &variant_iter);
626 if (!pcmk_dbus_type_check(reply, &variant_iter, DBUS_TYPE_STRING,
627 __func__, __LINE__)) {
628 pcmk__err("DBus query for %s property '%s' failed: Unexpected variant "
629 "type",
630 data->object, data->name);
631 goto cleanup;
632 }
633 dbus_message_iter_get_basic(&variant_iter, &value);
634
635 // There should be no more arguments (in variant or reply)
636 dbus_message_iter_next(&variant_iter);
637 if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_INVALID) {
638 pcmk__err("DBus query for %s property '%s' failed: Too many arguments "
639 "in reply",
640 data->object, data->name);
641 goto cleanup;
642 }
643 dbus_message_iter_next(&args);
644 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_INVALID) {
645 pcmk__err("DBus query for %s property '%s' failed: Too many arguments "
646 "in reply",
647 data->object, data->name);
648 goto cleanup;
649 }
650
651 pcmk__trace("DBus query result for %s: %s='%s'",
652 data->object, data->name, pcmk__s(value.str, ""));
653
654 if (data->callback) { // Query was asynchronous
655 data->callback(data->name, (value.str? value.str : ""), data->userdata);
656
657 } else { // Query was synchronous
658 output = strdup(value.str? value.str : "");
659 }
660
661 cleanup:
662 free_property_query(data);
663 return output;
664 }
665
666 static void
667 async_query_result_cb(DBusPendingCall *pending, void *user_data)
668 {
669 DBusMessage *reply = NULL;
670 char *value = NULL;
671
672 if (pending) {
673 reply = dbus_pending_call_steal_reply(pending);
674 }
675
676 value = handle_query_result(reply, user_data);
677 free(value);
678
679 if (reply) {
680 dbus_message_unref(reply);
681 }
682 }
683
684 /*!
685 * \internal
686 * \brief Query a property on a DBus object
687 *
688 * \param[in,out] connection An active connection to DBus
689 * \param[in] target DBus name that the query should be sent to
690 * \param[in] obj DBus object path for object with the property
691 * \param[in] iface DBus interface for property to query
692 * \param[in] name Name of property to query
693 * \param[in] callback If not NULL, perform query asynchronously and call
694 * this function when query completes
695 * \param[in,out] userdata Caller-provided data to provide to \p callback
696 * \param[out] pending If \p callback is not NULL, this will be set to
697 * handle for the reply (or NULL on error)
698 * \param[in] timeout Abort query if it takes longer than this (ms)
699 *
700 * \return NULL if \p callback is non-NULL (i.e. asynchronous), otherwise a
701 * newly allocated string with property value
702 * \note It is the caller's responsibility to free the result with free().
703 */
704 char *
705 pcmk_dbus_get_property(DBusConnection *connection, const char *target,
706 const char *obj, const gchar * iface, const char *name,
707 property_callback_func callback, void *userdata,
708 DBusPendingCall **pending, int timeout)
709 {
710 DBusMessage *msg;
711 char *output = NULL;
712 struct property_query *query_data = NULL;
713
|
(1) Event path: |
Condition "connection != NULL", taking true branch. |
|
(2) Event path: |
Condition "target != NULL", taking true branch. |
|
(3) Event path: |
Condition "obj != NULL", taking true branch. |
|
(4) Event path: |
Condition "iface != NULL", taking true branch. |
|
(5) Event path: |
Condition "name != NULL", taking true branch. |
714 CRM_CHECK((connection != NULL) && (target != NULL) && (obj != NULL)
715 && (iface != NULL) && (name != NULL), return NULL);
716
|
(6) Event path: |
Switch case default. |
|
(7) Event path: |
Condition "trace_cs == NULL", taking true branch. |
|
(8) Event path: |
Condition "crm_is_callsite_active(trace_cs, _level, 0)", taking false branch. |
|
(9) Event path: |
Breaking from switch. |
717 pcmk__trace("Querying DBus %s for %s property '%s'", target, obj, name);
718
719 // Create a new message to use to invoke method
720 msg = dbus_message_new_method_call(target, obj, BUS_PROPERTY_IFACE, "Get");
|
(10) Event path: |
Condition "msg == NULL", taking false branch. |
721 if (msg == NULL) {
722 pcmk__err("DBus query for %s property '%s' failed: Unable to create "
723 "message",
724 obj, name);
725 return NULL;
726 }
727
728 // Add the interface name and property name as message arguments
|
(11) Event path: |
Condition "!dbus_message_append_args(msg, 115 /* (int)115 */, &iface, 115 /* (int)115 */, &name, 0 /* (int)0 */)", taking false branch. |
729 if (!dbus_message_append_args(msg,
730 DBUS_TYPE_STRING, &iface,
731 DBUS_TYPE_STRING, &name,
732 DBUS_TYPE_INVALID)) {
733 pcmk__err("DBus query for %s property '%s' failed: Could not append "
734 "arguments",
735 obj, name);
736 dbus_message_unref(msg);
737 return NULL;
738 }
739
740 query_data = malloc(sizeof(struct property_query));
|
(12) Event path: |
Condition "query_data == NULL", taking false branch. |
741 if (query_data == NULL) {
742 pcmk__crit("DBus query for %s property '%s' failed: Out of memory", obj,
743 name);
744 dbus_message_unref(msg);
745 return NULL;
746 }
747
748 query_data->target = strdup(target);
749 query_data->object = strdup(obj);
750 query_data->callback = callback;
751 query_data->userdata = userdata;
752 query_data->name = strdup(name);
|
(13) Event path: |
Condition "query_data->target != NULL", taking true branch. |
|
(14) Event path: |
Condition "query_data->object != NULL", taking true branch. |
|
(15) Event path: |
Condition "query_data->name != NULL", taking true branch. |
753 CRM_CHECK((query_data->target != NULL)
754 && (query_data->object != NULL)
755 && (query_data->name != NULL),
756 free_property_query(query_data);
757 dbus_message_unref(msg);
758 return NULL);
759
|
(16) Event path: |
Condition "query_data->callback", taking true branch. |
760 if (query_data->callback) { // Asynchronous
761 DBusPendingCall *local_pending;
762
763 local_pending = pcmk_dbus_send(msg, connection, async_query_result_cb,
764 query_data, timeout);
|
(17) Event path: |
Condition "local_pending == NULL", taking true branch. |
765 if (local_pending == NULL) {
766 // async_query_result_cb() was not called in this case
|
CID (unavailable; MK=97a1a5674be67ba9d2619ae707320ec0) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS): |
|
(18) Event assign_union_field: |
The union field "in" of "_pp" is written. |
|
(19) Event inconsistent_union_field_access: |
In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in". |
767 g_clear_pointer(&query_data, free_property_query);
768 }
769
770 if (pending) {
771 *pending = local_pending;
772 }
773
774 } else { // Synchronous
775 DBusMessage *reply = pcmk_dbus_send_recv(msg, connection, NULL,
776 timeout);
777
778 output = handle_query_result(reply, query_data);
779
780 if (reply) {
781 dbus_message_unref(reply);
782 }
783 }
784
785 dbus_message_unref(msg);
786
787 return output;
788 }
789