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