This source file includes following definitions.
- pcmk_dbus_connect
- pcmk_dbus_disconnect
- pcmk_dbus_find_error
- pcmk_dbus_send_recv
- pcmk_dbus_send
- pcmk_dbus_type_check
- pcmk_dbus_lookup_result
- pcmk_dbus_lookup_cb
- pcmk_dbus_get_property
- pcmk_dbus_connection_dispatch_status
- pcmk_dbus_connections_dispatch
- dbus_watch_flags_to_string
- pcmk_dbus_watch_dispatch
- pcmk_dbus_watch_destroy
- pcmk_dbus_watch_add
- pcmk_dbus_watch_toggle
- pcmk_dbus_watch_remove
- pcmk_dbus_timeout_dispatch
- pcmk_dbus_timeout_add
- pcmk_dbus_timeout_remove
- pcmk_dbus_timeout_toggle
- pcmk_dbus_connection_setup_with_select
1
2
3
4
5
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 *
27 pcmk_dbus_connect(void)
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) {
40 pcmk_dbus_connection_setup_with_select(connection);
41 }
42 return connection;
43 }
44
45 void
46 pcmk_dbus_disconnect(DBusConnection *connection)
47 {
48 return;
49 }
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
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
161 if (error) {
162 dbus_error_init(error);
163 }
164
165 if (timeout <= 0) {
166
167 timeout = DBUS_TIMEOUT_USE_DEFAULT;
168 }
169
170
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
183 dbus_pending_call_block(pending);
184
185
186 reply = dbus_pending_call_steal_reply(pending);
187 }
188
189 (void)pcmk_dbus_find_error(pending, reply, error);
190
191 if(pending) {
192
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
214 timeout = DBUS_TIMEOUT_USE_DEFAULT;
215 }
216
217
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
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);
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,
401 obj,
402 BUS_PROPERTY_IFACE,
403 method);
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
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
538 struct mainloop_fd_callbacks pcmk_dbus_cb = {
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
625
626 void
627 pcmk_dbus_connection_setup_with_select(DBusConnection *c)
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 }