This source file includes following definitions.
- update_dispatch_status
- dispatch_messages
- dbus_watch_flags_to_string
- dispatch_fd_data
- watch_fd_closed
- add_dbus_watch
- toggle_dbus_watch
- remove_dbus_watch
- register_watch_functions
- timer_popped
- add_dbus_timer
- remove_dbus_timer
- toggle_dbus_timer
- register_timer_functions
- pcmk_dbus_connect
- pcmk_dbus_disconnect
- pcmk_dbus_find_error
- pcmk_dbus_send_recv
- pcmk_dbus_send
- pcmk_dbus_type_check
- free_property_query
- handle_query_result
- async_query_result_cb
- pcmk_dbus_get_property
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12 #include <crm/services.h>
13 #include <dbus/dbus.h>
14 #include <pcmk-dbus.h>
15
16
17
18
19
20
21 static GList *conn_dispatches = NULL;
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37 static void
38 update_dispatch_status(DBusConnection *connection,
39 DBusDispatchStatus new_status, void *data)
40 {
41 if (new_status == DBUS_DISPATCH_DATA_REMAINS) {
42 crm_trace("DBus connection has messages available for dispatch");
43 conn_dispatches = g_list_prepend(conn_dispatches, connection);
44 } else {
45 crm_trace("DBus connection has no messages available for dispatch "
46 "(status %d)", new_status);
47 }
48 }
49
50
51
52
53
54 static void
55 dispatch_messages(void)
56 {
57 for (GList *gIter = conn_dispatches; gIter != NULL; gIter = gIter->next) {
58 DBusConnection *connection = gIter->data;
59
60 while (dbus_connection_get_dispatch_status(connection)
61 == DBUS_DISPATCH_DATA_REMAINS) {
62 crm_trace("Dispatching available messages on DBus connection");
63 dbus_connection_dispatch(connection);
64 }
65 }
66 g_list_free(conn_dispatches);
67 conn_dispatches = NULL;
68 }
69
70
71
72
73
74
75
76
77
78
79 static const char*
80 dbus_watch_flags_to_string(int flags)
81 {
82 const char *watch_type;
83
84 if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) {
85 watch_type = "read/write";
86 } else if (flags & DBUS_WATCH_READABLE) {
87 watch_type = "read";
88 } else if (flags & DBUS_WATCH_WRITABLE) {
89 watch_type = "write";
90 } else {
91 watch_type = "neither read nor write";
92 }
93 return watch_type;
94 }
95
96
97
98
99
100
101
102
103
104
105
106
107 static int
108 dispatch_fd_data(gpointer userdata)
109 {
110 bool oom = FALSE;
111 DBusWatch *watch = userdata;
112 int flags = dbus_watch_get_flags(watch);
113 bool enabled = dbus_watch_get_enabled (watch);
114
115 crm_trace("Dispatching DBus watch for file descriptor %d "
116 "with flags 0x%x (%s)",
117 dbus_watch_get_unix_fd(watch), flags,
118 dbus_watch_flags_to_string(flags));
119
120 if (enabled && (flags & (DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE))) {
121 oom = !dbus_watch_handle(watch, flags);
122
123 } else if (enabled) {
124 oom = !dbus_watch_handle(watch, DBUS_WATCH_ERROR);
125 }
126
127 if (flags != dbus_watch_get_flags(watch)) {
128 flags = dbus_watch_get_flags(watch);
129 crm_trace("Dispatched DBus file descriptor watch: now 0x%x (%s)",
130 flags, dbus_watch_flags_to_string(flags));
131 }
132
133 if (oom) {
134 crm_crit("Could not dispatch DBus file descriptor data: Out of memory");
135 } else {
136 dispatch_messages();
137 }
138 return 0;
139 }
140
141 static void
142 watch_fd_closed(gpointer userdata)
143 {
144 crm_trace("DBus watch for file descriptor %d is now closed",
145 dbus_watch_get_unix_fd((DBusWatch *) userdata));
146 }
147
148 static struct mainloop_fd_callbacks pcmk_dbus_cb = {
149 .dispatch = dispatch_fd_data,
150 .destroy = watch_fd_closed,
151 };
152
153 static dbus_bool_t
154 add_dbus_watch(DBusWatch *watch, void *data)
155 {
156 int fd = dbus_watch_get_unix_fd(watch);
157
158 mainloop_io_t *client = mainloop_add_fd("dbus", G_PRIORITY_DEFAULT, fd,
159 watch, &pcmk_dbus_cb);
160
161 crm_trace("Added DBus watch for file descriptor %d", fd);
162 dbus_watch_set_data(watch, client, NULL);
163 return TRUE;
164 }
165
166 static void
167 toggle_dbus_watch(DBusWatch *watch, void *data)
168 {
169
170 crm_debug("DBus watch for file descriptor %d is now %s",
171 dbus_watch_get_unix_fd(watch),
172 (dbus_watch_get_enabled(watch)? "enabled" : "disabled"));
173 }
174
175 static void
176 remove_dbus_watch(DBusWatch *watch, void *data)
177 {
178 crm_trace("Removed DBus watch for file descriptor %d",
179 dbus_watch_get_unix_fd(watch));
180 mainloop_del_fd((mainloop_io_t *) dbus_watch_get_data(watch));
181 }
182
183 static void
184 register_watch_functions(DBusConnection *connection)
185 {
186 dbus_connection_set_watch_functions(connection, add_dbus_watch,
187 remove_dbus_watch,
188 toggle_dbus_watch, NULL, NULL);
189 }
190
191
192
193
194
195
196
197
198 static gboolean
199 timer_popped(gpointer data)
200 {
201 crm_debug("%dms DBus timer expired",
202 dbus_timeout_get_interval((DBusTimeout *) data));
203 dbus_timeout_handle(data);
204 return FALSE;
205 }
206
207 static dbus_bool_t
208 add_dbus_timer(DBusTimeout *timeout, void *data)
209 {
210 int interval_ms = dbus_timeout_get_interval(timeout);
211 guint id = g_timeout_add(interval_ms, timer_popped, timeout);
212
213 if (id) {
214 dbus_timeout_set_data(timeout, GUINT_TO_POINTER(id), NULL);
215 }
216 crm_trace("Added %dms DBus timer", interval_ms);
217 return TRUE;
218 }
219
220 static void
221 remove_dbus_timer(DBusTimeout *timeout, void *data)
222 {
223 void *vid = dbus_timeout_get_data(timeout);
224 guint id = GPOINTER_TO_UINT(vid);
225
226 crm_trace("Removing %dms DBus timer", dbus_timeout_get_interval(timeout));
227 if (id) {
228 g_source_remove(id);
229 dbus_timeout_set_data(timeout, 0, NULL);
230 }
231 }
232
233 static void
234 toggle_dbus_timer(DBusTimeout *timeout, void *data)
235 {
236 bool enabled = dbus_timeout_get_enabled(timeout);
237
238 crm_trace("Toggling %dms DBus timer %s",
239 dbus_timeout_get_interval(timeout), (enabled? "off": "on"));
240 if (enabled) {
241 add_dbus_timer(timeout, data);
242 } else {
243 remove_dbus_timer(timeout, data);
244 }
245 }
246
247 static void
248 register_timer_functions(DBusConnection *connection)
249 {
250 dbus_connection_set_timeout_functions(connection, add_dbus_timer,
251 remove_dbus_timer,
252 toggle_dbus_timer, NULL, NULL);
253 }
254
255
256
257
258
259 DBusConnection *
260 pcmk_dbus_connect(void)
261 {
262 DBusError err;
263 DBusConnection *connection;
264
265 dbus_error_init(&err);
266 connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
267 if (dbus_error_is_set(&err)) {
268 crm_err("Could not connect to DBus: %s", err.message);
269 dbus_error_free(&err);
270 return NULL;
271 }
272 if (connection == NULL) {
273 return NULL;
274 }
275
276
277
278
279 dbus_connection_set_exit_on_disconnect(connection, FALSE);
280
281
282 register_timer_functions(connection);
283 register_watch_functions(connection);
284 dbus_connection_set_dispatch_status_function(connection,
285 update_dispatch_status,
286 NULL, NULL);
287
288
289 update_dispatch_status(connection,
290 dbus_connection_get_dispatch_status(connection),
291 NULL);
292 return connection;
293 }
294
295 void
296 pcmk_dbus_disconnect(DBusConnection *connection)
297 {
298
299
300
301
302
303 return;
304 }
305
306
307 #define ERR_NO_REQUEST "org.clusterlabs.pacemaker.NoRequest"
308 #define ERR_NO_REPLY "org.clusterlabs.pacemaker.NoReply"
309 #define ERR_INVALID_REPLY "org.clusterlabs.pacemaker.InvalidReply"
310 #define ERR_INVALID_REPLY_METHOD "org.clusterlabs.pacemaker.InvalidReply.Method"
311 #define ERR_INVALID_REPLY_SIGNAL "org.clusterlabs.pacemaker.InvalidReply.Signal"
312 #define ERR_INVALID_REPLY_TYPE "org.clusterlabs.pacemaker.InvalidReply.Type"
313 #define ERR_SEND_FAILED "org.clusterlabs.pacemaker.SendFailed"
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330 bool
331 pcmk_dbus_find_error(DBusPendingCall *pending, DBusMessage *reply,
332 DBusError *ret)
333 {
334 DBusError error;
335
336 dbus_error_init(&error);
337
338 if (pending == NULL) {
339 dbus_set_error_const(&error, ERR_NO_REQUEST, "No request sent");
340
341 } else if (reply == NULL) {
342 dbus_set_error_const(&error, ERR_NO_REPLY, "No reply");
343
344 } else {
345 DBusMessageIter args;
346 int dtype = dbus_message_get_type(reply);
347
348 switch (dtype) {
349 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
350 {
351 char *sig = NULL;
352
353 dbus_message_iter_init(reply, &args);
354 crm_trace("Received DBus reply with argument type '%s'",
355 (sig = dbus_message_iter_get_signature(&args)));
356 if (sig != NULL) {
357 dbus_free(sig);
358 }
359 }
360 break;
361 case DBUS_MESSAGE_TYPE_INVALID:
362 dbus_set_error_const(&error, ERR_INVALID_REPLY,
363 "Invalid reply");
364 break;
365 case DBUS_MESSAGE_TYPE_METHOD_CALL:
366 dbus_set_error_const(&error, ERR_INVALID_REPLY_METHOD,
367 "Invalid reply (method call)");
368 break;
369 case DBUS_MESSAGE_TYPE_SIGNAL:
370 dbus_set_error_const(&error, ERR_INVALID_REPLY_SIGNAL,
371 "Invalid reply (signal)");
372 break;
373 case DBUS_MESSAGE_TYPE_ERROR:
374 dbus_set_error_from_message(&error, reply);
375 break;
376 default:
377 dbus_set_error(&error, ERR_INVALID_REPLY_TYPE,
378 "Unknown reply type %d", dtype);
379 }
380 }
381
382 if (dbus_error_is_set(&error)) {
383 crm_trace("DBus reply indicated error '%s' (%s)",
384 error.name, error.message);
385 if (ret) {
386 dbus_error_init(ret);
387 dbus_move_error(&error, ret);
388 } else {
389 dbus_error_free(&error);
390 }
391 return TRUE;
392 }
393
394 return FALSE;
395 }
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412 DBusMessage *
413 pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection,
414 DBusError *error, int timeout)
415 {
416 const char *method = NULL;
417 DBusMessage *reply = NULL;
418 DBusPendingCall* pending = NULL;
419
420 CRM_ASSERT(dbus_message_get_type (msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
421 method = dbus_message_get_member (msg);
422
423
424 if (error) {
425 dbus_error_init(error);
426 }
427
428 if (timeout <= 0) {
429
430 timeout = DBUS_TIMEOUT_USE_DEFAULT;
431 }
432
433
434 if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
435 if (error) {
436 dbus_set_error(error, ERR_SEND_FAILED,
437 "Could not queue DBus '%s' request", method);
438 }
439 return NULL;
440 }
441
442 dbus_connection_flush(connection);
443
444 if (pending) {
445
446 dbus_pending_call_block(pending);
447
448
449 reply = dbus_pending_call_steal_reply(pending);
450 }
451
452 (void) pcmk_dbus_find_error(pending, reply, error);
453
454 if (pending) {
455
456 dbus_pending_call_unref(pending);
457 }
458
459 return reply;
460 }
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476 DBusPendingCall *
477 pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection,
478 void (*done)(DBusPendingCall *pending, void *user_data),
479 void *user_data, int timeout)
480 {
481 const char *method = NULL;
482 DBusPendingCall* pending = NULL;
483
484 CRM_ASSERT(done);
485 CRM_ASSERT(dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
486 method = dbus_message_get_member(msg);
487
488 if (timeout <= 0) {
489
490 timeout = DBUS_TIMEOUT_USE_DEFAULT;
491 }
492
493
494 if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
495 crm_err("Could not send DBus %s message: failed", method);
496 return NULL;
497
498 } else if (pending == NULL) {
499 crm_err("Could not send DBus %s message: connection may be closed",
500 method);
501 return NULL;
502 }
503
504 if (dbus_pending_call_get_completed(pending)) {
505 crm_info("DBus %s message completed too soon", method);
506
507
508
509 }
510 if (!dbus_pending_call_set_notify(pending, done, user_data, NULL)) {
511 return NULL;
512 }
513 return pending;
514 }
515
516 bool
517 pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected,
518 const char *function, int line)
519 {
520 int dtype = 0;
521 DBusMessageIter lfield;
522
523 if (field == NULL) {
524 if (dbus_message_iter_init(msg, &lfield)) {
525 field = &lfield;
526 }
527 }
528
529 if (field == NULL) {
530 do_crm_log_alias(LOG_INFO, __FILE__, function, line,
531 "DBus reply has empty parameter list (expected '%c')",
532 expected);
533 return FALSE;
534 }
535
536 dtype = dbus_message_iter_get_arg_type(field);
537
538 if (dtype != expected) {
539 DBusMessageIter args;
540 char *sig;
541
542 dbus_message_iter_init(msg, &args);
543 sig = dbus_message_iter_get_signature(&args);
544 do_crm_log_alias(LOG_INFO, __FILE__, function, line,
545 "DBus reply has unexpected type "
546 "(expected '%c' not '%c' in '%s')",
547 expected, dtype, sig);
548 dbus_free(sig);
549 return FALSE;
550 }
551
552 return TRUE;
553 }
554
555
556
557
558
559
560
561
562
563
564 #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties"
565
566
567 typedef void (*property_callback_func)(const char *name,
568 const char *value,
569 void *userdata);
570
571
572 struct property_query {
573 char *name;
574 char *target;
575 char *object;
576 void *userdata;
577 property_callback_func callback;
578 };
579
580 static void
581 free_property_query(struct property_query *data)
582 {
583 free(data->target);
584 free(data->object);
585 free(data->name);
586 free(data);
587 }
588
589 static char *
590 handle_query_result(DBusMessage *reply, struct property_query *data)
591 {
592 DBusError error;
593 char *output = NULL;
594 DBusMessageIter args;
595 DBusMessageIter variant_iter;
596 DBusBasicValue value;
597
598
599 if (pcmk_dbus_find_error((void*)&error, reply, &error)) {
600 crm_err("DBus query for %s property '%s' failed: %s",
601 data->object, data->name, error.message);
602 dbus_error_free(&error);
603 goto cleanup;
604 }
605
606
607 dbus_message_iter_init(reply, &args);
608 if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_VARIANT,
609 __func__, __LINE__)) {
610 crm_err("DBus query for %s property '%s' failed: Unexpected reply type",
611 data->object, data->name);
612 goto cleanup;
613 }
614
615
616 dbus_message_iter_recurse(&args, &variant_iter);
617 if (!pcmk_dbus_type_check(reply, &variant_iter, DBUS_TYPE_STRING,
618 __func__, __LINE__)) {
619 crm_err("DBus query for %s property '%s' failed: "
620 "Unexpected variant type", data->object, data->name);
621 goto cleanup;
622 }
623 dbus_message_iter_get_basic(&variant_iter, &value);
624
625
626 dbus_message_iter_next(&variant_iter);
627 if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_INVALID) {
628 crm_err("DBus query for %s property '%s' failed: "
629 "Too many arguments in reply",
630 data->object, data->name);
631 goto cleanup;
632 }
633 dbus_message_iter_next(&args);
634 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_INVALID) {
635 crm_err("DBus query for %s property '%s' failed: "
636 "Too many arguments in reply", data->object, data->name);
637 goto cleanup;
638 }
639
640 crm_trace("DBus query result for %s: %s='%s'",
641 data->object, data->name, (value.str? value.str : ""));
642
643 if (data->callback) {
644 data->callback(data->name, (value.str? value.str : ""), data->userdata);
645
646 } else {
647 output = strdup(value.str? value.str : "");
648 }
649
650 cleanup:
651 free_property_query(data);
652 return output;
653 }
654
655 static void
656 async_query_result_cb(DBusPendingCall *pending, void *user_data)
657 {
658 DBusMessage *reply = NULL;
659 char *value = NULL;
660
661 if (pending) {
662 reply = dbus_pending_call_steal_reply(pending);
663 }
664
665 value = handle_query_result(reply, user_data);
666 free(value);
667
668 if (reply) {
669 dbus_message_unref(reply);
670 }
671 }
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693 char *
694 pcmk_dbus_get_property(DBusConnection *connection, const char *target,
695 const char *obj, const gchar * iface, const char *name,
696 property_callback_func callback, void *userdata,
697 DBusPendingCall **pending, int timeout)
698 {
699 DBusMessage *msg;
700 char *output = NULL;
701 struct property_query *query_data = NULL;
702
703 CRM_CHECK((connection != NULL) && (target != NULL) && (obj != NULL)
704 && (iface != NULL) && (name != NULL), return NULL);
705
706 crm_trace("Querying DBus %s for %s property '%s'",
707 target, obj, name);
708
709
710 msg = dbus_message_new_method_call(target, obj, BUS_PROPERTY_IFACE, "Get");
711 if (msg == NULL) {
712 crm_err("DBus query for %s property '%s' failed: "
713 "Unable to create message", obj, name);
714 return NULL;
715 }
716
717
718 if (!dbus_message_append_args(msg,
719 DBUS_TYPE_STRING, &iface,
720 DBUS_TYPE_STRING, &name,
721 DBUS_TYPE_INVALID)) {
722 crm_err("DBus query for %s property '%s' failed: "
723 "Could not append arguments", obj, name);
724 dbus_message_unref(msg);
725 return NULL;
726 }
727
728 query_data = malloc(sizeof(struct property_query));
729 if (query_data == NULL) {
730 crm_crit("DBus query for %s property '%s' failed: Out of memory",
731 obj, name);
732 dbus_message_unref(msg);
733 return NULL;
734 }
735
736 query_data->target = strdup(target);
737 query_data->object = strdup(obj);
738 query_data->callback = callback;
739 query_data->userdata = userdata;
740 query_data->name = strdup(name);
741 CRM_CHECK((query_data->target != NULL)
742 && (query_data->object != NULL)
743 && (query_data->name != NULL),
744 free_property_query(query_data);
745 dbus_message_unref(msg);
746 return NULL);
747
748 if (query_data->callback) {
749 DBusPendingCall *local_pending;
750
751 local_pending = pcmk_dbus_send(msg, connection, async_query_result_cb,
752 query_data, timeout);
753 if (local_pending == NULL) {
754
755 free_property_query(query_data);
756 query_data = NULL;
757 }
758
759 if (pending) {
760 *pending = local_pending;
761 }
762
763 } else {
764 DBusMessage *reply = pcmk_dbus_send_recv(msg, connection, NULL,
765 timeout);
766
767 output = handle_query_result(reply, query_data);
768
769 if (reply) {
770 dbus_message_unref(reply);
771 }
772 }
773
774 dbus_message_unref(msg);
775
776 return output;
777 }