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

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