pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dbus.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2014-2016 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This source code is licensed under the GNU Lesser General Public License
5  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
6  */
7 
8 #include <crm_internal.h>
9 #include <crm/crm.h>
10 #include <crm/services.h>
11 #include <dbus/dbus.h>
12 #include <pcmk-dbus.h>
13 
14 #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties"
15 
16 static GList *conn_dispatches = NULL;
17 
18 struct db_getall_data {
19  char *name;
20  char *target;
21  char *object;
22  void *userdata;
23  void (*callback)(const char *name, const char *value, void *userdata);
24 };
25 
26 static void
27 free_db_getall_data(struct db_getall_data *data)
28 {
29  free(data->target);
30  free(data->object);
31  free(data->name);
32  free(data);
33 }
34 
35 DBusConnection *
37 {
38  DBusError err;
39  DBusConnection *connection;
40 
41  dbus_error_init(&err);
42  connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
43  if (dbus_error_is_set(&err)) {
44  crm_err("Could not connect to System DBus: %s", err.message);
45  dbus_error_free(&err);
46  return NULL;
47  }
48  if(connection) {
50  }
51  return connection;
52 }
53 
54 void
55 pcmk_dbus_disconnect(DBusConnection *connection)
56 {
57  return;
58 }
59 
75 bool
76 pcmk_dbus_find_error(DBusPendingCall *pending, DBusMessage *reply,
77  DBusError *ret)
78 {
79  DBusError error;
80 
81  dbus_error_init(&error);
82 
83  if(pending == NULL) {
84  dbus_set_error_const(&error, "org.clusterlabs.pacemaker.NoRequest",
85  "No request sent");
86 
87  } else if(reply == NULL) {
88  dbus_set_error_const(&error, "org.clusterlabs.pacemaker.NoReply",
89  "No reply");
90 
91  } else {
92  DBusMessageIter args;
93  int dtype = dbus_message_get_type(reply);
94  char *sig;
95 
96  switch(dtype) {
97  case DBUS_MESSAGE_TYPE_METHOD_RETURN:
98  dbus_message_iter_init(reply, &args);
99  sig = dbus_message_iter_get_signature(&args);
100  crm_trace("DBus call returned output args '%s'", sig);
101  dbus_free(sig);
102  break;
103  case DBUS_MESSAGE_TYPE_INVALID:
104  dbus_set_error_const(&error,
105  "org.clusterlabs.pacemaker.InvalidReply",
106  "Invalid reply");
107  break;
108  case DBUS_MESSAGE_TYPE_METHOD_CALL:
109  dbus_set_error_const(&error,
110  "org.clusterlabs.pacemaker.InvalidReply.Method",
111  "Invalid reply (method call)");
112  break;
113  case DBUS_MESSAGE_TYPE_SIGNAL:
114  dbus_set_error_const(&error,
115  "org.clusterlabs.pacemaker.InvalidReply.Signal",
116  "Invalid reply (signal)");
117  break;
118  case DBUS_MESSAGE_TYPE_ERROR:
119  dbus_set_error_from_message(&error, reply);
120  break;
121  default:
122  dbus_set_error(&error,
123  "org.clusterlabs.pacemaker.InvalidReply.Type",
124  "Unknown reply type %d", dtype);
125  }
126  }
127 
128  if (dbus_error_is_set(&error)) {
129  crm_trace("DBus reply indicated error '%s' (%s)",
130  error.name, error.message);
131  if (ret) {
132  dbus_error_init(ret);
133  dbus_move_error(&error, ret);
134  } else {
135  dbus_error_free(&error);
136  }
137  return TRUE;
138  }
139 
140  return FALSE;
141 }
142 
158 DBusMessage *
159 pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection,
160  DBusError *error, int timeout)
161 {
162  const char *method = NULL;
163  DBusMessage *reply = NULL;
164  DBusPendingCall* pending = NULL;
165 
166  CRM_ASSERT(dbus_message_get_type (msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
167  method = dbus_message_get_member (msg);
168 
169  /* Ensure caller can reliably check whether error is set */
170  if (error) {
171  dbus_error_init(error);
172  }
173 
174  if (timeout <= 0) {
175  /* DBUS_TIMEOUT_USE_DEFAULT (-1) tells DBus to use a sane default */
176  timeout = DBUS_TIMEOUT_USE_DEFAULT;
177  }
178 
179  // send message and get a handle for a reply
180  if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
181  if(error) {
182  dbus_set_error(error, "org.clusterlabs.pacemaker.SendFailed",
183  "Could not queue DBus '%s' request", method);
184  }
185  return NULL;
186  }
187 
188  dbus_connection_flush(connection);
189 
190  if(pending) {
191  /* block until we receive a reply */
192  dbus_pending_call_block(pending);
193 
194  /* get the reply message */
195  reply = dbus_pending_call_steal_reply(pending);
196  }
197 
198  (void)pcmk_dbus_find_error(pending, reply, error);
199 
200  if(pending) {
201  /* free the pending message handle */
202  dbus_pending_call_unref(pending);
203  }
204 
205  return reply;
206 }
207 
222 DBusPendingCall *
223 pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection,
224  void(*done)(DBusPendingCall *pending, void *user_data),
225  void *user_data, int timeout)
226 {
227  const char *method = NULL;
228  DBusPendingCall* pending = NULL;
229 
230  CRM_ASSERT(done);
231  CRM_ASSERT(dbus_message_get_type (msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
232  method = dbus_message_get_member (msg);
233 
234 
235  if (timeout <= 0) {
236  /* DBUS_TIMEOUT_USE_DEFAULT (-1) tells DBus to use a sane default */
237  timeout = DBUS_TIMEOUT_USE_DEFAULT;
238  }
239 
240  // send message and get a handle for a reply
241  if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
242  crm_err("Send with reply failed for %s", method);
243  return NULL;
244 
245  } else if (pending == NULL) {
246  crm_err("No pending call found for %s: Connection to System DBus may be closed", method);
247  return NULL;
248  }
249 
250  crm_trace("DBus %s call sent", method);
251  if (dbus_pending_call_get_completed(pending)) {
252  crm_info("DBus %s call completed too soon", method);
253  if(done) {
254 #if 0
255  /* This sounds like a good idea, but allegedly it breaks things */
256  done(pending, user_data);
257  pending = NULL;
258 #else
259  CRM_ASSERT(dbus_pending_call_set_notify(pending, done, user_data, NULL));
260 #endif
261  }
262 
263  } else if(done) {
264  CRM_ASSERT(dbus_pending_call_set_notify(pending, done, user_data, NULL));
265  }
266  return pending;
267 }
268 
269 bool
270 pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected,
271  const char *function, int line)
272 {
273  int dtype = 0;
274  DBusMessageIter lfield;
275 
276  if(field == NULL) {
277  if(dbus_message_iter_init(msg, &lfield)) {
278  field = &lfield;
279  }
280  }
281 
282  if(field == NULL) {
283  do_crm_log_alias(LOG_ERR, __FILE__, function, line,
284  "Empty parameter list in reply expecting '%c'", expected);
285  return FALSE;
286  }
287 
288  dtype = dbus_message_iter_get_arg_type(field);
289 
290  if(dtype != expected) {
291  DBusMessageIter args;
292  char *sig;
293 
294  dbus_message_iter_init(msg, &args);
295  sig = dbus_message_iter_get_signature(&args);
296  do_crm_log_alias(LOG_ERR, __FILE__, function, line,
297  "Unexpected DBus type, expected %c in '%s' instead of %c",
298  expected, sig, dtype);
299  dbus_free(sig);
300  return FALSE;
301  }
302 
303  return TRUE;
304 }
305 
306 static char *
307 pcmk_dbus_lookup_result(DBusMessage *reply, struct db_getall_data *data)
308 {
309  DBusError error;
310  char *output = NULL;
311  DBusMessageIter dict;
312  DBusMessageIter args;
313 
314  if (pcmk_dbus_find_error((void*)&error, reply, &error)) {
315  crm_err("Cannot get properties from %s for %s: %s",
316  data->target, data->object, error.message);
317  dbus_error_free(&error);
318  goto cleanup;
319  }
320 
321  dbus_message_iter_init(reply, &args);
322  if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __FUNCTION__, __LINE__)) {
323  crm_err("Invalid reply from %s for %s", data->target, data->object);
324  goto cleanup;
325  }
326 
327  dbus_message_iter_recurse(&args, &dict);
328  while (dbus_message_iter_get_arg_type (&dict) != DBUS_TYPE_INVALID) {
329  DBusMessageIter sv;
330  DBusMessageIter v;
331  DBusBasicValue name;
332  DBusBasicValue value;
333 
334  if(!pcmk_dbus_type_check(reply, &dict, DBUS_TYPE_DICT_ENTRY, __FUNCTION__, __LINE__)) {
335  dbus_message_iter_next (&dict);
336  continue;
337  }
338 
339  dbus_message_iter_recurse(&dict, &sv);
340  while (dbus_message_iter_get_arg_type (&sv) != DBUS_TYPE_INVALID) {
341  int dtype = dbus_message_iter_get_arg_type(&sv);
342 
343  switch(dtype) {
344  case DBUS_TYPE_STRING:
345  dbus_message_iter_get_basic(&sv, &name);
346 
347  if(data->name && strcmp(name.str, data->name) != 0) {
348  dbus_message_iter_next (&sv); /* Skip the value */
349  }
350  break;
351  case DBUS_TYPE_VARIANT:
352  dbus_message_iter_recurse(&sv, &v);
353  if(pcmk_dbus_type_check(reply, &v, DBUS_TYPE_STRING, __FUNCTION__, __LINE__)) {
354  dbus_message_iter_get_basic(&v, &value);
355 
356  crm_trace("Property %s[%s] is '%s'", data->object, name.str, value.str);
357  if(data->callback) {
358  data->callback(name.str, value.str, data->userdata);
359 
360  } else {
361  free(output);
362  output = strdup(value.str);
363  }
364 
365  if(data->name) {
366  goto cleanup;
367  }
368  }
369  break;
370  default:
371  pcmk_dbus_type_check(reply, &sv, DBUS_TYPE_STRING, __FUNCTION__, __LINE__);
372  }
373  dbus_message_iter_next (&sv);
374  }
375 
376  dbus_message_iter_next (&dict);
377  }
378 
379  if(data->name && data->callback) {
380  crm_trace("No value for property %s[%s]", data->object, data->name);
381  data->callback(data->name, NULL, data->userdata);
382  }
383 
384  cleanup:
385  free_db_getall_data(data);
386  return output;
387 }
388 
389 static void
390 pcmk_dbus_lookup_cb(DBusPendingCall *pending, void *user_data)
391 {
392  DBusMessage *reply = NULL;
393  char *value = NULL;
394 
395  if(pending) {
396  reply = dbus_pending_call_steal_reply(pending);
397  }
398 
399  value = pcmk_dbus_lookup_result(reply, user_data);
400  free(value);
401 
402  if(reply) {
403  dbus_message_unref(reply);
404  }
405 }
406 
407 char *
408 pcmk_dbus_get_property(DBusConnection *connection, const char *target,
409  const char *obj, const gchar * iface, const char *name,
410  void (*callback)(const char *name, const char *value, void *userdata),
411  void *userdata, DBusPendingCall **pending, int timeout)
412 {
413  DBusMessage *msg;
414  const char *method = "GetAll";
415  char *output = NULL;
416  struct db_getall_data *query_data = NULL;
417 
418  crm_debug("Calling: %s on %s", method, target);
419  msg = dbus_message_new_method_call(target, // target for the method call
420  obj, // object to call on
421  BUS_PROPERTY_IFACE, // interface to call on
422  method); // method name
423  if (NULL == msg) {
424  crm_err("Call to %s failed: No message", method);
425  return NULL;
426  }
427 
428  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID));
429 
430  query_data = malloc(sizeof(struct db_getall_data));
431  if(query_data == NULL) {
432  crm_err("Call to %s failed: malloc failed", method);
433  return NULL;
434  }
435 
436  query_data->target = strdup(target);
437  query_data->object = strdup(obj);
438  query_data->callback = callback;
439  query_data->userdata = userdata;
440  query_data->name = NULL;
441 
442  if(name) {
443  query_data->name = strdup(name);
444  }
445 
446  if (query_data->callback) {
447  DBusPendingCall *local_pending;
448 
449  local_pending = pcmk_dbus_send(msg, connection, pcmk_dbus_lookup_cb,
450  query_data, timeout);
451  if (local_pending == NULL) {
452  // pcmk_dbus_lookup_cb() was not called in this case
453  free_db_getall_data(query_data);
454  query_data = NULL;
455  }
456 
457  if (pending) {
458  *pending = local_pending;
459  }
460 
461  } else {
462  DBusMessage *reply = pcmk_dbus_send_recv(msg, connection, NULL, timeout);
463 
464  output = pcmk_dbus_lookup_result(reply, query_data);
465 
466  if(reply) {
467  dbus_message_unref(reply);
468  }
469  }
470 
471  dbus_message_unref(msg);
472 
473  return output;
474 }
475 
476 static void
477 pcmk_dbus_connection_dispatch_status(DBusConnection *connection,
478  DBusDispatchStatus new_status, void *data)
479 {
480  crm_trace("New status %d for connection %p", new_status, connection);
481  if (new_status == DBUS_DISPATCH_DATA_REMAINS){
482  conn_dispatches = g_list_prepend(conn_dispatches, connection);
483  }
484 }
485 
486 static void
487 pcmk_dbus_connections_dispatch(void)
488 {
489  GList *gIter = NULL;
490 
491  for (gIter = conn_dispatches; gIter != NULL; gIter = gIter->next) {
492  DBusConnection *connection = gIter->data;
493 
494  while (dbus_connection_get_dispatch_status(connection) == DBUS_DISPATCH_DATA_REMAINS) {
495  crm_trace("Dispatching for connection %p", connection);
496  dbus_connection_dispatch(connection);
497  }
498  }
499 
500  g_list_free(conn_dispatches);
501  conn_dispatches = NULL;
502 }
503 
504 /* Copied from dbus-watch.c */
505 
506 static const char*
507 dbus_watch_flags_to_string(int flags)
508 {
509  const char *watch_type;
510 
511  if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) {
512  watch_type = "readwrite";
513  } else if (flags & DBUS_WATCH_READABLE) {
514  watch_type = "read";
515  } else if (flags & DBUS_WATCH_WRITABLE) {
516  watch_type = "write";
517  } else {
518  watch_type = "not read or write";
519  }
520  return watch_type;
521 }
522 
523 static int
524 pcmk_dbus_watch_dispatch(gpointer userdata)
525 {
526  bool oom = FALSE;
527  DBusWatch *watch = userdata;
528  int flags = dbus_watch_get_flags(watch);
529  bool enabled = dbus_watch_get_enabled (watch);
530  mainloop_io_t *client = dbus_watch_get_data(watch);
531 
532  crm_trace("Dispatching client %p: %s", client, dbus_watch_flags_to_string(flags));
533  if (enabled && (flags & (DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE))) {
534  oom = !dbus_watch_handle(watch, flags);
535 
536  } else if(enabled) {
537  oom = !dbus_watch_handle(watch, DBUS_WATCH_ERROR);
538  }
539 
540  if(flags != dbus_watch_get_flags(watch)) {
541  flags = dbus_watch_get_flags(watch);
542  crm_trace("Dispatched client %p: %s (%d)", client,
543  dbus_watch_flags_to_string(flags), flags);
544  }
545 
546  if(oom) {
547  crm_err("DBus encountered OOM while attempting to dispatch %p (%s)",
548  client, dbus_watch_flags_to_string(flags));
549 
550  } else {
551  pcmk_dbus_connections_dispatch();
552  }
553 
554  return 0;
555 }
556 
557 static void
558 pcmk_dbus_watch_destroy(gpointer userdata)
559 {
560  mainloop_io_t *client = dbus_watch_get_data(userdata);
561  crm_trace("Destroyed %p", client);
562 }
563 
564 
566  .dispatch = pcmk_dbus_watch_dispatch,
567  .destroy = pcmk_dbus_watch_destroy,
568 };
569 
570 static dbus_bool_t
571 pcmk_dbus_watch_add(DBusWatch *watch, void *data)
572 {
573  int fd = dbus_watch_get_unix_fd(watch);
574 
575  mainloop_io_t *client = mainloop_add_fd(
576  "dbus", G_PRIORITY_DEFAULT, fd, watch, &pcmk_dbus_cb);
577 
578  crm_trace("Added watch %p with fd=%d to client %p", watch, fd, client);
579  dbus_watch_set_data(watch, client, NULL);
580  return TRUE;
581 }
582 
583 static void
584 pcmk_dbus_watch_toggle(DBusWatch *watch, void *data)
585 {
586  mainloop_io_t *client = dbus_watch_get_data(watch);
587  crm_notice("DBus client %p is now %s",
588  client, (dbus_watch_get_enabled(watch)? "enabled" : "disabled"));
589 }
590 
591 
592 static void
593 pcmk_dbus_watch_remove(DBusWatch *watch, void *data)
594 {
595  mainloop_io_t *client = dbus_watch_get_data(watch);
596 
597  crm_trace("Removed client %p (%p)", client, data);
598  mainloop_del_fd(client);
599 }
600 
601 static gboolean
602 pcmk_dbus_timeout_dispatch(gpointer data)
603 {
604  crm_info("Timeout %p expired", data);
605  dbus_timeout_handle(data);
606  return FALSE;
607 }
608 
609 static dbus_bool_t
610 pcmk_dbus_timeout_add(DBusTimeout *timeout, void *data)
611 {
612  guint id = g_timeout_add(dbus_timeout_get_interval(timeout),
613  pcmk_dbus_timeout_dispatch, timeout);
614 
615  crm_trace("Adding timeout %p (%d)", timeout, dbus_timeout_get_interval(timeout));
616 
617  if(id) {
618  dbus_timeout_set_data(timeout, GUINT_TO_POINTER(id), NULL);
619  }
620  return TRUE;
621 }
622 
623 static void
624 pcmk_dbus_timeout_remove(DBusTimeout *timeout, void *data)
625 {
626  void *vid = dbus_timeout_get_data(timeout);
627  guint id = GPOINTER_TO_UINT(vid);
628 
629  crm_trace("Removing timeout %p (%p)", timeout, data);
630 
631  if(id) {
632  g_source_remove(id);
633  dbus_timeout_set_data(timeout, 0, NULL);
634  }
635 }
636 
637 static void
638 pcmk_dbus_timeout_toggle(DBusTimeout *timeout, void *data)
639 {
640  bool enabled = dbus_timeout_get_enabled(timeout);
641 
642  crm_trace("Toggling timeout for %p to %s", timeout, enabled?"off":"on");
643 
644  if(enabled) {
645  pcmk_dbus_timeout_add(timeout, data);
646  } else {
647  pcmk_dbus_timeout_remove(timeout, data);
648  }
649 }
650 
651 /* Inspired by http://www.kolej.mff.cuni.cz/~vesej3am/devel/dbus-select.c */
652 
653 void
655 {
656  dbus_connection_set_exit_on_disconnect(c, FALSE);
657  dbus_connection_set_timeout_functions(c, pcmk_dbus_timeout_add,
658  pcmk_dbus_timeout_remove,
659  pcmk_dbus_timeout_toggle, NULL, NULL);
660  dbus_connection_set_watch_functions(c, pcmk_dbus_watch_add,
661  pcmk_dbus_watch_remove,
662  pcmk_dbus_watch_toggle, NULL, NULL);
663  dbus_connection_set_dispatch_status_function(c, pcmk_dbus_connection_dispatch_status, NULL, NULL);
664  pcmk_dbus_connection_dispatch_status(c, dbus_connection_get_dispatch_status(c), NULL);
665 }
Services API.
A dumping ground.
#define crm_notice(fmt, args...)
Definition: logging.h:365
mainloop_io_t * mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks *callbacks)
Definition: mainloop.c:881
#define BUS_PROPERTY_IFACE
Definition: dbus.c:14
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition: dbus.c:55
#define DBUS_TIMEOUT_USE_DEFAULT
Definition: pcmk-dbus.h:14
struct mainloop_io_s mainloop_io_t
Definition: mainloop.h:32
void pcmk_dbus_connection_setup_with_select(DBusConnection *c)
Definition: dbus.c:654
int(* dispatch)(gpointer userdata)
Definition: mainloop.h:115
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:219
#define do_crm_log_alias(level, file, function, line, fmt, args...)
Log a message as if it came from a different code location.
Definition: logging.h:286
bool pcmk_dbus_find_error(DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition: dbus.c:76
#define crm_debug(fmt, args...)
Definition: logging.h:368
#define crm_trace(fmt, args...)
Definition: logging.h:369
char * pcmk_dbus_get_property(DBusConnection *connection, const char *target, const char *obj, const gchar *iface, const char *name, void(*callback)(const char *name, const char *value, void *userdata), void *userdata, DBusPendingCall **pending, int timeout)
Definition: dbus.c:408
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition: dbus.c:223
DBusConnection * pcmk_dbus_connect(void)
Definition: dbus.c:36
const char * target
Definition: pcmk_fence.c:28
#define crm_err(fmt, args...)
Definition: logging.h:363
#define CRM_ASSERT(expr)
Definition: results.h:42
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition: dbus.c:270
char data[0]
Definition: internal.h:90
void mainloop_del_fd(mainloop_io_t *client)
Definition: mainloop.c:925
char * name
Definition: pcmk_fence.c:30
unsigned int timeout
Definition: pcmk_fence.c:31
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition: dbus.c:159
#define crm_info(fmt, args...)
Definition: logging.h:366
struct mainloop_fd_callbacks pcmk_dbus_cb
Definition: dbus.c:565
uint64_t flags
Definition: remote.c:149