105b261ecSmrg/*
205b261ecSmrg * Copyright © 2006-2007 Daniel Stone
305b261ecSmrg *
405b261ecSmrg * Permission is hereby granted, free of charge, to any person obtaining a
505b261ecSmrg * copy of this software and associated documentation files (the "Software"),
605b261ecSmrg * to deal in the Software without restriction, including without limitation
705b261ecSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense,
805b261ecSmrg * and/or sell copies of the Software, and to permit persons to whom the
905b261ecSmrg * Software is furnished to do so, subject to the following conditions:
1005b261ecSmrg *
1105b261ecSmrg * The above copyright notice and this permission notice (including the next
1205b261ecSmrg * paragraph) shall be included in all copies or substantial portions of the
1305b261ecSmrg * Software.
1405b261ecSmrg *
1505b261ecSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1605b261ecSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1705b261ecSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1805b261ecSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1905b261ecSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
2005b261ecSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2105b261ecSmrg * DEALINGS IN THE SOFTWARE.
2205b261ecSmrg *
2305b261ecSmrg * Author: Daniel Stone <daniel@fooishbar.org>
2405b261ecSmrg */
2505b261ecSmrg
2605b261ecSmrg#ifdef HAVE_DIX_CONFIG_H
2705b261ecSmrg#include <dix-config.h>
2805b261ecSmrg#endif
2905b261ecSmrg
3005b261ecSmrg#include <dbus/dbus.h>
3105b261ecSmrg#include <sys/select.h>
3205b261ecSmrg
3305b261ecSmrg#include "dix.h"
3405b261ecSmrg#include "os.h"
3535c4bbdfSmrg#include "dbus-core.h"
3605b261ecSmrg
3705b261ecSmrg/* How often to attempt reconnecting when we get booted off the bus. */
3835c4bbdfSmrg#define RECONNECT_DELAY (10 * 1000)     /* in ms */
3905b261ecSmrg
4005b261ecSmrgstruct dbus_core_info {
4105b261ecSmrg    int fd;
4205b261ecSmrg    DBusConnection *connection;
4305b261ecSmrg    OsTimerPtr timer;
4435c4bbdfSmrg    struct dbus_core_hook *hooks;
4505b261ecSmrg};
461b5d61b8Smrgstatic struct dbus_core_info bus_info = { .fd = -1 };
4705b261ecSmrg
4835c4bbdfSmrgstatic CARD32 reconnect_timer(OsTimerPtr timer, CARD32 time, void *arg);
4905b261ecSmrg
5005b261ecSmrgstatic void
511b5d61b8Smrgsocket_handler(int fd, int ready, void *data)
5205b261ecSmrg{
5305b261ecSmrg    struct dbus_core_info *info = data;
5405b261ecSmrg
551b5d61b8Smrg    if (info->connection) {
5605b261ecSmrg        do {
5705b261ecSmrg            dbus_connection_read_write_dispatch(info->connection, 0);
584642e01fSmrg        } while (info->connection &&
594642e01fSmrg                 dbus_connection_get_is_connected(info->connection) &&
6035c4bbdfSmrg                 dbus_connection_get_dispatch_status(info->connection) ==
6135c4bbdfSmrg                 DBUS_DISPATCH_DATA_REMAINS);
6205b261ecSmrg    }
6305b261ecSmrg}
6405b261ecSmrg
6505b261ecSmrg/**
6605b261ecSmrg * Disconnect (if we haven't already been forcefully disconnected), clean up
6705b261ecSmrg * after ourselves, and call all registered disconnect hooks.
6805b261ecSmrg */
6905b261ecSmrgstatic void
7005b261ecSmrgteardown(void)
7105b261ecSmrg{
7235c4bbdfSmrg    struct dbus_core_hook *hook;
7305b261ecSmrg
7405b261ecSmrg    if (bus_info.timer) {
7505b261ecSmrg        TimerFree(bus_info.timer);
7605b261ecSmrg        bus_info.timer = NULL;
7705b261ecSmrg    }
7805b261ecSmrg
7905b261ecSmrg    /* We should really have pre-disconnect hooks and run them here, for
8005b261ecSmrg     * completeness.  But then it gets awkward, given that you can't
8105b261ecSmrg     * guarantee that they'll be called ... */
8205b261ecSmrg    if (bus_info.connection)
8305b261ecSmrg        dbus_connection_unref(bus_info.connection);
8405b261ecSmrg
8505b261ecSmrg    if (bus_info.fd != -1)
861b5d61b8Smrg        RemoveNotifyFd(bus_info.fd);
8705b261ecSmrg    bus_info.fd = -1;
8805b261ecSmrg    bus_info.connection = NULL;
8905b261ecSmrg
9005b261ecSmrg    for (hook = bus_info.hooks; hook; hook = hook->next) {
9105b261ecSmrg        if (hook->disconnect)
9205b261ecSmrg            hook->disconnect(hook->data);
9305b261ecSmrg    }
9405b261ecSmrg}
9505b261ecSmrg
9605b261ecSmrg/**
9705b261ecSmrg * This is a filter, which only handles the disconnected signal, which
9805b261ecSmrg * doesn't go to the normal message handling function.  This takes
9905b261ecSmrg * precedence over the message handling function, so have have to be
10005b261ecSmrg * careful to ignore anything we don't want to deal with here.
10105b261ecSmrg */
10205b261ecSmrgstatic DBusHandlerResult
10335c4bbdfSmrgmessage_filter(DBusConnection * connection, DBusMessage * message, void *data)
10405b261ecSmrg{
10505b261ecSmrg    /* If we get disconnected, then take everything down, and attempt to
10605b261ecSmrg     * reconnect immediately (assuming it's just a restart).  The
10705b261ecSmrg     * connection isn't valid at this point, so throw it out immediately. */
10835c4bbdfSmrg    if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) {
10935c4bbdfSmrg        DebugF("[dbus-core] disconnected from bus\n");
11005b261ecSmrg        bus_info.connection = NULL;
11105b261ecSmrg        teardown();
11205b261ecSmrg
11305b261ecSmrg        if (bus_info.timer)
11405b261ecSmrg            TimerFree(bus_info.timer);
11505b261ecSmrg        bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL);
11605b261ecSmrg
11705b261ecSmrg        return DBUS_HANDLER_RESULT_HANDLED;
11805b261ecSmrg    }
11905b261ecSmrg
12005b261ecSmrg    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
12105b261ecSmrg}
12205b261ecSmrg
12305b261ecSmrg/**
12405b261ecSmrg * Attempt to connect to the system bus, and set a filter to deal with
12505b261ecSmrg * disconnection (see message_filter above).
12605b261ecSmrg *
12705b261ecSmrg * @return 1 on success, 0 on failure.
12805b261ecSmrg */
12905b261ecSmrgstatic int
13005b261ecSmrgconnect_to_bus(void)
13105b261ecSmrg{
13205b261ecSmrg    DBusError error;
13335c4bbdfSmrg    struct dbus_core_hook *hook;
13405b261ecSmrg
13505b261ecSmrg    dbus_error_init(&error);
13605b261ecSmrg    bus_info.connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
13705b261ecSmrg    if (!bus_info.connection || dbus_error_is_set(&error)) {
13835c4bbdfSmrg        LogMessage(X_ERROR, "dbus-core: error connecting to system bus: %s (%s)\n",
13905b261ecSmrg               error.name, error.message);
14005b261ecSmrg        goto err_begin;
14105b261ecSmrg    }
14205b261ecSmrg
14305b261ecSmrg    /* Thankyou.  Really, thankyou. */
14405b261ecSmrg    dbus_connection_set_exit_on_disconnect(bus_info.connection, FALSE);
14505b261ecSmrg
14605b261ecSmrg    if (!dbus_connection_get_unix_fd(bus_info.connection, &bus_info.fd)) {
14735c4bbdfSmrg        ErrorF("[dbus-core] couldn't get fd for system bus\n");
14805b261ecSmrg        goto err_unref;
14905b261ecSmrg    }
15005b261ecSmrg
15105b261ecSmrg    if (!dbus_connection_add_filter(bus_info.connection, message_filter,
15205b261ecSmrg                                    &bus_info, NULL)) {
15335c4bbdfSmrg        ErrorF("[dbus-core] couldn't add filter: %s (%s)\n", error.name,
15405b261ecSmrg               error.message);
15505b261ecSmrg        goto err_fd;
15605b261ecSmrg    }
15705b261ecSmrg
15805b261ecSmrg    dbus_error_free(&error);
1591b5d61b8Smrg    SetNotifyFd(bus_info.fd, socket_handler, X_NOTIFY_READ, &bus_info);
16005b261ecSmrg
16105b261ecSmrg    for (hook = bus_info.hooks; hook; hook = hook->next) {
16205b261ecSmrg        if (hook->connect)
16305b261ecSmrg            hook->connect(bus_info.connection, hook->data);
16405b261ecSmrg    }
16505b261ecSmrg
16605b261ecSmrg    return 1;
16705b261ecSmrg
16835c4bbdfSmrg err_fd:
16905b261ecSmrg    bus_info.fd = -1;
17035c4bbdfSmrg err_unref:
17105b261ecSmrg    dbus_connection_unref(bus_info.connection);
17205b261ecSmrg    bus_info.connection = NULL;
17335c4bbdfSmrg err_begin:
17405b261ecSmrg    dbus_error_free(&error);
17505b261ecSmrg
17605b261ecSmrg    return 0;
17705b261ecSmrg}
17805b261ecSmrg
17905b261ecSmrgstatic CARD32
18035c4bbdfSmrgreconnect_timer(OsTimerPtr timer, CARD32 time, void *arg)
18105b261ecSmrg{
18205b261ecSmrg    if (connect_to_bus()) {
18305b261ecSmrg        TimerFree(bus_info.timer);
18405b261ecSmrg        bus_info.timer = NULL;
18505b261ecSmrg        return 0;
18605b261ecSmrg    }
18705b261ecSmrg    else {
18805b261ecSmrg        return RECONNECT_DELAY;
18905b261ecSmrg    }
19005b261ecSmrg}
19105b261ecSmrg
19205b261ecSmrgint
19335c4bbdfSmrgdbus_core_add_hook(struct dbus_core_hook *hook)
19405b261ecSmrg{
19535c4bbdfSmrg    struct dbus_core_hook **prev;
19605b261ecSmrg
19735c4bbdfSmrg    for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next);
19805b261ecSmrg
19905b261ecSmrg    hook->next = NULL;
20005b261ecSmrg    *prev = hook;
20105b261ecSmrg
20205b261ecSmrg    /* If we're already connected, call the connect hook. */
20305b261ecSmrg    if (bus_info.connection)
20405b261ecSmrg        hook->connect(bus_info.connection, hook->data);
20505b261ecSmrg
20605b261ecSmrg    return 1;
20705b261ecSmrg}
20805b261ecSmrg
20905b261ecSmrgvoid
21035c4bbdfSmrgdbus_core_remove_hook(struct dbus_core_hook *hook)
21105b261ecSmrg{
21235c4bbdfSmrg    struct dbus_core_hook **prev;
21305b261ecSmrg
21405b261ecSmrg    for (prev = &bus_info.hooks; *prev; prev = &(*prev)->next) {
21505b261ecSmrg        if (*prev == hook) {
21605b261ecSmrg            *prev = hook->next;
21705b261ecSmrg            break;
21805b261ecSmrg        }
21905b261ecSmrg    }
22005b261ecSmrg}
22105b261ecSmrg
22205b261ecSmrgint
22335c4bbdfSmrgdbus_core_init(void)
22405b261ecSmrg{
22505b261ecSmrg    memset(&bus_info, 0, sizeof(bus_info));
22605b261ecSmrg    bus_info.fd = -1;
22705b261ecSmrg    bus_info.hooks = NULL;
22835c4bbdfSmrg    if (!connect_to_bus())
22935c4bbdfSmrg        bus_info.timer = TimerSet(NULL, 0, 1, reconnect_timer, NULL);
23005b261ecSmrg
23105b261ecSmrg    return 1;
23205b261ecSmrg}
23305b261ecSmrg
23405b261ecSmrgvoid
23535c4bbdfSmrgdbus_core_fini(void)
23605b261ecSmrg{
23705b261ecSmrg    teardown();
23805b261ecSmrg}
239