root/lib/services/dbus.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. update_dispatch_status
  2. dispatch_messages
  3. dbus_watch_flags_to_string
  4. dispatch_fd_data
  5. watch_fd_closed
  6. add_dbus_watch
  7. toggle_dbus_watch
  8. remove_dbus_watch
  9. register_watch_functions
  10. timer_popped
  11. add_dbus_timer
  12. remove_dbus_timer
  13. toggle_dbus_timer
  14. register_timer_functions
  15. pcmk_dbus_connect
  16. pcmk_dbus_disconnect
  17. pcmk_dbus_find_error
  18. pcmk_dbus_send_recv
  19. pcmk_dbus_send
  20. pcmk_dbus_type_check
  21. free_property_query
  22. handle_query_result
  23. async_query_result_cb
  24. pcmk_dbus_get_property

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

/* [previous][next][first][last][top][bottom][index][help] */