pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
dbus.c
Go to the documentation of this file.
1/*
2 * Copyright 2014-2024 the Pacemaker project contributors
3 *
4 * The version control history for this file may have further details.
5 *
6 * This source code is licensed under the GNU Lesser General Public License
7 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8 */
9
10#include <crm_internal.h>
11#include <crm/crm.h>
12#include <dbus/dbus.h>
13#include <pcmk-dbus.h>
14
15/*
16 * DBus message dispatch
17 */
18
19// List of DBus connections (DBusConnection*) with messages available
20static GList *conn_dispatches = NULL;
21
36static void
37update_dispatch_status(DBusConnection *connection,
38 DBusDispatchStatus new_status, void *data)
39{
40 if (new_status == DBUS_DISPATCH_DATA_REMAINS) {
41 crm_trace("DBus connection has messages available for dispatch");
42 conn_dispatches = g_list_prepend(conn_dispatches, connection);
43 } else {
44 crm_trace("DBus connection has no messages available for dispatch "
45 "(status %d)", new_status);
46 }
47}
48
53static void
54dispatch_messages(void)
55{
56 for (GList *gIter = conn_dispatches; gIter != NULL; gIter = gIter->next) {
57 DBusConnection *connection = gIter->data;
58
59 while (dbus_connection_get_dispatch_status(connection)
60 == DBUS_DISPATCH_DATA_REMAINS) {
61 crm_trace("Dispatching available messages on DBus connection");
62 dbus_connection_dispatch(connection);
63 }
64 }
65 g_list_free(conn_dispatches);
66 conn_dispatches = NULL;
67}
68
69
70/*
71 * DBus file descriptor watches
72 *
73 * The DBus library allows the caller to register functions for the library to
74 * use for file descriptor notifications via a main loop.
75 */
76
77/* Copied from dbus-watch.c */
78static const char*
79dbus_watch_flags_to_string(int flags)
80{
81 const char *watch_type;
82
83 if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) {
84 watch_type = "read/write";
85 } else if (flags & DBUS_WATCH_READABLE) {
86 watch_type = "read";
87 } else if (flags & DBUS_WATCH_WRITABLE) {
88 watch_type = "write";
89 } else {
90 watch_type = "neither read nor write";
91 }
92 return watch_type;
93}
94
106static int
107dispatch_fd_data(gpointer userdata)
108{
109 bool oom = FALSE;
110 DBusWatch *watch = userdata;
111 int flags = dbus_watch_get_flags(watch);
112 bool enabled = dbus_watch_get_enabled (watch);
113
114 crm_trace("Dispatching DBus watch for file descriptor %d "
115 "with flags %#x (%s)",
116 dbus_watch_get_unix_fd(watch), flags,
117 dbus_watch_flags_to_string(flags));
118
119 if (enabled && (flags & (DBUS_WATCH_READABLE|DBUS_WATCH_WRITABLE))) {
120 oom = !dbus_watch_handle(watch, flags);
121
122 } else if (enabled) {
123 oom = !dbus_watch_handle(watch, DBUS_WATCH_ERROR);
124 }
125
126 if (flags != dbus_watch_get_flags(watch)) {
127 flags = dbus_watch_get_flags(watch);
128 crm_trace("Dispatched DBus file descriptor watch: now %#x (%s)",
129 flags, dbus_watch_flags_to_string(flags));
130 }
131
132 if (oom) {
133 crm_crit("Could not dispatch DBus file descriptor data: Out of memory");
134 } else {
135 dispatch_messages();
136 }
137 return 0;
138}
139
140static void
141watch_fd_closed(gpointer userdata)
142{
143 crm_trace("DBus watch for file descriptor %d is now closed",
144 dbus_watch_get_unix_fd((DBusWatch *) userdata));
145}
146
147static struct mainloop_fd_callbacks pcmk_dbus_cb = {
148 .dispatch = dispatch_fd_data,
149 .destroy = watch_fd_closed,
150};
151
152static dbus_bool_t
153add_dbus_watch(DBusWatch *watch, void *data)
154{
155 int fd = dbus_watch_get_unix_fd(watch);
156
157 mainloop_io_t *client = mainloop_add_fd("dbus", G_PRIORITY_DEFAULT, fd,
158 watch, &pcmk_dbus_cb);
159
160 crm_trace("Added DBus watch for file descriptor %d", fd);
161 dbus_watch_set_data(watch, client, NULL);
162 return TRUE;
163}
164
165static void
166toggle_dbus_watch(DBusWatch *watch, void *data)
167{
168 // @TODO Should this do something more?
169 crm_debug("DBus watch for file descriptor %d is now %s",
170 dbus_watch_get_unix_fd(watch),
171 (dbus_watch_get_enabled(watch)? "enabled" : "disabled"));
172}
173
174static void
175remove_dbus_watch(DBusWatch *watch, void *data)
176{
177 crm_trace("Removed DBus watch for file descriptor %d",
178 dbus_watch_get_unix_fd(watch));
179 mainloop_del_fd((mainloop_io_t *) dbus_watch_get_data(watch));
180}
181
182static void
183register_watch_functions(DBusConnection *connection)
184{
185 dbus_connection_set_watch_functions(connection, add_dbus_watch,
186 remove_dbus_watch,
187 toggle_dbus_watch, NULL, NULL);
188}
189
190/*
191 * DBus main loop timeouts
192 *
193 * The DBus library allows the caller to register functions for the library to
194 * use for managing timers via a main loop.
195 */
196
197static gboolean
198timer_popped(gpointer data)
199{
200 crm_debug("%dms DBus timer expired",
201 dbus_timeout_get_interval((DBusTimeout *) data));
202 dbus_timeout_handle(data);
203 return FALSE;
204}
205
206static dbus_bool_t
207add_dbus_timer(DBusTimeout *timeout, void *data)
208{
209 int interval_ms = dbus_timeout_get_interval(timeout);
210 guint id = pcmk__create_timer(interval_ms, timer_popped, timeout);
211
212 if (id) {
213 dbus_timeout_set_data(timeout, GUINT_TO_POINTER(id), NULL);
214 }
215 crm_trace("Added %dms DBus timer", interval_ms);
216 return TRUE;
217}
218
219static void
220remove_dbus_timer(DBusTimeout *timeout, void *data)
221{
222 void *vid = dbus_timeout_get_data(timeout);
223 guint id = GPOINTER_TO_UINT(vid);
224
225 crm_trace("Removing %dms DBus timer", dbus_timeout_get_interval(timeout));
226 if (id) {
227 g_source_remove(id);
228 dbus_timeout_set_data(timeout, 0, NULL);
229 }
230}
231
232static void
233toggle_dbus_timer(DBusTimeout *timeout, void *data)
234{
235 bool enabled = dbus_timeout_get_enabled(timeout);
236
237 crm_trace("Toggling %dms DBus timer %s",
238 dbus_timeout_get_interval(timeout), (enabled? "off": "on"));
239 if (enabled) {
240 add_dbus_timer(timeout, data);
241 } else {
242 remove_dbus_timer(timeout, data);
243 }
244}
245
246static void
247register_timer_functions(DBusConnection *connection)
248{
249 dbus_connection_set_timeout_functions(connection, add_dbus_timer,
250 remove_dbus_timer,
251 toggle_dbus_timer, NULL, NULL);
252}
253
254/*
255 * General DBus utilities
256 */
257
258DBusConnection *
260{
261 DBusError err;
262 DBusConnection *connection;
263
264 dbus_error_init(&err);
265 connection = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
266 if (dbus_error_is_set(&err)) {
267 crm_err("Could not connect to DBus: %s", err.message);
268 dbus_error_free(&err);
269 return NULL;
270 }
271 if (connection == NULL) {
272 return NULL;
273 }
274
275 /* Tell libdbus not to exit the process when a disconnect happens. This
276 * defaults to FALSE but is toggled on by the dbus_bus_get() call above.
277 */
278 dbus_connection_set_exit_on_disconnect(connection, FALSE);
279
280 // Set custom handlers for various situations
281 register_timer_functions(connection);
282 register_watch_functions(connection);
283 dbus_connection_set_dispatch_status_function(connection,
284 update_dispatch_status,
285 NULL, NULL);
286
287 // Call the dispatch function to check for any messages waiting already
288 update_dispatch_status(connection,
289 dbus_connection_get_dispatch_status(connection),
290 NULL);
291 return connection;
292}
293
294void
295pcmk_dbus_disconnect(DBusConnection *connection)
296{
297 /* We acquire our dbus connection with dbus_bus_get(), which makes it a
298 * shared connection. Therefore, we can't close or free it here. The
299 * best we can do is decrement the reference count so dbus knows when
300 * there are no more clients connected to it.
301 */
302 dbus_connection_unref(connection);
303}
304
305// Custom DBus error names to use
306#define ERR_NO_REQUEST "org.clusterlabs.pacemaker.NoRequest"
307#define ERR_NO_REPLY "org.clusterlabs.pacemaker.NoReply"
308#define ERR_INVALID_REPLY "org.clusterlabs.pacemaker.InvalidReply"
309#define ERR_INVALID_REPLY_METHOD "org.clusterlabs.pacemaker.InvalidReply.Method"
310#define ERR_INVALID_REPLY_SIGNAL "org.clusterlabs.pacemaker.InvalidReply.Signal"
311#define ERR_INVALID_REPLY_TYPE "org.clusterlabs.pacemaker.InvalidReply.Type"
312#define ERR_SEND_FAILED "org.clusterlabs.pacemaker.SendFailed"
313
329bool
330pcmk_dbus_find_error(const DBusPendingCall *pending, DBusMessage *reply,
331 DBusError *ret)
332{
333 DBusError error;
334
335 dbus_error_init(&error);
336
337 if (pending == NULL) {
338 dbus_set_error_const(&error, ERR_NO_REQUEST, "No request sent");
339
340 } else if (reply == NULL) {
341 dbus_set_error_const(&error, ERR_NO_REPLY, "No reply");
342
343 } else {
344 DBusMessageIter args;
345 int dtype = dbus_message_get_type(reply);
346
347 switch (dtype) {
348 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
349 {
350 char *sig = NULL;
351
352 dbus_message_iter_init(reply, &args);
353 crm_trace("Received DBus reply with argument type '%s'",
354 (sig = dbus_message_iter_get_signature(&args)));
355 if (sig != NULL) {
356 dbus_free(sig);
357 }
358 }
359 break;
360 case DBUS_MESSAGE_TYPE_INVALID:
361 dbus_set_error_const(&error, ERR_INVALID_REPLY,
362 "Invalid reply");
363 break;
364 case DBUS_MESSAGE_TYPE_METHOD_CALL:
365 dbus_set_error_const(&error, ERR_INVALID_REPLY_METHOD,
366 "Invalid reply (method call)");
367 break;
368 case DBUS_MESSAGE_TYPE_SIGNAL:
369 dbus_set_error_const(&error, ERR_INVALID_REPLY_SIGNAL,
370 "Invalid reply (signal)");
371 break;
372 case DBUS_MESSAGE_TYPE_ERROR:
373 dbus_set_error_from_message(&error, reply);
374 break;
375 default:
376 dbus_set_error(&error, ERR_INVALID_REPLY_TYPE,
377 "Unknown reply type %d", dtype);
378 }
379 }
380
381 if (dbus_error_is_set(&error)) {
382 crm_trace("DBus reply indicated error '%s' (%s)",
383 error.name, error.message);
384 if (ret) {
385 dbus_error_init(ret);
386 dbus_move_error(&error, ret);
387 } else {
388 dbus_error_free(&error);
389 }
390 return TRUE;
391 }
392
393 return FALSE;
394}
395
411DBusMessage *
412pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection,
413 DBusError *error, int timeout)
414{
415 const char *method = NULL;
416 DBusMessage *reply = NULL;
417 DBusPendingCall* pending = NULL;
418
419 pcmk__assert(dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
420 method = dbus_message_get_member (msg);
421
422 /* Ensure caller can reliably check whether error is set */
423 if (error) {
424 dbus_error_init(error);
425 }
426
427 if (timeout <= 0) {
428 /* DBUS_TIMEOUT_USE_DEFAULT (-1) tells DBus to use a sane default */
430 }
431
432 // send message and get a handle for a reply
433 if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
434 if (error) {
435 dbus_set_error(error, ERR_SEND_FAILED,
436 "Could not queue DBus '%s' request", method);
437 }
438 return NULL;
439 }
440
441 dbus_connection_flush(connection);
442
443 if (pending) {
444 /* block until we receive a reply */
445 dbus_pending_call_block(pending);
446
447 /* get the reply message */
448 reply = dbus_pending_call_steal_reply(pending);
449 }
450
451 (void) pcmk_dbus_find_error(pending, reply, error);
452
453 if (pending) {
454 /* free the pending message handle */
455 dbus_pending_call_unref(pending);
456 }
457
458 return reply;
459}
460
475DBusPendingCall *
476pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection,
477 void (*done)(DBusPendingCall *pending, void *user_data),
478 void *user_data, int timeout)
479{
480 const char *method = NULL;
481 DBusPendingCall* pending = NULL;
482
483 pcmk__assert(done != NULL);
484 pcmk__assert(dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL);
485 method = dbus_message_get_member(msg);
486
487 if (timeout <= 0) {
488 /* DBUS_TIMEOUT_USE_DEFAULT (-1) tells DBus to use a sane default */
490 }
491
492 // send message and get a handle for a reply
493 if (!dbus_connection_send_with_reply(connection, msg, &pending, timeout)) {
494 crm_err("Could not send DBus %s message: failed", method);
495 return NULL;
496
497 } else if (pending == NULL) {
498 crm_err("Could not send DBus %s message: connection may be closed",
499 method);
500 return NULL;
501 }
502
503 if (dbus_pending_call_get_completed(pending)) {
504 crm_info("DBus %s message completed too soon", method);
505 /* Calling done() directly in this case instead of setting notify below
506 * breaks things
507 */
508 }
509 if (!dbus_pending_call_set_notify(pending, done, user_data, NULL)) {
510 return NULL;
511 }
512 return pending;
513}
514
515bool
516pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected,
517 const char *function, int line)
518{
519 int dtype = 0;
520 DBusMessageIter lfield;
521
522 if (field == NULL) {
523 if (dbus_message_iter_init(msg, &lfield)) {
524 field = &lfield;
525 }
526 }
527
528 if (field == NULL) {
529 do_crm_log_alias(LOG_INFO, __FILE__, function, line,
530 "DBus reply has empty parameter list (expected '%c')",
531 expected);
532 return FALSE;
533 }
534
535 dtype = dbus_message_iter_get_arg_type(field);
536
537 if (dtype != expected) {
538 DBusMessageIter args;
539 char *sig;
540
541 dbus_message_iter_init(msg, &args);
542 sig = dbus_message_iter_get_signature(&args);
543 do_crm_log_alias(LOG_INFO, __FILE__, function, line,
544 "DBus reply has unexpected type "
545 "(expected '%c' not '%c' in '%s')",
546 expected, dtype, sig);
547 dbus_free(sig);
548 return FALSE;
549 }
550
551 return TRUE;
552}
553
554
555/*
556 * Property queries
557 */
558
559/* DBus APIs often provide queryable properties that use this standard
560 * interface. See:
561 * https://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties
562 */
563#define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties"
564
565// Callback prototype for when a DBus property query result is received
566typedef void (*property_callback_func)(const char *name, // Property name
567 const char *value, // Property value
568 void *userdata); // Caller-provided data
569
570// Data needed by DBus property queries
571struct property_query {
572 char *name; // Property name being queried
573 char *target; // Name of DBus bus that query should be sent to
574 char *object; // DBus object path for object with the property
575 void *userdata; // Caller-provided data to supply to callback
576 property_callback_func callback; // Function to call when result is received
577};
578
579static void
580free_property_query(struct property_query *data)
581{
582 free(data->target);
583 free(data->object);
584 free(data->name);
585 free(data);
586}
587
588static char *
589handle_query_result(DBusMessage *reply, struct property_query *data)
590{
591 DBusError error;
592 char *output = NULL;
593 DBusMessageIter args;
594 DBusMessageIter variant_iter;
595 DBusBasicValue value;
596
597 dbus_error_init(&error);
598
599 // First, check if the reply contains an error
600 if (pcmk_dbus_find_error((void*)&error, reply, &error)) {
601 crm_err("DBus query for %s property '%s' failed: %s",
602 data->object, data->name, error.message);
603 dbus_error_free(&error);
604 goto cleanup;
605 }
606
607 // The lone output argument should be a DBus variant type
608 dbus_message_iter_init(reply, &args);
609 if (!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_VARIANT,
610 __func__, __LINE__)) {
611 crm_err("DBus query for %s property '%s' failed: Unexpected reply type",
612 data->object, data->name);
613 goto cleanup;
614 }
615
616 // The variant should be a string
617 dbus_message_iter_recurse(&args, &variant_iter);
618 if (!pcmk_dbus_type_check(reply, &variant_iter, DBUS_TYPE_STRING,
619 __func__, __LINE__)) {
620 crm_err("DBus query for %s property '%s' failed: "
621 "Unexpected variant type", data->object, data->name);
622 goto cleanup;
623 }
624 dbus_message_iter_get_basic(&variant_iter, &value);
625
626 // There should be no more arguments (in variant or reply)
627 dbus_message_iter_next(&variant_iter);
628 if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_INVALID) {
629 crm_err("DBus query for %s property '%s' failed: "
630 "Too many arguments in reply",
631 data->object, data->name);
632 goto cleanup;
633 }
634 dbus_message_iter_next(&args);
635 if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_INVALID) {
636 crm_err("DBus query for %s property '%s' failed: "
637 "Too many arguments in reply", data->object, data->name);
638 goto cleanup;
639 }
640
641 crm_trace("DBus query result for %s: %s='%s'",
642 data->object, data->name, (value.str? value.str : ""));
643
644 if (data->callback) { // Query was asynchronous
645 data->callback(data->name, (value.str? value.str : ""), data->userdata);
646
647 } else { // Query was synchronous
648 output = strdup(value.str? value.str : "");
649 }
650
651 cleanup:
652 free_property_query(data);
653 return output;
654}
655
656static void
657async_query_result_cb(DBusPendingCall *pending, void *user_data)
658{
659 DBusMessage *reply = NULL;
660 char *value = NULL;
661
662 if (pending) {
663 reply = dbus_pending_call_steal_reply(pending);
664 }
665
666 value = handle_query_result(reply, user_data);
667 free(value);
668
669 if (reply) {
670 dbus_message_unref(reply);
671 }
672}
673
694char *
695pcmk_dbus_get_property(DBusConnection *connection, const char *target,
696 const char *obj, const gchar * iface, const char *name,
697 property_callback_func callback, void *userdata,
698 DBusPendingCall **pending, int timeout)
699{
700 DBusMessage *msg;
701 char *output = NULL;
702 struct property_query *query_data = NULL;
703
704 CRM_CHECK((connection != NULL) && (target != NULL) && (obj != NULL)
705 && (iface != NULL) && (name != NULL), return NULL);
706
707 crm_trace("Querying DBus %s for %s property '%s'",
708 target, obj, name);
709
710 // Create a new message to use to invoke method
711 msg = dbus_message_new_method_call(target, obj, BUS_PROPERTY_IFACE, "Get");
712 if (msg == NULL) {
713 crm_err("DBus query for %s property '%s' failed: "
714 "Unable to create message", obj, name);
715 return NULL;
716 }
717
718 // Add the interface name and property name as message arguments
719 if (!dbus_message_append_args(msg,
720 DBUS_TYPE_STRING, &iface,
721 DBUS_TYPE_STRING, &name,
722 DBUS_TYPE_INVALID)) {
723 crm_err("DBus query for %s property '%s' failed: "
724 "Could not append arguments", obj, name);
725 dbus_message_unref(msg);
726 return NULL;
727 }
728
729 query_data = malloc(sizeof(struct property_query));
730 if (query_data == NULL) {
731 crm_crit("DBus query for %s property '%s' failed: Out of memory",
732 obj, name);
733 dbus_message_unref(msg);
734 return NULL;
735 }
736
737 query_data->target = strdup(target);
738 query_data->object = strdup(obj);
739 query_data->callback = callback;
740 query_data->userdata = userdata;
741 query_data->name = strdup(name);
742 CRM_CHECK((query_data->target != NULL)
743 && (query_data->object != NULL)
744 && (query_data->name != NULL),
745 free_property_query(query_data);
746 dbus_message_unref(msg);
747 return NULL);
748
749 if (query_data->callback) { // Asynchronous
750 DBusPendingCall *local_pending;
751
752 local_pending = pcmk_dbus_send(msg, connection, async_query_result_cb,
753 query_data, timeout);
754 if (local_pending == NULL) {
755 // async_query_result_cb() was not called in this case
756 free_property_query(query_data);
757 query_data = NULL;
758 }
759
760 if (pending) {
761 *pending = local_pending;
762 }
763
764 } else { // Synchronous
765 DBusMessage *reply = pcmk_dbus_send_recv(msg, connection, NULL,
766 timeout);
767
768 output = handle_query_result(reply, query_data);
769
770 if (reply) {
771 dbus_message_unref(reply);
772 }
773 }
774
775 dbus_message_unref(msg);
776
777 return output;
778}
const char * name
Definition cib.c:26
guint pcmk__create_timer(guint interval_ms, GSourceFunc fn, gpointer data)
Definition utils.c:405
uint64_t flags
Definition remote.c:3
char data[0]
Definition cpg.c:10
A dumping ground.
#define BUS_PROPERTY_IFACE
Definition dbus.c:563
char * pcmk_dbus_get_property(DBusConnection *connection, const char *target, const char *obj, const gchar *iface, const char *name, property_callback_func callback, void *userdata, DBusPendingCall **pending, int timeout)
Definition dbus.c:695
#define ERR_NO_REPLY
Definition dbus.c:307
#define ERR_SEND_FAILED
Definition dbus.c:312
DBusConnection * pcmk_dbus_connect(void)
Definition dbus.c:259
#define ERR_INVALID_REPLY_TYPE
Definition dbus.c:311
#define ERR_INVALID_REPLY
Definition dbus.c:308
#define ERR_INVALID_REPLY_METHOD
Definition dbus.c:309
#define ERR_INVALID_REPLY_SIGNAL
Definition dbus.c:310
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition dbus.c:412
void(* property_callback_func)(const char *name, const char *value, void *userdata)
Definition dbus.c:566
bool pcmk_dbus_find_error(const DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition dbus.c:330
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition dbus.c:476
#define ERR_NO_REQUEST
Definition dbus.c:306
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition dbus.c:516
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition dbus.c:295
#define crm_info(fmt, args...)
Definition logging.h:365
#define crm_crit(fmt, args...)
Definition logging.h:354
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#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:268
#define crm_trace(fmt, args...)
Definition logging.h:370
void mainloop_del_fd(mainloop_io_t *client)
Definition mainloop.c:1006
struct mainloop_io_s mainloop_io_t
Definition mainloop.h:41
mainloop_io_t * mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks *callbacks)
Definition mainloop.c:962
#define DBUS_TIMEOUT_USE_DEFAULT
Definition pcmk-dbus.h:19
unsigned int timeout
Definition pcmk_fence.c:34
const char * target
Definition pcmk_fence.c:31
#define pcmk__assert(expr)