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