/* * virdbus.c: helper for using DBus * * Copyright (C) 2012 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * */ #include #include "virdbus.h" #include "memory.h" #include "virterror_internal.h" #include "logging.h" #include "threads.h" #define VIR_FROM_THIS VIR_FROM_DBUS #ifdef HAVE_DBUS static DBusConnection *systembus = NULL; static DBusConnection *sessionbus = NULL; static virOnceControl systemonce = VIR_ONCE_CONTROL_INITIALIZER; static virOnceControl sessiononce = VIR_ONCE_CONTROL_INITIALIZER; static DBusError systemdbuserr; static DBusError sessiondbuserr; static dbus_bool_t virDBusAddWatch(DBusWatch *watch, void *data); static void virDBusRemoveWatch(DBusWatch *watch, void *data); static void virDBusToggleWatch(DBusWatch *watch, void *data); static DBusConnection *virDBusBusInit(DBusBusType type, DBusError *dbuserr) { DBusConnection *bus; /* Allocate and initialize a new HAL context */ dbus_connection_set_change_sigpipe(FALSE); dbus_threads_init_default(); dbus_error_init(dbuserr); if (!(bus = dbus_bus_get(type, dbuserr))) return NULL; dbus_connection_set_exit_on_disconnect(bus, FALSE); /* Register dbus watch callbacks */ if (!dbus_connection_set_watch_functions(bus, virDBusAddWatch, virDBusRemoveWatch, virDBusToggleWatch, bus, NULL)) { return NULL; } return bus; } static void virDBusSystemBusInit(void) { systembus = virDBusBusInit(DBUS_BUS_SYSTEM, &systemdbuserr); } DBusConnection *virDBusGetSystemBus(void) { if (virOnce(&systemonce, virDBusSystemBusInit) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to run one time DBus initializer")); return NULL; } if (!systembus) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to get DBus system bus connection: %s"), systemdbuserr.message ? systemdbuserr.message : "watch setup failed"); return NULL; } return systembus; } static void virDBusSessionBusInit(void) { sessionbus = virDBusBusInit(DBUS_BUS_SESSION, &sessiondbuserr); } DBusConnection *virDBusGetSessionBus(void) { if (virOnce(&sessiononce, virDBusSessionBusInit) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("Unable to run one time DBus initializer")); return NULL; } if (!sessionbus) { virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to get DBus session bus connection: %s"), sessiondbuserr.message ? sessiondbuserr.message : "watch setup failed"); return NULL; } return sessionbus; } struct virDBusWatch { int watch; DBusConnection *bus; }; static void virDBusWatchCallback(int fdatch ATTRIBUTE_UNUSED, int fd ATTRIBUTE_UNUSED, int events, void *opaque) { DBusWatch *watch = opaque; struct virDBusWatch *info; int dbus_flags = 0; info = dbus_watch_get_data(watch); if (events & VIR_EVENT_HANDLE_READABLE) dbus_flags |= DBUS_WATCH_READABLE; if (events & VIR_EVENT_HANDLE_WRITABLE) dbus_flags |= DBUS_WATCH_WRITABLE; if (events & VIR_EVENT_HANDLE_ERROR) dbus_flags |= DBUS_WATCH_ERROR; if (events & VIR_EVENT_HANDLE_HANGUP) dbus_flags |= DBUS_WATCH_HANGUP; (void)dbus_watch_handle(watch, dbus_flags); while (dbus_connection_dispatch(info->bus) == DBUS_DISPATCH_DATA_REMAINS) /* keep dispatching while data remains */; } static int virDBusTranslateWatchFlags(int dbus_flags) { unsigned int flags = 0; if (dbus_flags & DBUS_WATCH_READABLE) flags |= VIR_EVENT_HANDLE_READABLE; if (dbus_flags & DBUS_WATCH_WRITABLE) flags |= VIR_EVENT_HANDLE_WRITABLE; if (dbus_flags & DBUS_WATCH_ERROR) flags |= VIR_EVENT_HANDLE_ERROR; if (dbus_flags & DBUS_WATCH_HANGUP) flags |= VIR_EVENT_HANDLE_HANGUP; return flags; } static void virDBusWatchFree(void *data) { struct virDBusWatch *info = data; VIR_FREE(info); } static dbus_bool_t virDBusAddWatch(DBusWatch *watch, void *data) { int flags = 0; int fd; struct virDBusWatch *info; if (VIR_ALLOC(info) < 0) return 0; if (dbus_watch_get_enabled(watch)) flags = virDBusTranslateWatchFlags(dbus_watch_get_flags(watch)); # if HAVE_DBUS_WATCH_GET_UNIX_FD fd = dbus_watch_get_unix_fd(watch); # else fd = dbus_watch_get_fd(watch); # endif info->bus = (DBusConnection *)data; info->watch = virEventAddHandle(fd, flags, virDBusWatchCallback, watch, NULL); if (info->watch < 0) { VIR_FREE(info); return 0; } dbus_watch_set_data(watch, info, virDBusWatchFree); return 1; } static void virDBusRemoveWatch(DBusWatch *watch, void *data ATTRIBUTE_UNUSED) { struct virDBusWatch *info; info = dbus_watch_get_data(watch); (void)virEventRemoveHandle(info->watch); } static void virDBusToggleWatch(DBusWatch *watch, void *data ATTRIBUTE_UNUSED) { int flags = 0; struct virDBusWatch *info; if (dbus_watch_get_enabled(watch)) flags = virDBusTranslateWatchFlags(dbus_watch_get_flags(watch)); info = dbus_watch_get_data(watch); (void)virEventUpdateHandle(info->watch, flags); } #else /* ! HAVE_DBUS */ DBusConnection *virDBusGetSystemBus(void) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("DBus support not compiled into this binary")); return NULL; } DBusConnection *virDBusGetSessionBus(void) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("DBus support not compiled into this binary")); return NULL; } #endif /* ! HAVE_DBUS */