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