aboutsummaryrefslogtreecommitdiff
path: root/src/platform-cros.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/platform-cros.c')
-rw-r--r--src/platform-cros.c613
1 files changed, 613 insertions, 0 deletions
diff --git a/src/platform-cros.c b/src/platform-cros.c
new file mode 100644
index 0000000..5a9d7ab
--- /dev/null
+++ b/src/platform-cros.c
@@ -0,0 +1,613 @@
+/*
+ * platform-cros.c - CrOS platform DBus integration
+ * Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "config.h"
+
+#include <ctype.h>
+#include <dbus/dbus.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+
+#include <event2/event.h>
+
+#include "src/dbus.h"
+#include "src/platform.h"
+#include "src/tlsdate.h"
+#include "src/util.h"
+
+static const char kMatchFormatData[] = "interface='%s',member='%s',arg0='%s'";
+static const char *kMatchFormat = kMatchFormatData;
+static const char kMatchNoArgFormatData[] = "interface='%s',member='%s'";
+static const char *kMatchNoArgFormat = kMatchNoArgFormatData;
+
+static const char kLibCrosDestData[] = "org.chromium.LibCrosService";
+static const char *kLibCrosDest = kLibCrosDestData;
+static const char kLibCrosInterfaceData[] = "org.chromium.LibCrosServiceInterface";
+static const char *kLibCrosInterface = kLibCrosInterfaceData;
+static const char kLibCrosPathData[] = "/org/chromium/LibCrosService";
+static const char *kLibCrosPath = kLibCrosPathData;
+static const char kResolveNetworkProxyData[] = "ResolveNetworkProxy";
+static const char *kResolveNetworkProxy = kResolveNetworkProxyData;
+
+static const char kDBusInterfaceData[] = "org.freedesktop.DBus";
+static const char *kDBusInterface = kDBusInterfaceData;
+static const char kNameOwnerChangedData[] = "NameOwnerChanged";
+static const char *kNameOwnerChanged = kNameOwnerChangedData;
+static const char kNameAcquiredData[] = "NameAcquired";
+static const char *kNameAcquired = kNameAcquiredData;
+
+static const char kManagerInterfaceData[] = "org.chromium.flimflam.Manager";
+static const char *kManagerInterface = kManagerInterfaceData;
+
+static const char kServiceInterfaceData[] = "org.chromium.flimflam.Service";
+static const char *kServiceInterface = kServiceInterfaceData;
+static const char kMemberData[] = "PropertyChanged";
+static const char *kMember = kMemberData;
+
+static const char kProxyConfigData[] = "ProxyConfig";
+static const char *kProxyConfig = kProxyConfigData;
+static const char kDefaultServiceData[] = "DefaultService";
+static const char *kDefaultService = kDefaultServiceData;
+
+static const char kResolveInterfaceData[] = "org.torproject.tlsdate.Resolver";
+static const char *kResolveInterface = kResolveInterfaceData;
+static const char kResolveMemberData[] = "ProxyChange";
+static const char *kResolveMember = kResolveMemberData;
+
+/* TODO(wad) Integrate with cros_system_api/dbus/service_constants.h */
+static const char kPowerManagerInterfaceData[] = "org.chromium.PowerManager";
+static const char *kPowerManagerInterface = kPowerManagerInterfaceData;
+static const char kSuspendDoneData[] = "SuspendDone";
+static const char *kSuspendDone = kSuspendDoneData;
+
+static const char kErrorServiceUnknownData[] = "org.freedesktop.DBus.Error.ServiceUnknown";
+static const char *kErrorServiceUnknown = kErrorServiceUnknownData;
+
+struct platform_state
+{
+ struct event_base *base;
+ struct state *state;
+ DBusMessage **resolve_msg;
+ int resolve_msg_count;
+ uint32_t resolve_network_proxy_serial;
+};
+
+static
+bool
+get_valid_hostport (const char *hostport, char *out, size_t len)
+{
+ bool host = true;
+ const char *end = hostport + strlen (hostport);
+ const char *c;
+ *out = '\0';
+ /* Hosts begin with alphanumeric only. */
+ if (!isalnum (*hostport))
+ {
+ info ("Host does not start with alnum");
+ return false;
+ }
+ *out++ = *hostport;
+ for (c = hostport + 1; c < end && len > 0; ++c, ++out, --len)
+ {
+ *out = *c;
+ if (host)
+ {
+ if (isalnum (*c) || *c == '-' || *c == '.')
+ {
+ continue;
+ }
+ if (*c == ':')
+ {
+ host = false;
+ continue;
+ }
+ }
+ else
+ {
+ if (isdigit (*c))
+ continue;
+ }
+ *out = '\0';
+ return false;
+ }
+ *out = '\0';
+ return true;
+}
+
+/* Convert PAC return format to tlsdated url format */
+/* TODO(wad) support multiple proxies when Chromium does:
+ * PROXY x.x.x.x:yyyy; PROXY z.z.z.z:aaaaa
+ */
+static
+void
+canonicalize_pac (const char *pac_fmt, char *proxy_url, size_t len)
+{
+ size_t type_len;
+ size_t copied = 0;
+ const char *space;
+ /* host[255]:port[6]\0 */
+ char hostport[6 + 255 + 2];
+ proxy_url[0] = '\0';
+ if (len < 1)
+ return;
+ if (!strcmp (pac_fmt, "DIRECT"))
+ {
+ return;
+ }
+ /* Find type */
+ space = strchr (pac_fmt, ' ');
+ if (!space)
+ return;
+ type_len = space - pac_fmt;
+ if (!get_valid_hostport (space + 1, hostport, sizeof (hostport)))
+ {
+ error ("invalid host:port: %s", space + 1);
+ return;
+ }
+ proxy_url[0] = '\0';
+ if (!strncmp (pac_fmt, "PROXY", type_len))
+ {
+ copied = snprintf (proxy_url, len, "http://%s", hostport);
+ }
+ else if (!strncmp (pac_fmt, "SOCKS", type_len))
+ {
+ copied = snprintf (proxy_url, len, "socks4://%s", hostport);
+ }
+ else if (!strncmp (pac_fmt, "SOCKS5", type_len))
+ {
+ copied = snprintf (proxy_url, len, "socks5://%s", hostport);
+ }
+ else if (!strncmp (pac_fmt, "HTTPS", type_len))
+ {
+ copied = snprintf (proxy_url, len, "https://%s", hostport);
+ }
+ else
+ {
+ error ("pac_fmt unmatched: '%s' %zu", pac_fmt, type_len);
+ }
+ if (copied >= len)
+ {
+ error ("canonicalize_pac: truncation '%s'", proxy_url);
+ proxy_url[0] = '\0';
+ return;
+ }
+}
+
+static
+DBusHandlerResult
+handle_service_change (DBusConnection *connection,
+ DBusMessage *message,
+ struct platform_state *ctx)
+{
+ DBusMessageIter iter, subiter;
+ DBusError error;
+ const char *pname;
+ const char *pval;
+ const char *service;
+ dbus_error_init (&error);
+ verb_debug ("[event:cros:%s]: fired", __func__);
+ /* TODO(wad) Track the current DefaultService only fire when it changes */
+ service = dbus_message_get_path (message);
+ if (!service)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ /* Shill emits string:ProxyConfig variant string:"..." */
+ if (!dbus_message_iter_init (message, &iter))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_get_basic (&iter, &pname);
+ /* Make sure we are only firing on a ProxyConfig property change. */
+ if (strcmp (pname, kProxyConfig))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (!dbus_message_iter_next (&iter))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_recurse (&iter, &subiter);
+ if (dbus_message_iter_get_arg_type (&subiter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_get_basic (&subiter, &pval);
+ /* Right now, nothing is done with the Shill proxy value because
+ * Chromium handles .pac resolution. This may be more useful for
+ * ignoring incomplete proxy values sent while a user is typing.
+ */
+ action_kickoff_time_sync (-1, EV_TIMEOUT, ctx->state);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static
+DBusHandlerResult
+handle_manager_change (DBusConnection *connection,
+ DBusMessage *message,
+ struct platform_state *ctx)
+{
+ DBusMessageIter iter, subiter;
+ DBusError error;
+ const char *pname;
+ const char *pval;
+ verb_debug ("[event:cros:%s]: fired", __func__);
+ dbus_error_init (&error);
+ if (!dbus_message_iter_init (message, &iter))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_get_basic (&iter, &pname);
+ /* Make sure we caught the right property. */
+ if (strcmp (pname, kDefaultService))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (!dbus_message_iter_next (&iter))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_recurse (&iter, &subiter);
+ if (dbus_message_iter_get_arg_type (&subiter) != DBUS_TYPE_OBJECT_PATH)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_get_basic (&subiter, &pval);
+ /* TODO(wad) Filter on the currently active service in pval. */
+ verb_debug ("[event:cros:%s] service change on path %s",
+ __func__, pval);
+ action_kickoff_time_sync (-1, EV_TIMEOUT, ctx->state);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static
+DBusHandlerResult
+handle_suspend_done (DBusConnection *connection,
+ DBusMessage *message,
+ struct platform_state *ctx)
+{
+ DBusMessageIter iter;
+ DBusError error;
+ const char *pname;
+ verb_debug ("[event:cros:%s]: fired", __func__);
+ /* Coming back from resume, trigger a continuity and time
+ * check just in case none of the other events happen.
+ */
+ action_kickoff_time_sync (-1, EV_TIMEOUT, ctx->state);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static
+DBusHandlerResult
+handle_proxy_change (DBusConnection *connection,
+ DBusMessage *message,
+ struct platform_state *ctx)
+{
+ DBusMessageIter iter;
+ DBusError error;
+ const char *pname;
+ const char *pval;
+ char time_host[MAX_PROXY_URL];
+ int url_len = 0;
+ struct source *src = ctx->state->opts.sources;
+ verb_debug ("[event:cros:%s]: fired", __func__);
+ if (ctx->state->opts.cur_source && ctx->state->opts.cur_source->next)
+ src = ctx->state->opts.cur_source->next;
+ if (!ctx->state->resolving)
+ {
+ info ("[event:cros:%s] Unexpected ResolveNetworkProxy signal seen",
+ __func__);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ dbus_error_init (&error);
+ /* Shill emits string:ProxyConfig variant string:"..." */
+ if (!dbus_message_iter_init (message, &iter))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_get_basic (&iter, &pname);
+ /* Make sure this was the resolution we asked for */
+ url_len = snprintf (time_host, sizeof (time_host), "https://%s:%s",
+ src->host, src->port);
+ if (url_len >= sizeof (time_host))
+ {
+ error ("[event:cros:%s]: current source url is too long",
+ __func__);
+ }
+ if (strcmp (pname, time_host))
+ {
+ error ("[event:cros:%s]: resolved host mismatch: %s v %s",
+ __func__, pname, time_host);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (!dbus_message_iter_next (&iter))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_get_basic (&iter, &pval);
+ ctx->state->resolving = 0;
+ canonicalize_pac (pval, ctx->state->dynamic_proxy, sizeof (ctx->state->dynamic_proxy));
+ trigger_event (ctx->state, E_TLSDATE, 1);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static
+DBusHandlerResult
+handle_dbus_change (DBusConnection *connection,
+ DBusMessage *message,
+ struct platform_state *ctx)
+{
+ DBusMessageIter iter;
+ DBusError error;
+ const char *pname;
+ verb_debug ("[event:cros:%s]: fired", __func__);
+ dbus_error_init (&error);
+ if (!dbus_message_iter_init (message, &iter))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_HANDLED;
+ dbus_message_iter_get_basic (&iter, &pname);
+ /* Make sure we caught the right property. */
+ if (strcmp (pname, kLibCrosDest))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ action_kickoff_time_sync (-1, EV_TIMEOUT, ctx->state);
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static
+void
+action_resolve_proxy (evutil_socket_t fd, short what, void *arg)
+{
+ struct platform_state *ctx = arg;
+ struct dbus_state *dbus_state = ctx->state->dbus;
+ DBusConnection *conn = dbus_state->conn;
+ struct source *src = ctx->state->opts.sources;
+ verb_debug ("[event:%s] fired", __func__);
+ /* Emulate tlsdate-monitor.c:build_argv and choose the next source */
+ if (ctx->state->opts.cur_source && ctx->state->opts.cur_source->next)
+ src = ctx->state->opts.cur_source->next;
+ if (ctx->state->resolving || ctx->resolve_network_proxy_serial)
+ {
+ /* Note, this is not the same as the response signal. It just avoids
+ * multiple requests in a single dispatch window.
+ */
+ info ("[event:%s] no resolve_proxy sent; pending method_reply",
+ __func__);
+ return;
+ }
+ ctx->state->dynamic_proxy[0] = '\0';
+ if (ctx->resolve_msg[src->id] == NULL)
+ {
+ info ("[event:%s] no dynamic proxy for %s:%s", __func__,
+ src->host, src->port);
+ trigger_event (ctx->state, E_TLSDATE, 1);
+ return;
+ }
+ info ("[event:%s] resolving proxy for %s:%s", __func__,
+ src->host, src->port);
+ ctx->state->resolving = 1;
+ if (!dbus_connection_send (conn,
+ ctx->resolve_msg[src->id],
+ &ctx->resolve_network_proxy_serial))
+ {
+ error ("[event:%s] cannot send ResolveNetworkProxy query!", __func__);
+ return;
+ }
+}
+
+static
+DBusHandlerResult
+dbus_filter (DBusConnection *connection, DBusMessage *message, void *data)
+{
+ struct platform_state *state = data;
+ /* Terminate gracefully if DBus goes away. */
+ if (dbus_message_is_signal (message, DBUS_INTERFACE_LOCAL, "Disconnected"))
+ {
+ error ("[cros] DBus system bus has become inaccessible. Terminating.");
+ /* Trigger a graceful teardown. */
+ kill (getpid(), SIGINT);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ /* Hand it over to the service dispatcher. */
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_CALL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /* Handle explicitly defined signals only. */
+ if (dbus_message_is_signal (message, kDBusInterface, kNameAcquired))
+ {
+ info ("[cros] DBus name acquired successfully");
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ if (dbus_message_is_signal (message, kServiceInterface, kMember))
+ return handle_service_change (connection, message, state);
+ if (dbus_message_is_signal (message, kManagerInterface, kMember))
+ return handle_manager_change (connection, message, state);
+ if (dbus_message_is_signal (message, kResolveInterface, kResolveMember))
+ return handle_proxy_change (connection, message, state);
+ if (dbus_message_is_signal (message, kDBusInterface, kNameOwnerChanged))
+ return handle_dbus_change (connection, message, state);
+ if (dbus_message_is_signal (message, kPowerManagerInterface, kSuspendDone))
+ return handle_suspend_done (connection, message, state);
+ if (dbus_message_is_error (message, kErrorServiceUnknown))
+ {
+ info ("[cros] org.chromium.LibCrosService.ResolveNetworkProxy is missing");
+ info ("[cros] skipping proxy resolution for now");
+ /* Fire off tlsdate rather than letting it fail silently. */
+ state->resolve_network_proxy_serial = 0;
+ state->state->resolving = 0;
+ trigger_event (state->state, E_TLSDATE, 1);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ /* Indicates a successful resolve request was issued. */
+ if (dbus_message_get_type (message) == DBUS_MESSAGE_TYPE_METHOD_RETURN)
+ {
+ uint32_t serial = dbus_message_get_reply_serial (message);
+ if (serial == state->resolve_network_proxy_serial)
+ {
+ state->resolve_network_proxy_serial = 0;
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+ info ("[cros] unknown DBus METHOD_RETURN seen: %u", serial);
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ verb_debug ("[cros] unknown message received: "
+ "type=%s dest=%s interface=%s member=%s path=%s sig=%s error_name=%s",
+ dbus_message_type_to_string (dbus_message_get_type (message)),
+ dbus_message_get_destination (message),
+ dbus_message_get_interface (message),
+ dbus_message_get_member (message),
+ dbus_message_get_path (message),
+ dbus_message_get_signature (message),
+ dbus_message_get_error_name (message));
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+int
+add_match (DBusConnection *conn, const char *interface, const char *member,
+ const char *arg0)
+{
+ char match[1024];
+ DBusError error;
+ int len;
+ dbus_error_init (&error);
+ if (arg0)
+ {
+ len = snprintf (match, sizeof (match), kMatchFormat,
+ interface, member, arg0);
+ }
+ else
+ {
+ len = snprintf (match, sizeof (match), kMatchNoArgFormat,
+ interface, member);
+ }
+ if (len >= sizeof (match) || len < 0)
+ {
+ error ("[dbus] match truncated for '%s,%s'", interface, member);
+ return 1;
+ }
+ dbus_bus_add_match (conn, match, &error);
+ if (dbus_error_is_set (&error))
+ {
+ error ("[dbus] failed to add_match for '%s,%s'; error: %s, %s",
+ interface, member, error.name, error.message);
+ dbus_error_free (&error);
+ return 1;
+ }
+ return 0;
+}
+
+DBusMessage *
+new_resolver_message(const struct source *src)
+{
+ char time_host[MAX_PROXY_URL];
+ void *time_host_ptr = &time_host;
+ int url_len;
+ DBusMessage *res;
+ DBusMessageIter args;
+ if (!src->proxy || strcmp (src->proxy, "dynamic"))
+ {
+ return NULL;
+ }
+ res = dbus_message_new_method_call (kLibCrosDest, kLibCrosPath,
+ kLibCrosInterface, kResolveNetworkProxy);
+ if (!res)
+ {
+ error ("[cros] could not setup dynamic proxy for source %d", src->id);
+ return NULL;
+ }
+ /* Build the time_host */
+ url_len = snprintf (time_host, sizeof (time_host), "https://%s:%s",
+ src->host, src->port);
+ if (url_len >= sizeof (time_host))
+ {
+ fatal ("[cros] source %d url is too long! (%d)", src->id, url_len);
+ }
+ /* Finish the message */
+ dbus_message_iter_init_append (res, &args);
+ if (!dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &time_host_ptr) ||
+ !dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &kResolveInterface) ||
+ !dbus_message_iter_append_basic (&args, DBUS_TYPE_STRING, &kResolveMember))
+ {
+ fatal ("[cros could not append arguments for resolver message");
+ }
+ return res;
+}
+
+int
+platform_init_cros (struct state *state)
+{
+ /* Watch for per-service ProxyConfig property changes */
+ struct event_base *base = state->base;
+ struct dbus_state *dbus_state = state->dbus;
+ struct source *src = NULL;
+ int sources = 0;
+ DBusConnection *conn = dbus_state->conn;
+ DBusError error;
+ struct platform_state *platform_state =
+ calloc (1, sizeof (struct platform_state));
+ if (!platform_state)
+ {
+ error ("[cros] could not allocate platform_state");
+ return -1;
+ }
+ /* TODO(wad) Follow up with dbus_error_free() where needed. */
+ dbus_error_init (&error);
+ /* Add watches for: proxy changes, default service changes, proxy resolution,
+ * LibCrosService ownership, and power state changes.
+ */
+ if (add_match (conn, kServiceInterface, kMember, kProxyConfig) ||
+ add_match (conn, kManagerInterface, kMember, kDefaultService) ||
+ add_match (conn, kResolveInterface, kResolveMember, NULL) ||
+ add_match (conn, kDBusInterface, kNameOwnerChanged, kLibCrosDest) ||
+ add_match (conn, kPowerManagerInterface, kSuspendDone, NULL))
+ return 1;
+
+ /* Allocate one per source */
+ for (src = state->opts.sources; src; src = src->next, ++sources);
+ platform_state->resolve_msg_count = sources;
+ platform_state->resolve_msg = calloc (sources, sizeof (DBusMessage *));
+ if (!platform_state->resolve_msg)
+ {
+ error ("[cros] cannot allocate resolver messages");
+ free (platform_state);
+ return -1;
+ }
+ for (src = state->opts.sources; src; src = src->next)
+ {
+ if (src->id >= sources)
+ fatal ("Source ID is greater than available sources!");
+ platform_state->resolve_msg[src->id] = new_resolver_message (src);
+ if (platform_state->resolve_msg[src->id])
+ src->proxy = state->dynamic_proxy;
+ }
+ state->dynamic_proxy[0] = '\0';
+ if (state->opts.proxy && !strcmp (state->opts.proxy, "dynamic"))
+ {
+ info ("[cros] default dynamic proxy support");
+ state->opts.proxy = state->dynamic_proxy;
+ }
+ platform_state->base = base;
+ platform_state->state = state;
+ /* Add the dynamic resolver if tlsdate doesn't already have one. */
+ if (!state->events[E_RESOLVER])
+ {
+ state->events[E_RESOLVER] = event_new (base, -1, EV_TIMEOUT,
+ action_resolve_proxy,
+ platform_state);
+ if (!state->events[E_RESOLVER])
+ /* Let's not clean up DBus. */
+ fatal ("Could not allocated resolver event");
+ /* Wake up as a NET event since it'll self-block until DBus has a chance
+ * to send it.
+ */
+ event_priority_set (state->events[E_RESOLVER], PRI_NET);
+ }
+ /* Each platform can attach their own filter, but the filter func needs to be
+ * willing to DBUS_HANDLER_RESULT_NOT_YET_HANDLED on unexpected events.
+ */
+ /* TODO(wad) add the clean up function as the callback. */
+ if (!dbus_connection_add_filter (conn,
+ dbus_filter, platform_state, NULL))
+ {
+ error ("Failed to register signal handler callback");
+ return 1;
+ }
+ return 0;
+}