summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorDoohwan Kim <dh8210.kim@samsung.com>2015-02-26 17:39:16 +0900
committerDoohwan Kim <dh8210.kim@samsung.com>2015-02-26 17:39:45 +0900
commitda07ee69267b4557028357eae802d2a7a57c34e7 (patch)
tree02a5d7590fc35cd3ad08aa8894ca5f32947a1da1 /src/common
parent34c41c0eecd122a3dd92b30f30267ea4d951343a (diff)
downloadmurphy-tizen_3.0.2015.q1_common.tar.gz
murphy-tizen_3.0.2015.q1_common.tar.bz2
murphy-tizen_3.0.2015.q1_common.zip
Signed-off-by: Doohwan Kim <dh8210.kim@samsung.com> Change-Id: I2ad7e23e121506673a724f512fea429489928451
Diffstat (limited to 'src/common')
-rw-r--r--src/common/Makefile7
-rw-r--r--src/common/dbus-error.h82
-rw-r--r--src/common/dbus-libdbus-glue.c374
-rw-r--r--src/common/dbus-libdbus-transport.c1745
-rw-r--r--src/common/dbus-libdbus.c2078
-rw-r--r--src/common/dbus-libdbus.h362
-rw-r--r--src/common/dbus-sdbus-glue.c140
-rw-r--r--src/common/dbus-sdbus-transport.c1782
-rw-r--r--src/common/dbus-sdbus.c1955
-rw-r--r--src/common/dbus-sdbus.h398
-rw-r--r--src/common/dbus-transport.c1721
-rw-r--r--src/common/dbus-transport.h55
-rw-r--r--src/common/debug-auto-register.c47
-rw-r--r--src/common/debug-info.h60
-rw-r--r--src/common/debug.c418
-rw-r--r--src/common/debug.h111
-rw-r--r--src/common/dgram-transport.c866
-rw-r--r--src/common/ecore-glue.c372
-rw-r--r--src/common/ecore-glue.h46
-rw-r--r--src/common/env.c156
-rw-r--r--src/common/env.h53
-rw-r--r--src/common/file-utils.c380
-rw-r--r--src/common/file-utils.h79
-rw-r--r--src/common/fragbuf.c276
-rw-r--r--src/common/fragbuf.h95
-rw-r--r--src/common/glib-glue.c362
-rw-r--r--src/common/glib-glue.h49
-rw-r--r--src/common/hashtbl.c377
-rw-r--r--src/common/hashtbl.h102
-rw-r--r--src/common/internal-transport.c585
-rw-r--r--src/common/json.c599
-rw-r--r--src/common/json.h248
-rw-r--r--src/common/libdbus-glue.c374
-rw-r--r--src/common/libdbus.c1471
-rw-r--r--src/common/libdbus.h178
-rw-r--r--src/common/list.h199
-rw-r--r--src/common/log.c413
-rw-r--r--src/common/log.h157
-rw-r--r--src/common/macros.h160
-rw-r--r--src/common/mainloop.c2625
-rw-r--r--src/common/mainloop.h431
-rw-r--r--src/common/mask.h464
-rw-r--r--src/common/mm.c1414
-rw-r--r--src/common/mm.h185
-rw-r--r--src/common/msg.c2222
-rw-r--r--src/common/msg.h461
-rw-r--r--src/common/murphy-common.pc.in11
-rw-r--r--src/common/murphy-dbus-libdbus.pc.in11
-rw-r--r--src/common/murphy-dbus-sdbus.pc.in11
-rw-r--r--src/common/murphy-ecore.pc.in11
-rw-r--r--src/common/murphy-glib.pc.in11
-rw-r--r--src/common/murphy-libdbus.pc.in11
-rw-r--r--src/common/murphy-pulse.pc.in11
-rw-r--r--src/common/murphy-qt.pc.in11
-rw-r--r--src/common/native-types.c1626
-rw-r--r--src/common/native-types.h368
-rw-r--r--src/common/process.c1077
-rw-r--r--src/common/process.h75
-rw-r--r--src/common/pulse-glue.c353
-rw-r--r--src/common/pulse-glue.h45
-rw-r--r--src/common/pulse-subloop.c572
-rw-r--r--src/common/pulse-subloop.h54
-rw-r--r--src/common/qt-glue-priv.h114
-rw-r--r--src/common/qt-glue.cpp413
-rw-r--r--src/common/qt-glue.h49
-rw-r--r--src/common/refcnt.h116
-rw-r--r--src/common/socket-utils.c101
-rw-r--r--src/common/socket-utils.h43
-rw-r--r--src/common/stream-transport.c853
-rw-r--r--src/common/tests/Makefile.am144
-rw-r--r--src/common/tests/dbus-pump.c350
-rw-r--r--src/common/tests/dbus-sdbus-test.c563
-rw-r--r--src/common/tests/dbus-test.c513
-rw-r--r--src/common/tests/fragbuf-test.c384
-rw-r--r--src/common/tests/glib-pump.c149
-rw-r--r--src/common/tests/hash-test.c419
-rw-r--r--src/common/tests/hash12-test.c112
-rw-r--r--src/common/tests/internal-transport-test.c784
-rw-r--r--src/common/tests/libdbus-test.c482
-rw-r--r--src/common/tests/libdbus-transport-test.c847
-rw-r--r--src/common/tests/mainloop-ecore-test.c115
-rw-r--r--src/common/tests/mainloop-glib-test.c145
-rw-r--r--src/common/tests/mainloop-pulse-test.c142
-rw-r--r--src/common/tests/mainloop-qt-test.cpp109
-rw-r--r--src/common/tests/mainloop-qt-test.h46
-rw-r--r--src/common/tests/mainloop-test.c1795
-rw-r--r--src/common/tests/mask-test.c130
-rw-r--r--src/common/tests/mkdir-test.c21
-rw-r--r--src/common/tests/mm-test.c284
-rw-r--r--src/common/tests/msg-test.c823
-rw-r--r--src/common/tests/native-test.c307
-rw-r--r--src/common/tests/path-test.c50
-rw-r--r--src/common/tests/process-test.c151
-rw-r--r--src/common/tests/sdbus-error-message.c99
-rw-r--r--src/common/tests/sdbus-test.c226
-rw-r--r--src/common/tests/transport-test.c998
-rw-r--r--src/common/tlv.c662
-rw-r--r--src/common/tlv.h130
-rw-r--r--src/common/transport.c829
-rw-r--r--src/common/transport.h559
-rw-r--r--src/common/utils.c211
-rw-r--r--src/common/utils.h40
-rw-r--r--src/common/websocket.c96
-rw-r--r--src/common/websocket.h113
-rw-r--r--src/common/websocklib.c2105
-rw-r--r--src/common/websocklib.h238
-rw-r--r--src/common/wsck-transport.c995
-rw-r--r--src/common/wsck-transport.h142
108 files changed, 48989 insertions, 0 deletions
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644
index 0000000..2c0a593
--- /dev/null
+++ b/src/common/Makefile
@@ -0,0 +1,7 @@
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
diff --git a/src/common/dbus-error.h b/src/common/dbus-error.h
new file mode 100644
index 0000000..1704cec
--- /dev/null
+++ b/src/common/dbus-error.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_ERROR_H__
+#define __MURPHY_DBUS_ERROR_H__
+
+#define __ERR(error) "org.freedesktop.DBus.Error."#error
+
+#define MRP_DBUS_ERROR_FAILED __ERR(Failed)
+#define MRP_DBUS_ERROR_NO_MEMORY __ERR(NoMemory)
+#define MRP_DBUS_ERROR_SERVICE_UNKNOWN __ERR(ServiceUnknown)
+#define MRP_DBUS_ERROR_NAME_HAS_NO_OWNER __ERR(NameHasNoOwner)
+#define MRP_DBUS_ERROR_NO_REPLY __ERR(NoReply)
+#define MRP_DBUS_ERROR_IO_ERROR __ERR(IOError)
+#define MRP_DBUS_ERROR_BAD_ADDRESS __ERR(BadAddress)
+#define MRP_DBUS_ERROR_NOT_SUPPORTED __ERR(NotSupported)
+#define MRP_DBUS_ERROR_LIMITS_EXCEEDED __ERR(LimitsExceeded)
+#define MRP_DBUS_ERROR_ACCESS_DENIED __ERR(AccessDenied)
+#define MRP_DBUS_ERROR_AUTH_FAILED __ERR(AuthFailed)
+#define MRP_DBUS_ERROR_NO_SERVER __ERR(NoServer)
+#define MRP_DBUS_ERROR_TIMEOUT __ERR(Timeout)
+#define MRP_DBUS_ERROR_NO_NETWORK __ERR(NoNetwork)
+#define MRP_DBUS_ERROR_ADDRESS_IN_USE __ERR(AddressInUse)
+#define MRP_DBUS_ERROR_DISCONNECTED __ERR(Disconnected)
+#define MRP_DBUS_ERROR_INVALID_ARGS __ERR(InvalidArgs)
+#define MRP_DBUS_ERROR_FILE_NOT_FOUND __ERR(FileNotFound)
+#define MRP_DBUS_ERROR_FILE_EXISTS __ERR(FileExists)
+#define MRP_DBUS_ERROR_UNKNOWN_METHOD __ERR(UnknownMethod)
+#define MRP_DBUS_ERROR_UNKNOWN_OBJECT __ERR(UnknownObject)
+#define MRP_DBUS_ERROR_UNKNOWN_INTERFACE __ERR(UnknownInterface)
+#define MRP_DBUS_ERROR_UNKNOWN_PROPERTY __ERR(UnknownProperty)
+#define MRP_DBUS_ERROR_PROPERTY_READ_ONLY __ERR(PropertyReadOnly)
+#define MRP_DBUS_ERROR_TIMED_OUT __ERR(TimedOut)
+#define MRP_DBUS_ERROR_MATCH_RULE_NOT_FOUND __ERR(MatchRuleNotFound)
+#define MRP_DBUS_ERROR_MATCH_RULE_INVALID __ERR(MatchRuleInvalid)
+#define MRP_DBUS_ERROR_SPAWN_EXEC_FAILED __ERR(Spawn.ExecFailed)
+#define MRP_DBUS_ERROR_SPAWN_FORK_FAILED __ERR(Spawn.ForkFailed)
+#define MRP_DBUS_ERROR_SPAWN_CHILD_EXITED __ERR(Spawn.ChildExited)
+#define MRP_DBUS_ERROR_SPAWN_CHILD_SIGNALED __ERR(Spawn.ChildSignaled)
+#define MRP_DBUS_ERROR_SPAWN_FAILED __ERR(Spawn.Failed)
+#define MRP_DBUS_ERROR_SPAWN_SETUP_FAILED __ERR(Spawn.FailedToSetup)
+#define MRP_DBUS_ERROR_SPAWN_CONFIG_INVALID __ERR(Spawn.ConfigInvalid)
+#define MRP_DBUS_ERROR_SPAWN_SERVICE_INVALID __ERR(Spawn.ServiceNotValid)
+#define MRP_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND __ERR(Spawn.ServiceNotFound)
+#define MRP_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID __ERR(Spawn.PermissionsInvalid)
+#define MRP_DBUS_ERROR_SPAWN_FILE_INVALID __ERR(Spawn.FileInvalid)
+#define MRP_DBUS_ERROR_SPAWN_NO_MEMORY __ERR(Spawn.NoMemory)
+#define MRP_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN __ERR(UnixProcessIdUnknown)
+#define MRP_DBUS_ERROR_INVALID_SIGNATURE __ERR(InvalidSignature)
+#define MRP_DBUS_ERROR_INVALID_FILE_CONTENT __ERR(InvalidFileContent)
+#define MRP_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN __ERR(AdtAuditDataUnknown)
+#define MRP_DBUS_ERROR_OBJECT_PATH_IN_USE __ERR(ObjectPathInUse)
+#define MRP_DBUS_ERROR_INCONSISTENT_MESSAGE __ERR(InconsistentMessage)
+#define MRP_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN __ERR(SELinuxSecurityContextUnknown)
+
+#endif /* __MURPHY_MRP_DBUS_ERROR_H__ */
diff --git a/src/common/dbus-libdbus-glue.c b/src/common/dbus-libdbus-glue.c
new file mode 100644
index 0000000..d1bb093
--- /dev/null
+++ b/src/common/dbus-libdbus-glue.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dbus/dbus.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+
+typedef struct dbus_glue_s dbus_glue_t;
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_io_watch_t *mw;
+ DBusWatch *dw;
+ mrp_list_hook_t hook;
+} watch_t;
+
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_timer_t *mt;
+ DBusTimeout *dt;
+ mrp_list_hook_t hook;
+} timeout_t;
+
+
+struct dbus_glue_s {
+ DBusConnection *conn;
+ mrp_mainloop_t *ml;
+ mrp_list_hook_t watches;
+ mrp_list_hook_t timers;
+ mrp_deferred_t *pump;
+};
+
+
+static dbus_int32_t data_slot = -1;
+
+static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *watch = (watch_t *)user_data;
+ DBusConnection *conn = watch->glue->conn;
+ unsigned int mask = 0;
+
+ MRP_UNUSED(mw);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ mask |= DBUS_WATCH_READABLE;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= DBUS_WATCH_WRITABLE;
+ if (events & MRP_IO_EVENT_HUP)
+ mask |= DBUS_WATCH_HANGUP;
+ if (events & MRP_IO_EVENT_ERR)
+ mask |= DBUS_WATCH_ERROR;
+
+ dbus_connection_ref(conn);
+ dbus_watch_handle(watch->dw, mask);
+ dbus_connection_unref(conn);
+}
+
+
+static void watch_freed_cb(void *data)
+{
+ watch_t *watch = (watch_t *)data;
+
+ if (watch != NULL) {
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+ mrp_free(watch);
+ }
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *dw, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ watch_t *watch;
+ mrp_io_watch_t *mw;
+ mrp_io_event_t mask;
+ int fd;
+ unsigned int flags;
+
+ mrp_debug("adding D-BUS watch %p (%s)", dw,
+ dbus_watch_get_enabled(dw) ? "enabled" : "disabled");
+
+ if (!dbus_watch_get_enabled(dw))
+ return TRUE;
+
+ fd = dbus_watch_get_unix_fd(dw);
+ flags = dbus_watch_get_flags(dw);
+ mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= MRP_IO_EVENT_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= MRP_IO_EVENT_OUT;
+
+ mrp_debug("event mask for fd %d: %s%s", fd,
+ mask & MRP_IO_EVENT_IN ? "read" : "",
+ mask & MRP_IO_EVENT_OUT ? "write" : "");
+
+ if ((watch = mrp_allocz(sizeof(*watch))) != NULL) {
+ mrp_list_init(&watch->hook);
+ mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch);
+
+ if (mw != NULL) {
+ watch->glue = glue;
+ watch->mw = mw;
+ watch->dw = dw;
+ dbus_watch_set_data(dw, watch, watch_freed_cb);
+ mrp_list_append(&glue->watches, &watch->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(watch);
+ }
+
+ return FALSE;
+}
+
+
+static void del_watch(DBusWatch *dw, void *data)
+{
+ watch_t *watch = (watch_t *)dbus_watch_get_data(dw);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS watch %p...", dw);
+
+ if (watch != NULL) {
+ mrp_del_io_watch(watch->mw);
+ watch->mw = NULL;
+ }
+}
+
+
+static void toggle_watch(DBusWatch *dw, void *data)
+{
+ mrp_debug("Toggling D-BUS watch %p...", dw);
+
+ if (dbus_watch_get_enabled(dw))
+ add_watch(dw, data);
+ else
+ del_watch(dw, data);
+}
+
+
+static void dispatch_timeout(mrp_timer_t *mt, void *user_data)
+{
+ timeout_t *timer = (timeout_t *)user_data;
+
+ MRP_UNUSED(mt);
+
+ mrp_debug("dispatching D-BUS timeout %p...", timer->dt);
+
+ dbus_timeout_handle(timer->dt);
+}
+
+
+static void timeout_freed_cb(void *data)
+{
+ timeout_t *timer = (timeout_t *)data;
+
+ if (timer != NULL) {
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *dt, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ timeout_t *timer;
+ mrp_timer_t *mt;
+ unsigned int msecs;
+
+ mrp_debug("adding D-BUS timeout %p...", dt);
+
+ if ((timer = mrp_allocz(sizeof(*timer))) != NULL) {
+ mrp_list_init(&timer->hook);
+ msecs = dbus_timeout_get_interval(dt);
+ mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer);
+
+ if (mt != NULL) {
+ timer->glue = glue;
+ timer->mt = mt;
+ timer->dt = dt;
+ dbus_timeout_set_data(dt, timer, timeout_freed_cb);
+ mrp_list_append(&glue->timers, &timer->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(timer);
+ }
+
+ return FALSE;
+}
+
+
+static void del_timeout(DBusTimeout *dt, void *data)
+{
+ timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS timeout %p...", dt);
+
+ if (timer != NULL) {
+ mrp_del_timer(timer->mt);
+ timer->mt = NULL;
+ }
+}
+
+
+static void toggle_timeout(DBusTimeout *dt, void *data)
+{
+ mrp_debug("toggling D-BUS timeout %p...", dt);
+
+ if (dbus_timeout_get_enabled(dt))
+ add_timeout(dt, data);
+ else
+ del_timeout(dt, data);
+}
+
+
+static void wakeup_mainloop(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+
+ mrp_debug("waking up mainloop...");
+
+ mrp_enable_deferred(glue->pump);
+}
+
+
+static void glue_free_cb(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ mrp_list_hook_t *p, *n;
+ watch_t *watch;
+ timeout_t *timer;
+
+ mrp_list_foreach(&glue->watches, p, n) {
+ watch = mrp_list_entry(p, typeof(*watch), hook);
+
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+
+ mrp_free(watch);
+ }
+
+ mrp_list_foreach(&glue->timers, p, n) {
+ timer = mrp_list_entry(p, typeof(*timer), hook);
+
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+
+ mrp_free(glue);
+}
+
+
+static void pump_cb(mrp_deferred_t *d, void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ mrp_debug("dispatching dbus connection %p...", glue->conn);
+
+ if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE)
+ mrp_disable_deferred(d);
+}
+
+
+static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status,
+ void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ MRP_UNUSED(conn);
+
+ switch (status) {
+ case DBUS_DISPATCH_COMPLETE:
+ mrp_debug("dispatching status for %p: complete", conn);
+ mrp_disable_deferred(glue->pump);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ mrp_debug("dispatching status for %p: not complete yet", conn);
+ mrp_enable_deferred(glue->pump);
+ break;
+ }
+}
+
+
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn)
+{
+ dbus_glue_t *glue;
+
+ if (!dbus_connection_allocate_data_slot(&data_slot))
+ return FALSE;
+
+ if (dbus_connection_get_data(conn, data_slot) != NULL)
+ return FALSE;
+
+ if ((glue = mrp_allocz(sizeof(*glue))) != NULL) {
+ mrp_list_init(&glue->watches);
+ mrp_list_init(&glue->timers);
+ glue->pump = mrp_add_deferred(ml, pump_cb, glue);
+
+ if (glue->pump == NULL) {
+ mrp_free(glue);
+ return FALSE;
+ }
+
+ glue->ml = ml;
+ glue->conn = conn;
+ }
+ else
+ return FALSE;
+
+ if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb))
+ return FALSE;
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+ glue, NULL);
+
+ dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop,
+ glue, NULL);
+
+ return
+ dbus_connection_set_watch_functions(conn, add_watch, del_watch,
+ toggle_watch, glue, NULL) &&
+ dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout,
+ toggle_timeout, glue, NULL);
+}
diff --git a/src/common/dbus-libdbus-transport.c b/src/common/dbus-libdbus-transport.c
new file mode 100644
index 0000000..e19dac8
--- /dev/null
+++ b/src/common/dbus-libdbus-transport.c
@@ -0,0 +1,1745 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/dbus-libdbus.h>
+#include <murphy/common/dbus-transport.h>
+
+#define DBUS "dbus"
+#define DBUSL 4
+
+#define TRANSPORT_PATH "/murphy/transport"
+#define TRANSPORT_INTERFACE "Murphy.Transport"
+#define TRANSPORT_MESSAGE "DeliverMessage"
+#define TRANSPORT_DATA "DeliverData"
+#define TRANSPORT_RAW "DeliverRaw"
+#define TRANSPORT_METHOD "DeliverMessage"
+
+#define ANY_ADDRESS "any"
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ mrp_dbus_t *dbus; /* D-BUS connection */
+ int bound : 1; /* whether bound to an address */
+ int peer_resolved : 1; /* connected and peer name known */
+ mrp_dbusaddr_t local; /* address we're bound to */
+ mrp_dbusaddr_t remote; /* address we're connected to */
+} dbus_t;
+
+
+static uint32_t nauto; /* for autobinding */
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data);
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg);
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *msg, const char **sender_id);
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag);
+static void *data_decode(mrp_dbus_msg_t *msg, uint16_t *tag,
+ const char **sender_id);
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size);
+static void *raw_decode(mrp_dbus_msg_t *msg, size_t *sizep,
+ const char **sender_id);
+
+
+static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr,
+ socklen_t size)
+{
+ const char *p, *e;
+ char *q;
+ size_t l, n;
+
+ if (size < sizeof(*addr)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (strncmp(str, DBUS":", DBUSL + 1))
+ return 0;
+ else
+ str += DBUSL + 1;
+
+ /*
+ * The format of the address is
+ * dbus:[bus-address]@address/path
+ * eg.
+ * dbus:[session]@:1.33/client1, or
+ * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1
+ */
+
+ p = str;
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa) - 1;
+
+ /* get bus address */
+ if (*p != '[') {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ p++;
+
+ e = strchr(p, ']');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save bus address */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_bus = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e + 1;
+
+ /* get (local or remote) address on bus */
+ if (*p != '@')
+ addr->db_addr = ANY_ADDRESS;
+ else {
+ p++;
+ e = strchr(p, '/');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save address on bus */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_addr = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e;
+ }
+
+ /* get object path */
+ if (*p != '/') {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p);
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ addr->db_path = q;
+ addr->db_family = MRP_AF_DBUS;
+
+ return sizeof(*addr);
+}
+
+
+static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src)
+{
+ char *p, *q;
+ size_t l, n;
+
+ dst->db_family = src->db_family;
+
+ /* copy bus address */
+ p = src->db_bus;
+ q = dst->db_fqa;
+ l = sizeof(dst->db_fqa) - 1;
+
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy address */
+ p = src->db_addr;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_addr = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy path */
+ p = src->db_path;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_path = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ return dst;
+}
+
+
+static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr;
+
+ return (a && a->db_family == MRP_AF_DBUS && addrlen == sizeof(*a));
+}
+
+
+static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender,
+ const char *path)
+{
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ const char *p;
+ char *q;
+ int l, n;
+
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa) - 1;
+ p = ANY_ADDRESS;
+ n = 3;
+
+ addr->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_addr = q;
+ p = sender;
+ n = strlen(sender);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_path = q;
+ p = path;
+ n = strlen(p);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+
+ return sizeof(addrp);
+}
+
+
+static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ socklen_t len;
+
+ len = parse_address(str, (mrp_dbusaddr_t *)addr, size);
+
+ if (len > 0) {
+ if (typep != NULL)
+ *typep = DBUS;
+ }
+
+ return len;
+}
+
+
+static int dbus_open(mrp_transport_t *mt)
+{
+ MRP_UNUSED(mt);
+
+ return TRUE;
+}
+
+
+static int dbus_createfrom(mrp_transport_t *mt, void *conn)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = (mrp_dbus_t *)conn;
+
+ t->dbus = mrp_dbus_ref(dbus);
+
+ if (t->dbus != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = NULL;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+ const char *method;
+
+ if (t->bound) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (t->dbus == NULL) {
+ dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (dbus == NULL) {
+ errno = ECONNRESET;
+ goto fail;
+ }
+ else {
+ t->dbus = dbus;
+
+ if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) {
+ if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) {
+ errno = EADDRINUSE; /* XXX TODO, should check error... */
+ goto fail;
+ }
+ }
+ }
+ }
+ else {
+ /* XXX TODO: should check given address against address of the bus */
+ }
+
+ copy_address(&t->local, addr);
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ break;
+ default:
+ errno = EPROTOTYPE;
+ goto fail;
+ }
+
+ if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t)) {
+ errno = EIO;
+ goto fail;
+ }
+ else {
+ t->bound = TRUE;
+ return TRUE;
+ }
+
+ fail:
+ if (dbus != NULL) {
+ mrp_dbus_unref(dbus);
+ t->dbus = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp;
+ char astr[MRP_SOCKADDR_SIZE];
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++);
+
+ alen = dbus_resolve(astr, &addr, sizeof(addr), NULL);
+
+ if (alen > 0)
+ return dbus_bind(mt, &addr, alen);
+ else
+ return FALSE;
+}
+
+
+static void dbus_close(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr;
+ const char *method;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+ if (t->bound) {
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ default:
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ }
+
+ addr = (mrp_dbusaddr_t *)&t->local;
+ mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t);
+ }
+
+ if (t->connected && t->remote.db_addr != NULL)
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+
+ mrp_dbus_unref(t->dbus);
+ t->dbus = NULL;
+}
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ mrp_msg_t *msg;
+
+ MRP_UNUSED(dbus);
+
+ msg = msg_decode(dmsg, &sender_path);
+
+ if (msg != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsg(mt, msg, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ uint16_t tag;
+ void *decoded;
+
+ MRP_UNUSED(dbus);
+
+ decoded = data_decode(dmsg, &tag, &sender_path);
+
+ if (decoded != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode custom data message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ void *data;
+ size_t size;
+
+ MRP_UNUSED(dbus);
+
+ data = raw_decode(dmsg, &size, &sender_path);
+
+ if (data != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvraw(mt, data, size, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvrawfrom(mt, data, size, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode raw message.");
+ }
+
+ return TRUE;
+}
+
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ dbus_t *t = (dbus_t *)user_data;
+ mrp_sockaddr_t addr;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ peer_address(&addr, owner, t->remote.db_path);
+ copy_address(&t->remote, (mrp_dbusaddr_t *)&addr);
+ t->peer_resolved = TRUE;
+ }
+ else {
+ /*
+ * XXX TODO:
+ * It would be really tempting here to call
+ * mt->evt.closed(mt, ECONNRESET, mt->user_data)
+ * to notify the user about the fact our peer went down.
+ * However, that would not be in line with the other
+ * transports which call the closed event handler only
+ * upon foricble transport closes upon errors.
+ *
+ * The transport interface abstraction (especially the
+ * available set of events) anyway needs some eyeballing,
+ * so the right thing to do might be to define a new event
+ * for disconnection and call the handler for that here...
+ */
+ }
+
+}
+
+
+static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (t->dbus == NULL) {
+ t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (t->dbus == NULL) {
+ errno = ECONNRESET;
+ return FALSE;
+ }
+ }
+ else {
+ /* XXX TODO: check given address against address of the bus */
+ }
+
+ if (!t->bound)
+ if (!dbus_autobind(mt, addrp))
+ return FALSE;
+
+ if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) {
+ copy_address(&t->remote, addr);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dbus_disconnect(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+
+ if (t->connected && t->remote.db_addr != NULL) {
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+ mrp_clear(&t->remote);
+ t->peer_resolved = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = msg_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_MESSAGE,
+ t->local.db_path, msg);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendmsgto(mt, msg, addr, alen);
+}
+
+
+static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+
+ MRP_UNUSED(mt);
+ MRP_UNUSED(data);
+ MRP_UNUSED(size);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = raw_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_RAW,
+ t->local.db_path, data, size);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendrawto(mt, data, size, addr, alen);
+}
+
+
+static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = data_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_DATA,
+ t->local.db_path, data, tag);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_senddatato(mt, data, tag, addr, alen);
+}
+
+
+static const char *get_array_signature(uint16_t type)
+{
+#define MAP(from, to) \
+ case MRP_MSG_FIELD_##from: \
+ return MRP_DBUS_TYPE_##to##_AS_STRING;
+
+ switch (type) {
+ MAP(STRING, STRING);
+ MAP(BOOL , BOOLEAN);
+ MAP(UINT8 , UINT16);
+ MAP(SINT8 , INT16);
+ MAP(UINT16, UINT16);
+ MAP(SINT16, INT16);
+ MAP(UINT32, UINT32);
+ MAP(SINT32, INT32);
+ MAP(UINT64, UINT64);
+ MAP(SINT64, INT64);
+ MAP(DOUBLE, DOUBLE);
+ MAP(BLOB , BYTE);
+ default:
+ return NULL;
+ }
+}
+
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg)
+{
+ /*
+ * Notes: There is a type mismatch between our and DBUS types for
+ * 8-bit integers (D-BUS does not have a signed 8-bit type)
+ * and boolean types (D-BUS has uint32_t booleans, C99 fails
+ * to specify the type and gcc uses a signed char). The
+ * QUIRKY versions of the macros take care of these mismatches.
+ */
+
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+ uint16_t base;
+ uint32_t asize, i;
+ const char *sig;
+ int type, len;
+ void *vptr;
+ dbus_bool_t bln;
+ uint16_t u16, blb;
+ int16_t s16;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &msg->nfield))
+ goto fail;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ switch (f->type) {
+ BASIC_SIMPLE(m, STRING, STRING , f->str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, f->bln, bln);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , f->u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , f->s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , f->u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , f->s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , f->u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , f->s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , f->u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , f->s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , f->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ vptr = f->blb;
+ len = (int)f->size[0];
+ sig = get_array_signature(f->type);
+ asize = len;
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ blb = ((uint8_t *)f->blb)[i];
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, &blb))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ switch (base) {
+ ARRAY_SIMPLE(m, STRING, STRING , f->astr[i]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, f->abln[i], bln);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , f->au8[i] , u16);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , f->as8[i] , s16);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , f->au16[i]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , f->as16[i]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , f->au32[i]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , f->as32[i]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , f->au64[i]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , f->adbl[i]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return FALSE;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *m, const char **sender_id)
+{
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ \
+ if (!mrp_msg_append(msg, tag, type, (_var))) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ if (!mrp_msg_append(msg, tag, type, (_mvar))) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+#define APPEND_ARRAY(_type, _var) \
+ case MRP_MSG_FIELD_##_type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY | \
+ MRP_MSG_FIELD_##_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ mrp_msg_t *msg;
+ mrp_msg_value_t v;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ uint16_t nfield, tag, type, base, i;
+ uint32_t n, j;
+ int asize;
+ const char *sender, *sig;
+
+ msg = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ switch (type) {
+ BASIC_SIMPLE(m, STRING, STRING , v.str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, v.bln, u32);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , v.u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , v.s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , v.u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , v.s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , v.u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , v.s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , v.u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , v.s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , v.dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ {
+ uint8_t blb[n];
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ asize = n;
+ if (!mrp_msg_append(msg, tag, type, asize, blb))
+ goto fail;
+ }
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = get_array_signature(base);
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ char *astr[n];
+ uint32_t dbln[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(m, STRING, STRING , astr[j]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, abln[j], dbln[j]);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , au8[j] , au16[j]);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , as8[j] , as16[j]);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , au16[j]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , as16[j]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , au32[j]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , as32[j]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , au64[j]);
+ ARRAY_SIMPLE(m, SINT64, INT64 , as64[j]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , adbl[j]);
+ default:
+ goto fail;
+ }
+ }
+
+ switch (base) {
+ APPEND_ARRAY(STRING, astr);
+ APPEND_ARRAY(BOOL , abln);
+ APPEND_ARRAY(UINT8 , au8 );
+ APPEND_ARRAY(SINT8 , as8 );
+ APPEND_ARRAY(UINT16, au16);
+ APPEND_ARRAY(SINT16, as16);
+ APPEND_ARRAY(UINT32, au32);
+ APPEND_ARRAY(SINT32, as32);
+ APPEND_ARRAY(UINT64, au64);
+ APPEND_ARRAY(SINT64, as64);
+ APPEND_ARRAY(DOUBLE, adbl);
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ errno = EBADMSG;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+#undef APPEND_ARRAY
+}
+
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag)
+{
+#define BASIC_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type, base;
+ mrp_msg_value_t *v;
+ void *vptr;
+ uint32_t n, j;
+ int i, blblen;
+ const char *sig;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t bln, asize;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ BASIC_SIMPLE(STRING, STRING , v->str);
+ BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln);
+ BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16);
+ BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16);
+ BASIC_SIMPLE(UINT16, UINT16 , v->u16);
+ BASIC_SIMPLE(SINT16, INT16 , v->s16);
+ BASIC_SIMPLE(UINT32, UINT32 , v->u32);
+ BASIC_SIMPLE(SINT32, INT32 , v->s32);
+ BASIC_SIMPLE(UINT64, UINT64 , v->u64);
+ BASIC_SIMPLE(SINT64, INT64 , v->s64);
+ BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ sig = get_array_signature(f->type);
+ blblen = mrp_data_get_blob_size(data, descr, i);
+ asize = blblen;
+
+ if (blblen == -1)
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < blblen; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE,
+ f->blb + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ n = mrp_data_get_array_size(data, descr, i);
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(STRING, STRING , v->astr[j]);
+ ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln);
+ ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16);
+ ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16);
+ ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]);
+ ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]);
+ ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]);
+ ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]);
+ ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]);
+ ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+static void *data_decode(mrp_dbus_msg_t *m, uint16_t *tagp,
+ const char **sender_id)
+{
+#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+ void *data;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t tag, type, base;
+ mrp_msg_value_t *v;
+ uint32_t n, j, size;
+ int i;
+ const char *sender, *sig;
+ uint32_t u32;
+ uint16_t u16;
+ int16_t s16;
+
+ tag = 0;
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ *tagp = tag;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ if (nfield != descr->nfield)
+ goto fail;
+
+ fields = descr->fields;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (type) {
+ HANDLE_SIMPLE(&im, STRING, STRING , v->str);
+ HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32);
+ HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16);
+ HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16);
+ HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16);
+ HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16);
+ HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32);
+ HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32);
+ HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64);
+ HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64);
+ HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &size))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t blb[size];
+
+ for (j = 0; j < size; j++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+
+ v->blb = mrp_alloc(size);
+
+ if (v->blb == NULL && size != 0)
+ goto fail;
+
+ memcpy(v->blb, blb, size);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ uint32_t dbln[n];
+ uint16_t au16[n];
+ int16_t as16[n];
+
+ switch (base) {
+ HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]);
+ HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]);
+ HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]);
+ HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]);
+ HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]);
+ HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]);
+ HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]);
+ HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]);
+ HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]);
+ HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]);
+ HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]);
+ }
+
+ if (base == MRP_MSG_FIELD_STRING) {
+ v->astr[j] = mrp_strdup(v->astr[j]);
+ if (v->astr[j] == NULL)
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+
+ if (f->type == MRP_MSG_FIELD_STRING) {
+ v->str = mrp_strdup(v->str);
+ if (v->str == NULL)
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ mrp_data_free(data, tag);
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size)
+{
+ mrp_dbus_msg_t *m;
+ const char *sig;
+ uint32_t i, n;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+ n = size;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, data + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+
+ return m;
+
+ fail:
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+}
+
+
+static void *raw_decode(mrp_dbus_msg_t *m, size_t *sizep,
+ const char **sender_id)
+{
+ const char *sender, *sig;
+ void *data;
+ uint32_t n, i;
+
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t databuf[n];
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, databuf + i))
+ goto fail;
+
+ data = mrp_alloc(n);
+
+ if (data == NULL && n != 0)
+ goto fail;
+
+ memcpy(data, databuf, n);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ if (sizep != NULL)
+ *sizep = (size_t)n;
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve,
+ dbus_open, dbus_createfrom, dbus_close, NULL,
+ dbus_bind, NULL, NULL,
+ dbus_connect, dbus_disconnect,
+ dbus_sendmsg, dbus_sendmsgto,
+ dbus_sendraw, dbus_sendrawto,
+ dbus_senddata, dbus_senddatato,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
diff --git a/src/common/dbus-libdbus.c b/src/common/dbus-libdbus.c
new file mode 100644
index 0000000..6dcf9fa
--- /dev/null
+++ b/src/common/dbus-libdbus.c
@@ -0,0 +1,2078 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-libdbus.h>
+
+
+#define DBUS_ADMIN_SERVICE "org.freedesktop.DBus"
+#define DBUS_ADMIN_INTERFACE "org.freedesktop.DBus"
+#define DBUS_ADMIN_PATH "/org/freedesktop/DBus"
+#define DBUS_NAME_CHANGED "NameOwnerChanged"
+
+
+struct mrp_dbus_s {
+ char *address; /* bus address */
+ DBusConnection *conn; /* actual D-BUS connection */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_htbl_t *methods; /* method handler table */
+ mrp_htbl_t *signals; /* signal handler table */
+ mrp_list_hook_t name_trackers; /* peer (name) watchers */
+ mrp_list_hook_t calls; /* pending calls */
+ uint32_t call_id; /* next call id */
+ const char *unique_name; /* our unique D-BUS address */
+ int priv; /* whether a private connection */
+ int signal_filter; /* if signal dispatching is set up */
+ int register_fallback; /* if the fallback object is set up */
+ mrp_refcnt_t refcnt; /* reference count */
+};
+
+
+struct mrp_dbus_msg_s {
+ DBusMessage *msg; /* actual D-BUS message */
+ mrp_refcnt_t refcnt; /* reference count */
+ mrp_list_hook_t iterators; /* iterator stack */
+ mrp_list_hook_t arrays; /* implicitly freed related arrays */
+};
+
+
+typedef struct {
+ DBusMessageIter it; /* actual iterator */
+ char *peeked; /* peeked contents, or NULL */
+ mrp_list_hook_t hook; /* hook to iterator stack */
+} msg_iter_t;
+
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char **items;
+ size_t nitem;
+} msg_array_t;
+
+/*
+ * Notes:
+ *
+ * At the moment we administer DBUS method and signal handlers
+ * in a very primitive way (subject to be changed later). For
+ * every bus instance we maintain two hash tables, one for methods
+ * and another for signals. Each method and signal handler is
+ * hashed in only by it's method/signal name to a linked list of
+ * method or signal handlers.
+ *
+ * When dispatching a method, we look up the chain with a matching
+ * method name, or the chain for "" in case a matching chain is
+ * not found, and invoke the handler which best matches the
+ * received message (by looking at the path, interface and name).
+ * Only one such handler is invoked at most.
+ *
+ * For signals we look up both the chain with a matching name and
+ * the chain for "" and invoke all signal handlers that match the
+ * received message (regardless of their return value).
+ */
+
+
+typedef struct {
+ char *member; /* signal/method name */
+ mrp_list_hook_t handlers; /* handlers with matching member */
+} handler_list_t;
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *sender;
+ char *path;
+ char *interface;
+ char *member;
+ mrp_dbus_handler_t handler;
+ void *user_data;
+} handler_t;
+
+#define method_t handler_t
+#define signal_t handler_t
+
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to name tracker list */
+ char *name; /* name to track */
+ mrp_dbus_name_cb_t cb; /* status change callback */
+ void *user_data; /* opaque callback user data */
+ int32_t qid; /* initial query ID */
+} name_tracker_t;
+
+
+typedef struct {
+ mrp_dbus_t *dbus; /* DBUS connection */
+ int32_t id; /* call id */
+ mrp_dbus_reply_cb_t cb; /* completion notification callback */
+ void *user_data; /* opaque callback data */
+ DBusPendingCall *pend; /* pending DBUS call */
+ mrp_list_hook_t hook; /* hook to list of pending calls */
+} call_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml; /* mainloop for bus connection */
+ const char *address; /* address of bus */
+} bus_spec_t;
+
+static mrp_htbl_t *buses;
+
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static void purge_name_trackers(mrp_dbus_t *dbus);
+static void purge_calls(mrp_dbus_t *dbus);
+static void handler_list_free_cb(void *key, void *entry);
+static void handler_free(handler_t *h);
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ void *data);
+static void call_free(call_t *call);
+static void free_msg_array(msg_array_t *a);
+
+
+
+static int purge_filters(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ handler_list_t *l = (handler_list_t *)entry;
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ MRP_UNUSED(key);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_dbus_remove_filter(dbus,
+ h->sender, h->path, h->interface,
+ h->member, NULL);
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+void dbus_disconnect(mrp_dbus_t *dbus)
+{
+ if (dbus) {
+ mrp_htbl_remove(buses, dbus->conn, FALSE);
+
+ if (dbus->signals) {
+ mrp_htbl_foreach(dbus->signals, purge_filters, dbus);
+ mrp_htbl_destroy(dbus->signals, TRUE);
+ }
+ if (dbus->methods)
+ mrp_htbl_destroy(dbus->methods, TRUE);
+
+ if (dbus->conn != NULL) {
+ if (dbus->signal_filter)
+ dbus_connection_remove_filter(dbus->conn, dispatch_signal,
+ dbus);
+ if (dbus->register_fallback)
+ dbus_connection_unregister_object_path(dbus->conn, "/");
+ if (dbus->priv)
+ dbus_connection_close(dbus->conn);
+ dbus_connection_unref(dbus->conn);
+ }
+
+ purge_name_trackers(dbus);
+ purge_calls(dbus);
+
+ mrp_free(dbus->address);
+ dbus->conn = NULL;
+ dbus->ml = NULL;
+
+ mrp_free(dbus);
+ }
+}
+
+
+static int bus_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t bus_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (ptrdiff_t)key;
+ h >>= 2 * sizeof(key);
+
+ return h;
+}
+
+
+static int find_bus_by_spec(void *key, void *object, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)object;
+ bus_spec_t *spec = (bus_spec_t *)user_data;
+
+ MRP_UNUSED(key);
+
+ if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address)
+{
+ mrp_htbl_config_t hcfg;
+ bus_spec_t spec;
+
+ if (buses == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = bus_cmp;
+ hcfg.hash = bus_hash;
+ hcfg.free = NULL;
+
+ buses = mrp_htbl_create(&hcfg);
+
+ return NULL;
+ }
+ else {
+ spec.ml = ml;
+ spec.address = address;
+
+ return mrp_htbl_find(buses, find_bus_by_spec, &spec);
+ }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp)
+{
+ static struct DBusObjectPathVTable vtable = {
+ .message_function = dispatch_method
+ };
+
+ mrp_htbl_config_t hcfg;
+ mrp_dbus_t *dbus;
+
+ if ((dbus = dbus_get(ml, address)) != NULL)
+ return mrp_dbus_ref(dbus);
+
+ if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL)
+ return NULL;
+
+ mrp_list_init(&dbus->calls);
+ mrp_list_init(&dbus->name_trackers);
+ mrp_refcnt_init(&dbus->refcnt);
+
+ dbus->ml = ml;
+
+
+ mrp_dbus_error_init(errp);
+
+ /*
+ * connect to the bus
+ */
+
+ if (!strcmp(address, "system"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SYSTEM, errp);
+ else if (!strcmp(address, "session"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SESSION, errp);
+ else {
+ dbus->conn = dbus_connection_open_private(address, errp);
+ dbus->priv = TRUE;
+
+ if (dbus->conn == NULL || !dbus_bus_register(dbus->conn, errp))
+ goto fail;
+ }
+
+ if (dbus->conn == NULL)
+ goto fail;
+
+ dbus->address = mrp_strdup(address);
+ dbus->unique_name = dbus_bus_get_unique_name(dbus->conn);
+
+ /*
+ * set up with mainloop
+ */
+
+ if (!mrp_dbus_setup_connection(ml, dbus->conn))
+ goto fail;
+
+ /*
+ * set up our message dispatchers and take our name on the bus
+ */
+
+ if (!dbus_connection_add_filter(dbus->conn, dispatch_signal, dbus, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up signal dispatching.");
+ goto fail;
+ }
+ dbus->signal_filter = TRUE;
+
+ if (!dbus_connection_register_fallback(dbus->conn, "/", &vtable, dbus)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up method dispatching.");
+ goto fail;
+ }
+ dbus->register_fallback = TRUE;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = handler_list_free_cb;
+
+ if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS method table.");
+ goto fail;
+ }
+
+ if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS signal table.");
+ goto fail;
+ }
+
+
+ /*
+ * install handler for NameOwnerChanged for tracking clients/peers
+ */
+
+ if (!mrp_dbus_add_signal_handler(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name_owner_change_cb, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to install NameOwnerChanged handler.");
+ goto fail;
+ }
+
+ /* install a 'safe' filter to avoid receiving all name change signals */
+ mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ DBUS_ADMIN_SERVICE, NULL);
+
+ mrp_list_init(&dbus->name_trackers);
+ dbus->call_id = 1;
+
+ if (mrp_htbl_insert(buses, dbus->conn, dbus))
+ return dbus;
+
+ fail:
+ dbus_disconnect(dbus);
+ return NULL;
+}
+
+
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus)
+{
+ return mrp_ref_obj(dbus, refcnt);
+}
+
+
+int mrp_dbus_unref(mrp_dbus_t *dbus)
+{
+ if (mrp_unref_obj(dbus, refcnt)) {
+ dbus_disconnect(dbus);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int flags, status;
+
+ mrp_dbus_error_init(error);
+
+ flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+ status = dbus_bus_request_name(dbus->conn, name, flags, error);
+
+ if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ return TRUE;
+ else {
+ if (status == DBUS_REQUEST_NAME_REPLY_EXISTS) {
+ if (error)
+ dbus_error_free(error);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "name already taken");
+ }
+ return FALSE;
+ }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ mrp_dbus_error_init(error);
+
+ if (dbus_bus_release_name(dbus->conn, name, error) != -1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus)
+{
+ return dbus->unique_name;
+}
+
+
+static void name_owner_query_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ name_tracker_t *t = (name_tracker_t *)data;
+ DBusMessage *msg = m->msg;
+ const char *owner;
+ int state;
+
+ if (t->cb != NULL) { /* tracker still active */
+ t->qid = 0;
+ state = dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &owner,
+ DBUS_TYPE_INVALID))
+ owner = "<unknown>";
+
+ t->cb(dbus, t->name, state, owner, t->user_data);
+ }
+ else /* already requested to delete */
+ mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ const char *name, *prev, *next;
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+ DBusMessage *msg;
+
+ MRP_UNUSED(data);
+
+ msg = m->msg;
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &prev,
+ DBUS_TYPE_STRING, &next,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+#if 0
+ /*
+ * Notes: XXX TODO
+ * In principle t->cb could call mrp_dbus_forget for some other D-BUS
+ * address than name. If that happened to be n (== p->hook.next) this
+ * would result in a crash or memory corruption in the next iteration
+ * of this loop (when handling n). We can easily get around this
+ * problem by
+ *
+ * 1. administering in mrp_dbus_t that we're handing a NameOwnerChange
+ * 2. checking for this in mrp_dbus_forget_name and if it is the case
+ * only marking the affected entry for deletion
+ * 3. removing entries marked for deletion in this loop (or just
+ * ignoring them and making another pass in the end removing any
+ * such entry).
+ */
+#endif
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (!strcmp(name, t->name))
+ t->cb(dbus, name, next && *next, next, t->user_data);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ name_tracker_t *t;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ if ((t->name = mrp_strdup(name)) != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+
+ if (mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL)) {
+ mrp_list_append(&dbus->name_trackers, &t->hook);
+
+ t->qid = mrp_dbus_call(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, "GetNameOwner", 5000,
+ name_owner_query_cb, t,
+ DBUS_TYPE_STRING, t->name,
+ DBUS_TYPE_INVALID);
+ return TRUE;
+ }
+ else {
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_dbus_remove_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL);
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) {
+ mrp_list_delete(&t->hook);
+ mrp_free(t->name);
+
+ if (!t->qid)
+ mrp_free(t);
+ else {
+ t->cb = NULL;
+ t->user_data = NULL;
+ t->name = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_name_trackers(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ mrp_list_delete(p);
+ mrp_dbus_remove_filter(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ t->name, NULL);
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+}
+
+
+static handler_t *handler_alloc(const char *sender, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_t *h;
+
+ if ((h = mrp_allocz(sizeof(*h))) != NULL) {
+ h->sender = mrp_strdup(sender);
+ h->path = mrp_strdup(path);
+ h->interface = mrp_strdup(interface);
+ h->member = mrp_strdup(member);
+
+ if ((path && !h->path) || !h->interface || !h->member) {
+ handler_free(h);
+ return NULL;
+ }
+
+ h->handler = handler;
+ h->user_data = user_data;
+
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static void handler_free(handler_t *h)
+{
+ if (h != NULL) {
+ mrp_free(h->sender);
+ mrp_free(h->path);
+ mrp_free(h->interface);
+ mrp_free(h->member);
+
+ mrp_free(h);
+ }
+}
+
+
+static handler_list_t *handler_list_alloc(const char *member)
+{
+ handler_list_t *l;
+
+ if ((l = mrp_allocz(sizeof(*l))) != NULL) {
+ if ((l->member = mrp_strdup(member)) != NULL)
+ mrp_list_init(&l->handlers);
+ else {
+ mrp_free(l);
+ l = NULL;
+ }
+ }
+
+ return l;
+}
+
+
+static inline void handler_list_free(handler_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_list_delete(p);
+ handler_free(h);
+ }
+
+ mrp_free(l->member);
+ mrp_free(l);
+}
+
+
+static void handler_list_free_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ handler_list_free((handler_list_t *)entry);
+}
+
+
+static inline int handler_specificity(handler_t *h)
+{
+ int score = 0;
+
+ if (h->path && *h->path)
+ score |= 0x4;
+ if (h->interface && *h->interface)
+ score |= 0x2;
+ if (h->member && *h->member)
+ score |= 0x1;
+
+ return score;
+}
+
+
+static void handler_list_insert(handler_list_t *l, handler_t *handler)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+ int score;
+
+ score = handler_specificity(handler);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (score >= handler_specificity(h)) {
+ mrp_list_append(h->hook.prev, &handler->hook); /* add before h */
+ return;
+ }
+ }
+
+ mrp_list_append(&l->handlers, &handler->hook);
+}
+
+
+static handler_t *handler_list_lookup(handler_list_t *l, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (h->handler == handler && user_data == h->user_data &&
+ path && !strcmp(path, h->path) &&
+ interface && !strcmp(interface, h->interface) &&
+ member && !strcmp(member, h->member))
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static handler_t *handler_list_find(handler_list_t *l, const char *path,
+ const char *interface, const char *member)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member))
+ return h;
+ }
+
+ return NULL;
+#undef MATCHES
+}
+
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+ if ((methods = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ mrp_htbl_insert(dbus->methods, methods->member, methods);
+ }
+
+ m = handler_alloc(NULL, path, interface, member, handler, user_data);
+ if (m != NULL) {
+ handler_list_insert(methods, m);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL)
+ return FALSE;
+
+ m = handler_list_lookup(methods, path, interface, member,
+ handler, user_data);
+ if (m != NULL) {
+ mrp_list_delete(&m->hook);
+ handler_free(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) {
+ if ((signals = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) {
+ handler_list_free(signals);
+ return FALSE;
+ }
+ }
+
+ s = handler_alloc(sender, path, interface, member, handler, user_data);
+ if (s != NULL) {
+ handler_list_insert(signals, s);
+ return TRUE;
+ }
+ else {
+ handler_free(s);
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, signals->member, TRUE);
+ return FALSE;
+ }
+}
+
+
+
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ MRP_UNUSED(sender);
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL)
+ return FALSE;
+
+ s = handler_list_lookup(signals, path, interface, member,
+ handler, user_data);
+ if (s != NULL) {
+ mrp_list_delete(&s->hook);
+ handler_free(s);
+
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, (void *)member, TRUE);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int success;
+
+
+ if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data)) {
+ va_start(ap, member);
+ success = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ if (success)
+ return TRUE;
+ else
+ mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ va_start(ap, member);
+ status &= mrp_dbus_remove_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+#define ADD_TAG(tag, value) do { \
+ if (value != NULL) { \
+ l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \
+ tag, value); \
+ if (l >= n) \
+ return FALSE; \
+ n -= l; \
+ p += l; \
+ } \
+ } while (0)
+
+ va_list ap;
+ DBusError error;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ dbus_error_init(&error);
+ dbus_bus_add_match(dbus->conn, filter, &error);
+
+ if (dbus_error_is_set(&error)) {
+ mrp_log_error("Failed to install filter '%s' (error: %s).", filter,
+ mrp_dbus_errmsg(&error));
+ dbus_error_free(&error);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+}
+
+
+int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ dbus_bus_remove_match(dbus->conn, filter, NULL);
+ return TRUE;
+#undef ADD_TAG
+}
+
+
+int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+static inline mrp_dbus_msg_t *create_message(DBusMessage *msg)
+{
+ mrp_dbus_msg_t *m;
+
+ if (msg != NULL) {
+ if ((m = mrp_allocz(sizeof(*m))) != NULL) {
+ mrp_refcnt_init(&m->refcnt);
+ mrp_list_init(&m->iterators);
+ mrp_list_init(&m->arrays);
+ m->msg = dbus_message_ref(msg);
+ }
+ }
+ else
+ m = NULL;
+
+ return m;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m)
+{
+ return mrp_ref_obj(m, refcnt);
+}
+
+
+static void rewind_message(mrp_dbus_msg_t *m)
+{
+ mrp_list_hook_t *p, *n;
+ msg_iter_t *it;
+ msg_array_t *a;
+
+ mrp_list_foreach(&m->iterators, p, n) {
+ it = mrp_list_entry(p, typeof(*it), hook);
+
+ mrp_list_delete(&it->hook);
+ mrp_free(it->peeked);
+ mrp_free(it);
+ }
+
+ mrp_list_foreach(&m->arrays, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+
+ free_msg_array(a);
+ }
+}
+
+
+static void free_message(mrp_dbus_msg_t *m)
+{
+ mrp_list_hook_t *p, *n;
+ msg_iter_t *it;
+ msg_array_t *a;
+
+ mrp_list_foreach(&m->iterators, p, n) {
+ it = mrp_list_entry(p, typeof(*it), hook);
+
+ mrp_list_delete(&it->hook);
+ mrp_free(it->peeked);
+ mrp_free(it);
+ }
+
+ mrp_list_foreach(&m->arrays, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+
+ free_msg_array(a);
+ }
+
+ mrp_free(m);
+}
+
+
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m)
+{
+ DBusMessage *msg;
+
+ if (mrp_unref_obj(m, refcnt)) {
+ msg = m->msg;
+
+ free_message(m);
+
+ if (msg != NULL)
+ dbus_message_unref(msg);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ int r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ handler_list_t *l;
+ handler_t *h;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+ retry:
+ if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+ if (m == NULL)
+ m = create_message(msg);
+
+ if (m != NULL && h->handler(dbus, m, h->user_data))
+ r = DBUS_HANDLER_RESULT_HANDLED;
+
+ goto out;
+ }
+ }
+ else {
+ if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+ goto retry;
+ }
+
+ out:
+ mrp_dbus_msg_unref(m);
+
+ if (r == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ return r;
+}
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !h->field || !*h->field || \
+ !strcmp(field, h->field))
+
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ mrp_list_hook_t *p, *n;
+ handler_list_t *l;
+ handler_t *h;
+ int retried = FALSE;
+ int handled = FALSE;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("%s(path='%s', interface='%s', member='%s')...",
+ __FUNCTION__,
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) {
+ retry:
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) {
+ if (m == NULL)
+ m = create_message(msg);
+
+ if (m == NULL)
+ goto out;
+
+ h->handler(dbus, m, h->user_data);
+ handled = TRUE;
+
+ rewind_message(m);
+ }
+ }
+ }
+
+ if (!retried) {
+ if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+ retried = TRUE;
+ goto retry;
+ }
+ }
+
+ if (!handled)
+ mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ out:
+ mrp_dbus_msg_unref(m);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static int append_args_inttype(DBusMessage *msg, int type, va_list ap)
+{
+ void *vptr;
+ void **aptr;
+ int atype, alen;
+ int r = TRUE;
+
+ while (type != MRP_DBUS_TYPE_INVALID && r) {
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE:
+ case MRP_DBUS_TYPE_BOOLEAN:
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16:
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32:
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64:
+ case MRP_DBUS_TYPE_DOUBLE:
+ case MRP_DBUS_TYPE_UNIX_FD:
+ vptr = va_arg(ap, void *);
+ r = dbus_message_append_args(msg, type, vptr, DBUS_TYPE_INVALID);
+ break;
+
+ case MRP_DBUS_TYPE_STRING:
+ case MRP_DBUS_TYPE_OBJECT_PATH:
+ case MRP_DBUS_TYPE_SIGNATURE:
+ vptr = va_arg(ap, void *);
+ r = dbus_message_append_args(msg, type, &vptr, DBUS_TYPE_INVALID);
+ break;
+
+ case MRP_DBUS_TYPE_ARRAY:
+ atype = va_arg(ap, int);
+ aptr = va_arg(ap, void **);
+ alen = va_arg(ap, int);
+ r = dbus_message_append_args(msg, DBUS_TYPE_ARRAY,
+ atype, &aptr, alen, DBUS_TYPE_INVALID);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ type = va_arg(ap, int);
+ }
+
+ return r;
+}
+
+
+static void call_reply_cb(DBusPendingCall *pend, void *user_data)
+{
+ call_t *call = (call_t *)user_data;
+ DBusMessage *reply;
+ mrp_dbus_msg_t *m;
+
+ reply = dbus_pending_call_steal_reply(pend);
+ m = create_message(reply);
+
+ call->pend = NULL;
+ mrp_list_delete(&call->hook);
+
+ call->cb(call->dbus, m, call->user_data);
+
+ mrp_dbus_msg_unref(m);
+ dbus_message_unref(reply);
+ dbus_pending_call_unref(pend);
+
+ call_free(call);
+}
+
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, int type, ...)
+{
+ va_list ap;
+ int32_t id;
+ call_t *call;
+ DBusMessage *msg;
+ DBusPendingCall *pend;
+ int success;
+
+ call = NULL;
+ pend = NULL;
+
+ msg = dbus_message_new_method_call(dest, path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (cb == NULL) {
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ dbus_message_unref(msg);
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ mrp_dbus_msg_t *m)
+{
+ int32_t id;
+ call_t *call;
+ DBusPendingCall *pend;
+ DBusMessage *msg;
+ int method;
+
+ call = NULL;
+ pend = NULL;
+ msg = m->msg;
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL) {
+ if (cb != NULL)
+ goto fail;
+ else
+ method = FALSE;
+ }
+ else
+ method = TRUE;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (!dbus_message_set_destination(msg, dest))
+ goto fail;
+ if (!dbus_message_set_path(msg, path))
+ goto fail;
+ if (!dbus_message_set_interface(msg, interface))
+ goto fail;
+ if (!dbus_message_set_member(msg, member))
+ goto fail;
+
+ if (cb == NULL) {
+ if (method)
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *m)
+{
+ return dbus_connection_send(dbus->conn, m->msg, NULL);
+}
+
+
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ if (call->id == id) {
+ mrp_list_delete(p);
+
+ dbus_pending_call_cancel(call->pend);
+ dbus_pending_call_unref(call->pend);
+ call->pend = NULL;
+
+ call_free(call);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg, *rpl;
+ int success;
+
+ msg = m->msg;
+ rpl = dbus_message_new_method_return(msg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ const char *errname, const char *errmsg, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg, *rpl;
+ int success;
+
+ msg = m->msg;
+ rpl = dbus_message_new_error(msg, errname, errmsg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+ if (call != NULL)
+ mrp_free(call);
+}
+
+
+static void purge_calls(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ mrp_list_delete(&call->hook);
+
+ if (call->pend != NULL)
+ dbus_pending_call_unref(call->pend);
+
+ mrp_free(call);
+ }
+}
+
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg;
+ int success;
+
+ msg = dbus_message_new_signal(path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (dest && *dest && !dbus_message_set_destination(msg, dest))
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+
+ fail:
+ /*
+ * XXX TODO: Hmm... IIRC, libdbus unrefs messages upon failure. If it
+ * was really so, this would corrupt/crash. Check this from
+ * libdbus code.
+ */
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ return 0;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ mrp_dbus_msg_t *m;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_method_call(destination, path, interface, member);
+
+ if (msg != NULL) {
+ m = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else
+ m = NULL;
+
+ return m;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus,
+ mrp_dbus_msg_t *m)
+{
+ mrp_dbus_msg_t *mr;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_method_return(m->msg);
+
+ if (msg != NULL) {
+ mr = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else
+ mr = NULL;
+
+ return mr;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *m,
+ mrp_dbus_err_t *err)
+{
+ mrp_dbus_msg_t *me;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_error(m->msg, err->name, err->message);
+
+ if (msg != NULL) {
+ me = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else
+ me = NULL;
+
+ return me;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ mrp_dbus_msg_t *m;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_signal(path, interface, member);
+
+ if (msg != NULL) {
+ if (!destination || dbus_message_set_destination(msg, destination)) {
+ m = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else {
+ dbus_message_unref(msg);
+ m = NULL;
+ }
+ }
+ else
+ m = NULL;
+
+ return m;
+}
+
+
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *m)
+{
+ return (mrp_dbus_msg_type_t)dbus_message_get_type(m->msg);
+}
+
+#define WRAP_GETTER(type, what) \
+ type mrp_dbus_msg_##what(mrp_dbus_msg_t *m) \
+ { \
+ return dbus_message_get_##what(m->msg); \
+ } \
+ struct __mrp_dbus_allow_trailing_semicolon
+
+WRAP_GETTER(const char *, path);
+WRAP_GETTER(const char *, interface);
+WRAP_GETTER(const char *, member);
+WRAP_GETTER(const char *, destination);
+WRAP_GETTER(const char *, sender);
+
+#undef WRAP_GETTER
+
+
+static msg_iter_t *message_iterator(mrp_dbus_msg_t *m, int append)
+{
+ msg_iter_t *it;
+
+ if (mrp_list_empty(&m->iterators)) {
+ if ((it = mrp_allocz(sizeof(*it))) != NULL) {
+ mrp_list_init(&it->hook);
+ mrp_list_append(&m->iterators, &it->hook);
+
+ if (append)
+ dbus_message_iter_init_append(m->msg, &it->it);
+ else
+ dbus_message_iter_init(m->msg, &it->it);
+ }
+ }
+ else
+ it = mrp_list_entry(&m->iterators.next, typeof(*it), hook);
+
+ return it;
+}
+
+
+msg_iter_t *current_iterator(mrp_dbus_msg_t *m)
+{
+ msg_iter_t *it;
+
+ if (!mrp_list_empty(&m->iterators))
+ it = mrp_list_entry(m->iterators.prev, typeof(*it), hook);
+ else
+ it = NULL;
+
+ return it;
+}
+
+
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ msg_iter_t *it, *parent;
+
+ if ((parent = current_iterator(m)) == NULL &&
+ (parent = message_iterator(m, TRUE)) == NULL)
+ return FALSE;
+
+ if ((it = mrp_allocz(sizeof(*it))) != NULL) {
+ mrp_list_init(&it->hook);
+
+ if (dbus_message_iter_open_container(&parent->it, type, contents,
+ &it->it)) {
+ mrp_list_append(&m->iterators, &it->hook);
+
+ return TRUE;
+ }
+
+ mrp_free(it);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m)
+{
+ msg_iter_t *it, *parent;
+ int r;
+
+ it = current_iterator(m);
+
+ if (it == NULL || it == message_iterator(m, FALSE))
+ return FALSE;
+
+ mrp_list_delete(&it->hook);
+
+ if ((parent = current_iterator(m)) != NULL)
+ r = dbus_message_iter_close_container(&parent->it, &it->it);
+ else
+ r = FALSE;
+
+ mrp_free(it);
+
+ return r;
+}
+
+
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ msg_iter_t *it;
+
+ if (!dbus_type_is_basic(type))
+ return FALSE;
+
+ if ((it = current_iterator(m)) != NULL ||
+ (it = message_iterator(m, TRUE)) != NULL) {
+ if (type != MRP_DBUS_TYPE_STRING &&
+ type != MRP_DBUS_TYPE_OBJECT_PATH &&
+ type != MRP_DBUS_TYPE_SIGNATURE)
+ return dbus_message_iter_append_basic(&it->it, type, valuep);
+ else
+ return dbus_message_iter_append_basic(&it->it, type, &valuep);
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ msg_iter_t *it, *parent;
+ char *signature;
+
+ if ((parent = current_iterator(m)) == NULL &&
+ (parent = message_iterator(m, FALSE)) == NULL)
+ return FALSE;
+
+ if (dbus_message_iter_get_arg_type(&parent->it) != type)
+ return FALSE;
+
+ if ((it = mrp_allocz(sizeof(*it))) != NULL) {
+ mrp_list_init(&it->hook);
+ mrp_list_append(&m->iterators, &it->hook);
+
+ dbus_message_iter_recurse(&parent->it, &it->it);
+
+ if (contents != NULL) {
+ /* XXX TODO: proper signature checking */
+ signature = dbus_message_iter_get_signature(&it->it);
+ if (strcmp(contents, signature))
+ mrp_log_error("*** %s(): signature mismath ('%s' != '%s')",
+ __FUNCTION__, contents, signature);
+ mrp_free(signature);
+ }
+
+ dbus_message_iter_next(&parent->it);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m)
+{
+ msg_iter_t *it;
+
+ if ((it = current_iterator(m)) == NULL || it == message_iterator(m, FALSE))
+ return FALSE;
+
+ mrp_list_delete(&it->hook);
+
+ mrp_free(it->peeked);
+ mrp_free(it);
+
+ return TRUE;
+}
+
+
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ msg_iter_t *it;
+
+ if (!dbus_type_is_basic(type))
+ return FALSE;
+
+ if ((it = current_iterator(m)) != NULL ||
+ (it = message_iterator(m, FALSE)) != NULL) {
+ if (dbus_message_iter_get_arg_type(&it->it) == type) {
+ dbus_message_iter_get_basic(&it->it, valuep);
+ dbus_message_iter_next(&it->it);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void free_msg_array(msg_array_t *a)
+{
+ if (a == NULL)
+ return;
+
+ mrp_list_delete(&a->hook);
+ mrp_free(a->items);
+ mrp_free(a);
+}
+
+
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type,
+ void **itemsp, size_t *nitemp)
+{
+ msg_iter_t *it;
+ msg_array_t *a;
+ DBusMessageIter sub;
+ void *items;
+ int nitem, atype;
+
+ if (!dbus_type_is_basic(type))
+ return FALSE;
+
+ if ((it = current_iterator(m)) == NULL &&
+ (it = message_iterator(m, FALSE)) == NULL)
+ return FALSE;
+
+ if (dbus_message_iter_get_arg_type(&it->it) != DBUS_TYPE_ARRAY)
+ return FALSE;
+
+ dbus_message_iter_recurse(&it->it, &sub);
+ atype = dbus_message_iter_get_arg_type(&sub);
+
+ if (atype == MRP_DBUS_TYPE_INVALID) {
+ items = NULL;
+ nitem = 0;
+
+ goto out;
+ }
+
+ if (atype != type)
+ return FALSE;
+
+ /* for fixed types, just use the libdbus function */
+ if (type != MRP_DBUS_TYPE_STRING && type != MRP_DBUS_TYPE_OBJECT_PATH) {
+ nitem = -1;
+ items = NULL;
+ dbus_message_iter_get_fixed_array(&sub, (void *)&items, &nitem);
+
+ if (nitem == -1)
+ return FALSE;
+ }
+ /* for string-like types, collect items into an implicitly freed array */
+ else {
+ a = mrp_allocz(sizeof(*a));
+
+ if (a == NULL)
+ return FALSE;
+
+ mrp_list_init(&a->hook);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ if (mrp_reallocz(a->items, a->nitem, a->nitem + 1) != NULL) {
+ dbus_message_iter_get_basic(&sub, a->items + a->nitem);
+ a->nitem++;
+ dbus_message_iter_next(&sub);
+ }
+ else {
+ free_msg_array(a);
+ return FALSE;
+ }
+ }
+
+ mrp_list_append(&m->arrays, &a->hook);
+
+ items = a->items;
+ nitem = a->nitem;
+ }
+
+ out:
+ dbus_message_iter_next(&it->it);
+
+ *itemsp = items;
+ *nitemp = (size_t)nitem;
+
+ return TRUE;
+}
+
+
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents)
+{
+ msg_iter_t *it;
+ DBusMessageIter sub;
+ char type;
+
+ if ((it = current_iterator(m)) != NULL ||
+ (it = message_iterator(m, FALSE)) != NULL) {
+ type = dbus_message_iter_get_arg_type(&it->it);
+
+ if (dbus_type_is_container(type)) {
+ mrp_free(it->peeked);
+
+ if (contents != NULL) {
+ dbus_message_iter_recurse(&it->it, &sub);
+ it->peeked = dbus_message_iter_get_signature(&sub);
+ *contents = it->peeked;
+ }
+ }
+
+ return type;
+ }
+
+ return MRP_DBUS_TYPE_INVALID;
+}
diff --git a/src/common/dbus-libdbus.h b/src/common/dbus-libdbus.h
new file mode 100644
index 0000000..8c729d9
--- /dev/null
+++ b/src/common/dbus-libdbus.h
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_LIBDBUS_H__
+#define __MURPHY_DBUS_LIBDBUS_H__
+
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-error.h>
+#include <dbus/dbus.h>
+
+/** Type for a D-Bus (connection). */
+struct mrp_dbus_s;
+typedef struct mrp_dbus_s mrp_dbus_t;
+
+/** Type for a D-Bus message. */
+struct mrp_dbus_msg_s;
+typedef struct mrp_dbus_msg_s mrp_dbus_msg_t;
+
+/** Type for a D-Bus error. */
+typedef DBusError mrp_dbus_err_t;
+
+/** D-BUS method or signal callback type. */
+typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+/** Create a new connection to the given bus. */
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp);
+#define mrp_dbus_get mrp_dbus_connect
+
+/** Set up a DBusConnection with a mainloop. */
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn);
+
+/** Increase the reference count of the given DBus (connection). */
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus);
+
+/** Decrease the reference count of the given DBus (connection). */
+int mrp_dbus_unref(mrp_dbus_t *dbus);
+
+/** Acquire the given name on the given bus (connection). */
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Release the given name on the given bus (connection). */
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Type for a name tracking callback. */
+typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int,
+ const char *, void *);
+/** Start tracking the given name. */
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+/** Stop tracking the given name. */
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+
+/** Export a method to the bus. */
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Remove an exported method. */
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Install a filter and add a handler for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove the signal handler and filter for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Remove a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove a filter for the given message on the bus. */
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Add a signal handler for the gvien signal on the bus. */
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Remove the given signal handler for the given signal on the bus. */
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Type of a method call reply callback. */
+typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
+ void *user_data);
+
+/** Call the given method on the bus. */
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest,
+ const char *path, const char *interface,
+ const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ int dbus_type, ...);
+
+/** Cancel an ongoing method call on the bus. */
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id);
+
+/** Send a reply to the given method call on the bus. */
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, int type, ...);
+
+/** Send an error reply to the given method call on the bus. */
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
+ const char *errname, const char *errmsg,
+ int type, ...);
+
+/** Emit the given signal on the bus. */
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...);
+
+/** Send the given method call message on the bus. */
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ mrp_dbus_msg_t *msg);
+
+/** Send the given message on the bus. */
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg);
+
+/** Get our unique name on the bus. */
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus);
+
+/** Initialize the given error. */
+static inline mrp_dbus_err_t *mrp_dbus_error_init(mrp_dbus_err_t *err)
+{
+ if (err != NULL)
+ dbus_error_init(err);
+
+ return err;
+}
+
+/** Set the given error buffer up with the error name and message. */
+static inline mrp_dbus_err_t *mrp_dbus_error_set(mrp_dbus_err_t *err,
+ const char *name,
+ const char *message)
+{
+ dbus_set_error_const(err, name, message);
+
+ return err;
+}
+
+
+/** Get the error message from the given bus error message. */
+static inline const char *mrp_dbus_errmsg(mrp_dbus_err_t *err)
+{
+ if (err && dbus_error_is_set(err))
+ return err->message;
+ else
+ return "unknown DBUS error";
+}
+
+
+/** Increase the reference count of a message. */
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m);
+
+/** Decrease the reference count of a message, freeing it if necessary. */
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m);
+
+
+/** Create a new method call message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Create a new method return message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus,
+ mrp_dbus_msg_t *msg);
+
+/** Create a new error reply message. */
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *msg,
+ mrp_dbus_err_t *err);
+
+/** Create a new signal message. */
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Bus message types. */
+typedef enum {
+# define TYPE(type) MRP_DBUS_MESSAGE_TYPE_##type = DBUS_MESSAGE_TYPE_##type
+ TYPE(INVALID),
+ TYPE(METHOD_CALL),
+ TYPE(METHOD_RETURN),
+ TYPE(ERROR),
+ TYPE(SIGNAL)
+# undef TYPE
+} mrp_dbus_msg_type_t;
+
+/** Get the type of the given message. */
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *msg);
+
+/** Message type checking convenience functions. */
+#define TYPE_CHECK_FUNCTION(type, TYPE) \
+ static inline int mrp_dbus_msg_is_##type(mrp_dbus_msg_t *msg) \
+ { \
+ return mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_##TYPE; \
+ } \
+ struct __mrp_dbus_allow_traling_semicolon
+
+TYPE_CHECK_FUNCTION(method_call , METHOD_CALL);
+TYPE_CHECK_FUNCTION(method_return, METHOD_RETURN);
+TYPE_CHECK_FUNCTION(error , ERROR);
+TYPE_CHECK_FUNCTION(signal , SIGNAL);
+
+/** Message argument types. */
+typedef enum {
+#define TYPE(t) MRP_DBUS_TYPE_##t = DBUS_TYPE_##t
+ TYPE(INVALID),
+ TYPE(BYTE),
+ TYPE(BOOLEAN),
+ TYPE(INT16),
+ TYPE(UINT16),
+ TYPE(INT32),
+ TYPE(UINT32),
+ TYPE(INT64),
+ TYPE(UINT64),
+ TYPE(DOUBLE),
+ TYPE(STRING),
+ TYPE(OBJECT_PATH),
+ TYPE(SIGNATURE),
+ TYPE(UNIX_FD),
+ TYPE(ARRAY),
+ TYPE(VARIANT),
+ TYPE(STRUCT),
+ TYPE(DICT_ENTRY),
+#undef TYPE
+} mrp_dbus_type_t;
+
+/** Message argument types as strings. */
+#define MRP_DBUS_TYPE_BYTE_AS_STRING DBUS_TYPE_BYTE_AS_STRING
+#define MRP_DBUS_TYPE_BOOLEAN_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING
+#define MRP_DBUS_TYPE_INT16_AS_STRING DBUS_TYPE_INT16_AS_STRING
+#define MRP_DBUS_TYPE_UINT16_AS_STRING DBUS_TYPE_UINT16_AS_STRING
+#define MRP_DBUS_TYPE_INT32_AS_STRING DBUS_TYPE_INT32_AS_STRING
+#define MRP_DBUS_TYPE_UINT32_AS_STRING DBUS_TYPE_UINT32_AS_STRING
+#define MRP_DBUS_TYPE_INT64_AS_STRING DBUS_TYPE_INT64_AS_STRING
+#define MRP_DBUS_TYPE_UINT64_AS_STRING DBUS_TYPE_UINT64_AS_STRING
+#define MRP_DBUS_TYPE_DOUBLE_AS_STRING DBUS_TYPE_DOUBLE_AS_STRING
+#define MRP_DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+#define MRP_DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING
+#define MRP_DBUS_TYPE_SIGNATURE_AS_STRING DBUS_TYPE_SIGNATURE_AS_STRING
+#define MRP_DBUS_TYPE_UNIX_FD_AS_STRING DBUS_TYPE_UNIX_FD_AS_STRING
+#define MRP_DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_ARRAY_AS_STRING
+#define MRP_DBUS_TYPE_VARIANT_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+#define MRP_DBUS_TYPE_STRUCT_AS_STRING DBUS_TYPE_STRUCT_AS_STRING
+#define MRP_DBUS_TYPE_DICT_ENTRY_AS_STRING DBUS_TYPE_DICT_ENTRY_AS_STRING
+
+/** Get the path of the given message. */
+const char *mrp_dbus_msg_path(mrp_dbus_msg_t *msg);
+
+/** Get the interface of the given message. */
+const char *mrp_dbus_msg_interface(mrp_dbus_msg_t *msg);
+
+/** Get the member of the given message. */
+const char *mrp_dbus_msg_member(mrp_dbus_msg_t *msg);
+
+/** Get the destination of the given message. */
+const char *mrp_dbus_msg_destination(mrp_dbus_msg_t *msg);
+
+/** Get the sender of the given message. */
+const char *mrp_dbus_msg_sender(mrp_dbus_msg_t *msg);
+
+/** Open a new container of the given type and cotained types. */
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents);
+
+/** Close the current container. */
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m);
+
+/** Append an argument of a basic type to the given message. */
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Get the type of the current message argument. */
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents);
+
+/** Open the current container (of the given type and contents) for reading. */
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *msg, char type,
+ const char *contents);
+
+/** Exit from the container being currently read. */
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m);
+
+/** Read the next argument (of basic type) from the given message. */
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Read the next array of one of the basic types. */
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *msg, char type,
+ void **itemsp, size_t *nitemp);
+
+#endif /* __MURPHY_DBUS_LIBDBUS_H__ */
diff --git a/src/common/dbus-sdbus-glue.c b/src/common/dbus-sdbus-glue.c
new file mode 100644
index 0000000..a2b98c6
--- /dev/null
+++ b/src/common/dbus-sdbus-glue.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+
+#include <murphy/common/dbus-sdbus.h>
+
+#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000))
+#define MSEC_TO_USEC(msec) ((uint64_t)(msec) * 1000)
+
+typedef struct {
+ sd_bus *bus;
+ mrp_mainloop_t *ml;
+ mrp_subloop_t *sl;
+ int events;
+} bus_glue_t;
+
+
+static int bus_prepare(void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ return FALSE;
+}
+
+
+static int bus_query(void *user_data, struct pollfd *fds, int nfd, int *timeout)
+{
+ bus_glue_t *b = (bus_glue_t *)user_data;
+ uint64_t usec;
+
+ if (nfd > 0) {
+ fds[0].fd = sd_bus_get_fd(b->bus);
+ fds[0].events = sd_bus_get_events(b->bus) | POLLIN | POLLHUP;
+ fds[0].revents = 0;
+
+ if (sd_bus_get_timeout(b->bus, &usec) < 0)
+ *timeout = -1;
+ else
+ *timeout = USEC_TO_MSEC(usec);
+
+ mrp_debug("fd: %d, events: 0x%x, timeout: %u", fds[0].fd,
+ fds[0].events, *timeout);
+ }
+
+ return 1;
+}
+
+
+static int bus_check(void *user_data, struct pollfd *fds, int nfd)
+{
+ bus_glue_t *b = (bus_glue_t *)user_data;
+
+ if (nfd > 0) {
+ b->events = fds[0].revents;
+
+ if (b->events != 0)
+ return TRUE;
+ }
+ else
+ b->events = 0;
+
+ return FALSE;
+}
+
+
+static void bus_dispatch(void *user_data)
+{
+ bus_glue_t *b = (bus_glue_t *)user_data;
+
+ mrp_debug("dispatching events 0x%x to sd_bus %p", b->events, b->bus);
+
+ if (b->events & MRP_IO_EVENT_HUP)
+ mrp_debug("sd_bus peer has closed the connection");
+
+ while (sd_bus_process(b->bus, NULL) > 0)
+ sd_bus_flush(b->bus);
+
+ mrp_debug("done dispatching");
+}
+
+
+int mrp_dbus_setup_with_mainloop(mrp_mainloop_t *ml, sd_bus *bus)
+{
+ static mrp_subloop_ops_t bus_ops = {
+ .prepare = bus_prepare,
+ .query = bus_query,
+ .check = bus_check,
+ .dispatch = bus_dispatch
+ };
+
+ bus_glue_t *b;
+
+
+ if ((b = mrp_allocz(sizeof(*b))) != NULL) {
+ /* XXX TODO: Hmm... is this really needed ? */
+ while (sd_bus_process(bus, NULL) > 0)
+ sd_bus_flush(bus);
+
+ b->bus = bus;
+ b->ml = ml;
+ b->sl = mrp_add_subloop(ml, &bus_ops, b);
+
+ if (b->sl != NULL)
+ return TRUE;
+ else
+ mrp_free(b);
+ }
+
+ return FALSE;
+}
diff --git a/src/common/dbus-sdbus-transport.c b/src/common/dbus-sdbus-transport.c
new file mode 100644
index 0000000..3b62ac3
--- /dev/null
+++ b/src/common/dbus-sdbus-transport.c
@@ -0,0 +1,1782 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/dbus-sdbus.h>
+#include <murphy/common/dbus-transport.h>
+
+#define DBUS "dbus"
+#define DBUSL 4
+
+#define TRANSPORT_PATH "/murphy/transport"
+#define TRANSPORT_INTERFACE "Murphy.Transport"
+#define TRANSPORT_MESSAGE "DeliverMessage"
+#define TRANSPORT_DATA "DeliverData"
+#define TRANSPORT_RAW "DeliverRaw"
+#define TRANSPORT_METHOD "DeliverMessage"
+
+#define ANY_ADDRESS "any"
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ mrp_dbus_t *dbus; /* D-BUS connection */
+ int bound : 1; /* whether bound to an address */
+ int peer_resolved : 1; /* connected and peer name known */
+ mrp_dbusaddr_t local; /* address we're bound to */
+ mrp_dbusaddr_t remote; /* address we're connected to */
+} dbus_t;
+
+
+static uint32_t nauto; /* for autobinding */
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data);
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg);
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *msg, const char **sender_id);
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag);
+static void *data_decode(mrp_dbus_msg_t *msg, uint16_t *tag,
+ const char **sender_id);
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size);
+static void *raw_decode(mrp_dbus_msg_t *msg, size_t *sizep,
+ const char **sender_id);
+
+
+static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr,
+ socklen_t size)
+{
+ const char *p, *e;
+ char *q;
+ size_t l, n;
+
+ if (size < sizeof(*addr)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (strncmp(str, DBUS":", DBUSL + 1))
+ return 0;
+ else
+ str += DBUSL + 1;
+
+ /*
+ * The format of the address is
+ * dbus:[bus-address]@address/path
+ * eg.
+ * dbus:[session]@:1.33/client1, or
+ * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1
+ */
+
+ p = str;
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+
+ /* get bus address */
+ if (*p != '[') {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ p++;
+
+ e = strchr(p, ']');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save bus address */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_bus = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e + 1;
+
+ /* get (local or remote) address on bus */
+ if (*p != '@')
+ addr->db_addr = ANY_ADDRESS;
+ else {
+ p++;
+ e = strchr(p, '/');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save address on bus */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_addr = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e;
+ }
+
+ /* get object path */
+ if (*p != '/') {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p);
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ addr->db_path = q;
+ addr->db_family = MRP_AF_DBUS;
+
+ return sizeof(*addr);
+}
+
+
+static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src)
+{
+ char *p, *q;
+ size_t l, n;
+
+ dst->db_family = src->db_family;
+
+ /* copy bus address */
+ p = src->db_bus;
+ q = dst->db_fqa;
+ l = sizeof(dst->db_fqa);
+
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy address */
+ p = src->db_addr;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_addr = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy path */
+ p = src->db_path;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_path = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ return dst;
+}
+
+
+static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr;
+
+ return (a && a->db_family == MRP_AF_DBUS && addrlen == sizeof(*a));
+}
+
+
+static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender,
+ const char *path)
+{
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ const char *p;
+ char *q;
+ int l, n;
+
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+ p = ANY_ADDRESS;
+ n = 3;
+
+ addr->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_addr = q;
+ p = sender;
+ n = strlen(sender);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_path = q;
+ p = path;
+ n = strlen(p);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+
+ return sizeof(addrp);
+}
+
+
+static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ socklen_t len;
+
+ len = parse_address(str, (mrp_dbusaddr_t *)addr, size);
+
+ if (len > 0) {
+ if (typep != NULL)
+ *typep = DBUS;
+ }
+
+ return len;
+}
+
+
+static int dbus_open(mrp_transport_t *mt)
+{
+ MRP_UNUSED(mt);
+
+ return TRUE;
+}
+
+
+static int dbus_createfrom(mrp_transport_t *mt, void *conn)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = (mrp_dbus_t *)conn;
+
+ t->dbus = mrp_dbus_ref(dbus);
+
+ if (t->dbus != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = NULL;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+ const char *method;
+
+ if (t->bound) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (t->dbus == NULL) {
+ dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (dbus == NULL) {
+ errno = ECONNRESET;
+ goto fail;
+ }
+ else {
+ t->dbus = dbus;
+
+ if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) {
+ if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) {
+ errno = EADDRINUSE; /* XXX TODO, should check error... */
+ goto fail;
+ }
+ }
+ }
+ }
+ else {
+ /* XXX TODO: should check given address against address of the bus */
+ }
+
+ copy_address(&t->local, addr);
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ break;
+ default:
+ errno = EPROTOTYPE;
+ goto fail;
+ }
+
+ if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t)) {
+ errno = EIO;
+ goto fail;
+ }
+ else {
+ t->bound = TRUE;
+ return TRUE;
+ }
+
+ fail:
+ if (dbus != NULL) {
+ mrp_dbus_unref(dbus);
+ t->dbus = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp;
+ char astr[MRP_SOCKADDR_SIZE];
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++);
+
+ alen = dbus_resolve(astr, &addr, sizeof(addr), NULL);
+
+ if (alen > 0)
+ return dbus_bind(mt, &addr, alen);
+ else
+ return FALSE;
+}
+
+
+static void dbus_close(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr;
+ const char *method;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+ if (t->bound) {
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ default:
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ }
+
+ addr = (mrp_dbusaddr_t *)&t->local;
+ mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t);
+ }
+
+ if (t->connected && t->remote.db_addr != NULL)
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+
+ mrp_dbus_unref(t->dbus);
+ t->dbus = NULL;
+}
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ mrp_msg_t *msg;
+
+ MRP_UNUSED(dbus);
+
+ msg = msg_decode(dmsg, &sender_path);
+
+ if (msg != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsg(mt, msg, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ uint16_t tag;
+ void *decoded;
+
+ MRP_UNUSED(dbus);
+
+ decoded = data_decode(dmsg, &tag, &sender_path);
+
+ if (decoded != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode custom data message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ void *data;
+ size_t size;
+
+ MRP_UNUSED(dbus);
+
+ data = raw_decode(dmsg, &size, &sender_path);
+
+ if (data != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvraw(mt, data, size, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvrawfrom(mt, data, size, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode raw message.");
+ }
+
+ return TRUE;
+}
+
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ dbus_t *t = (dbus_t *)user_data;
+ mrp_sockaddr_t addr;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ peer_address(&addr, owner, t->remote.db_path);
+ copy_address(&t->remote, (mrp_dbusaddr_t *)&addr);
+ t->peer_resolved = TRUE;
+ }
+ else {
+ /*
+ * XXX TODO:
+ * It would be really tempting here to call
+ * mt->evt.closed(mt, ECONNRESET, mt->user_data)
+ * to notify the user about the fact our peer went down.
+ * However, that would not be in line with the other
+ * transports which call the closed event handler only
+ * upon foricble transport closes upon errors.
+ *
+ * The transport interface abstraction (especially the
+ * available set of events) anyway needs some eyeballing,
+ * so the right thing to do might be to define a new event
+ * for disconnection and call the handler for that here...
+ */
+ }
+
+}
+
+
+static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (t->dbus == NULL) {
+ t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (t->dbus == NULL) {
+ errno = ECONNRESET;
+ return FALSE;
+ }
+ }
+ else {
+ /* XXX TODO: check given address against address of the bus */
+ }
+
+ if (!t->bound)
+ if (!dbus_autobind(mt, addrp))
+ return FALSE;
+
+ if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) {
+ copy_address(&t->remote, addr);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dbus_disconnect(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+
+ if (t->connected && t->remote.db_addr != NULL) {
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+ mrp_clear(&t->remote);
+ t->peer_resolved = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = msg_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_MESSAGE,
+ t->local.db_path, msg);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendmsgto(mt, msg, addr, alen);
+}
+
+
+static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+
+ MRP_UNUSED(mt);
+ MRP_UNUSED(data);
+ MRP_UNUSED(size);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = raw_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_RAW,
+ t->local.db_path, data, size);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendrawto(mt, data, size, addr, alen);
+}
+
+
+static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = data_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_DATA,
+ t->local.db_path, data, tag);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_senddatato(mt, data, tag, addr, alen);
+}
+
+
+static const char *get_array_signature(uint16_t type)
+{
+#define MAP(from, to) \
+ case MRP_MSG_FIELD_##from: \
+ return MRP_DBUS_TYPE_##to##_AS_STRING;
+
+ switch (type) {
+ MAP(STRING, STRING);
+ MAP(BOOL , BOOLEAN);
+ MAP(UINT8 , UINT16);
+ MAP(SINT8 , INT16);
+ MAP(UINT16, UINT16);
+ MAP(SINT16, INT16);
+ MAP(UINT32, UINT32);
+ MAP(SINT32, INT32);
+ MAP(UINT64, UINT64);
+ MAP(SINT64, INT64);
+ MAP(DOUBLE, DOUBLE);
+ MAP(BLOB , BYTE);
+ default:
+ return NULL;
+ }
+}
+
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg)
+{
+ /*
+ * Notes: There is a type mismatch between our and DBUS types for
+ * 8-bit integers (D-BUS does not have a signed 8-bit type)
+ * and boolean types (D-BUS has uint32_t booleans, C99 fails
+ * to specify the type and gcc uses a signed char). The
+ * QUIRKY versions of the macros take care of these mismatches.
+ */
+
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_STRING(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = (_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_STRING(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = _val; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+ uint16_t base;
+ uint32_t asize, i;
+ const char *sig;
+ int type, len;
+ void *vptr;
+ uint32_t bln;
+ uint16_t u16, blb;
+ int16_t s16;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &msg->nfield))
+ goto fail;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ switch (f->type) {
+ BASIC_STRING(m, STRING, STRING , f->str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, f->bln, bln);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , f->u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , f->s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , f->u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , f->s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , f->u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , f->s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , f->u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , f->s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , f->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ vptr = f->blb;
+ len = (int)f->size[0];
+ sig = get_array_signature(f->type);
+ asize = len;
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ blb = ((uint8_t *)f->blb)[i];
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &blb))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ switch (base) {
+ ARRAY_STRING(m, STRING, STRING , f->astr[i]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, f->abln[i], bln);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , f->au8[i] , u16);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , f->as8[i] , s16);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , f->au16[i]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , f->as16[i]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , f->au32[i]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , f->as32[i]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , f->au64[i]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , f->adbl[i]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return FALSE;
+
+#undef BASIC_SIMPLE
+#undef BASIC_STRING
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_STRING
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *m, const char **sender_id)
+{
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ \
+ if (!mrp_msg_append(msg, tag, type, (_var))) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ if (!mrp_msg_append(msg, tag, type, (_mvar))) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+#define APPEND_ARRAY(_type, _var) \
+ case MRP_MSG_FIELD_##_type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY | \
+ MRP_MSG_FIELD_##_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ mrp_msg_t *msg;
+ mrp_msg_value_t v;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ uint16_t nfield, tag, type, base, i;
+ uint32_t n, j;
+ int asize;
+ const char *sender, *sig;
+
+ msg = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ switch (type) {
+ BASIC_SIMPLE(m, STRING, STRING , v.str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, v.bln, u32);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , v.u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , v.s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , v.u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , v.s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , v.u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , v.s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , v.u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , v.s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , v.dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ {
+ uint8_t blb[n];
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ asize = n;
+ if (!mrp_msg_append(msg, tag, type, asize, blb))
+ goto fail;
+ }
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = get_array_signature(base);
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ char *astr[n];
+ uint32_t dbln[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(m, STRING, STRING , astr[j]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, abln[j], dbln[j]);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , au8[j] , au16[j]);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , as8[j] , as16[j]);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , au16[j]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , as16[j]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , au32[j]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , as32[j]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , au64[j]);
+ ARRAY_SIMPLE(m, SINT64, INT64 , as64[j]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , adbl[j]);
+ default:
+ goto fail;
+ }
+ }
+
+ switch (base) {
+ APPEND_ARRAY(STRING, astr);
+ APPEND_ARRAY(BOOL , abln);
+ APPEND_ARRAY(UINT8 , au8 );
+ APPEND_ARRAY(SINT8 , as8 );
+ APPEND_ARRAY(UINT16, au16);
+ APPEND_ARRAY(SINT16, as16);
+ APPEND_ARRAY(UINT32, au32);
+ APPEND_ARRAY(SINT32, as32);
+ APPEND_ARRAY(UINT64, au64);
+ APPEND_ARRAY(SINT64, as64);
+ APPEND_ARRAY(DOUBLE, adbl);
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ errno = EBADMSG;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+#undef APPEND_ARRAY
+}
+
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag)
+{
+#define BASIC_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_STRING(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = _val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_STRING(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = _val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type, base;
+ mrp_msg_value_t *v;
+ void *vptr;
+ uint32_t n, j;
+ int i, blblen;
+ const char *sig;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t bln, asize;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ BASIC_STRING(STRING, STRING , v->str);
+ BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln);
+ BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16);
+ BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16);
+ BASIC_SIMPLE(UINT16, UINT16 , v->u16);
+ BASIC_SIMPLE(SINT16, INT16 , v->s16);
+ BASIC_SIMPLE(UINT32, UINT32 , v->u32);
+ BASIC_SIMPLE(SINT32, INT32 , v->s32);
+ BASIC_SIMPLE(UINT64, UINT64 , v->u64);
+ BASIC_SIMPLE(SINT64, INT64 , v->s64);
+ BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ sig = get_array_signature(f->type);
+ blblen = mrp_data_get_blob_size(data, descr, i);
+ asize = blblen;
+
+ if (blblen == -1)
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < blblen; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE,
+ f->blb + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ n = mrp_data_get_array_size(data, descr, i);
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_STRING(STRING, STRING , v->astr[j]);
+ ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln);
+ ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16);
+ ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16);
+ ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]);
+ ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]);
+ ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]);
+ ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]);
+ ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]);
+ ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+static void *data_decode(mrp_dbus_msg_t *m, uint16_t *tagp,
+ const char **sender_id)
+{
+#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+ void *data;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t tag, type, base;
+ mrp_msg_value_t *v;
+ uint32_t n, j, size;
+ int i;
+ const char *sender, *sig;
+ uint32_t u32;
+ uint16_t u16;
+ int16_t s16;
+
+ tag = 0;
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ *tagp = tag;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ if (nfield != descr->nfield)
+ goto fail;
+
+ fields = descr->fields;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (type) {
+ HANDLE_SIMPLE(&im, STRING, STRING , v->str);
+ HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32);
+ HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16);
+ HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16);
+ HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16);
+ HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16);
+ HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32);
+ HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32);
+ HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64);
+ HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64);
+ HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &size))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t blb[size];
+
+ for (j = 0; j < size; j++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+
+ v->blb = mrp_alloc(size);
+
+ if (v->blb == NULL && size != 0)
+ goto fail;
+
+ memcpy(v->blb, blb, size);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ uint32_t dbln[n];
+ uint16_t au16[n];
+ int16_t as16[n];
+
+ switch (base) {
+ HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]);
+ HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]);
+ HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]);
+ HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]);
+ HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]);
+ HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]);
+ HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]);
+ HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]);
+ HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]);
+ HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]);
+ HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]);
+ }
+
+ if (base == MRP_MSG_FIELD_STRING) {
+ v->astr[j] = mrp_strdup(v->astr[j]);
+ if (v->astr[j] == NULL)
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+
+ if (f->type == MRP_MSG_FIELD_STRING) {
+ v->str = mrp_strdup(v->str);
+ if (v->str == NULL)
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ mrp_data_free(data, tag);
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size)
+{
+ mrp_dbus_msg_t *m;
+ const char *sig;
+ uint32_t i, n;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+ n = size;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, data + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+
+ return m;
+
+ fail:
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+}
+
+
+static void *raw_decode(mrp_dbus_msg_t *m, size_t *sizep,
+ const char **sender_id)
+{
+ const char *sender, *sig;
+ void *data;
+ uint32_t n, i;
+
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t databuf[n];
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, databuf + i))
+ goto fail;
+
+ data = mrp_alloc(n);
+
+ if (data == NULL && n != 0)
+ goto fail;
+
+ memcpy(data, databuf, n);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ if (sizep != NULL)
+ *sizep = (size_t)n;
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve,
+ dbus_open, dbus_createfrom, dbus_close, NULL,
+ dbus_bind, NULL, NULL,
+ dbus_connect, dbus_disconnect,
+ dbus_sendmsg, dbus_sendmsgto,
+ dbus_sendraw, dbus_sendrawto,
+ dbus_senddata, dbus_senddatato,
+ NULL, NULL,
+ NULL, NULL);
diff --git a/src/common/dbus-sdbus.c b/src/common/dbus-sdbus.c
new file mode 100644
index 0000000..a2fb9e9
--- /dev/null
+++ b/src/common/dbus-sdbus.c
@@ -0,0 +1,1955 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-sdbus.h>
+
+#define BUS_SERVICE "org.freedesktop.DBus"
+#define BUS_PATH "/org/freedesktop/DBus"
+#define BUS_INTERFACE "org.freedesktop.DBus"
+#define BUS_NAME_CHANGED "NameOwnerChanged"
+#define BUS_GET_OWNER "GetNameOwner"
+
+/* XXX check these... */
+#define SDBUS_ERROR_FAILED "org.DBus.error.failed"
+
+/* D-Bus name request flags and reply statuses. */
+#define SDBUS_NAME_REQUEST_REPLACE 0x2
+#define SDBUS_NAME_REQUEST_DONTQ 0x4
+
+#define SDBUS_NAME_STATUS_OWNER 0x1
+#define SDBUS_NAME_STATUS_QUEUING 0x2
+#define SDBUS_NAME_STATUS_EXISTS 0x3
+#define SDBUS_NAME_STATUS_GOTIT 0x4
+
+#define SDBUS_NAME_STATUS_RELEASED 0x1
+#define SDBUS_NAME_STATUS_UNKNOWN 0x2
+#define SDBUS_NAME_STATUS_FOREIGN 0x3
+
+#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000))
+#define MSEC_TO_USEC(msec) ((uint64_t)(msec) * 1000)
+
+struct mrp_dbus_s {
+ char *address; /* bus address */
+ sd_bus *bus; /* actual D-BUS connection */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_subloop_t *sl; /* subloop for pumping the bus */
+ mrp_htbl_t *objects; /* object path (refcount) table */
+ mrp_htbl_t *methods; /* method handler table */
+ mrp_htbl_t *signals; /* signal handler table */
+ mrp_list_hook_t name_trackers; /* peer (name) watchers */
+ mrp_list_hook_t calls; /* pending calls */
+ uint32_t call_id; /* next call id */
+ const char *unique_name; /* our unique D-BUS address */
+ int priv; /* whether a private connection */
+ int signal_filter; /* if signal dispatching is set up */
+ int register_fallback; /* if the fallback object is set up */
+ mrp_refcnt_t refcnt; /* reference count */
+};
+
+
+struct mrp_dbus_msg_s {
+ sd_bus_message *msg; /* actual D-Bus message */
+ mrp_refcnt_t refcnt; /* reference count */
+ mrp_list_hook_t arrays; /* implicitly freed related arrays */
+};
+
+
+typedef struct {
+ mrp_dbus_type_t type;
+ mrp_list_hook_t hook;
+ void *items;
+ size_t nitem;
+} msg_array_t;
+
+/*
+ * Notes:
+ *
+ * At the moment we administer DBUS method and signal handlers
+ * in a very primitive way (subject to be changed later). For
+ * every bus instance we maintain two hash tables, one for methods
+ * and another for signals. Each method and signal handler is
+ * hashed in only by it's method/signal name to a linked list of
+ * method or signal handlers.
+ *
+ * When dispatching a method, we look up the chain with a matching
+ * method name, or the chain for "" in case a matching chain is
+ * not found, and invoke the handler which best matches the
+ * received message (by looking at the path, interface and name).
+ * Only one such handler is invoked at most.
+ *
+ * For signals we look up both the chain with a matching name and
+ * the chain for "" and invoke all signal handlers that match the
+ * received message (regardless of their return value).
+ */
+
+typedef struct {
+ char *path; /* object path */
+ int cnt; /* reference count */
+} object_t;
+
+typedef struct {
+ char *member; /* signal/method name */
+ mrp_list_hook_t handlers; /* handlers with matching member */
+} handler_list_t;
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *sender;
+ char *path;
+ char *interface;
+ char *member;
+ mrp_dbus_handler_t handler;
+ void *user_data;
+} handler_t;
+
+#define method_t handler_t
+#define signal_t handler_t
+
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to name tracker list */
+ char *name; /* name to track */
+ mrp_dbus_name_cb_t cb; /* status change callback */
+ void *user_data; /* opaque callback user data */
+ int32_t qid; /* initial query ID */
+} name_tracker_t;
+
+
+typedef struct {
+ mrp_dbus_t *dbus; /* DBUS connection */
+ int32_t id; /* call id */
+ mrp_dbus_reply_cb_t cb; /* completion notification callback */
+ void *user_data; /* opaque callback data */
+ uint64_t serial; /* DBUS call */
+ mrp_list_hook_t hook; /* hook to list of pending calls */
+ sd_bus_message *msg; /* original message */
+} call_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml; /* mainloop for bus connection */
+ const char *address; /* address of bus */
+} bus_spec_t;
+
+static mrp_htbl_t *buses;
+
+
+
+static int dispatch_signal(sd_bus *b, int r, sd_bus_message *msg, void *data);
+static int dispatch_method(sd_bus *b, int r, sd_bus_message *msg, void *data);
+
+static void purge_name_trackers(mrp_dbus_t *dbus);
+static void purge_calls(mrp_dbus_t *dbus);
+static void handler_list_free_cb(void *key, void *entry);
+static void handler_free(handler_t *h);
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ void *data);
+static void call_free(call_t *call);
+static void object_free_cb(void *key, void *entry);
+
+
+static int purge_objects(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ object_t *o = (object_t *)entry;
+
+ MRP_UNUSED(key);
+
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static int purge_filters(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ handler_list_t *l = (handler_list_t *)entry;
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ MRP_UNUSED(key);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_dbus_remove_filter(dbus,
+ h->sender, h->path, h->interface,
+ h->member, NULL);
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static void dbus_disconnect(mrp_dbus_t *dbus)
+{
+ if (dbus) {
+ mrp_htbl_remove(buses, dbus->bus, FALSE);
+
+ if (dbus->objects) {
+ mrp_htbl_foreach(dbus->signals, purge_objects, dbus);
+ mrp_htbl_destroy(dbus->objects, TRUE);
+ }
+
+ if (dbus->signals) {
+ mrp_htbl_foreach(dbus->signals, purge_filters, dbus);
+ mrp_htbl_destroy(dbus->signals, TRUE);
+ }
+ if (dbus->methods)
+ mrp_htbl_destroy(dbus->methods, TRUE);
+
+ purge_name_trackers(dbus);
+ purge_calls(dbus);
+
+ if (dbus->bus != NULL) {
+ if (dbus->signal_filter)
+ sd_bus_remove_filter(dbus->bus, dispatch_signal, dbus);
+ if (dbus->register_fallback)
+ sd_bus_remove_fallback(dbus->bus, "/", dispatch_method, dbus);
+ if (dbus->priv)
+ sd_bus_close(dbus->bus);
+ else
+ sd_bus_unref(dbus->bus);
+ }
+
+ mrp_free(dbus->address);
+ dbus->bus = NULL;
+ dbus->ml = NULL;
+
+ mrp_free(dbus);
+ }
+}
+
+
+static int bus_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t bus_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (ptrdiff_t)key;
+ h >>= 2 * sizeof(key);
+
+ return h;
+}
+
+
+static int find_bus_by_spec(void *key, void *object, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)object;
+ bus_spec_t *spec = (bus_spec_t *)user_data;
+
+ MRP_UNUSED(key);
+
+ if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address)
+{
+ mrp_htbl_config_t hcfg;
+ bus_spec_t spec;
+
+ if (buses == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = bus_cmp;
+ hcfg.hash = bus_hash;
+ hcfg.free = NULL;
+
+ buses = mrp_htbl_create(&hcfg);
+
+ return NULL;
+ }
+ else {
+ spec.ml = ml;
+ spec.address = address;
+
+ return mrp_htbl_find(buses, find_bus_by_spec, &spec);
+ }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp)
+{
+ mrp_htbl_config_t hcfg;
+ mrp_dbus_t *dbus;
+
+ if ((dbus = dbus_get(ml, address)) != NULL)
+ return mrp_dbus_ref(dbus);
+
+ if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL)
+ return NULL;
+
+ mrp_list_init(&dbus->calls);
+ mrp_list_init(&dbus->name_trackers);
+ mrp_refcnt_init(&dbus->refcnt);
+
+ dbus->ml = ml;
+
+ mrp_dbus_error_init(errp);
+
+ /*
+ * connect to the bus
+ */
+
+ if (!strcmp(address, "system")) {
+ if (sd_bus_open_system(&dbus->bus) != 0)
+ goto fail;
+ }
+ else if (!strcmp(address, "session")) {
+ if (sd_bus_open_user(&dbus->bus) != 0)
+ goto fail;
+ }
+ else {
+ dbus->priv = TRUE;
+
+ if (sd_bus_new(&dbus->bus) != 0)
+ goto fail;
+ else {
+ if (sd_bus_set_address(dbus->bus, address) != 0)
+ goto fail;
+
+ if (sd_bus_start(dbus->bus) != 0)
+ goto fail;
+ }
+ }
+
+ dbus->address = mrp_strdup(address);
+ if (sd_bus_get_unique_name(dbus->bus, &dbus->unique_name) != 0)
+ goto fail;
+
+ /*
+ * set up with mainloop
+ */
+
+ if (!mrp_dbus_setup_with_mainloop(ml, dbus->bus))
+ goto fail;
+
+ /*
+ * set up our message dispatchers and take our name on the bus
+ */
+
+ if (sd_bus_add_filter(dbus->bus, dispatch_signal, dbus) != 0) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to set up signal dispatching.");
+ goto fail;
+ }
+ dbus->signal_filter = TRUE;
+
+ if (sd_bus_add_fallback(dbus->bus, "/", dispatch_method, dbus) != 0) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to set up method dispatching.");
+ goto fail;
+ }
+ dbus->register_fallback = TRUE;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = object_free_cb;
+
+ if ((dbus->objects = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS object path table.");
+ goto fail;
+ }
+
+ hcfg.free = handler_list_free_cb;
+
+ if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS method table.");
+ goto fail;
+ }
+
+ if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS signal table.");
+ goto fail;
+ }
+
+
+ /*
+ * install handler for NameOwnerChanged for tracking clients/peers
+ */
+
+ if (!mrp_dbus_add_signal_handler(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name_owner_change_cb,
+ NULL)) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to install NameOwnerChanged handler.");
+ goto fail;
+ }
+
+ /* install a 'safe' filter to avoid receiving all name change signals */
+ mrp_dbus_install_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, BUS_SERVICE, NULL);
+
+ mrp_list_init(&dbus->name_trackers);
+ dbus->call_id = 1;
+
+ if (mrp_htbl_insert(buses, dbus->bus, dbus))
+ return dbus;
+
+ fail:
+ dbus_disconnect(dbus);
+ return NULL;
+}
+
+
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus)
+{
+ return mrp_ref_obj(dbus, refcnt);
+}
+
+
+int mrp_dbus_unref(mrp_dbus_t *dbus)
+{
+ if (mrp_unref_obj(dbus, refcnt)) {
+ dbus_disconnect(dbus);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int flags = SDBUS_NAME_REQUEST_REPLACE | SDBUS_NAME_REQUEST_DONTQ;
+ int status;
+
+ mrp_dbus_error_init(error);
+
+ status = sd_bus_request_name(dbus->bus, name, flags);
+
+ if (status == SDBUS_NAME_STATUS_OWNER || status == SDBUS_NAME_STATUS_GOTIT)
+ return TRUE;
+ else {
+ mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to request name");
+
+ return FALSE;
+ }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int status;
+
+ mrp_dbus_error_init(error);
+
+ status = sd_bus_release_name(dbus->bus, name);
+
+ if (status == SDBUS_NAME_STATUS_RELEASED)
+ return TRUE;
+ else {
+ mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to release name");
+
+ return FALSE;
+ }
+}
+
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus)
+{
+ return dbus->unique_name;
+}
+
+
+static void name_owner_query_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ name_tracker_t *t = (name_tracker_t *)data;
+ const char *owner;
+ int state;
+
+ if (t->cb != NULL) { /* tracker still active */
+ t->qid = 0;
+ state = !mrp_dbus_msg_is_error(m);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &owner))
+ owner = "<unknown>";
+
+ t->cb(dbus, t->name, state, owner, t->user_data);
+ }
+ else /* already requested to delete */
+ mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ const char *name, *prev, *next;
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ MRP_UNUSED(data);
+
+ if (mrp_dbus_msg_type(m) != MRP_DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &name) ||
+ !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &prev) ||
+ !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &next))
+ return FALSE;
+
+#if 0
+ /*
+ * Notes: XXX TODO
+ * In principle t->cb could call mrp_dbus_forget for some other D-BUS
+ * address than name. If that happened to be n (== p->hook.next) this
+ * would result in a crash or memory corruption in the next iteration
+ * of this loop (when handling n). We can easily get around this
+ * problem by
+ *
+ * 1. administering in mrp_dbus_t that we're handing a NameOwnerChange
+ * 2. checking for this in mrp_dbus_forget_name and if it is the case
+ * only marking the affected entry for deletion
+ * 3. removing entries marked for deletion in this loop (or just
+ * ignoring them and making another pass in the end removing any
+ * such entry).
+ */
+#endif
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (!strcmp(name, t->name))
+ t->cb(dbus, name, next && *next, next, t->user_data);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ name_tracker_t *t;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ if ((t->name = mrp_strdup(name)) != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+
+ if (mrp_dbus_install_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name,
+ NULL)) {
+ mrp_list_append(&dbus->name_trackers, &t->hook);
+
+ t->qid = mrp_dbus_call(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_GET_OWNER, 5000,
+ name_owner_query_cb, t,
+ MRP_DBUS_TYPE_STRING, t->name,
+ MRP_DBUS_TYPE_INVALID);
+ return TRUE;
+ }
+ else {
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_dbus_remove_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name,
+ NULL);
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) {
+ mrp_list_delete(&t->hook);
+ mrp_free(t->name);
+
+ if (!t->qid)
+ mrp_free(t);
+ else {
+ t->cb = NULL;
+ t->user_data = NULL;
+ t->name = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_name_trackers(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ mrp_list_delete(p);
+ mrp_dbus_remove_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, t->name,
+ NULL);
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+}
+
+
+static handler_t *handler_alloc(const char *sender, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_t *h;
+
+ if ((h = mrp_allocz(sizeof(*h))) != NULL) {
+ h->sender = mrp_strdup(sender);
+ h->path = mrp_strdup(path);
+ h->interface = mrp_strdup(interface);
+ h->member = mrp_strdup(member);
+
+ if ((path && !h->path) || !h->interface || !h->member) {
+ handler_free(h);
+ return NULL;
+ }
+
+ h->handler = handler;
+ h->user_data = user_data;
+
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static void handler_free(handler_t *h)
+{
+ if (h != NULL) {
+ mrp_free(h->sender);
+ mrp_free(h->path);
+ mrp_free(h->interface);
+ mrp_free(h->member);
+
+ mrp_free(h);
+ }
+}
+
+
+static handler_list_t *handler_list_alloc(const char *member)
+{
+ handler_list_t *l;
+
+ if ((l = mrp_allocz(sizeof(*l))) != NULL) {
+ if ((l->member = mrp_strdup(member)) != NULL)
+ mrp_list_init(&l->handlers);
+ else {
+ mrp_free(l);
+ l = NULL;
+ }
+ }
+
+ return l;
+}
+
+
+static inline void handler_list_free(handler_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_list_delete(p);
+ handler_free(h);
+ }
+
+ mrp_free(l->member);
+ mrp_free(l);
+}
+
+
+static void handler_list_free_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ handler_list_free((handler_list_t *)entry);
+}
+
+
+static inline int handler_specificity(handler_t *h)
+{
+ int score = 0;
+
+ if (h->path && *h->path)
+ score |= 0x4;
+ if (h->interface && *h->interface)
+ score |= 0x2;
+ if (h->member && *h->member)
+ score |= 0x1;
+
+ return score;
+}
+
+
+static void handler_list_insert(handler_list_t *l, handler_t *handler)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+ int score;
+
+ score = handler_specificity(handler);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (score >= handler_specificity(h)) {
+ mrp_list_append(h->hook.prev, &handler->hook); /* add before h */
+ return;
+ }
+ }
+
+ mrp_list_append(&l->handlers, &handler->hook);
+}
+
+
+static handler_t *handler_list_lookup(handler_list_t *l, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (h->handler == handler && user_data == h->user_data &&
+ path && !strcmp(path, h->path) &&
+ interface && !strcmp(interface, h->interface) &&
+ member && !strcmp(member, h->member))
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static handler_t *handler_list_find(handler_list_t *l, const char *path,
+ const char *interface, const char *member)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member))
+ return h;
+ }
+
+ return NULL;
+#undef MATCHES
+}
+
+
+static void object_free_cb(void *key, void *entry)
+{
+ object_t *o = (object_t *)entry;
+
+ MRP_UNUSED(key);
+
+ mrp_free(o->path);
+ mrp_free(o);
+}
+
+
+static object_t *object_add(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ o = mrp_alloc(sizeof(*o));
+
+ if (o != NULL) {
+ o->path = mrp_strdup(path);
+ o->cnt = 1;
+
+ if (o->path == NULL) {
+ mrp_free(o);
+ return NULL;
+ }
+
+ if (sd_bus_add_object(dbus->bus, o->path, dispatch_method, dbus) == 0) {
+ if (mrp_htbl_insert(dbus->objects, o->path, o))
+ return o;
+ else
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+ }
+
+ mrp_free(o->path);
+ mrp_free(o);
+ }
+
+ return NULL;
+}
+
+
+static object_t *object_lookup(mrp_dbus_t *dbus, const char *path)
+{
+ return mrp_htbl_lookup(dbus->objects, (void *)path);
+}
+
+
+static int object_ref(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ if ((o = object_lookup(dbus, path)) != NULL) {
+ o->cnt++;
+ return TRUE;
+ }
+
+ if (object_add(dbus, path) != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static void object_unref(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ if ((o = object_lookup(dbus, path)) != NULL) {
+ o->cnt--;
+
+ if (o->cnt <= 0) {
+ mrp_htbl_remove(dbus->objects, (void *)path, FALSE);
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+
+ mrp_free(o->path);
+ mrp_free(o);
+ }
+ }
+}
+
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if (!object_ref(dbus, path))
+ return FALSE;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+ if ((methods = handler_list_alloc(member)) == NULL)
+ goto fail;
+
+ mrp_htbl_insert(dbus->methods, methods->member, methods);
+ }
+
+ m = handler_alloc(NULL, path, interface, member, handler, user_data);
+
+ if (m != NULL) {
+ handler_list_insert(methods, m);
+
+ return TRUE;
+ }
+
+ fail:
+ object_unref(dbus, path);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL)
+ return FALSE;
+
+ m = handler_list_lookup(methods, path, interface, member,
+ handler, user_data);
+ if (m != NULL) {
+ object_unref(dbus, path);
+ mrp_list_delete(&m->hook);
+ handler_free(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) {
+ if ((signals = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) {
+ handler_list_free(signals);
+ return FALSE;
+ }
+ }
+
+ s = handler_alloc(sender, path, interface, member, handler, user_data);
+ if (s != NULL) {
+ handler_list_insert(signals, s);
+ return TRUE;
+ }
+ else {
+ handler_free(s);
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, signals->member, TRUE);
+ return FALSE;
+ }
+}
+
+
+
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ MRP_UNUSED(sender);
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL)
+ return FALSE;
+
+ s = handler_list_lookup(signals, path, interface, member,
+ handler, user_data);
+ if (s != NULL) {
+ mrp_list_delete(&s->hook);
+ handler_free(s);
+
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, (void *)member, TRUE);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int success;
+
+
+ if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data)) {
+ va_start(ap, member);
+ success = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ if (success)
+ return TRUE;
+ else
+ mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ va_start(ap, member);
+ status &= mrp_dbus_remove_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+#define ADD_TAG(tag, value) do { \
+ if (value != NULL) { \
+ l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \
+ tag, value); \
+ if (l >= n) \
+ return FALSE; \
+ n -= l; \
+ p += l; \
+ } \
+ } while (0)
+
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ if (sd_bus_add_match(dbus->bus, filter, NULL, NULL) != 0) {
+ mrp_log_error("Failed to install filter '%s'.", filter);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+}
+
+
+int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ sd_bus_remove_match(dbus->bus, filter, NULL, NULL);
+
+ return TRUE;
+#undef ADD_TAG
+}
+
+
+int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+static int element_size(mrp_dbus_type_t type)
+{
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE: return sizeof(char);
+ case MRP_DBUS_TYPE_BOOLEAN: return sizeof(uint32_t);
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16: return sizeof(uint16_t);
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32: return sizeof(uint32_t);
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64: return sizeof(uint64_t);
+ case MRP_DBUS_TYPE_DOUBLE: return sizeof(double);
+ case MRP_DBUS_TYPE_STRING: return sizeof(char *);
+ case MRP_DBUS_TYPE_OBJECT_PATH: return sizeof(char *);
+ case MRP_DBUS_TYPE_SIGNATURE: return sizeof(char *);
+ default:
+ return FALSE;
+ }
+
+}
+
+
+static inline mrp_dbus_msg_t *create_message(sd_bus_message *msg, int ref)
+{
+ mrp_dbus_msg_t *m;
+
+ if (msg != NULL) {
+ if ((m = mrp_allocz(sizeof(*m))) != NULL) {
+ mrp_refcnt_init(&m->refcnt);
+ mrp_list_init(&m->arrays);
+ if (ref)
+ m->msg = sd_bus_message_ref(msg);
+ else
+ m->msg = msg;
+ }
+
+ return m;
+ }
+ else
+ return NULL;
+}
+
+
+static void free_msg_array(msg_array_t *a)
+{
+ void *ptr;
+ size_t esize, i;
+ int string;
+
+ if (a == NULL)
+ return;
+
+ mrp_list_delete(&a->hook);
+
+ if ((esize = element_size(a->type)) != 0) {
+ if (a->type == MRP_DBUS_TYPE_STRING ||
+ a->type == MRP_DBUS_TYPE_OBJECT_PATH ||
+ a->type == MRP_DBUS_TYPE_SIGNATURE)
+ string = TRUE;
+ else
+ string = FALSE;
+
+ if (string)
+ for (i = 0, ptr = a->items; i < a->nitem; i++, ptr += esize)
+ mrp_free(ptr);
+
+ mrp_free(a->items);
+ }
+ else
+ mrp_log_error("Hmm... looks like we have a corrupted implicit array.");
+
+ mrp_free(a);
+}
+
+
+static void free_message(mrp_dbus_msg_t *m)
+{
+ mrp_list_hook_t *p, *n;
+ msg_array_t *a;
+
+ mrp_list_foreach(&m->arrays, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_msg_array(a);
+ }
+
+ mrp_free(m);
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m)
+{
+ return mrp_ref_obj(m, refcnt);
+}
+
+
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m)
+{
+ if (mrp_unref_obj(m, refcnt)) {
+ sd_bus_message_unref(m->msg);
+ free_message(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static inline int verify_type(sd_bus_message *msg, int expected_type)
+{
+ uint8_t type;
+
+ if (sd_bus_message_get_type(msg, &type) != 0 || type == expected_type)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static int dispatch_method(sd_bus *bus,int ret, sd_bus_message *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ const char *path = sd_bus_message_get_path(msg);
+ const char *interface = sd_bus_message_get_interface(msg);
+ const char *member = sd_bus_message_get_member(msg);
+ int r = FALSE;
+ handler_list_t *l;
+ handler_t *h;
+
+ MRP_UNUSED(bus);
+ MRP_UNUSED(ret);
+
+ if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_METHOD_CALL) || !member)
+ return r;
+
+ mrp_debug("path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+ retry:
+ if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+ sd_bus_message_rewind(msg, TRUE);
+
+ if (m == NULL)
+ m = create_message(msg, TRUE);
+
+ if (h->handler(dbus, m, h->user_data))
+ r = TRUE;
+
+ goto out;
+ }
+ }
+ else {
+ if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+ goto retry;
+ }
+
+ out:
+ if (!r)
+ mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ mrp_dbus_msg_unref(m);
+
+ return r;
+}
+
+
+static int dispatch_signal(sd_bus *bus,int ret, sd_bus_message *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !h->field || !*h->field || \
+ !strcmp(field, h->field))
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ const char *path = sd_bus_message_get_path(msg);
+ const char *interface = sd_bus_message_get_interface(msg);
+ const char *member = sd_bus_message_get_member(msg);
+ mrp_list_hook_t *p, *n;
+ handler_list_t *l;
+ handler_t *h;
+ int retried = FALSE;
+ int handled = FALSE;
+
+ MRP_UNUSED(bus);
+ MRP_UNUSED(ret);
+
+ if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_SIGNAL) || !member)
+ return FALSE;
+
+ mrp_debug("%s(path='%s', interface='%s', member='%s')...",
+ __FUNCTION__, SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) {
+ retry:
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) {
+ sd_bus_message_rewind(msg, TRUE);
+
+ if (m == NULL)
+ m = create_message(msg, TRUE);
+
+ h->handler(dbus, m, h->user_data);
+ handled = TRUE;
+ }
+ }
+ }
+
+ if (!retried) {
+ if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+ retried = TRUE;
+ goto retry;
+ }
+ }
+
+ if (!handled)
+ mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ mrp_dbus_msg_unref(m);
+
+ return FALSE;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static int append_args_strtype(mrp_dbus_msg_t *msg, const char *types,
+ va_list ap)
+{
+ MRP_UNUSED(msg);
+ MRP_UNUSED(types);
+ MRP_UNUSED(ap);
+
+ return FALSE;
+}
+
+
+static int append_args_inttype(sd_bus_message *msg, int type, va_list ap)
+{
+ void *vptr;
+ int atype, elen, i;
+ void **aptr;
+ int alen;
+ char stype[2] = { '\0', '\0' };
+ int r = 0;
+
+ (void)append_args_strtype;
+
+ while (type != MRP_DBUS_TYPE_INVALID) {
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE:
+ case MRP_DBUS_TYPE_BOOLEAN:
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16:
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32:
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64:
+ case MRP_DBUS_TYPE_DOUBLE:
+ case MRP_DBUS_TYPE_STRING:
+ case MRP_DBUS_TYPE_OBJECT_PATH:
+ case MRP_DBUS_TYPE_SIGNATURE:
+ case MRP_DBUS_TYPE_UNIX_FD:
+ vptr = va_arg(ap, void *);
+ r = sd_bus_message_append_basic(msg, type, vptr);
+ break;
+
+ case MRP_DBUS_TYPE_ARRAY:
+ atype = va_arg(ap, int);
+ aptr = va_arg(ap, void **);
+ alen = va_arg(ap, int);
+
+ switch (atype) {
+#define LEN(_type, _size) case MRP_DBUS_TYPE_##_type: elen = _size; break
+ LEN(BYTE , sizeof(uint8_t));
+ LEN(BOOLEAN , sizeof(uint32_t));
+ LEN(INT16 , sizeof(int16_t));
+ LEN(UINT16 , sizeof(uint16_t));
+ LEN(INT32 , sizeof(int32_t));
+ LEN(UINT32 , sizeof(uint32_t));
+ LEN(INT64 , sizeof(int64_t));
+ LEN(UINT64 , sizeof(uint64_t));
+ LEN(DOUBLE , sizeof(double));
+ LEN(STRING , sizeof(const char *));
+ LEN(OBJECT_PATH, sizeof(const char *));
+ LEN(SIGNATURE , sizeof(const char *));
+ LEN(UNIX_FD , sizeof(int));
+#undef LEN
+ default:
+ return FALSE;
+ }
+
+ stype[0] = atype;
+ if (sd_bus_message_open_container(msg, type, stype) != 0)
+ return FALSE;
+ for (i = 0; i < alen; i++, aptr += elen)
+ if (sd_bus_message_append_basic(msg, atype, aptr) != 0)
+ return FALSE;
+ if (sd_bus_message_close_container(msg) != 0)
+ return FALSE;
+ else
+ return TRUE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ type = va_arg(ap, int);
+ }
+
+ return (r == 0 ? TRUE : FALSE);
+}
+
+
+static int call_reply_cb(sd_bus *bus, int ret, sd_bus_message *msg,
+ void *user_data)
+{
+ call_t *call = (call_t *)user_data;
+ mrp_dbus_msg_t *reply = create_message(msg, TRUE);
+ sd_bus_error error;
+
+ MRP_UNUSED(bus);
+
+ call->serial = 0;
+ mrp_list_delete(&call->hook);
+
+ if (ret == 0) {
+ reply = create_message(msg, TRUE);
+ sd_bus_message_rewind(reply->msg, TRUE);
+ }
+ else {
+ sd_bus_message *err = NULL;
+
+ if (ret == ETIMEDOUT)
+ error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_TIMEOUT,
+ "D-Bus call timed out");
+ else
+ error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_FAILED,
+ "D-Bus call failed");
+
+ if (sd_bus_message_new_method_error(bus, call->msg, &error, &err) == 0)
+ reply = create_message(err, FALSE);
+ else
+ reply = NULL;
+ }
+
+ call->cb(call->dbus, reply, call->user_data);
+ call_free(call);
+ mrp_dbus_msg_unref(reply);
+
+ return TRUE;
+}
+
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, int type, ...)
+{
+ va_list ap;
+ int32_t id;
+ call_t *call;
+ sd_bus_message *msg;
+ int success;
+
+ call = NULL;
+
+ if (sd_bus_message_new_method_call(dbus->bus, dest, path, interface, member,
+ &msg) != 0)
+ return 0;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (type == MRP_DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (cb == NULL) {
+ sd_bus_message_set_no_reply(msg, TRUE);
+ if (sd_bus_send(dbus->bus, msg, NULL) != 0)
+ goto fail;
+ sd_bus_message_unref(msg);
+ }
+ else {
+ if (sd_bus_send_with_reply(dbus->bus, msg, call_reply_cb, call,
+ timeout * 1000, &call->serial) != 0)
+ goto fail;
+
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->msg = msg;
+ }
+
+ return id;
+
+ fail:
+ sd_bus_message_unref(msg);
+ call_free(call);
+
+ return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *m)
+{
+ /*bus_message_dump(m->msg);*/
+
+ if (sd_bus_send(dbus->bus, m->msg, NULL) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ if (call->id == id) {
+ mrp_list_delete(p);
+
+ sd_bus_send_with_reply_cancel(dbus->bus, call->serial);
+ call->serial = 0;
+
+ call_free(call);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *rpl;
+ int success;
+
+ if (sd_bus_message_new_method_return(dbus->bus, m->msg, &rpl) != 0)
+ return FALSE;
+
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, rpl, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ const char *errname, const char *errmsg, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *rpl;
+ int success;
+ sd_bus_error err = SD_BUS_ERROR_NULL;;
+
+ sd_bus_error_set_const(&err, errname, errmsg);
+
+ if (sd_bus_message_new_method_error(dbus->bus, m->msg, &err, &rpl) != 0)
+ return FALSE;
+
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, rpl, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+ if (call != NULL) {
+ sd_bus_message_unref(call->msg);
+ mrp_free(call);
+ }
+}
+
+
+static void purge_calls(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ mrp_list_delete(&call->hook);
+
+ if (call->serial != 0)
+ sd_bus_send_with_reply_cancel(dbus->bus, call->serial);
+
+ mrp_free(call);
+ }
+}
+
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *msg;
+ int success;
+
+ if (sd_bus_message_new_signal(dbus->bus, path, interface, member,
+ &msg) != 0)
+ return 0;
+
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (dest != NULL)
+ if (sd_bus_message_set_destination(msg, dest) != 0)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, msg, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(msg);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(msg);
+
+ return 0;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *dbus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ sd_bus_message *msg;
+
+ if (sd_bus_message_new_method_call(dbus->bus, destination,
+ path, interface, member, &msg) == 0)
+ return create_message(msg, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *dbus,
+ mrp_dbus_msg_t *msg)
+{
+ sd_bus_message *req, *rpl;
+
+ req = (sd_bus_message *)msg;
+
+ if (sd_bus_message_new_method_return(dbus->bus, req, &rpl) == 0)
+ return create_message(rpl, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ mrp_dbus_err_t *err)
+{
+ sd_bus_message *req, *rpl;
+
+ req = m->msg;
+
+ if (sd_bus_message_new_method_error(dbus->bus, req, err, &rpl) == 0)
+ return create_message(rpl, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *dbus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ sd_bus_message *msg = NULL;
+
+ if (sd_bus_message_new_signal(dbus->bus, path, interface, member,
+ &msg) == 0) {
+ if (destination != NULL) {
+ if (sd_bus_message_set_destination(msg, destination) != 0) {
+ sd_bus_message_unref(msg);
+ msg = NULL;
+ }
+ }
+ }
+
+ return create_message(msg, FALSE);
+}
+
+
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *m)
+{
+ uint8_t type;
+
+ if (sd_bus_message_get_type(m->msg, &type) == 0)
+ return (mrp_dbus_msg_type_t)type;
+ else
+ return MRP_DBUS_MESSAGE_TYPE_INVALID;
+}
+
+#define WRAP_GETTER(type, what) \
+ type mrp_dbus_msg_##what(mrp_dbus_msg_t *m) \
+ { \
+ return sd_bus_message_get_##what((sd_bus_message *)m->msg); \
+ } \
+ struct __mrp_dbus_allow_trailing_semicolon
+
+WRAP_GETTER(const char *, path);
+WRAP_GETTER(const char *, interface);
+WRAP_GETTER(const char *, member);
+WRAP_GETTER(const char *, destination);
+WRAP_GETTER(const char *, sender);
+
+#undef WRAP_GETTER
+
+
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ return sd_bus_message_open_container(m->msg, type, contents) == 0;
+}
+
+
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m)
+{
+ return sd_bus_message_close_container(m->msg) == 0;
+}
+
+
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ return sd_bus_message_append_basic(m->msg, type, valuep) == 0;
+}
+
+
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ return sd_bus_message_enter_container(m->msg, type, contents) == 1;
+}
+
+
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m)
+{
+ return sd_bus_message_exit_container(m->msg) == 1;
+}
+
+
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ return sd_bus_message_read_basic(m->msg, type, valuep) == 1;
+}
+
+
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type,
+ void **itemsp, size_t *nitemp)
+{
+ char sub[2] = { (char)type, '\0' };
+ msg_array_t *a;
+ int offs;
+ size_t esize;
+
+ if ((esize = element_size(type)) == 0)
+ return FALSE;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sub))
+ return FALSE;
+
+ if ((a = mrp_allocz(sizeof(*a))) == NULL)
+ goto fail;
+
+ a->type = type;
+ mrp_list_init(&a->hook);
+
+ offs = 0;
+ while (mrp_dbus_msg_arg_type(m, NULL) != MRP_DBUS_TYPE_INVALID) {
+ if (!mrp_realloc(a->items, offs + esize))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, type, a->items + offs))
+ goto fail;
+ else
+ a->nitem++;
+
+ offs += esize;
+ }
+
+ mrp_dbus_msg_exit_container(m);
+
+ mrp_list_append(&m->arrays, &a->hook);
+ *itemsp = a->items;
+ *nitemp = a->nitem;
+
+ return TRUE;
+
+ fail:
+ mrp_dbus_msg_exit_container(m);
+ free_msg_array(a);
+
+ return FALSE;
+}
+
+
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents)
+{
+ char type;
+
+ if (sd_bus_message_peek_type(m->msg, &type, contents) >= 0)
+ return (mrp_dbus_type_t)type;
+ else
+ return MRP_DBUS_TYPE_INVALID;
+}
diff --git a/src/common/dbus-sdbus.h b/src/common/dbus-sdbus.h
new file mode 100644
index 0000000..982b0f6
--- /dev/null
+++ b/src/common/dbus-sdbus.h
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SD_BUS_H__
+#define __MURPHY_SD_BUS_H__
+
+#include <systemd/sd-bus.h>
+#include <systemd/sd-bus-protocol.h>
+
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-error.h>
+
+/** Type for a D-Bus (connection). */
+struct mrp_dbus_s;
+typedef struct mrp_dbus_s mrp_dbus_t;
+
+/** Type for a D-Bus message. */
+typedef struct mrp_dbus_msg_s mrp_dbus_msg_t;
+
+/** Type for a D-Bus error. */
+typedef sd_bus_error mrp_dbus_err_t;
+
+/** D-BUS method or signal callback type. */
+typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+/** Create a new connection to the given bus. */
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp);
+#define mrp_dbus_get mrp_dbus_connect
+
+/** Set up an sd-bus instance with a mainloop. */
+int mrp_dbus_setup_sd_bus(mrp_mainloop_t *ml, sd_bus *bus);
+
+/** Increase the reference count of the given DBus (connection). */
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus);
+
+/** Decrease the reference count of the given DBus (connection). */
+int mrp_dbus_unref(mrp_dbus_t *dbus);
+
+/** Acquire the given name on the given bus (connection). */
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Release the given name on the given bus (connection). */
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Type for a name tracking callback. */
+typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int,
+ const char *, void *);
+/** Start tracking the given name. */
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+/** Stop tracking the given name. */
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+
+/** Export a method to the bus. */
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Remove an exported method. */
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Install a filter and add a handler for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove the signal handler and filter for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Remove a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove a filter for the given message on the bus. */
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Add a signal handler for the gvien signal on the bus. */
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Remove the given signal handler for the given signal on the bus. */
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Type of a method call reply callback. */
+typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
+ void *user_data);
+
+/** Call the given method on the bus. */
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest,
+ const char *path, const char *interface,
+ const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ int dbus_type, ...);
+
+/** Cancel an ongoing method call on the bus. */
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id);
+
+/** Send a reply to the given method call on the bus. */
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, int type, ...);
+
+/** Send an error reply to the given method call on the bus. */
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
+ const char *errname, const char *errmsg,
+ int type, ...);
+
+/** Emit the given signal on the bus. */
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...);
+
+/** Send the given message on the bus. */
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg);
+
+/** Get our unique name on the bus. */
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus);
+
+/** Initialize the given error. */
+static inline mrp_dbus_err_t *mrp_dbus_error_init(mrp_dbus_err_t *err)
+{
+ if (err != NULL)
+ memset(err, 0, sizeof(*err));
+
+ return err;
+}
+
+
+/** Set the given error buffer up with the error name and message. */
+static inline mrp_dbus_err_t *mrp_dbus_error_set(mrp_dbus_err_t *err,
+ const char *name,
+ const char *message)
+{
+ sd_bus_error_set(err, name, "%s", message);
+
+ return err;
+}
+
+
+/** Get the error message from the given bus error message. */
+static inline const char *mrp_dbus_errmsg(mrp_dbus_err_t *err)
+{
+ if (err && sd_bus_error_is_set(err))
+ return err->message;
+ else
+ return "unknown DBUS error";
+}
+
+
+/** Increase the reference count of a message. */
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m);
+
+/** Decrease the reference count of a message, freeing it if necessary. */
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m);
+
+
+/** Create a new method call message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Create a new method return message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus,
+ mrp_dbus_msg_t *msg);
+
+/** Create a new error reply message. */
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *msg,
+ mrp_dbus_err_t *err);
+
+/** Create a new signal message. */
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Bus message types. */
+typedef enum {
+#ifndef SD_BUS_MESSAGE_TYPE_INVALID
+# define SD_BUS_MESSAGE_TYPE_INVALID _SD_BUS_MESSAGE_TYPE_INVALID
+#endif
+# define MAP(t, f) MRP_DBUS_MESSAGE_TYPE_##t = SD_BUS_MESSAGE_TYPE_##f
+ MAP(INVALID , INVALID),
+ MAP(METHOD_CALL , METHOD_CALL),
+ MAP(METHOD_RETURN, METHOD_RETURN),
+ MAP(ERROR , METHOD_ERROR),
+ MAP(SIGNAL , SIGNAL)
+# undef MAP
+} mrp_dbus_msg_type_t;
+
+/** Get the type of the given message. */
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *msg);
+
+/** Message type checking convenience functions. */
+#define TYPE_CHECK_FUNCTION(type, TYPE) \
+ static inline int mrp_dbus_msg_is_##type(mrp_dbus_msg_t *msg) \
+ { \
+ return mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_##TYPE; \
+ } \
+ struct __mrp_dbus_allow_traling_semicolon
+
+TYPE_CHECK_FUNCTION(method_call , METHOD_CALL);
+TYPE_CHECK_FUNCTION(method_return, METHOD_RETURN);
+TYPE_CHECK_FUNCTION(error , ERROR);
+TYPE_CHECK_FUNCTION(signal , SIGNAL);
+
+/** Message argument types. */
+typedef enum {
+#ifndef SD_BUS_TYPE_INVALID
+# define SD_BUS_TYPE_INVALID _SD_BUS_TYPE_INVALID
+#endif
+#define TYPE(t) MRP_DBUS_TYPE_##t = SD_BUS_TYPE_##t
+ TYPE(INVALID),
+ TYPE(BYTE),
+ TYPE(BOOLEAN),
+ TYPE(INT16),
+ TYPE(UINT16),
+ TYPE(INT32),
+ TYPE(UINT32),
+ TYPE(INT64),
+ TYPE(UINT64),
+ TYPE(DOUBLE),
+ TYPE(STRING),
+ TYPE(OBJECT_PATH),
+ TYPE(SIGNATURE),
+ TYPE(UNIX_FD),
+ TYPE(ARRAY),
+ TYPE(VARIANT),
+ TYPE(STRUCT),
+ TYPE(DICT_ENTRY),
+ TYPE(STRUCT_BEGIN),
+ TYPE(STRUCT_END),
+ TYPE(DICT_ENTRY_BEGIN),
+ TYPE(DICT_ENTRY_END)
+#undef TYPE
+} mrp_dbus_type_t;
+
+/** Message argument types as strings. */
+static const char _type_as_string[][2] = {
+#define MAP(_type) [SD_BUS_TYPE_##_type] = { SD_BUS_TYPE_##_type, '\0' }
+ MAP(BYTE),
+ MAP(BOOLEAN),
+ MAP(INT16),
+ MAP(UINT16),
+ MAP(INT32),
+ MAP(UINT32),
+ MAP(INT64),
+ MAP(UINT64),
+ MAP(DOUBLE),
+ MAP(STRING),
+ MAP(OBJECT_PATH),
+ MAP(SIGNATURE),
+ MAP(UNIX_FD),
+ MAP(ARRAY),
+ MAP(VARIANT),
+ MAP(STRUCT),
+ MAP(DICT_ENTRY),
+ MAP(STRUCT_BEGIN),
+ MAP(STRUCT_END),
+ MAP(DICT_ENTRY_BEGIN),
+ MAP(DICT_ENTRY_END)
+#undef MAP
+};
+
+#define _STRTYPE(_type) _type_as_string[SD_BUS_TYPE_##_type]
+#define _EVAL(_type) _type
+#define MRP_DBUS_TYPE_BYTE_AS_STRING _EVAL(_STRTYPE(BYTE))
+#define MRP_DBUS_TYPE_BOOLEAN_AS_STRING _EVAL(_STRTYPE(BOOLEAN))
+#define MRP_DBUS_TYPE_INT16_AS_STRING _EVAL(_STRTYPE(INT16))
+#define MRP_DBUS_TYPE_UINT16_AS_STRING _EVAL(_STRTYPE(UINT16))
+#define MRP_DBUS_TYPE_INT32_AS_STRING _EVAL(_STRTYPE(INT32))
+#define MRP_DBUS_TYPE_UINT32_AS_STRING _EVAL(_STRTYPE(UINT32))
+#define MRP_DBUS_TYPE_INT64_AS_STRING _EVAL(_STRTYPE(INT64))
+#define MRP_DBUS_TYPE_UINT64_AS_STRING _EVAL(_STRTYPE(UINT64))
+#define MRP_DBUS_TYPE_DOUBLE_AS_STRING _EVAL(_STRTYPE(DOUBLE))
+#define MRP_DBUS_TYPE_STRING_AS_STRING _EVAL(_STRTYPE(STRING))
+#define MRP_DBUS_TYPE_OBJECT_PATH_AS_STRING _EVAL(_STRTYPE(OBJECT_PATH))
+#define MRP_DBUS_TYPE_SIGNATURE_AS_STRING _EVAL(_STRTYPE(SIGNATURE))
+#define MRP_DBUS_TYPE_UNIX_FD_AS_STRING _EVAL(_STRTYPE(UNIX_FD))
+#define MRP_DBUS_TYPE_ARRAY_AS_STRING _EVAL(_STRTYPE(ARRAY))
+#define MRP_DBUS_TYPE_VARIANT_AS_STRING _EVAL(_STRTYPE(VARIANT))
+#define MRP_DBUS_TYPE_STRUCT_AS_STRING _EVAL(_STRTYPE(STRUCT))
+#define MRP_DBUS_TYPE_DICT_ENTRY_AS_STRING _EVAL(_STRTYPE(DICT_ENTRY))
+
+/** Get the path of the given message. */
+const char *mrp_dbus_msg_path(mrp_dbus_msg_t *msg);
+
+/** Get the interface of the given message. */
+const char *mrp_dbus_msg_interface(mrp_dbus_msg_t *msg);
+
+/** Get the member of the given message. */
+const char *mrp_dbus_msg_member(mrp_dbus_msg_t *msg);
+
+/** Get the destination of the given message. */
+const char *mrp_dbus_msg_destination(mrp_dbus_msg_t *msg);
+
+/** Get the sender of the given message. */
+const char *mrp_dbus_msg_sender(mrp_dbus_msg_t *msg);
+
+/** Open a new container of the given type and cotained types. */
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents);
+
+/** Close the current container. */
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m);
+
+/** Append an argument of a basic type to the given message. */
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Get the type of the current message argument. */
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents);
+
+/** Open the current container (of the given type and contents) for reading. */
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *msg, char type,
+ const char *contents);
+
+/** Exit from the container being currently read. */
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m);
+
+/** Read the next argument (of basic type) from the given message. */
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Read the next array of one of the basic types. */
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type,
+ void **itemsp, size_t *nitemp);
+
+/** Set up an sd_bus to be pumped by a murphy mainloop. */
+int mrp_dbus_setup_with_mainloop(mrp_mainloop_t *ml, sd_bus *bus);
+#endif /* __MURPHY_SD_BUS_H__ */
diff --git a/src/common/dbus-transport.c b/src/common/dbus-transport.c
new file mode 100644
index 0000000..dda1c8e
--- /dev/null
+++ b/src/common/dbus-transport.c
@@ -0,0 +1,1721 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/libdbus.h>
+#include <murphy/common/dbus-transport.h>
+
+#define DBUS "dbus"
+#define DBUSL 4
+
+#define TRANSPORT_PATH "/murphy/transport"
+#define TRANSPORT_INTERFACE "Murphy.Transport"
+#define TRANSPORT_MESSAGE "DeliverMessage"
+#define TRANSPORT_DATA "DeliverData"
+#define TRANSPORT_RAW "DeliverRaw"
+#define TRANSPORT_METHOD "DeliverMessage"
+
+#define ANY_ADDRESS "any"
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ mrp_dbus_t *dbus; /* D-BUS connection */
+ int bound : 1; /* whether bound to an address */
+ int peer_resolved : 1; /* connected and peer name known */
+ mrp_dbusaddr_t local; /* address we're bound to */
+ mrp_dbusaddr_t remote; /* address we're connected to */
+} dbus_t;
+
+
+static uint32_t nauto; /* for autobinding */
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+static int dbus_data_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+static int dbus_raw_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data);
+
+static DBusMessage *msg_encode(const char *sender_id, mrp_msg_t *msg);
+static mrp_msg_t *msg_decode(DBusMessage *m, const char **sender_id);
+
+static DBusMessage *data_encode(const char *sender_id,
+ void *data, uint16_t tag);
+static void *data_decode(DBusMessage *m, uint16_t *tag, const char **sender_id);
+
+static DBusMessage *raw_encode(const char *sender_id, void *data, size_t size);
+static void *raw_decode(DBusMessage *m, size_t *sizep, const char **sender_id);
+
+
+
+static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr,
+ socklen_t size)
+{
+ const char *p, *e;
+ char *q;
+ size_t l, n;
+
+ if (size < sizeof(*addr)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (strncmp(str, DBUS":", DBUSL + 1))
+ return 0;
+ else
+ str += DBUSL + 1;
+
+ /*
+ * The format of the address is
+ * dbus:[bus-address]@address/path
+ * eg.
+ * dbus:[session]@:1.33/client1, or
+ * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1
+ */
+
+ p = str;
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+
+ /* get bus address */
+ if (*p != '[') {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ p++;
+
+ e = strchr(p, ']');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save bus address */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_bus = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e + 1;
+
+ /* get (local or remote) address on bus */
+ if (*p != '@')
+ addr->db_addr = ANY_ADDRESS;
+ else {
+ p++;
+ e = strchr(p, '/');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save address on bus */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_addr = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e;
+ }
+
+ /* get object path */
+ if (*p != '/') {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p);
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ addr->db_path = q;
+ addr->db_family = MRP_AF_DBUS;
+
+ return sizeof(*addr);
+}
+
+
+static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src)
+{
+ char *p, *q;
+ size_t l, n;
+
+ dst->db_family = src->db_family;
+
+ /* copy bus address */
+ p = src->db_bus;
+ q = dst->db_fqa;
+ l = sizeof(dst->db_fqa);
+
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy address */
+ p = src->db_addr;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_addr = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy path */
+ p = src->db_path;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_path = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ return dst;
+}
+
+
+static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr;
+
+ return (a && a->db_family == MRP_AF_DBUS && addrlen == sizeof(*a));
+}
+
+
+static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender,
+ const char *path)
+{
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ const char *p;
+ char *q;
+ int l, n;
+
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+ p = ANY_ADDRESS;
+ n = 3;
+
+ addr->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_addr = q;
+ p = sender;
+ n = strlen(sender);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_path = q;
+ p = path;
+ n = strlen(p);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+
+ return sizeof(addrp);
+}
+
+
+static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ socklen_t len;
+
+ len = parse_address(str, (mrp_dbusaddr_t *)addr, size);
+
+ if (len > 0) {
+ if (typep != NULL)
+ *typep = DBUS;
+ }
+
+ return len;
+}
+
+
+static int dbus_open(mrp_transport_t *mt)
+{
+ MRP_UNUSED(mt);
+
+ return TRUE;
+}
+
+
+static int dbus_createfrom(mrp_transport_t *mt, void *conn)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = (mrp_dbus_t *)conn;
+
+ t->dbus = mrp_dbus_ref(dbus);
+
+ if (t->dbus != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = NULL;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ int (*cb)(mrp_dbus_t *, DBusMessage *, void *);
+ const char *method;
+
+ if (t->bound) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (t->dbus == NULL) {
+ dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (dbus == NULL) {
+ errno = ECONNRESET;
+ goto fail;
+ }
+ else {
+ t->dbus = dbus;
+
+ if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) {
+ if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) {
+ errno = EADDRINUSE; /* XXX TODO, should check error... */
+ goto fail;
+ }
+ }
+ }
+ }
+ else {
+ /* XXX TODO: should check given address against address of the bus */
+ }
+
+ copy_address(&t->local, addr);
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ break;
+ default:
+ errno = EPROTOTYPE;
+ goto fail;
+ }
+
+ if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t)) {
+ errno = EIO;
+ goto fail;
+ }
+ else {
+ t->bound = TRUE;
+ return TRUE;
+ }
+
+ fail:
+ if (dbus != NULL) {
+ mrp_dbus_unref(dbus);
+ t->dbus = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp;
+ char astr[MRP_SOCKADDR_SIZE];
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++);
+
+ alen = dbus_resolve(astr, &addr, sizeof(addr), NULL);
+
+ if (alen > 0)
+ return dbus_bind(mt, &addr, alen);
+ else
+ return FALSE;
+}
+
+
+static void dbus_close(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr;
+ const char *method;
+ int (*cb)(mrp_dbus_t *, DBusMessage *, void *);
+
+ if (t->bound) {
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ default:
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ }
+
+ addr = (mrp_dbusaddr_t *)&t->local;
+ mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t);
+ }
+
+ if (t->connected && t->remote.db_addr != NULL)
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+
+ mrp_dbus_unref(t->dbus);
+ t->dbus = NULL;
+}
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ mrp_msg_t *msg;
+
+ MRP_UNUSED(dbus);
+
+ msg = msg_decode(dmsg, &sender_path);
+
+ if (msg != NULL) {
+ sender = dbus_message_get_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsg(mt, msg, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_data_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ uint16_t tag;
+ void *decoded;
+
+ MRP_UNUSED(dbus);
+
+ decoded = data_decode(dmsg, &tag, &sender_path);
+
+ if (decoded != NULL) {
+ sender = dbus_message_get_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode custom data message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_raw_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ void *data;
+ size_t size;
+
+ MRP_UNUSED(dbus);
+
+ data = raw_decode(dmsg, &size, &sender_path);
+
+ if (data != NULL) {
+ sender = dbus_message_get_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvraw(mt, data, size, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvrawfrom(mt, data, size, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode raw message.");
+ }
+
+ return TRUE;
+}
+
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ dbus_t *t = (dbus_t *)user_data;
+ mrp_sockaddr_t addr;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ peer_address(&addr, owner, t->remote.db_path);
+ copy_address(&t->remote, (mrp_dbusaddr_t *)&addr);
+ t->peer_resolved = TRUE;
+ }
+ else {
+ /*
+ * XXX TODO:
+ * It would be really tempting here to call
+ * mt->evt.closed(mt, ECONNRESET, mt->user_data)
+ * to notify the user about the fact our peer went down.
+ * However, that would not be in line with the other
+ * transports which call the closed event handler only
+ * upon foricble transport closes upon errors.
+ *
+ * The transport interface abstraction (especially the
+ * available set of events) anyway needs some eyeballing,
+ * so the right thing to do might be to define a new event
+ * for disconnection and call the handler for that here...
+ */
+ }
+
+}
+
+
+static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (t->dbus == NULL) {
+ t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (t->dbus == NULL) {
+ errno = ECONNRESET;
+ return FALSE;
+ }
+ }
+ else {
+ /* XXX TODO: check given address against address of the bus */
+ }
+
+ if (!t->bound)
+ if (!dbus_autobind(mt, addrp))
+ return FALSE;
+
+ if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) {
+ copy_address(&t->remote, addr);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dbus_disconnect(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+
+ if (t->connected && t->remote.db_addr != NULL) {
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+ mrp_clear(&t->remote);
+ t->peer_resolved = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ DBusMessage *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = msg_encode(t->local.db_path, msg);
+
+ if (m != NULL) {
+ if (mrp_dbus_send(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_MESSAGE,
+ 0, NULL, NULL, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ dbus_message_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendmsgto(mt, msg, addr, alen);
+}
+
+
+static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ DBusMessage *m;
+ int success;
+
+
+ MRP_UNUSED(mt);
+ MRP_UNUSED(data);
+ MRP_UNUSED(size);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = raw_encode(t->local.db_path, data, size);
+
+ if (m != NULL) {
+ if (mrp_dbus_send(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_RAW,
+ 0, NULL, NULL, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ dbus_message_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendrawto(mt, data, size, addr, alen);
+}
+
+
+static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ DBusMessage *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = data_encode(t->local.db_path, data, tag);
+
+ if (m != NULL) {
+ if (mrp_dbus_send(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_DATA,
+ 0, NULL, NULL, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ dbus_message_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_senddatato(mt, data, tag, addr, alen);
+}
+
+
+static const char *get_array_signature(uint16_t type)
+{
+#define MAP(from, to) \
+ case MRP_MSG_FIELD_##from: \
+ return DBUS_TYPE_##to##_AS_STRING;
+
+ switch (type) {
+ MAP(STRING, STRING);
+ MAP(BOOL , BOOLEAN);
+ MAP(UINT8 , UINT16);
+ MAP(SINT8 , INT16);
+ MAP(UINT16, UINT16);
+ MAP(SINT16, INT16);
+ MAP(UINT32, UINT32);
+ MAP(SINT32, INT32);
+ MAP(UINT64, UINT64);
+ MAP(SINT64, INT64);
+ MAP(DOUBLE, DOUBLE);
+ MAP(BLOB , BYTE );
+ default:
+ return NULL;
+ }
+}
+
+
+static DBusMessage *msg_encode(const char *sender_id, mrp_msg_t *msg)
+{
+ /*
+ * Notes: There is a type mismatch between our and DBUS types for
+ * 8-bit integers (D-BUS does not have a signed 8-bit type)
+ * and boolean types (D-BUS has uint32_t booleans, C99 fails
+ * to specify the type and gcc uses a signed char). The
+ * QUIRKY versions of the macros take care of these mismatches.
+ */
+
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+ DBusMessage *m;
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+ uint16_t base;
+ uint32_t asize, i;
+ DBusMessageIter im, ia;
+ const char *sig;
+ int type, len;
+ void *vptr;
+ dbus_bool_t bln;
+ uint16_t u16;
+ int16_t s16;
+
+ m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+ if (m == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(m, &im);
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_OBJECT_PATH, &sender_id))
+ goto fail;
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &msg->nfield))
+ goto fail;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->tag) ||
+ !dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ switch (f->type) {
+ BASIC_SIMPLE(&im, STRING, STRING , f->str);
+ BASIC_QUIRKY(&im, BOOL , BOOLEAN, f->bln, bln);
+ BASIC_QUIRKY(&im, UINT8 , UINT16 , f->u8 , u16);
+ BASIC_QUIRKY(&im, SINT8 , INT16 , f->s8 , s16);
+ BASIC_SIMPLE(&im, UINT16, UINT16 , f->u16);
+ BASIC_SIMPLE(&im, SINT16, INT16 , f->s16);
+ BASIC_SIMPLE(&im, UINT32, UINT32 , f->u32);
+ BASIC_SIMPLE(&im, SINT32, INT32 , f->s32);
+ BASIC_SIMPLE(&im, UINT64, UINT64 , f->u64);
+ BASIC_SIMPLE(&im, SINT64, INT64 , f->s64);
+ BASIC_SIMPLE(&im, DOUBLE, DOUBLE , f->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ vptr = f->blb;
+ len = (int)f->size[0];
+ sig = get_array_signature(f->type);
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia) ||
+ !dbus_message_iter_append_fixed_array(&ia, sig[0],
+ &vptr, len) ||
+ !dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ sig = get_array_signature(base);
+
+ if (!dbus_message_iter_append_basic(&im,
+ DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ switch (base) {
+ ARRAY_SIMPLE(&ia, STRING, STRING , f->astr[i]);
+ ARRAY_QUIRKY(&ia, BOOL , BOOLEAN, f->abln[i], bln);
+ ARRAY_QUIRKY(&ia, UINT8 , UINT16 , f->au8[i] , u16);
+ ARRAY_QUIRKY(&ia, SINT8 , INT16 , f->as8[i] , s16);
+ ARRAY_SIMPLE(&ia, UINT16, UINT16 , f->au16[i]);
+ ARRAY_SIMPLE(&ia, SINT16, INT16 , f->as16[i]);
+ ARRAY_SIMPLE(&ia, UINT32, UINT32 , f->au32[i]);
+ ARRAY_SIMPLE(&ia, SINT32, INT32 , f->as32[i]);
+ ARRAY_SIMPLE(&ia, UINT64, UINT64 , f->au64[i]);
+ ARRAY_SIMPLE(&ia, DOUBLE, DOUBLE , f->adbl[i]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ dbus_message_unref(m);
+
+ errno = ECOMM;
+
+ return FALSE;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_msg_t *msg_decode(DBusMessage *m, const char **sender_id)
+{
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_var)); \
+ dbus_message_iter_next(_i); \
+ \
+ if (!mrp_msg_append(msg, tag, type, (_var))) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_dvar)); \
+ dbus_message_iter_next(_i); \
+ \
+ _mvar = _dvar; \
+ if (!mrp_msg_append(msg, tag, type, (_mvar))) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_var)); \
+ dbus_message_iter_next(_i); \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_dvar)); \
+ dbus_message_iter_next(_i); \
+ \
+ _mvar = _dvar; \
+ break
+
+#define APPEND_ARRAY(_type, _var) \
+ case MRP_MSG_FIELD_##_type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY | \
+ MRP_MSG_FIELD_##_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ mrp_msg_t *msg;
+ mrp_msg_value_t v;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ DBusMessageIter im, ia;
+ uint16_t nfield, tag, type, base, i;
+ uint32_t n, j;
+ int asize;
+ const char *sender;
+
+ msg = NULL;
+
+ if (!dbus_message_iter_init(m, &im))
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &sender);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &nfield);
+ dbus_message_iter_next(&im);
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &tag);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &type);
+ dbus_message_iter_next(&im);
+
+ switch (type) {
+ BASIC_SIMPLE(&im, STRING, STRING , v.str);
+ BASIC_QUIRKY(&im, BOOL , BOOLEAN, v.bln, u32);
+ BASIC_QUIRKY(&im, UINT8 , UINT16 , v.u8 , u16);
+ BASIC_QUIRKY(&im, SINT8 , INT16 , v.s8 , s16);
+ BASIC_SIMPLE(&im, UINT16, UINT16 , v.u16);
+ BASIC_SIMPLE(&im, SINT16, INT16 , v.s16);
+ BASIC_SIMPLE(&im, UINT32, UINT32 , v.u32);
+ BASIC_SIMPLE(&im, SINT32, INT32 , v.s32);
+ BASIC_SIMPLE(&im, UINT64, UINT64 , v.u64);
+ BASIC_SIMPLE(&im, SINT64, INT64 , v.s64);
+ BASIC_SIMPLE(&im, DOUBLE, DOUBLE , v.dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (dbus_message_iter_get_element_type(&im) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_get_fixed_array(&ia, &v.blb, &asize);
+ dbus_message_iter_next(&im);
+ if (!mrp_msg_append(msg, tag, type, asize, v.blb))
+ goto fail;
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT32)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &n);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+ goto fail;
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_next(&im);
+
+ {
+ char *astr[n];
+ uint32_t dbln[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(&ia, STRING, STRING , astr[j]);
+ ARRAY_QUIRKY(&ia, BOOL , BOOLEAN, abln[j], dbln[j]);
+ ARRAY_QUIRKY(&ia, UINT8 , UINT16 , au8[j] , au16[j]);
+ ARRAY_QUIRKY(&ia, SINT8 , INT16 , as8[j] , as16[j]);
+ ARRAY_SIMPLE(&ia, UINT16, UINT16 , au16[j]);
+ ARRAY_SIMPLE(&ia, SINT16, INT16 , as16[j]);
+ ARRAY_SIMPLE(&ia, UINT32, UINT32 , au32[j]);
+ ARRAY_SIMPLE(&ia, SINT32, INT32 , as32[j]);
+ ARRAY_SIMPLE(&ia, UINT64, UINT64 , au64[j]);
+ ARRAY_SIMPLE(&ia, SINT64, INT64 , as64[j]);
+ ARRAY_SIMPLE(&ia, DOUBLE, DOUBLE , adbl[j]);
+ default:
+ goto fail;
+ }
+ }
+
+ switch (base) {
+ APPEND_ARRAY(STRING, astr);
+ APPEND_ARRAY(BOOL , abln);
+ APPEND_ARRAY(UINT8 , au8 );
+ APPEND_ARRAY(SINT8 , as8 );
+ APPEND_ARRAY(UINT16, au16);
+ APPEND_ARRAY(SINT16, as16);
+ APPEND_ARRAY(UINT32, au32);
+ APPEND_ARRAY(SINT32, as32);
+ APPEND_ARRAY(UINT64, au64);
+ APPEND_ARRAY(SINT64, as64);
+ APPEND_ARRAY(DOUBLE, adbl);
+ default:
+ goto fail;
+ }
+ }
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ errno = EBADMSG;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+#undef APPEND_ARRAY
+}
+
+
+static DBusMessage *data_encode(const char *sender_id, void *data, uint16_t tag)
+{
+#define BASIC_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!dbus_message_iter_append_basic(&im, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!dbus_message_iter_append_basic(&im, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!dbus_message_iter_append_basic(&ia, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!dbus_message_iter_append_basic(&ia, type, vptr)) \
+ goto fail; \
+ break
+
+ DBusMessage *m;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type, base;
+ mrp_msg_value_t *v;
+ void *vptr;
+ uint32_t n, j;
+ int i, blblen;
+ DBusMessageIter im, ia;
+ const char *sig;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t bln;
+
+ m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+ if (m == NULL)
+ return NULL;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+
+ dbus_message_iter_init_append(m, &im);
+
+ if (!dbus_message_iter_append_basic(&im,
+ DBUS_TYPE_OBJECT_PATH, &sender_id))
+ goto fail;
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->tag) ||
+ !dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ BASIC_SIMPLE(STRING, STRING , v->str);
+ BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln);
+ BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16);
+ BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16);
+ BASIC_SIMPLE(UINT16, UINT16 , v->u16);
+ BASIC_SIMPLE(SINT16, INT16 , v->s16);
+ BASIC_SIMPLE(UINT32, UINT32 , v->u32);
+ BASIC_SIMPLE(SINT32, INT32 , v->s32);
+ BASIC_SIMPLE(UINT64, UINT64 , v->u64);
+ BASIC_SIMPLE(SINT64, INT64 , v->s64);
+ BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ sig = get_array_signature(f->type);
+ blblen = mrp_data_get_blob_size(data, descr, i);
+
+ if (blblen == -1)
+ goto fail;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia) ||
+ !dbus_message_iter_append_fixed_array(&ia, sig[0],
+ &f->blb, blblen) ||
+ !dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ n = mrp_data_get_array_size(data, descr, i);
+ sig = get_array_signature(base);
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(STRING, STRING , v->astr[j]);
+ ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln);
+ ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16);
+ ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16);
+ ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]);
+ ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]);
+ ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]);
+ ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]);
+ ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]);
+ ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ dbus_message_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+static void *data_decode(DBusMessage *m, uint16_t *tagp, const char **sender_id)
+{
+#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_var)); \
+ dbus_message_iter_next(_i); \
+ break
+
+#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_dvar)); \
+ dbus_message_iter_next(_i); \
+ \
+ _mvar = _dvar; \
+ break
+
+ void *data;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t tag, type, base;
+ mrp_msg_value_t *v;
+ uint32_t n, j, size;
+ int i, blblen;
+ DBusMessageIter im, ia;
+ const char *sender;
+ uint32_t u32;
+ uint16_t u16;
+ int16_t s16;
+
+ tag = 0;
+ data = NULL;
+
+ if (!dbus_message_iter_init(m, &im))
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &sender);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &tag);
+ dbus_message_iter_next(&im);
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ *tagp = tag;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &nfield);
+ dbus_message_iter_next(&im);
+
+ if (nfield != descr->nfield)
+ goto fail;
+
+ fields = descr->fields;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &tag);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &type);
+ dbus_message_iter_next(&im);
+
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (type) {
+ HANDLE_SIMPLE(&im, STRING, STRING , v->str);
+ HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32);
+ HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16);
+ HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16);
+ HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16);
+ HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16);
+ HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32);
+ HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32);
+ HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64);
+ HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64);
+ HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (dbus_message_iter_get_element_type(&ia) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_get_fixed_array(&ia, &v->blb, &blblen);
+ dbus_message_iter_next(&im);
+ v->blb = mrp_datadup(v->blb, blblen);
+ if (v->blb == NULL)
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT32)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &n);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_next(&im);
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ uint32_t dbln[n];
+ uint16_t au16[n];
+ int16_t as16[n];
+
+ switch (base) {
+ HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]);
+ HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]);
+ HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]);
+ HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]);
+ HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]);
+ HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]);
+ HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]);
+ HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]);
+ HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]);
+ HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]);
+ HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]);
+ }
+
+ if (base == MRP_MSG_FIELD_STRING) {
+ v->astr[j] = mrp_strdup(v->astr[j]);
+ if (v->astr[j] == NULL)
+ goto fail;
+ }
+ }
+ }
+
+ if (f->type == MRP_MSG_FIELD_STRING) {
+ v->str = mrp_strdup(v->str);
+ if (v->str == NULL)
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ mrp_data_free(data, tag);
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+static DBusMessage *raw_encode(const char *sender_id, void *data, size_t size)
+{
+ DBusMessage *m;
+ DBusMessageIter im, ia;
+ const char *sig;
+ int len;
+
+ m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+ if (m != NULL) {
+ dbus_message_iter_init_append(m, &im);
+
+ if (!dbus_message_iter_append_basic(&im,
+ DBUS_TYPE_OBJECT_PATH, &sender_id))
+ goto fail;
+
+ sig = DBUS_TYPE_BYTE_AS_STRING;
+ len = (int)size;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY, sig, &ia) ||
+ !dbus_message_iter_append_fixed_array(&ia, sig[0], &data, len) ||
+ !dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+
+ return m;
+ }
+ else
+ return NULL;
+
+ fail:
+ if (m != NULL)
+ dbus_message_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+}
+
+
+static void *raw_decode(DBusMessage *m, size_t *sizep, const char **sender_id)
+{
+ DBusMessageIter im, ia;
+ const char *sender;
+ void *data;
+ int len;
+
+ data = NULL;
+
+ if (!dbus_message_iter_init(m, &im))
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &sender);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_element_type(&ia) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_get_fixed_array(&ia, &data, &len);
+
+ data = mrp_datadup(data, len);
+
+ if (sizep != NULL)
+ *sizep = (size_t)len;
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve,
+ dbus_open, dbus_createfrom, dbus_close, NULL,
+ dbus_bind, NULL, NULL,
+ dbus_connect, dbus_disconnect,
+ dbus_sendmsg, dbus_sendmsgto,
+ dbus_sendraw, dbus_sendrawto,
+ dbus_senddata, dbus_senddatato,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
+
diff --git a/src/common/dbus-transport.h b/src/common/dbus-transport.h
new file mode 100644
index 0000000..77b5335
--- /dev/null
+++ b/src/common/dbus-transport.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_TRANSPORT_H__
+#define __MURPHY_DBUS_TRANSPORT_H__
+
+#include <murphy/common/transport.h>
+
+#define MRP_AF_DBUS 0xDB
+
+#define MRP_DBUSADDR_BASE \
+ __SOCKADDR_COMMON(db_); \
+ char *db_bus; /* D-BUS bus address */ \
+ char *db_addr; /* address on bus */ \
+ char *db_path /* instance path */ \
+
+typedef struct {
+ MRP_DBUSADDR_BASE;
+} _mrp_dbusaddr_base_t;
+
+
+typedef struct {
+ MRP_DBUSADDR_BASE;
+ char db_fqa[MRP_SOCKADDR_SIZE - sizeof(_mrp_dbusaddr_base_t)];
+} mrp_dbusaddr_t;
+
+
+
+#endif /* __MURPHY_DBUS_TRANSPORT_H__ */
diff --git a/src/common/debug-auto-register.c b/src/common/debug-auto-register.c
new file mode 100644
index 0000000..974e343
--- /dev/null
+++ b/src/common/debug-auto-register.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+static void __attribute__((constructor)) register_debug_data(void)
+{
+ mrp_debug_file_t *df;
+ int i;
+
+ for (i = 0; (df = debug_files[i]) != NULL; i++)
+ mrp_debug_register_file(df);
+}
+
+static void __attribute__((destructor)) unregister_debug_data(void)
+{
+ mrp_debug_file_t *df;
+ int i;
+
+ for (i = 0; (df = debug_files[i]) != NULL; i++)
+ mrp_debug_unregister_file(df);
+}
+
diff --git a/src/common/debug-info.h b/src/common/debug-info.h
new file mode 100644
index 0000000..970d4c9
--- /dev/null
+++ b/src/common/debug-info.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DEBUG_INFO_H__
+#define __MURPHY_DEBUG_INFO_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * line number information for a single function
+ */
+
+typedef struct {
+ const char *func; /* name of the function */
+ int line; /* start at this line */
+} mrp_debug_info_t;
+
+
+/*
+ * funcion - line number mapping for a single file
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook for startup registration */
+ const char *file; /* file name */
+ mrp_debug_info_t *info; /* function information */
+} mrp_debug_file_t;
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_DEBUG_INFO_H__ */
diff --git a/src/common/debug.c b/src/common/debug.c
new file mode 100644
index 0000000..9f5e091
--- /dev/null
+++ b/src/common/debug.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE
+#include <link.h>
+#include <elf.h>
+
+#include <stdarg.h>
+#include <limits.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/debug.h>
+
+#define WILDCARD "*"
+
+int mrp_debug_stamp = 0; /* debug config stamp */
+
+static int debug_enabled; /* debug messages enabled */
+static mrp_htbl_t *rules_on; /* enabling rules */
+static mrp_htbl_t *rules_off; /* disabling rules */
+
+static void free_rule_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ mrp_free(entry);
+}
+
+
+static int init_rules(void)
+{
+ mrp_htbl_config_t hcfg;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = free_rule_cb;
+
+ rules_on = mrp_htbl_create(&hcfg);
+ rules_off = mrp_htbl_create(&hcfg);
+
+ if (rules_on == NULL || rules_off == NULL)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static void reset_rules(void)
+{
+ if (rules_on != NULL) {
+ mrp_htbl_destroy(rules_on , TRUE);
+ rules_on = NULL;
+ }
+ if (rules_off != NULL) {
+ mrp_htbl_destroy(rules_off, TRUE);
+ rules_off = NULL;
+ }
+}
+
+
+void mrp_debug_reset(void)
+{
+ debug_enabled = FALSE;
+ reset_rules();
+}
+
+
+int mrp_debug_enable(int enabled)
+{
+ int prev = debug_enabled;
+
+ debug_enabled = !!enabled;
+ mrp_log_enable(MRP_LOG_MASK_DEBUG);
+ mrp_debug_stamp++;
+
+ return prev;
+}
+
+
+static int add_rule(const char *func, const char *file, int line, int off)
+{
+ mrp_htbl_t *ht;
+ char *rule, *r, buf[PATH_MAX * 2];
+
+ if (rules_on == NULL)
+ if (!init_rules())
+ return FALSE;
+
+ r = rule = NULL;
+
+ if (!off)
+ ht = rules_on;
+ else
+ ht = rules_off;
+
+ if (func != NULL && file == NULL && line == 0) {
+ r = mrp_htbl_lookup(ht, (void *)func);
+ rule = (char *)func;
+ }
+ else if (func != NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ r = mrp_htbl_lookup(ht, (void *)buf);
+ rule = buf;
+ }
+ else if (func == NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "@%s", file);
+ r = mrp_htbl_lookup(ht, (void *)buf);
+ rule = buf;
+ }
+ else if (func == NULL && file != NULL && line > 0) {
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ r = mrp_htbl_lookup(ht, (void *)buf);
+ rule = buf;
+ }
+
+ if (r != NULL)
+ return FALSE;
+
+ rule = mrp_strdup(rule);
+ if (rule == NULL)
+ return FALSE;
+
+ if (mrp_htbl_insert(ht, rule, rule)) {
+ mrp_debug_stamp++;
+
+ return TRUE;
+ }
+ else {
+ mrp_free(rule);
+
+ return FALSE;
+ }
+}
+
+
+static int del_rule(const char *func, const char *file, int line, int off)
+{
+ mrp_htbl_t *ht;
+ char *r, buf[PATH_MAX * 2];
+
+ if (rules_on == NULL)
+ if (!init_rules())
+ return FALSE;
+
+ r = NULL;
+
+ if (!off)
+ ht = rules_on;
+ else
+ ht = rules_off;
+
+ if (func != NULL && file == NULL && line == 0) {
+ r = mrp_htbl_remove(ht, (void *)func, TRUE);
+ }
+ else if (func != NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+ }
+ else if (func == NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "@%s", file);
+ r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+ }
+ else if (func == NULL && file != NULL && line > 0) {
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+ }
+
+ if (r != NULL) {
+ mrp_debug_stamp++;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_debug_set_config(const char *cmd)
+{
+ char buf[2 * PATH_MAX + 1], *colon, *at, *eq;
+ char *func, *file, *end;
+ size_t len;
+ int del, off, line;
+
+ if (*cmd == '+' || *cmd == '-')
+ del = (*cmd++ == '-');
+ else
+ del = FALSE;
+
+ eq = strchr(cmd, '=');
+
+ if (eq == NULL) {
+ strncpy(buf, cmd, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ off = FALSE;
+ }
+ else {
+ if (!strcmp(eq + 1, "on"))
+ off = FALSE;
+ else if (!strcmp(eq + 1, "off"))
+ off = TRUE;
+ else
+ return FALSE;
+
+ len = eq - cmd;
+ if (len >= sizeof(buf))
+ len = sizeof(buf) - 1;
+
+ strncpy(buf, cmd, len);
+ buf[len] = '\0';
+ }
+
+ colon = strchr(buf, ':');
+
+ if (colon != NULL) {
+ if (strchr(buf, '@') != NULL)
+ return FALSE;
+
+ *colon = '\0';
+ func = NULL;
+ file = buf;
+ line = strtoul(colon + 1, &end, 10);
+
+ if (end && *end)
+ return FALSE;
+
+ mrp_log_info("%s file='%s', line=%d, %s", del ? "del" : "add",
+ file, line, off ? "off" : "on");
+ }
+ else {
+ at = strchr(buf, '@');
+
+ if (at != NULL) {
+ *at = '\0';
+ func = (at == buf ? NULL : buf);
+ file = at + 1;
+ line = 0;
+
+ mrp_log_info("%s func='%s', file='%s', %s", del ? "del" : "add",
+ func ? func : "", file, off ? "off" : "on");
+ }
+ else {
+ func = buf;
+ file = NULL;
+ line = 0;
+
+ mrp_log_info("%s func='%s' %s", del ? "del" : "add",
+ func, off ? "off" : "on");
+ }
+ }
+
+ if (!del)
+ return add_rule(func, file, line, off);
+ else
+ return del_rule(func, file, line, off);
+
+ return TRUE;
+}
+
+
+typedef struct {
+ FILE *fp;
+ int on;
+} dump_t;
+
+
+static int dump_rule_cb(void *key, void *object, void *user_data)
+{
+ dump_t *d = (dump_t *)user_data;
+ FILE *fp = d->fp;
+ const char *state = d->on ? "on" : "off";
+
+ MRP_UNUSED(key);
+
+ fprintf(fp, " %s %s\n", (char *)object, state);
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+int mrp_debug_dump_config(FILE *fp)
+{
+ dump_t d;
+
+ fprintf(fp, "Debugging is %sabled\n", debug_enabled ? "en" : "dis");
+
+ if (rules_on != NULL) {
+ fprintf(fp, "Debugging rules:\n");
+
+ d.fp = fp;
+ d.on = TRUE;
+ mrp_htbl_foreach(rules_on , dump_rule_cb, &d);
+ d.on = FALSE;
+ mrp_htbl_foreach(rules_off, dump_rule_cb, &d);
+ }
+ else
+ fprintf(fp, "No debugging rules defined.\n");
+
+ return TRUE;
+}
+
+
+void mrp_debug_msg(const char *file, int line, const char *func,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ mrp_log_msgv(MRP_LOG_DEBUG, file, line, func, format, ap);
+ va_end(ap);
+}
+
+
+int mrp_debug_check(const char *func, const char *file, int line)
+{
+ char buf[2 * PATH_MAX], *base;
+ void *key;
+
+ if (!debug_enabled || rules_on == NULL)
+ return FALSE;
+
+ base = NULL;
+ key = (void *)func;
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ base = strrchr(file, '/');
+ if (base != NULL)
+ base++;
+
+ key = buf;
+
+ snprintf(buf, sizeof(buf), "@%s", file);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ if (base != NULL) {
+ snprintf(buf, sizeof(buf), "@%s", base);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+ }
+
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ if (mrp_htbl_lookup(rules_on, (void *)WILDCARD) == NULL)
+ return FALSE;
+
+
+ check_suppress:
+ if (rules_off == NULL)
+ return TRUE;
+
+ key = (void *)func;
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ key = buf;
+
+ snprintf(buf, sizeof(buf), "@%s", file);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ if (base != NULL) {
+ snprintf(buf, sizeof(buf), "@%s", base);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+ }
+
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+
diff --git a/src/common/debug.h b/src/common/debug.h
new file mode 100644
index 0000000..3828402
--- /dev/null
+++ b/src/common/debug.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DEBUG_H__
+#define __MURPHY_DEBUG_H__
+
+#include <stdio.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug-info.h>
+
+MRP_CDECL_BEGIN
+
+/** Log a debug message if the invoking debug site is enabled. */
+#define mrp_debug(fmt, args...) do { \
+ static int __site_stamp = -1; \
+ static int __site_enabled; \
+ \
+ if (MRP_UNLIKELY(__site_stamp != mrp_debug_stamp)) { \
+ __site_enabled = mrp_debug_check(__FUNCTION__, \
+ __FILE__, __LINE__); \
+ __site_stamp = mrp_debug_stamp; \
+ } \
+ \
+ if (MRP_UNLIKELY(__site_enabled)) \
+ mrp_debug_msg(__LOC__, fmt, ## args); \
+ } while (0)
+
+
+/** mrp_debug variant with explicitly passed site info. */
+#define mrp_debug_at(_file, _line, _func, fmt, args...) do { \
+ static int __site_stamp = -1; \
+ static int __site_enabled; \
+ \
+ if (MRP_UNLIKELY(__site_stamp != mrp_debug_stamp)) { \
+ __site_enabled = mrp_debug_check(_func, _file, _line); \
+ __site_stamp = mrp_debug_stamp; \
+ } \
+ \
+ if (MRP_UNLIKELY(__site_enabled)) \
+ mrp_debug_msg(_file, _line, _func, fmt, ## args); \
+ } while (0)
+
+
+/** Run a block of code if the invoking debug site is enabled. */
+#define mrp_debug_code(...) do { \
+ static int __site_stamp = -1; \
+ static int __site_enabled; \
+ \
+ if (MRP_UNLIKELY(__site_stamp != mrp_debug_stamp)) { \
+ __site_enabled = mrp_debug_check(__FUNCTION__, \
+ __FILE__, __LINE__); \
+ __site_stamp = mrp_debug_stamp; \
+ } \
+ \
+ if (MRP_UNLIKELY(__site_enabled)) { \
+ __VA_ARGS__; \
+ } \
+ } while (0)
+
+/** Global debug configuration stamp, exported for minimum-overhead checking. */
+extern int mrp_debug_stamp;
+
+/** Enable/disable debug messages globally. */
+int mrp_debug_enable(int enabled);
+
+/** Reset all debug configuration to the defaults. */
+void mrp_debug_reset(void);
+
+/** Apply the debug configuration settings given in cmd. */
+int mrp_debug_set_config(const char *cmd);
+
+/** Dump the active debug configuration. */
+int mrp_debug_dump_config(FILE *fp);
+
+/** Low-level log wrapper for debug messages. */
+void mrp_debug_msg(const char *file, int line, const char *func,
+ const char *format, ...) MRP_PRINTF_LIKE(4, 5);
+
+/** Check if the given debug site is enabled. */
+int mrp_debug_check(const char *func, const char *file, int line);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_DEBUG_H__ */
diff --git a/src/common/dgram-transport.c b/src/common/dgram-transport.c
new file mode 100644
index 0000000..b54ed62
--- /dev/null
+++ b/src/common/dgram-transport.c
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)NULL)->sun_path)
+#endif
+
+#define UDP4 "udp4"
+#define UDP4L 4
+#define UDP6 "udp6"
+#define UDP6L 4
+#define UNXD "unxd"
+#define UNXDL 4
+
+
+#define DEFAULT_SIZE 1024 /* default input buffer size */
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ int sock; /* UDP socket */
+ int family; /* socket family */
+ mrp_io_watch_t *iow; /* socket I/O watch */
+ void *ibuf; /* input buffer */
+ size_t isize; /* input buffer size */
+ size_t idata; /* amount of input data */
+} dgrm_t;
+
+
+static void dgrm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data);
+static int dgrm_disconnect(mrp_transport_t *mu);
+static int open_socket(dgrm_t *u, int family);
+
+
+/*
+ * XXX TODO:
+ *
+ * There is an almost verbatim copy of this in stream-transport.c
+ * The only differences are the actual address type specifier
+ * prefixes... Combine these and separate the result out to a
+ * new transport-priv.[hc].
+ */
+
+
+static int parse_address(const char *str, int *familyp, char *nodep,
+ size_t nsize, char **servicep, const char **typep)
+{
+ char *node, *service;
+ const char *type;
+ int family;
+ size_t l, nl;
+
+ node = (char *)str;
+
+ if (!strncmp(node, UDP4":", l=UDP4L+1)) {
+ family = AF_INET;
+ type = UDP4;
+ node += l;
+ }
+ else if (!strncmp(node, UDP6":", l=UDP6L+1)) {
+ family = AF_INET6;
+ type = UDP6;
+ node += l;
+ }
+ else if (!strncmp(node, UNXD":", l=UNXDL+1)) {
+ family = AF_UNIX;
+ type = UNXD;
+ node += l;
+ }
+ else {
+ if (node[0] == '[') family = AF_INET6;
+ else if (node[0] == '/') family = AF_UNIX;
+ else if (node[0] == '@') family = AF_UNIX;
+ else family = AF_UNSPEC;
+
+ type = NULL;
+ }
+
+ switch (family) {
+ case AF_INET:
+ service = strrchr(node, ':');
+ if (service == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node;
+ service++;
+
+ case AF_INET6:
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else
+ nl = service - node;
+ service++;
+ break;
+
+ case AF_UNSPEC:
+ if (!strncmp(node, "tcp:", l=4))
+ node += l;
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+ family = AF_INET6;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else {
+ family = AF_INET;
+ nl = service - node;
+ }
+ service++;
+ break;
+
+ case AF_UNIX:
+ service = NULL;
+ nl = strlen(node);
+ }
+
+ if (nl >= nsize) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ strncpy(nodep, node, nl);
+ nodep[nl] = '\0';
+ *servicep = service;
+ *familyp = family;
+ if (typep != NULL)
+ *typep = type;
+
+ return 0;
+}
+
+
+static socklen_t dgrm_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ struct addrinfo *ai, hints;
+ struct sockaddr_un *un;
+ char node[UNIX_PATH_MAX], *port;
+ socklen_t len;
+
+ mrp_clear(&hints);
+
+ if (parse_address(str, &hints.ai_family, node, sizeof(node),
+ &port, typep) < 0)
+ return 0;
+
+ switch (hints.ai_family) {
+ case AF_UNIX:
+ un = &addr->unx;
+ len = MRP_OFFSET(typeof(*un), sun_path) + strlen(node) + 1;
+
+ if (size < len)
+ errno = ENOMEM;
+ else {
+ un->sun_family = AF_UNIX;
+ strncpy(un->sun_path, node, UNIX_PATH_MAX-1);
+ if (un->sun_path[0] == '@')
+ un->sun_path[0] = '\0';
+ }
+
+ /* When binding the socket, we don't need the null at the end */
+ len--;
+
+ break;
+
+ case AF_INET:
+ case AF_INET6:
+ default:
+ if (getaddrinfo(node, port, &hints, &ai) == 0) {
+ if (ai->ai_addrlen <= size) {
+ memcpy(addr, ai->ai_addr, ai->ai_addrlen);
+ len = ai->ai_addrlen;
+ }
+ else
+ len = 0;
+
+ freeaddrinfo(ai);
+ }
+ else
+ len = 0;
+ }
+
+ return len;
+}
+
+
+static int dgrm_open(mrp_transport_t *mu)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+
+ u->sock = -1;
+ u->family = -1;
+
+ return TRUE;
+}
+
+
+static int dgrm_createfrom(mrp_transport_t *mu, void *conn)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ int on;
+ mrp_io_event_t events;
+
+ u->sock = *(int *)conn;
+
+ if (u->sock >= 0) {
+ if (mu->flags & MRP_TRANSPORT_REUSEADDR) {
+ on = 1;
+ setsockopt(u->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ }
+ if (mu->flags & MRP_TRANSPORT_NONBLOCK) {
+ on = 1;
+ fcntl(u->sock, F_SETFL, O_NONBLOCK, on);
+ }
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ u->iow = mrp_add_io_watch(u->ml, u->sock, events, dgrm_recv_cb, u);
+
+ if (u->iow != NULL)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_bind(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+
+ if (u->sock != -1 || !u->connected) {
+ if (open_socket(u, addr->any.sa_family))
+ if (bind(u->sock, &addr->any, addrlen) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_listen(mrp_transport_t *mt, int backlog)
+{
+ MRP_UNUSED(mt);
+ MRP_UNUSED(backlog);
+
+ return TRUE; /* can be connected to without listening */
+}
+
+
+static void dgrm_close(mrp_transport_t *mu)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+
+ mrp_del_io_watch(u->iow);
+ u->iow = NULL;
+
+ mrp_free(u->ibuf);
+ u->ibuf = NULL;
+ u->isize = 0;
+ u->idata = 0;
+
+ if (u->sock >= 0){
+ close(u->sock);
+ u->sock = -1;
+ }
+}
+
+
+static void dgrm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ dgrm_t *u = (dgrm_t *)user_data;
+ mrp_transport_t *mu = (mrp_transport_t *)u;
+ mrp_sockaddr_t addr;
+ socklen_t addrlen;
+ uint32_t size;
+ ssize_t n;
+ void *data;
+ int old, error;
+
+ MRP_UNUSED(w);
+
+ if (events & MRP_IO_EVENT_IN) {
+ if (u->idata == u->isize) {
+ if (u->isize != 0) {
+ old = u->isize;
+ u->isize *= 2;
+ }
+ else {
+ old = 0;
+ u->isize = DEFAULT_SIZE;
+ }
+ if (!mrp_reallocz(u->ibuf, old, u->isize)) {
+ error = ENOMEM;
+ fatal_error:
+ closed:
+ dgrm_disconnect(mu);
+
+ if (u->evt.closed != NULL)
+ MRP_TRANSPORT_BUSY(mu, {
+ mu->evt.closed(mu, error, mu->user_data);
+ });
+
+ u->check_destroy(mu);
+ return;
+ }
+ }
+
+ if (recv(fd, &size, sizeof(size), MSG_PEEK) != sizeof(size)) {
+ error = EIO;
+ goto fatal_error;
+ }
+
+ size = ntohl(size);
+
+ if (u->isize < size + sizeof(size)) {
+ old = u->isize;
+ u->isize = size + sizeof(size);
+
+ if (!mrp_reallocz(u->ibuf, old, u->isize)) {
+ error = ENOMEM;
+ goto fatal_error;
+ }
+ }
+
+ addrlen = sizeof(addr);
+ n = recvfrom(fd, u->ibuf, size + sizeof(size), 0, &addr.any, &addrlen);
+
+ if (n != (ssize_t)(size + sizeof(size))) {
+ error = n < 0 ? EIO : EPROTO;
+ goto fatal_error;
+ }
+
+ data = u->ibuf + sizeof(size);
+ error = mu->recv_data(mu, data, size, &addr, addrlen);
+
+ if (error)
+ goto fatal_error;
+
+ if (u->check_destroy(mu))
+ return;
+ }
+
+ if (events & MRP_IO_EVENT_HUP) {
+ error = 0;
+ goto closed;
+ }
+}
+
+
+static int open_socket(dgrm_t *u, int family)
+{
+ mrp_io_event_t events;
+ int on;
+ long nb;
+
+ u->sock = socket(family, SOCK_DGRAM, 0);
+
+ if (u->sock != -1) {
+ if (u->flags & MRP_TRANSPORT_REUSEADDR) {
+ on = 1;
+ setsockopt(u->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ }
+ if (u->flags & MRP_TRANSPORT_NONBLOCK) {
+ nb = 1;
+ fcntl(u->sock, F_SETFL, O_NONBLOCK, nb);
+ }
+ if (u->flags & MRP_TRANSPORT_CLOEXEC) {
+ on = 1;
+ fcntl(u->sock, F_SETFL, O_CLOEXEC, on);
+ }
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ u->iow = mrp_add_io_watch(u->ml, u->sock, events, dgrm_recv_cb, u);
+
+ if (u->iow != NULL)
+ return TRUE;
+ else {
+ close(u->sock);
+ u->sock = -1;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_connect(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ int on;
+ long nb;
+
+ if (MRP_UNLIKELY(u->family != -1 && u->family != addr->any.sa_family))
+ return FALSE;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, addr->any.sa_family))
+ return FALSE;
+ }
+
+ if (connect(u->sock, &addr->any, addrlen) == 0) {
+ on = 1;
+ setsockopt(u->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ nb = 1;
+ fcntl(u->sock, F_SETFL, O_NONBLOCK, nb);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_disconnect(mrp_transport_t *mu)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct sockaddr none = { .sa_family = AF_UNSPEC, };
+
+
+ if (u->connected) {
+ connect(u->sock, &none, sizeof(none));
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dgrm_send(mrp_transport_t *mu, mrp_msg_t *msg)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct iovec iov[2];
+ void *buf;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (u->connected) {
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (size >= 0) {
+ len = htonl(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = size;
+
+ n = writev(u->sock, iov, 2);
+ mrp_free(buf);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for dgrm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendto(mrp_transport_t *mu, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct iovec iov[2];
+ void *buf;
+ ssize_t size, n;
+ uint32_t len;
+ struct msghdr hdr;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (size >= 0) {
+ len = htonl(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = size;
+
+ hdr.msg_name = addr;
+ hdr.msg_namelen = addrlen;
+ hdr.msg_iov = iov;
+ hdr.msg_iovlen = MRP_ARRAY_SIZE(iov);
+
+ hdr.msg_control = NULL;
+ hdr.msg_controllen = 0;
+ hdr.msg_flags = 0;
+
+ n = sendmsg(u->sock, &hdr, 0);
+ mrp_free(buf);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send failed",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendraw(mrp_transport_t *mu, void *data, size_t size)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ ssize_t n;
+
+ if (u->connected) {
+ n = write(u->sock, data, size);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for dgrm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendrawto(mrp_transport_t *mu, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ ssize_t n;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ n = sendto(u->sock, data, size, 0, &addr->any, addrlen);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send failed",
+ __FUNCTION__);
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int senddatato(mrp_transport_t *mu, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ mrp_data_descr_t *type;
+ ssize_t n;
+ void *buf;
+ size_t size, reserve, len;
+ uint32_t *lenp;
+ uint16_t *tagp;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ reserve = sizeof(*lenp) + sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size > 0) {
+ lenp = buf;
+ len = size - sizeof(*lenp);
+ tagp = buf + sizeof(*lenp);
+ *lenp = htobe32(len);
+ *tagp = htobe16(tag);
+
+ if (u->connected)
+ n = send(u->sock, buf, len + sizeof(*lenp), 0);
+ else
+ n = sendto(u->sock, buf, len + sizeof(*lenp), 0, &addr->any,
+ addrlen);
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)(len + sizeof(*lenp)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send"
+ " needs queuing", __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_senddata(mrp_transport_t *mu, void *data, uint16_t tag)
+{
+ if (mu->connected)
+ return senddatato(mu, data, tag, NULL, 0);
+ else
+ return FALSE;
+}
+
+
+static int dgrm_senddatato(mrp_transport_t *mu, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ return senddatato(mu, data, tag, addr, addrlen);
+}
+
+
+static int sendnativeto(mrp_transport_t *mu, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ mrp_typemap_t *map = u->map;
+ void *buf;
+ size_t size, reserve;
+ uint32_t *lenp;
+ ssize_t n;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ reserve = sizeof(*lenp);
+
+ if (mrp_encode_native(data, type_id, reserve, &buf, &size, map) > 0) {
+ lenp = buf;
+ *lenp = htobe32(size - sizeof(*lenp));
+
+ if (u->connected)
+ n = send(u->sock, buf, size, 0);
+ else
+ n = sendto(u->sock, buf, size, 0, &addr->any, addrlen);
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send"
+ " needs queuing", __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendnative(mrp_transport_t *mu, void *data, uint32_t type_id)
+{
+ if (mu->connected)
+ return sendnativeto(mu, data, type_id, NULL, 0);
+ else
+ return FALSE;
+}
+
+
+static int dgrm_sendnativeto(mrp_transport_t *mu, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ return sendnativeto(mu, data, type_id, addr, addrlen);
+}
+
+
+static int sendjsonto(mrp_transport_t *mu, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct iovec iov[2];
+ const char *s;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ if (u->connected && (s = mrp_json_object_to_string(msg)) != NULL) {
+ size = strlen(s);
+ len = htobe32(size);
+
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = (char *)s;
+ iov[1].iov_len = size;
+
+ if (u->connected)
+ n = writev(u->sock, iov, 2);
+ else {
+ struct msghdr mh;
+
+ mh.msg_name = &addr->any;
+ mh.msg_namelen = addrlen;
+ mh.msg_iov = iov;
+ mh.msg_iovlen = MRP_ARRAY_SIZE(iov);
+ mh.msg_control = NULL;
+ mh.msg_controllen = 0;
+ mh.msg_flags = 0;
+
+ n = sendmsg(u->sock, &mh, 0);
+ }
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for dgrm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendjson(mrp_transport_t *mu, mrp_json_t *msg)
+{
+ if (mu->connected)
+ return sendjsonto(mu, msg, NULL, 0);
+ else
+ return FALSE;
+}
+
+
+static int dgrm_sendjsonto(mrp_transport_t *mu, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ return sendjsonto(mu, msg, addr, addrlen);
+}
+
+
+MRP_REGISTER_TRANSPORT(udp4, UDP4, dgrm_t, dgrm_resolve,
+ dgrm_open, dgrm_createfrom, dgrm_close, NULL,
+ dgrm_bind, dgrm_listen, NULL,
+ dgrm_connect, dgrm_disconnect,
+ dgrm_send, dgrm_sendto,
+ dgrm_sendraw, dgrm_sendrawto,
+ dgrm_senddata, dgrm_senddatato,
+ NULL, NULL,
+ dgrm_sendnative, dgrm_sendnativeto,
+ dgrm_sendjson, dgrm_sendjsonto);
+
+MRP_REGISTER_TRANSPORT(udp6, UDP6, dgrm_t, dgrm_resolve,
+ dgrm_open, dgrm_createfrom, dgrm_close, NULL,
+ dgrm_bind, dgrm_listen, NULL,
+ dgrm_connect, dgrm_disconnect,
+ dgrm_send, dgrm_sendto,
+ dgrm_sendraw, dgrm_sendrawto,
+ dgrm_senddata, dgrm_senddatato,
+ NULL, NULL,
+ dgrm_sendnative, dgrm_sendnativeto,
+ dgrm_sendjson, dgrm_sendjsonto);
+
+MRP_REGISTER_TRANSPORT(unxdgrm, UNXD, dgrm_t, dgrm_resolve,
+ dgrm_open, dgrm_createfrom, dgrm_close, NULL,
+ dgrm_bind, dgrm_listen, NULL,
+ dgrm_connect, dgrm_disconnect,
+ dgrm_send, dgrm_sendto,
+ dgrm_sendraw, dgrm_sendrawto,
+ dgrm_senddata, dgrm_senddatato,
+ NULL, NULL,
+ dgrm_sendnative, dgrm_sendnativeto,
+ dgrm_sendjson, dgrm_sendjsonto);
diff --git a/src/common/ecore-glue.c b/src/common/ecore-glue.c
new file mode 100644
index 0000000..8ad6238
--- /dev/null
+++ b/src/common/ecore-glue.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <Ecore.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+
+typedef struct {
+ int ecore;
+} ecore_glue_t;
+
+
+typedef struct {
+ Ecore_Fd_Handler *ec_io;
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ mrp_io_event_t mask;
+ void *user_data;
+ void *glue_data;
+} io_t;
+
+
+typedef struct {
+ Ecore_Timer *ec_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} tmr_t;
+
+
+typedef struct {
+ Ecore_Timer *ec_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} dfr_t;
+
+
+#define D(fmt, args...) do { \
+ printf("* [%s]: "fmt"\n", __FUNCTION__ , ## args); \
+ } while (0)
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+static void del_io(void *glue_data, void *id);
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_timer(void *glue_data, void *id);
+static void mod_timer(void *glue_data, void *id, unsigned int msecs);
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_defer(void *glue_data, void *id);
+static void mod_defer(void *glue_data, void *id, int enabled);
+
+
+static int io_check_hup(int fd)
+{
+ char buf[1];
+ int saved_errno, n;
+
+ saved_errno = errno;
+ n = recv(fd, buf, 1, MSG_PEEK);
+ errno = saved_errno;
+
+ return (n == 0);
+}
+
+
+static Eina_Bool io_cb(void *user_data, Ecore_Fd_Handler *ec_io)
+{
+ io_t *io = (io_t *)user_data;
+ mrp_io_event_t events = MRP_IO_EVENT_NONE;
+ int fd = ecore_main_fd_handler_fd_get(ec_io);
+
+ if (ecore_main_fd_handler_active_get(ec_io, ECORE_FD_READ))
+ events |= MRP_IO_EVENT_IN;
+ if (ecore_main_fd_handler_active_get(ec_io, ECORE_FD_WRITE))
+ events |= MRP_IO_EVENT_OUT;
+ if (ecore_main_fd_handler_active_get(ec_io, ECORE_FD_ERROR))
+ events |= MRP_IO_EVENT_ERR;
+
+#if 0 /* Pfoof... ecore cannot monitor for HUP. */
+ if (ecore_main_fd_handler_active_get(ec_io, NO_SUCH_ECORE_EVENT))
+ events |= MRP_IO_EVENT_HUP;
+#else
+ if ((io->mask & MRP_IO_EVENT_HUP) && (events & MRP_IO_EVENT_IN))
+ if (io_check_hup(fd))
+ events |= MRP_IO_EVENT_HUP;
+#endif
+
+ io->cb(io->glue_data, io, fd, events, io->user_data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ int mask = 0;
+ io_t *io;
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io != NULL) {
+ if (events & MRP_IO_EVENT_IN) mask |= ECORE_FD_READ;
+ if (events & MRP_IO_EVENT_OUT) mask |= ECORE_FD_WRITE;
+#if 0 /* Pfoof... ecore cannot monitor for HUP. */
+ if (events & MRP_IO_EVENT_HUP) mask |= NO_SUCH_ECORE_EVENT;
+#endif
+ if (events & MRP_IO_EVENT_ERR) mask |= ECORE_FD_ERROR;
+
+ io->mask = events;
+ io->ec_io = ecore_main_fd_handler_add(fd, mask, io_cb, io, NULL, NULL);
+
+ if (io->ec_io != NULL) {
+ io->cb = cb;
+ io->user_data = user_data;
+ io->glue_data = glue_data;
+
+ return io;
+ }
+ else
+ mrp_free(io);
+ }
+
+ return NULL;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ io_t *io = (io_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ ecore_main_fd_handler_del(io->ec_io);
+ mrp_free(io);
+}
+
+
+static Eina_Bool timer_cb(void *user_data)
+{
+ tmr_t *t = (tmr_t *)user_data;
+
+ t->cb(t->glue_data, t, t->user_data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ double interval = (1.0 * msecs) / 1000.0;
+ tmr_t *t;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ t->ec_t = ecore_timer_add(interval, timer_cb, t);
+
+ if (t->ec_t != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->glue_data = glue_data;
+
+ return t;
+ }
+ else
+ mrp_free(t);
+ }
+
+ return NULL;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ tmr_t *t = (tmr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ ecore_timer_del(t->ec_t);
+ mrp_free(t);
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ tmr_t *t = (tmr_t *)id;
+ double interval = (1.0 * msecs) / 1000.0;
+
+ MRP_UNUSED(glue_data);
+
+ if (t != NULL) {
+ /*
+ * Notes:
+ * ecore_timer_reset needs to be called after updating the
+ * interval. Otherwise the change will not be effective.
+ *
+ * In practice, since we use mod_timer to update our super-
+ * loop briding timer to latch at the next mrp_mainloop_t
+ * timer moment, this could cause our mainloop to hang
+ * right in the beginning, since the bridging timer has an
+ * initial interval of (uint32_t)-1 (no next event). If we
+ * have no other event sources than timers this would cause
+ * our mainloop to hang indefinitely. If we have other event
+ * sources (I/O or signals), the mainloop would hang till a
+ * non-timer event comes in.
+ */
+ ecore_timer_interval_set(t->ec_t, interval);
+ ecore_timer_reset(t->ec_t);
+ }
+}
+
+
+static Eina_Bool defer_cb(void *user_data)
+{
+ dfr_t *d = (dfr_t *)user_data;
+
+ d->cb(d->glue_data, d, d->user_data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ dfr_t *d;
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d != NULL) {
+ d->ec_t = ecore_timer_add(0.0, defer_cb, d);
+
+ if (d->ec_t != NULL) {
+ d->cb = cb;
+ d->user_data = user_data;
+ d->glue_data = glue_data;
+
+ return d;
+ }
+ else
+ mrp_free(d);
+ }
+
+ return NULL;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ ecore_timer_del(d->ec_t);
+ mrp_free(d);
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (enabled)
+ ecore_timer_thaw(d->ec_t);
+ else
+ ecore_timer_freeze(d->ec_t);
+}
+
+
+static void unregister(void *data)
+{
+ MRP_UNUSED(data);
+}
+
+
+static mrp_superloop_ops_t ecore_ops = {
+ .add_io = add_io,
+ .del_io = del_io,
+ .add_timer = add_timer,
+ .del_timer = del_timer,
+ .mod_timer = mod_timer,
+ .add_defer = add_defer,
+ .del_defer = del_defer,
+ .mod_defer = mod_defer,
+ .unregister = unregister,
+};
+
+
+static const char *ecore_glue = "murphy-ecore-glue";
+static mrp_mainloop_t *ecore_ml;
+
+
+int mrp_mainloop_register_with_ecore(mrp_mainloop_t *ml)
+{
+ return mrp_set_superloop(ml, &ecore_ops, (void *)ecore_glue);
+}
+
+
+int mrp_mainloop_unregister_from_ecore(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+mrp_mainloop_t *mrp_mainloop_ecore_get(void)
+{
+ if (ecore_ml == NULL) {
+ ecore_init();
+
+ ecore_ml = mrp_mainloop_create();
+
+ if (ecore_ml != NULL) {
+ if (!mrp_mainloop_register_with_ecore(ecore_ml)) {
+ mrp_mainloop_destroy(ecore_ml);
+ ecore_ml = NULL;
+ }
+ }
+ }
+
+ return ecore_ml;
+}
diff --git a/src/common/ecore-glue.h b/src/common/ecore-glue.h
new file mode 100644
index 0000000..ebf53a6
--- /dev/null
+++ b/src/common/ecore-glue.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_ECORE_H__
+#define __MURPHY_ECORE_H__
+
+MRP_CDECL_BEGIN
+
+/** Register the given murphy mainloop with the ecore mainloop. */
+int mrp_mainloop_register_with_ecore(mrp_mainloop_t *ml);
+
+/** Unrgister the given murphy mainloop from the ecore mainloop. */
+int mrp_mainloop_unregister_from_ecore(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the ecore mainloop. */
+mrp_mainloop_t *mrp_mainloop_ecore_get(void);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_ECORE_H__ */
diff --git a/src/common/env.c b/src/common/env.c
new file mode 100644
index 0000000..4ea8bad
--- /dev/null
+++ b/src/common/env.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 2012 - 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+const char *mrp_env_config_key(const char *config, const char *key)
+{
+ const char *beg;
+ int len;
+
+ if (config != NULL) {
+ len = strlen(key);
+
+ beg = config;
+ while (beg != NULL) {
+ beg = strstr(beg, key);
+
+ if (beg != NULL) {
+ if ((beg == config || beg[-1] == ':') &&
+ (beg[len] == '=' || beg[len] == ':' || beg[len] == '\0'))
+ return (beg[len] == '=' ? beg + len + 1 : "");
+ else
+ beg++;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+int32_t mrp_env_config_int32(const char *cfg, const char *key, int32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = mrp_env_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+uint32_t mrp_env_config_uint32(const char *cfg, const char *key,
+ uint32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = mrp_env_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+int mrp_env_config_bool(const char *config, const char *key, bool defval)
+{
+ const char *v;
+
+ v = mrp_env_config_key(config, key);
+
+ if (v != NULL) {
+ if (*v) {
+ if ((!strncasecmp(v, "false", 5) && (v[5] == ':' || !v[5])) ||
+ (!strncasecmp(v, "true" , 4) && (v[4] == ':' || !v[4])))
+ return (v[0] == 't' || v[0] == 'T');
+ if ((!strncasecmp(v, "no" , 2) && (v[2] == ':' || !v[2])) ||
+ (!strncasecmp(v, "yes", 3) && (v[3] == ':' || !v[3])))
+ return (v[0] == 'y' || v[0] == 'Y');
+ if ((!strncasecmp(v, "on" , 2) && (v[2] == ':' || !v[2])) ||
+ (!strncasecmp(v, "off", 3) && (v[3] == ':' || !v[3])))
+ return (v[1] == 'n' || v[1] == 'N');
+ }
+ else if (*v == '\0')
+ return !defval; /* hmm... is this right */
+ }
+
+ return defval;
+}
+
+
+int mrp_env_config_string(const char *cfg, const char *key,
+ const char *defval, char *buf, size_t size)
+{
+ const char *v;
+ char *end;
+ int len;
+
+ v = mrp_env_config_key(cfg, key);
+
+ if (v == NULL)
+ v = defval;
+
+ end = strchr(v, ':');
+
+ if (end != NULL)
+ len = end - v;
+ else
+ len = strlen(v);
+
+ len = snprintf(buf, size, "%*.*s", len, len, v);
+
+ if (len >= (int)size - 1)
+ buf[size - 1] = '\0';
+
+ return len;
+}
diff --git a/src/common/env.h b/src/common/env.h
new file mode 100644
index 0000000..1a739e8
--- /dev/null
+++ b/src/common/env.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2012 - 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_ENV_H__
+#define __MURPHY_ENV_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/** Extract the value for the given key from the config string. */
+const char *mrp_env_config_key(const char *config, const char *key);
+
+/** Extract an int32 value for key, returning defval if not found. */
+int32_t mrp_env_config_int32(const char *cfg, const char *key, int32_t defval);
+
+/** Extract an uint32 value for key, returning defval if not found. */
+uint32_t mrp_env_config_uint32(const char *cfg, const char *key,
+ uint32_t defval);
+
+/** Extract a boolean value for key, returning defval if not found. */
+bool mrp_env_config_bool(const char *config, const char *key, bool defval);
+
+/** Extract a string value for key to buf (of size), defaulting to defval. */
+int mrp_env_config_string(const char *cfg, const char *key,
+ const char *defval, char *buf, size_t size);
+
+#endif /* __MURPHY_ENV_H__ */
diff --git a/src/common/file-utils.c b/src/common/file-utils.c
new file mode 100644
index 0000000..ce78c80
--- /dev/null
+++ b/src/common/file-utils.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/file-utils.h>
+
+
+static const char *translate_glob(const char *pattern, char *glob, size_t size)
+{
+ MRP_UNUSED(glob);
+ MRP_UNUSED(size);
+
+ /* XXX FIXME: translate pattern to glob-like */
+
+ return pattern;
+}
+
+
+static inline mrp_dirent_type_t dirent_type(mode_t mode)
+{
+#define MAP_TYPE(x, y) if (S_IS##x(mode)) return MRP_DIRENT_##y
+
+ MAP_TYPE(REG, REG);
+ MAP_TYPE(DIR, DIR);
+ MAP_TYPE(LNK, LNK);
+ MAP_TYPE(CHR, CHR);
+ MAP_TYPE(BLK, BLK);
+ MAP_TYPE(FIFO, FIFO);
+ MAP_TYPE(SOCK, SOCK);
+
+ return MRP_DIRENT_UNKNOWN;
+
+#undef MAP_TYPE
+}
+
+
+int mrp_scan_dir(const char *path, const char *pattern, mrp_dirent_type_t mask,
+ mrp_scan_dir_cb_t cb, void *user_data)
+{
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ regex_t regexp;
+ const char *prefix;
+ char glob[1024], file[PATH_MAX];
+ size_t size;
+ int stop;
+ mrp_dirent_type_t type;
+
+ if ((dp = opendir(path)) == NULL)
+ return FALSE;
+
+ if (pattern != NULL) {
+ prefix = MRP_PATTERN_GLOB;
+ size = sizeof(MRP_PATTERN_GLOB) - 1;
+
+ if (!strncmp(pattern, prefix, size)) {
+ pattern = translate_glob(pattern + size, glob, sizeof(glob));
+
+ if (pattern == NULL) {
+ closedir(dp);
+ return FALSE;
+ }
+ }
+ else {
+ prefix = MRP_PATTERN_REGEX;
+ size = sizeof(MRP_PATTERN_REGEX) - 1;
+
+ if (!strncmp(pattern, prefix, size))
+ pattern += size;
+ }
+
+ if (regcomp(&regexp, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
+ closedir(dp);
+ return FALSE;
+ }
+ }
+
+ stop = FALSE;
+ while ((de = readdir(dp)) != NULL && !stop) {
+ if (pattern != NULL && regexec(&regexp, de->d_name, 0, NULL, 0) != 0)
+ continue;
+
+ snprintf(file, sizeof(file), "%s/%s", path, de->d_name);
+
+ if (((mask & MRP_DIRENT_LNK ? lstat : stat))(file, &st) != 0)
+ continue;
+
+ type = dirent_type(st.st_mode);
+ if (!(type & mask))
+ continue;
+
+ stop = !cb(de->d_name, type, user_data);
+ }
+
+
+ closedir(dp);
+ if (pattern != NULL)
+ regfree(&regexp);
+
+ return TRUE;
+}
+
+
+int mrp_find_file(const char *file, const char **dirs, int mode, char *buf,
+ size_t size)
+{
+ const char *dir;
+ char path[PATH_MAX];
+ int i;
+
+ if (file[0] != '/') {
+ if (dirs != NULL) {
+ for (dir = dirs[i=0]; dir != NULL; dir = dirs[++i]) {
+ if (snprintf(path, sizeof(path), "%s/%s",
+ dir, file) >= (ssize_t)sizeof(path))
+ continue;
+
+ if (access(path, mode) == 0) {
+ file = path;
+ goto found;
+ }
+ }
+ }
+
+ if (snprintf(path, sizeof(path), "./%s", file) < (ssize_t)sizeof(path)) {
+ if (access(path, mode) == 0) {
+ file = path;
+ goto found;
+ }
+ }
+ }
+ else {
+ if (access(file, mode) == 0)
+ goto found;
+ }
+
+ errno = ENOENT;
+ return -1;
+
+ found:
+ if (buf != NULL && size > 0)
+ snprintf(buf, size, "%s", file);
+
+ return 0;
+}
+
+
+int mrp_mkdir(const char *path, mode_t mode)
+{
+ const char *p;
+ char *q, buf[PATH_MAX];
+ int n, undo[PATH_MAX / 2];
+ struct stat st;
+
+ if (path == NULL || path[0] == '\0') {
+ errno = path ? EINVAL : EFAULT;
+ return -1;
+ }
+
+ /*
+ * Notes:
+ * Our directory creation algorithm logic closely resembles what
+ * 'mkdir -p' does. We simply walk the given path component by
+ * component, testing if each one exist. If an existing one is
+ * not a directory we bail out. Missing ones we try to create with
+ * the given mode, bailing out if we fail.
+ *
+ * Unlike 'mkdir -p' whenever we fail we clean up by removing
+ * all directories we have created (or at least we try).
+ *
+ * Similarly to 'mkdir -p' we don't try to be overly 'smart' about
+ * the path we're handling. Especially we never try to treat '..'
+ * in any special way. This is very much intentional and the idea
+ * is to let the caller try to create a full directory hierarchy
+ * atomically, either succeeeding creating the full hierarchy, or
+ * none of it. To see the consequences of these design choices,
+ * consider what are the possible outcomes of a call like
+ *
+ * mrp_mkdir("/home/kli/bin/../sbin/../scripts/../etc/../doc", 0755);
+ */
+
+ p = path;
+ q = buf;
+ n = 0;
+ while (1) {
+ if (q - buf >= (ptrdiff_t)sizeof(buf) - 1) {
+ errno = ENAMETOOLONG;
+ goto cleanup;
+ }
+
+ if (*p && *p != '/') {
+ *q++ = *p++;
+ continue;
+ }
+
+ *q = '\0';
+
+ mrp_debug("checking/creating '%s'...", buf);
+
+ if (q != buf) {
+ if (stat(buf, &st) < 0) {
+ if (errno != ENOENT)
+ goto cleanup;
+
+ if (mkdir(buf, mode) < 0)
+ goto cleanup;
+
+ undo[n++] = q - buf;
+ }
+ else {
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ goto cleanup;
+ }
+ }
+ }
+
+ while (*p == '/')
+ p++;
+
+ if (!*p)
+ break;
+
+ *q++ = '/';
+ }
+
+ return 0;
+
+ cleanup:
+ while (--n >= 0) {
+ buf[undo[n]] = '\0';
+ mrp_debug("cleaning up '%s'...", buf);
+ rmdir(buf);
+ }
+
+ return -1;
+}
+
+
+char *mrp_normalize_path(char *buf, size_t size, const char *path)
+{
+ const char *p;
+ char *q;
+ int n, back[PATH_MAX / 2];
+
+ if (path == NULL)
+ return NULL;
+
+ if (*path == '\0') {
+ if (size > 0) {
+ *buf = '\0';
+ return buf;
+ }
+ else {
+ overflow:
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ }
+
+ p = path;
+ q = buf;
+ n = 0;
+
+ while (*p) {
+ if (q - buf + 1 >= (ptrdiff_t)size)
+ goto overflow;
+
+ if (*p == '/') {
+ back[n++] = q - buf;
+ *q++ = *p++;
+
+ skip_slashes:
+ while (*p == '/')
+ p++;
+
+ /*
+ * '.'
+ *
+ * We skip './' including all trailing slashes. Note that
+ * the code is arranged so that whenever we skip trailing
+ * slashes, we automatically check and skip also trailing
+ * './'s too...
+ */
+
+ if (p[0] == '.' && (p[1] == '/' || p[1] == '\0')) {
+ p++;
+ goto skip_slashes;
+ }
+
+ /*
+ * '..'
+ *
+ * We throw away the last incorrectly saved backtracking
+ * point (we saved it for this '../'). Then if we can still
+ * backtrack, we do so. Otherwise (we're at the beginning
+ * already), if the path is absolute, we just ignore the
+ * current '../' (can't go above '/'), otherwise we keep it
+ * for relative pathes.
+ */
+
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
+ n--; /* throw away */
+ if (n > 0) { /* can still backtrack */
+ if (back[n - 1] >= 0) /* previous not a '..' */
+ q = buf + back[n - 1] + 1;
+ }
+ else {
+ if (q > buf && buf[0] == '/') /* for absolute pathes */
+ q = buf + 1; /* reset to start */
+ else { /* for relative pathes */
+ if (q - buf + 4 >= (ptrdiff_t)size)
+ goto overflow;
+
+ q[0] = '.'; /* append this '..' */
+ q[1] = '.';
+ q[2] = '/';
+ q += 3;
+ back[n] = -1; /* block backtracking */
+ }
+ }
+
+ p += 2;
+ goto skip_slashes;
+ }
+ }
+ else
+ *q++ = *p++;
+ }
+
+ /*
+ * finally for other than '/' align trailing slashes
+ */
+
+ if (p > path + 1 && p[-1] != '/')
+ if (q > buf + 1 && q[-1] == '/')
+ q--;
+
+ *q = '\0';
+
+ return buf;
+}
diff --git a/src/common/file-utils.h b/src/common/file-utils.h
new file mode 100644
index 0000000..dd7266c
--- /dev/null
+++ b/src/common/file-utils.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_FILEUTILS_H__
+#define __MURPHY_FILEUTILS_H__
+
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * Routines for scanning a directory for matching entries.
+ */
+
+/** Directory entry types. */
+typedef enum {
+ MRP_DIRENT_UNKNOWN = 0, /* unknown */
+ MRP_DIRENT_NONE = 0x00, /* unknown */
+ MRP_DIRENT_FIFO = 0x01, /* FIFO */
+ MRP_DIRENT_CHR = 0x02, /* character device */
+ MRP_DIRENT_DIR = 0x04, /* directory */
+ MRP_DIRENT_BLK = 0x08, /* block device */
+ MRP_DIRENT_REG = 0x10, /* regular file */
+ MRP_DIRENT_LNK = 0x20, /* symbolic link */
+ MRP_DIRENT_SOCK = 0x40, /* socket */
+ MRP_DIRENT_ANY = 0xff, /* mask of all types */
+} mrp_dirent_type_t;
+
+
+#define MRP_PATTERN_GLOB "glob:" /* a globbing pattern */
+#define MRP_PATTERN_REGEX "regex:" /* a regexp pattern */
+
+
+/** Directory scanning callback type. */
+typedef int (*mrp_scan_dir_cb_t)(const char *entry, mrp_dirent_type_t type,
+ void *user_data);
+
+/** Scan a directory, calling cb with all matching entries. */
+int mrp_scan_dir(const char *path, const char *pattern, mrp_dirent_type_t mask,
+ mrp_scan_dir_cb_t cb, void *user_data);
+
+/** Do an #include-like search for the given file among the given dirs. */
+int mrp_find_file(const char *file, const char **dirs, int mode, char *buf,
+ size_t size);
+
+/** Create a directory, creating leading path as necessary. */
+int mrp_mkdir(const char *path, mode_t mode);
+
+
+/** Parse a path into a normalized form, removing ../'s and ./'s. */
+char *mrp_normalize_path(char *buf, size_t size, const char *path);
+
+#endif /* __MURPHY_FILEUTILS_H__ */
diff --git a/src/common/fragbuf.c b/src/common/fragbuf.c
new file mode 100644
index 0000000..740e138
--- /dev/null
+++ b/src/common/fragbuf.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <endian.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/fragbuf.h>
+
+struct mrp_fragbuf_s {
+ void *data; /* actual data buffer */
+ int size; /* size of the buffer */
+ int used; /* amount of data in the bufer */
+ int framed : 1; /* whether data is framed */
+};
+
+
+static void *fragbuf_ensure(mrp_fragbuf_t *buf, size_t size)
+{
+ int more;
+
+ if (buf->size - buf->used < (int)size) {
+ more = size - (buf->size - buf->used);
+
+ if (mrp_reallocz(buf->data, buf->size, buf->size + more) == NULL)
+ return NULL;
+ else
+ buf->size += more;
+ }
+
+ return buf->data + buf->used;
+}
+
+
+size_t mrp_fragbuf_used(mrp_fragbuf_t *buf)
+{
+ return buf->used;
+}
+
+
+size_t mrp_fragbuf_missing(mrp_fragbuf_t *buf)
+{
+ void *ptr;
+ int offs;
+ uint32_t size;
+
+ if (!buf->framed || !buf->used)
+ return 0;
+
+ /* find the last frame */
+ ptr = buf->data;
+ offs = 0;
+ while (offs < buf->used) {
+ size = be32toh(*(uint32_t *)ptr);
+ offs += sizeof(size) + size;
+ }
+
+ /* get the amount of data missing */
+ return offs - buf->used;
+}
+
+
+int fragbuf_init(mrp_fragbuf_t *buf, int framed, int pre_alloc)
+{
+ buf->data = NULL;
+ buf->size = 0;
+ buf->used = 0;
+ buf->framed = framed;
+
+ if (pre_alloc <= 0 || fragbuf_ensure(buf, pre_alloc))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+mrp_fragbuf_t *mrp_fragbuf_create(int framed, size_t pre_alloc)
+{
+ mrp_fragbuf_t *buf;
+
+ buf = mrp_allocz(sizeof(*buf));
+
+ if (buf != NULL) {
+ if (fragbuf_init(buf, framed, pre_alloc))
+ return buf;
+
+ mrp_free(buf);
+ }
+
+ return NULL;
+}
+
+
+void mrp_fragbuf_reset(mrp_fragbuf_t *buf)
+{
+ if (buf != NULL) {
+ mrp_free(buf->data);
+ buf->data = NULL;
+ buf->size = 0;
+ buf->used = 0;
+ }
+}
+
+void mrp_fragbuf_destroy(mrp_fragbuf_t *buf)
+{
+ if (buf != NULL) {
+ mrp_free(buf->data);
+ mrp_free(buf);
+ }
+}
+
+
+void *mrp_fragbuf_alloc(mrp_fragbuf_t *buf, size_t size)
+{
+ void *ptr;
+
+ ptr = fragbuf_ensure(buf, size);
+
+ if (ptr != NULL)
+ buf->used += size;
+
+ return ptr;
+}
+
+
+int mrp_fragbuf_trim(mrp_fragbuf_t *buf, void *ptr, size_t osize, size_t nsize)
+{
+ size_t diff;
+
+ if (ptr + osize == buf->data + buf->used) { /* looks like the last alloc */
+ if (nsize <= osize) {
+ diff = osize - nsize;
+ buf->used -= diff;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_fragbuf_push(mrp_fragbuf_t *buf, void *data, size_t size)
+{
+ void *ptr;
+
+ ptr = fragbuf_ensure(buf, size);
+
+ if (ptr != NULL) {
+ memcpy(ptr, data, size);
+ buf->used += size;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_fragbuf_pull(mrp_fragbuf_t *buf, void **datap, size_t *sizep)
+{
+ void *data;
+ uint32_t size;
+
+ if (buf == NULL || buf->used <= 0)
+ return FALSE;
+
+ if (MRP_UNLIKELY(*datap &&
+ (*datap < buf->data || *datap > buf->data + buf->used))) {
+ mrp_log_warning("%s(): *** looks like we're called with an unreset "
+ "datap pointer... ***", __FUNCTION__);
+ }
+
+ /* start of iteration */
+ if (*datap == NULL) {
+ if (!buf->framed) {
+ *datap = buf->data;
+ *sizep = buf->used;
+
+ return TRUE;
+ }
+ else {
+ if (buf->used < (int)sizeof(size))
+ return FALSE;
+
+ size = be32toh(*(uint32_t *)buf->data);
+
+ if (buf->used >= (int)(sizeof(size) + size)) {
+ *datap = buf->data + sizeof(size);
+ *sizep = size;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ }
+ /* continue iteration */
+ else {
+ if (!buf->framed) {
+ data = *datap + *sizep;
+
+ if (buf->data <= data && data < buf->data + buf->used) {
+ memmove(buf->data, data, buf->used - (data - buf->data));
+ buf->used -= (data - buf->data);
+
+ *datap = buf->data;
+ *sizep = buf->used;
+
+ return TRUE;
+ }
+ else {
+ if (data == buf->data + buf->used)
+ buf->used = 0;
+
+ return FALSE;
+ }
+ }
+ else {
+ if (*datap != buf->data + sizeof(size))
+ return FALSE;
+
+ size = be32toh(*(uint32_t *)buf->data);
+
+ if ((int)(size + sizeof(size)) <= buf->used) {
+ memmove(buf->data, buf->data + size + sizeof(size),
+ buf->used - (size + sizeof(size)));
+ buf->used -= size + sizeof(size);
+ }
+ else
+ return FALSE;
+
+ if (buf->used <= (int)sizeof(size))
+ return FALSE;
+
+ size = be32toh(*(uint32_t *)buf->data);
+ data = buf->data + sizeof(size);
+
+ if (buf->used >= (int)(size + sizeof(size))) {
+ *datap = data;
+ *sizep = size;
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ }
+}
diff --git a/src/common/fragbuf.h b/src/common/fragbuf.h
new file mode 100644
index 0000000..6e2ecc6
--- /dev/null
+++ b/src/common/fragbuf.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_FRAGBUF_H__
+#define __MURPHY_FRAGBUF_H__
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * Fragment collector buffers.
+ *
+ * As the name implies, a fragment collector buffer can be used
+ * to collect message fragments and reassemble messages that were
+ * transmitted in arbitrary pieces.
+ *
+ * Messages are expected to be transmitted in frames where each
+ * frame simply consist of a 32-bit message size followed by
+ * the actual message data. On the sending side you can simply
+ * send each message prefixed with its size. On the receiving side
+ * you keep feeding the received chunks of data to a fragment
+ * collector buffer (using mrp_fragbuf_push). After each chunk you
+ * can iterate through the fully reassembled messages (by calling
+ * mrp_fragbuf_pull until it returns FALSE). Messages are removed
+ * automatically from the collector buffer as you iterate through
+ * them.
+ *
+ * You can also create a collector buffer in frameless mode. Such a
+ * buffer will always return immediately all available data as you
+ * iterate through it.
+ */
+
+/** Buffer for collecting fragments of (framed or unframed) message data. */
+typedef struct mrp_fragbuf_s mrp_fragbuf_t;
+
+/** Initialize the given fragment collector buffer. */
+mrp_fragbuf_t *mrp_fragbuf_create(int framed, size_t pre_alloc);
+
+/** Initialize the given data collector buffer. */
+int mrp_fragbuf_init(mrp_fragbuf_t *buf, int framed, size_t pre_alloc);
+
+/** Reset the given data collector buffer. */
+void mrp_fragbuf_reset(mrp_fragbuf_t *buf);
+
+/** Destroy the given data collector buffer, freeing all associated memory. */
+void mrp_fragbuf_destroy(mrp_fragbuf_t *buf);
+
+/** Return the amount of buffer space currently in used in th buffer. */
+size_t mrp_fragbuf_used(mrp_fragbuf_t *buf);
+
+/** Return the amount of bytes missing from the last message. */
+size_t mrp_fragbuf_missing(mrp_fragbuf_t *buf);
+
+/** Allocate a buffer of the given size from the buffer. */
+void *mrp_fragbuf_alloc(mrp_fragbuf_t *buf, size_t size);
+
+/** Trim the last allocation to nsize bytes. */
+int mrp_fragbuf_trim(mrp_fragbuf_t *buf, void *ptr, size_t osize, size_t nsize);
+
+/** Append the given data to the buffer. */
+int mrp_fragbuf_push(mrp_fragbuf_t *buf, void *data, size_t size);
+
+/** Iterate through the given buffer, pulling and freeing assembled messages. */
+int mrp_fragbuf_pull(mrp_fragbuf_t *buf, void **data, size_t *size);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_FRAGBUF_H__ */
diff --git a/src/common/glib-glue.c b/src/common/glib-glue.c
new file mode 100644
index 0000000..2df2ea7
--- /dev/null
+++ b/src/common/glib-glue.c
@@ -0,0 +1,362 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+
+typedef struct {
+ GMainLoop *gml;
+} glib_glue_t;
+
+
+typedef struct {
+ GIOChannel *gl_ioc;
+ guint gl_iow;
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ mrp_io_event_t mask;
+ void *user_data;
+ void *glue_data;
+} io_t;
+
+
+typedef struct {
+ guint gl_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} tmr_t;
+
+
+typedef struct {
+ guint gl_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} dfr_t;
+
+
+#define D(fmt, args...) do { \
+ printf("* [%s]: "fmt"\n", __FUNCTION__ , ## args); \
+ } while (0)
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+static void del_io(void *glue_data, void *id);
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_timer(void *glue_data, void *id);
+static void mod_timer(void *glue_data, void *id, unsigned int msecs);
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_defer(void *glue_data, void *id);
+static void mod_defer(void *glue_data, void *id, int enabled);
+
+
+static gboolean io_cb(GIOChannel *ioc, GIOCondition cond, gpointer user_data)
+{
+ io_t *io = (io_t *)user_data;
+ mrp_io_event_t events = MRP_IO_EVENT_NONE;
+ int fd = g_io_channel_unix_get_fd(ioc);
+
+ if (cond & G_IO_IN)
+ events |= MRP_IO_EVENT_IN;
+ if (cond & G_IO_OUT)
+ events |= MRP_IO_EVENT_OUT;
+ if (cond & G_IO_ERR)
+ events |= MRP_IO_EVENT_ERR;
+ if (cond & G_IO_HUP)
+ events |= MRP_IO_EVENT_HUP;
+
+ io->cb(io->glue_data, io, fd, events, io->user_data);
+
+ return TRUE;
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ GIOCondition mask = 0;
+ GIOChannel *ioc;
+ io_t *io;
+
+ ioc = g_io_channel_unix_new(fd);
+
+ if (ioc == NULL)
+ return NULL;
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io != NULL) {
+ if (events & MRP_IO_EVENT_IN ) mask |= G_IO_IN;
+ if (events & MRP_IO_EVENT_OUT) mask |= G_IO_OUT;
+ if (events & MRP_IO_EVENT_HUP) mask |= G_IO_HUP;
+ if (events & MRP_IO_EVENT_ERR) mask |= G_IO_ERR;
+
+ io->mask = events;
+ io->gl_ioc = ioc;
+ io->gl_iow = g_io_add_watch(ioc, mask, io_cb, io);
+
+ if (io->gl_iow != 0) {
+ io->cb = cb;
+ io->user_data = user_data;
+ io->glue_data = glue_data;
+
+ return io;
+ }
+ else {
+ g_io_channel_unref(ioc);
+ mrp_free(io);
+ }
+ }
+
+ return NULL;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ io_t *io = (io_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ g_source_remove(io->gl_iow);
+ g_io_channel_unref(io->gl_ioc);
+ mrp_free(io);
+}
+
+
+static gboolean timer_cb(gpointer user_data)
+{
+ tmr_t *t = (tmr_t *)user_data;
+
+ t->cb(t->glue_data, t, t->user_data);
+
+ return TRUE;
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ tmr_t *t;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ t->gl_t = g_timeout_add(msecs, timer_cb, t);
+
+ if (t->gl_t != 0) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->glue_data = glue_data;
+
+ return t;
+ }
+ else
+ mrp_free(t);
+ }
+
+ return NULL;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ tmr_t *t = (tmr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ g_source_remove(t->gl_t);
+ mrp_free(t);
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ tmr_t *t = (tmr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (t != NULL) {
+ g_source_remove(t->gl_t);
+ t->gl_t = g_timeout_add(msecs, timer_cb, t);
+ }
+}
+
+
+static gboolean defer_cb(void *user_data)
+{
+ dfr_t *d = (dfr_t *)user_data;
+
+ d->cb(d->glue_data, d, d->user_data);
+
+ return TRUE;
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ dfr_t *d;
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d != NULL) {
+ d->gl_t = g_timeout_add(0, defer_cb, d);
+
+ if (d->gl_t != 0) {
+ d->cb = cb;
+ d->user_data = user_data;
+ d->glue_data = glue_data;
+
+ return d;
+ }
+ else
+ mrp_free(d);
+ }
+
+ return NULL;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (d->gl_t != 0)
+ g_source_remove(d->gl_t);
+
+ mrp_free(d);
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (enabled && !d->gl_t)
+ d->gl_t = g_timeout_add(0, defer_cb, d);
+ else if (!enabled && d->gl_t) {
+ g_source_remove(d->gl_t);
+ d->gl_t = 0;
+ }
+}
+
+
+static void unregister(void *data)
+{
+ glib_glue_t *glue = (glib_glue_t *)data;
+
+ g_main_loop_unref(glue->gml);
+
+ mrp_free(glue);
+}
+
+
+static mrp_superloop_ops_t glib_ops = {
+ .add_io = add_io,
+ .del_io = del_io,
+ .add_timer = add_timer,
+ .del_timer = del_timer,
+ .mod_timer = mod_timer,
+ .add_defer = add_defer,
+ .del_defer = del_defer,
+ .mod_defer = mod_defer,
+ .unregister = unregister,
+};
+
+
+int mrp_mainloop_register_with_glib(mrp_mainloop_t *ml, GMainLoop *gml)
+{
+ glib_glue_t *glue;
+
+ glue = mrp_allocz(sizeof(*glue));
+
+ if (glue != NULL) {
+ glue->gml = g_main_loop_ref(gml);
+
+ if (mrp_set_superloop(ml, &glib_ops, glue))
+ return TRUE;
+ else {
+ g_main_loop_unref(gml);
+ mrp_free(glue);
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_mainloop_unregister_from_glib(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+mrp_mainloop_t *mrp_mainloop_glib_get(GMainLoop *gml)
+{
+ mrp_mainloop_t *ml;
+
+ if (gml != NULL) {
+ ml = mrp_mainloop_create();
+
+ if (ml != NULL) {
+ if (mrp_mainloop_register_with_glib(ml, gml))
+ return ml;
+ else
+ mrp_mainloop_destroy(ml);
+ }
+ }
+
+ return NULL;
+}
diff --git a/src/common/glib-glue.h b/src/common/glib-glue.h
new file mode 100644
index 0000000..88fef45
--- /dev/null
+++ b/src/common/glib-glue.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_GLIB_H__
+#define __MURPHY_GLIB_H__
+
+#include <murphy/common/mainloop.h>
+#include <glib.h>
+
+MRP_CDECL_BEGIN
+
+/** Register the given murphy mainloop with the glib mainloop. */
+int mrp_mainloop_register_with_glib(mrp_mainloop_t *ml, GMainLoop *gml);
+
+/** Unrgister the given murphy mainloop from the glib mainloop. */
+int mrp_mainloop_unregister_from_glib(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the glib mainloop. */
+mrp_mainloop_t *mrp_mainloop_glib_get(GMainLoop *gml);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_GLIB_H__ */
diff --git a/src/common/hashtbl.c b/src/common/hashtbl.c
new file mode 100644
index 0000000..3f49d83
--- /dev/null
+++ b/src/common/hashtbl.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+#include "murphy/common/mm.h"
+#include "murphy/common/list.h"
+#include "murphy/common/hashtbl.h"
+
+#define MIN_NBUCKET 8
+#define MAX_NBUCKET 128
+
+typedef struct { /* a hash bucket */
+ mrp_list_hook_t entries; /* hook to hash table entries */
+ mrp_list_hook_t used; /* hook to list of buckets in use */
+} bucket_t;
+
+typedef struct { /* a hash table entry */
+ mrp_list_hook_t hook; /* hook to bucket chain */
+ void *key; /* key for this entry */
+ void *obj; /* object for this entry */
+} entry_t;
+
+typedef struct { /* iterator state */
+ mrp_list_hook_t *bp, *bn; /* current bucket hook pointers */
+ mrp_list_hook_t *ep, *en; /* current entry hook pointers */
+ entry_t *entry; /* current entry */
+ int verdict; /* remove-from-cb verdict */
+} iter_t;
+
+struct mrp_htbl_s {
+ bucket_t *buckets; /* hash table buckets */
+ size_t nbucket; /* this many of them */
+ mrp_list_hook_t used; /* buckets in use */
+ mrp_htbl_comp_fn_t comp; /* key comparison function */
+ mrp_htbl_hash_fn_t hash; /* key hash function */
+ mrp_htbl_free_fn_t free; /* function to free an entry */
+ iter_t *iter; /* active iterator state */
+};
+
+
+static size_t calc_buckets(size_t nbucket)
+{
+ size_t n;
+
+ if (nbucket < MIN_NBUCKET)
+ nbucket = MIN_NBUCKET;
+ if (nbucket > MAX_NBUCKET)
+ nbucket = MAX_NBUCKET;
+
+ for (n = MIN_NBUCKET; n < nbucket; n <<= 1)
+ ;
+
+ return n;
+}
+
+
+mrp_htbl_t *mrp_htbl_create(mrp_htbl_config_t *cfg)
+{
+ mrp_htbl_t *ht;
+ size_t i, nbucket;
+
+ if (cfg->comp && cfg->hash) {
+ if ((ht = mrp_allocz(sizeof(*ht))) != NULL) {
+ if (cfg->nbucket != 0)
+ nbucket = cfg->nbucket;
+ else {
+ if (cfg->nentry != 0)
+ nbucket = cfg->nentry / 4;
+ else
+ nbucket = 4 * MIN_NBUCKET;
+ }
+
+ ht->nbucket = calc_buckets(nbucket);
+ ht->comp = cfg->comp;
+ ht->hash = cfg->hash;
+ ht->free = cfg->free;
+
+ mrp_list_init(&ht->used);
+
+ ht->buckets = mrp_allocz(sizeof(*ht->buckets) * ht->nbucket);
+ if (ht->buckets != NULL) {
+ for (i = 0; i < ht->nbucket; i++) {
+ mrp_list_init(&ht->buckets[i].entries);
+ mrp_list_init(&ht->buckets[i].used);
+ }
+
+ return ht;
+ }
+ else {
+ mrp_free(ht);
+ ht = NULL;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+void mrp_htbl_destroy(mrp_htbl_t *ht, int free)
+{
+ if (ht != NULL) {
+ if (free)
+ mrp_htbl_reset(ht, free);
+
+ mrp_free(ht->buckets);
+ mrp_free(ht);
+ }
+}
+
+
+static inline void free_entry(mrp_htbl_t *ht, entry_t *entry, int free)
+{
+ if (free && ht->free)
+ ht->free(entry->key, entry->obj);
+ mrp_free(entry);
+}
+
+
+void mrp_htbl_reset(mrp_htbl_t *ht, int free)
+{
+ mrp_list_hook_t *bp, *bn, *ep, *en;
+ bucket_t *bucket;
+ entry_t *entry;
+
+ mrp_list_foreach(&ht->used, bp, bn) {
+ bucket = mrp_list_entry(bp, bucket_t, used);
+
+ mrp_list_foreach(&bucket->entries, ep, en) {
+ entry = mrp_list_entry(ep, entry_t, hook);
+ mrp_list_delete(ep);
+ free_entry(ht, entry, free);
+ }
+
+ mrp_list_delete(&bucket->used);
+ }
+}
+
+
+int mrp_htbl_insert(mrp_htbl_t *ht, void *key, void *object)
+{
+ uint32_t idx = ht->hash(key) & (ht->nbucket - 1);
+ bucket_t *bucket = ht->buckets + idx;
+ int first = mrp_list_empty(&bucket->entries);
+ entry_t *entry;
+
+ if ((entry = mrp_allocz(sizeof(*entry))) != NULL) {
+ entry->key = key;
+ entry->obj = object;
+ mrp_list_append(&bucket->entries, &entry->hook);
+ if (first)
+ mrp_list_append(&ht->used, &bucket->used);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static inline entry_t *lookup(mrp_htbl_t *ht, void *key, bucket_t **bucketp)
+{
+ uint32_t idx = ht->hash(key) & (ht->nbucket - 1);
+ bucket_t *bucket = ht->buckets + idx;
+ mrp_list_hook_t *p, *n;
+ entry_t *entry;
+
+ mrp_list_foreach(&bucket->entries, p, n) {
+ entry = mrp_list_entry(p, entry_t, hook);
+
+ if (!ht->comp(entry->key, key)) {
+ if (bucketp != NULL)
+ *bucketp = bucket;
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+
+void *mrp_htbl_lookup(mrp_htbl_t *ht, void *key)
+{
+ entry_t *entry;
+
+ entry = lookup(ht, key, NULL);
+ if (entry != NULL)
+ return entry->obj;
+ else
+ return NULL;
+}
+
+
+static void delete_from_bucket(mrp_htbl_t *ht, bucket_t *bucket, entry_t *entry)
+{
+ mrp_list_hook_t *eh = &entry->hook;
+
+
+ /*
+ * If there is an iterator active and this entry would
+ * have been the next one to iterate over, we need to
+ * update the iterator to skip to the next entry instead
+ * as this one will be removed. Failing to update the
+ * iterator could crash mrp_htbl_foreach or drive it into
+ * an infinite loop.
+ */
+
+ if (ht->iter != NULL && ht->iter->en == eh)
+ ht->iter->en = eh->next;
+
+ mrp_list_delete(eh);
+
+
+ /*
+ * If the bucket became empty, unlink it from the used list.
+ * If also there is an iterator active and this bucket would
+ * have been the next one to iterate over, we need to
+ * update the iterator to skip to the next bucket instead
+ * as this one just became empty and will be removed from
+ * the used bucket list. Failing to update the iterator
+ * could drive mrp_htbl_foreach into an infinite loop
+ * because of the unexpected hop from the used bucket list
+ * (to a single empty bucket).
+ */
+
+ if (mrp_list_empty(&bucket->entries)) {
+ if (ht->iter != NULL && ht->iter->bn == &bucket->used)
+ ht->iter->bn = bucket->used.next;
+
+ mrp_list_delete(&bucket->used);
+ }
+}
+
+
+void *mrp_htbl_remove(mrp_htbl_t *ht, void *key, int free)
+{
+ bucket_t *bucket;
+ entry_t *entry;
+ void *object;
+
+ /*
+ * We need to check the found entry and its hash-bucket
+ * against any potentially active iterator. Special care
+ * needs to be taken if the entry is being iterated over
+ * or if the bucket becomes empty and it would be the next
+ * bucket to iterate over. The former is taken care of
+ * here while the latter is handled in delete_from_bucket.
+ */
+ if ((entry = lookup(ht, key, &bucket)) != NULL) {
+ delete_from_bucket(ht, bucket, entry);
+ object = entry->obj;
+
+ if (ht->iter != NULL && entry == ht->iter->entry) /* being iterated */
+ ht->iter->verdict = free ? MRP_HTBL_ITER_DELETE : 0;
+ else {
+ free_entry(ht, entry, free);
+ }
+ }
+ else
+ object = NULL;
+
+ return object;
+}
+
+
+int mrp_htbl_foreach(mrp_htbl_t *ht, mrp_htbl_iter_cb_t cb, void *user_data)
+{
+ iter_t iter;
+ bucket_t *bucket;
+ entry_t *entry;
+ int cb_verdict, ht_verdict;
+
+ /*
+ * Now we can only handle a single callback-based iterator.
+ * If there is already one we're busy so just bail out.
+ */
+ if (ht->iter != NULL)
+ return FALSE;
+
+ mrp_clear(&iter);
+ ht->iter = &iter;
+
+ mrp_list_foreach(&ht->used, iter.bp, iter.bn) {
+ bucket = mrp_list_entry(iter.bp, bucket_t, used);
+
+ mrp_list_foreach(&bucket->entries, iter.ep, iter.en) {
+ iter.entry = entry = mrp_list_entry(iter.ep, entry_t, hook);
+ cb_verdict = cb(entry->key, entry->obj, user_data);
+ ht_verdict = iter.verdict;
+
+ /* delete was called from cb (unhashed entry and marked it) */
+ if (ht_verdict & MRP_HTBL_ITER_DELETE) {
+ free_entry(ht, entry, TRUE);
+ }
+ else {
+ /* cb wants us to unhash (safe even if unhashed in remove) */
+ if (cb_verdict & MRP_HTBL_ITER_UNHASH)
+ mrp_list_delete(iter.ep);
+ /* cb want us to free entry (and remove was not called) */
+ if (cb_verdict & MRP_HTBL_ITER_DELETE)
+ free_entry(ht, entry, TRUE);
+
+ /* cb wants to stop iterating */
+ if (!(cb_verdict & MRP_HTBL_ITER_MORE))
+ goto out;
+ }
+ }
+ }
+
+ out:
+ ht->iter = NULL;
+
+ return TRUE;
+}
+
+
+void *mrp_htbl_find(mrp_htbl_t *ht, mrp_htbl_find_cb_t cb, void *user_data)
+{
+ iter_t iter;
+ bucket_t *bucket;
+ entry_t *entry, *found;
+
+ /*
+ * Bail out if there is also an iterator active...
+ */
+ if (ht->iter != NULL)
+ return FALSE;
+
+ mrp_clear(&iter);
+ ht->iter = &iter;
+ found = NULL;
+
+ mrp_list_foreach(&ht->used, iter.bp, iter.bn) {
+ bucket = mrp_list_entry(iter.bp, bucket_t, used);
+
+ mrp_list_foreach(&bucket->entries, iter.ep, iter.en) {
+ entry = mrp_list_entry(iter.ep, entry_t, hook);
+
+ if (cb(entry->key, entry->obj, user_data)) {
+ found = entry->obj;
+ goto out;
+ }
+ }
+ }
+
+ out:
+ ht->iter = NULL;
+
+ return found;
+}
diff --git a/src/common/hashtbl.h b/src/common/hashtbl.h
new file mode 100644
index 0000000..6dd7c05
--- /dev/null
+++ b/src/common/hashtbl.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_HASHTBL_H__
+#define __MURPHY_HASHTBL_H__
+
+#include <stdint.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_htbl_s mrp_htbl_t;
+
+/** Prototype for key comparison functions. */
+typedef int (*mrp_htbl_comp_fn_t)(const void *key1, const void *key2);
+
+/** Prototoype for key hash functions. */
+typedef uint32_t (*mrp_htbl_hash_fn_t)(const void *key);
+
+/** Prototype for functions used to free entries. */
+typedef void (*mrp_htbl_free_fn_t)(void *key, void *object);
+
+
+/*
+ * hash table configuration
+ */
+typedef struct {
+ size_t nentry; /* estimated entries */
+ mrp_htbl_comp_fn_t comp; /* comparison function */
+ mrp_htbl_hash_fn_t hash; /* hash function */
+ mrp_htbl_free_fn_t free; /* freeing function */
+ size_t nbucket; /* number of buckets, or 0 */
+} mrp_htbl_config_t;
+
+
+/** Create a new hash table with the given configuration. */
+mrp_htbl_t *mrp_htbl_create(mrp_htbl_config_t *cfg);
+
+/** Destroy a hash table, free all entries unless @free is FALSE. */
+void mrp_htbl_destroy(mrp_htbl_t *ht, int free);
+
+/** Reset a hash table, also free all entries unless @free is FALSE. */
+void mrp_htbl_reset(mrp_htbl_t *ht, int free);
+
+/** Insert the given @key/@object pair to the hash table. */
+int mrp_htbl_insert(mrp_htbl_t *ht, void *key, void *object);
+
+/** Remove and return the object for @key, also free unless @free is FALSE. */
+void *mrp_htbl_remove(mrp_htbl_t *ht, void *key, int free);
+
+/** Look up the object corresponding to @key. */
+void *mrp_htbl_lookup(mrp_htbl_t *ht, void *key);
+
+/** Find the first matching entry in a hash table. */
+typedef int (*mrp_htbl_find_cb_t)(void *key, void *object, void *user_data);
+void *mrp_htbl_find(mrp_htbl_t *ht, mrp_htbl_find_cb_t cb, void *user_data);
+
+
+/*
+ * hash table iterators
+ */
+
+enum {
+ MRP_HTBL_ITER_STOP = 0x0, /* stop iterating */
+ MRP_HTBL_ITER_MORE = 0x1, /* keep iterating */
+ MRP_HTBL_ITER_UNHASH = 0x2, /* unhash without freeing */
+ MRP_HTBL_ITER_DELETE = 0x6, /* unhash and free */
+};
+
+typedef int (*mrp_htbl_iter_cb_t)(void *key, void *object, void *user_data);
+int mrp_htbl_foreach(mrp_htbl_t *ht, mrp_htbl_iter_cb_t cb, void *user_data);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_HASHTBL_H__ */
diff --git a/src/common/internal-transport.c b/src/common/internal-transport.c
new file mode 100644
index 0000000..7ffae37
--- /dev/null
+++ b/src/common/internal-transport.c
@@ -0,0 +1,585 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <murphy/common.h>
+
+#define INTERNAL "internal"
+
+typedef struct internal_s internal_t;
+
+struct internal_s {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ char name[MRP_SOCKADDR_SIZE]; /* bound connection name */
+ mrp_sockaddr_t address; /* internal connection name*/
+ bool active;
+ bool bound;
+ bool listening;
+
+ internal_t *endpoint; /* that we are connected to */
+};
+
+typedef struct {
+ void *data;
+ size_t size;
+ internal_t *u;
+ mrp_sockaddr_t *addr;
+ socklen_t addrlen;
+ bool free_data;
+ int offset;
+ bool custom;
+ uint16_t tag;
+
+ mrp_list_hook_t hook;
+} internal_message_t;
+
+
+/* storage for the global data, TODO: refactor away */
+static mrp_htbl_t *servers = NULL;
+static mrp_htbl_t *connections = NULL;
+static mrp_list_hook_t msg_queue;
+static mrp_deferred_t *d;
+static uint32_t cid;
+
+
+static void process_queue(mrp_deferred_t *d, void *user_data)
+{
+ internal_message_t *msg;
+ internal_t *endpoint;
+ mrp_list_hook_t *p, *n;
+
+ MRP_UNUSED(user_data);
+
+ mrp_disable_deferred(d);
+
+ mrp_list_foreach(&msg_queue, p, n) {
+
+ msg = mrp_list_entry(p, typeof(*msg), hook);
+
+ if (!msg) {
+ mrp_log_error("no message!");
+ goto end;
+ }
+
+ if (!msg->u->connected) {
+ if (!msg->addr) {
+ mrp_log_error("connected transport without address!");
+ goto end;
+ }
+
+ /* Find the recipient. Look first from the server table.*/
+ endpoint = mrp_htbl_lookup(servers, msg->addr->data);
+
+ if (!endpoint) {
+
+ /* Look next from the general connections table. */
+ endpoint = mrp_htbl_lookup(connections, msg->addr->data);
+ }
+ }
+ else {
+ endpoint = msg->u->endpoint;
+ }
+
+ if (!endpoint || !endpoint->recv_data) {
+ mrp_log_error("no endpoint matching the address");
+ goto end;
+ }
+
+ /* skip the length word when sending */
+ endpoint->recv_data(
+ (mrp_transport_t *) endpoint, msg->data + msg->offset,
+ msg->size, &msg->u->address, MRP_SOCKADDR_SIZE);
+
+end:
+ if (msg) {
+
+ if (msg->free_data) {
+ if (msg->custom) {
+ /* FIXME: should be mrp_data_free(msg->data, msg->tag); */
+ mrp_free(msg->data);
+ }
+ else
+ mrp_msg_unref(msg->data);
+ }
+
+ mrp_list_delete(&msg->hook);
+ mrp_free(msg);
+ }
+ }
+}
+
+
+static int internal_initialize_table(internal_t *u)
+{
+ mrp_htbl_config_t servers_conf;
+ mrp_htbl_config_t connections_conf;
+
+ MRP_UNUSED(u);
+
+ if (servers && connections && d)
+ return 0; /* already initialized */
+
+ servers_conf.comp = mrp_string_comp;
+ servers_conf.hash = mrp_string_hash;
+ servers_conf.free = NULL;
+ servers_conf.nbucket = 0;
+ servers_conf.nentry = 10;
+
+ servers = mrp_htbl_create(&servers_conf);
+
+ if (!servers)
+ goto error;
+
+ connections_conf.comp = mrp_string_comp;
+ connections_conf.hash = mrp_string_hash;
+ connections_conf.free = NULL;
+ connections_conf.nbucket = 0;
+ connections_conf.nentry = 10;
+
+ connections = mrp_htbl_create(&connections_conf);
+
+ if (!connections)
+ goto error;
+
+ mrp_list_init(&msg_queue);
+
+ cid = 0;
+
+ d = mrp_add_deferred(u->ml, process_queue, NULL);
+
+ if (!d)
+ goto error;
+
+ mrp_disable_deferred(d);
+
+ return 0;
+
+error:
+
+ if (servers)
+ mrp_htbl_destroy(servers, FALSE);
+
+ servers = NULL;
+
+ if (connections)
+ mrp_htbl_destroy(connections, FALSE);
+
+ connections = NULL;
+
+ return -1;
+}
+
+
+static socklen_t internal_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ int len;
+
+ MRP_UNUSED(size);
+
+ if (!str)
+ return 0;
+
+ len = strlen(str);
+
+ if (len <= 9 || len >= MRP_SOCKADDR_SIZE)
+ return 0;
+
+ if (strncmp("internal:", str, 9))
+ return 0;
+
+ if (typep)
+ *typep = INTERNAL;
+
+ memcpy(addr->data, str+9, len-9+1);
+
+ return len-9;
+}
+
+
+static int internal_open(mrp_transport_t *mu)
+{
+ internal_t *u = (internal_t *)mu;
+
+ if (internal_initialize_table(u) < 0)
+ return FALSE;
+
+ memset(u->name, 0, MRP_SOCKADDR_SIZE);
+ memset(u->address.data, 0, MRP_SOCKADDR_SIZE);
+
+ u->active = FALSE;
+
+ snprintf(u->address.data, MRP_SOCKADDR_SIZE, INTERNAL"_%d", cid++);
+
+ mrp_htbl_insert(connections, u->address.data, mu);
+
+ return TRUE;
+}
+
+
+static int internal_bind(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+
+ if (internal_initialize_table(u) < 0)
+ return FALSE;
+
+ memcpy(u->name, addr->data, addrlen+1);
+
+ mrp_htbl_insert(servers, u->name, u);
+
+ u->active = TRUE;
+ u->bound = TRUE;
+
+ return TRUE;
+}
+
+
+static int internal_listen(mrp_transport_t *mu, int backlog)
+{
+ internal_t *u = (internal_t *)mu;
+
+ MRP_UNUSED(backlog);
+
+ if (!u->bound)
+ return FALSE;
+
+ u->listening = TRUE;
+
+ return TRUE;
+}
+
+
+static int internal_accept(mrp_transport_t *mt, mrp_transport_t *mlt)
+{
+ internal_t *t = (internal_t *) mt;
+ internal_t *lt = (internal_t *) mlt;
+ internal_t *client = lt->endpoint;
+
+ t->endpoint = client;
+ client->endpoint = t;
+
+ lt->endpoint = NULL; /* connection process is now over */
+
+ return TRUE;
+}
+
+
+static void remove_messages(internal_t *u)
+{
+ internal_message_t *msg;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&msg_queue, p, n) {
+ msg = mrp_list_entry(p, typeof(*msg), hook);
+
+ if (strcmp(msg->addr->data, u->name) == 0
+ || strcmp(msg->addr->data, u->address.data) == 0) {
+
+ if (msg->free_data) {
+ if (msg->custom) {
+ /* FIXME: should be mrp_data_free(msg->data, msg->tag); */
+ mrp_free(msg->data);
+ }
+ else
+ mrp_msg_unref(msg->data);
+ }
+
+ mrp_list_delete(&msg->hook);
+ mrp_free(msg);
+ }
+ }
+}
+
+
+static void internal_close(mrp_transport_t *mu)
+{
+ internal_t *u = (internal_t *)mu;
+
+ /* Is this client or server? If server, go remove the connection from
+ * servers table. */
+
+ if (u->bound) {
+ /* server listening socket */
+ mrp_htbl_remove(servers, u->name, FALSE);
+ u->bound = FALSE;
+ }
+
+ mrp_htbl_remove(connections, u->address.data, FALSE);
+
+ u->active = FALSE;
+
+ remove_messages(u);
+}
+
+
+static int internal_connect(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ internal_t *host;
+ mrp_transport_t *mt;
+
+ MRP_UNUSED(addrlen);
+
+ /* client connecting */
+
+ if (!servers) {
+ mrp_log_error("no servers available for connecting");
+ return FALSE;
+ }
+
+ host = mrp_htbl_lookup(servers, addr->data);
+
+ if (!host) {
+ mrp_log_error("server '%s' wasn't found", addr->data);
+ return FALSE;
+ }
+
+ mt = (mrp_transport_t *) host;
+
+ host->endpoint = u; /* temporary connection data */
+
+ host->evt.connection(mt, mt->user_data);
+
+ return TRUE;
+}
+
+
+static int internal_disconnect(mrp_transport_t *mu)
+{
+ internal_t *u = (internal_t *)mu;
+
+ if (u->connected) {
+ internal_t *endpoint = u->endpoint;
+
+ if (endpoint) {
+ endpoint->endpoint = NULL;
+ mrp_transport_disconnect((mrp_transport_t *) endpoint);
+ }
+ u->endpoint = NULL;
+ }
+
+ return TRUE;
+}
+
+
+static int internal_sendto(mrp_transport_t *mu, mrp_msg_t *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ void *buf;
+ size_t size;
+ internal_message_t *msg;
+
+ size = mrp_msg_default_encode(data, &buf);
+
+ if (size == 0 || buf == NULL) {
+ return FALSE;
+ }
+
+ msg = mrp_allocz(sizeof(internal_message_t));
+
+ if (!msg)
+ return FALSE;
+
+ msg->addr = addr;
+ msg->addrlen = addrlen;
+ msg->data = buf;
+ msg->free_data = FALSE;
+ msg->offset = 0;
+ msg->size = size;
+ msg->u = u;
+ msg->custom = FALSE;
+
+ mrp_list_init(&msg->hook);
+ mrp_list_append(&msg_queue, &msg->hook);
+
+ mrp_enable_deferred(d);
+
+ return TRUE;
+}
+
+
+static int internal_send(mrp_transport_t *mu, mrp_msg_t *msg)
+{
+ if (!mu->connected) {
+ return FALSE;
+ }
+
+ return internal_sendto(mu, msg, NULL, 0);
+}
+
+
+static int internal_sendrawto(mrp_transport_t *mu, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ internal_message_t *msg;
+
+ msg = mrp_allocz(sizeof(internal_message_t));
+
+ if (!msg)
+ return FALSE;
+
+ msg->addr = addr;
+ msg->addrlen = addrlen;
+ msg->data = data;
+ msg->free_data = FALSE;
+ msg->offset = 0;
+ msg->size = size;
+ msg->u = u;
+ msg->custom = FALSE;
+
+ mrp_list_init(&msg->hook);
+ mrp_list_append(&msg_queue, &msg->hook);
+
+ mrp_enable_deferred(d);
+
+ return TRUE;
+}
+
+
+static int internal_sendraw(mrp_transport_t *mu, void *data, size_t size)
+{
+ if (!mu->connected) {
+ return FALSE;
+ }
+
+ return internal_sendrawto(mu, data, size, NULL, 0);
+}
+
+
+static size_t encode_custom_data(void *data, void **newdata, uint16_t tag)
+{
+ mrp_data_descr_t *type = mrp_msg_find_type(tag);
+ uint32_t *lenp;
+ uint16_t *tagp;
+ size_t reserve, size;
+ int len;
+ void *buf;
+
+ if (type == NULL) {
+ mrp_log_error("type not found!");
+ return 0;
+ }
+
+ reserve = sizeof(*lenp) + sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size == 0) {
+ mrp_log_error("data encoding failed");
+ return 0;
+ }
+
+ /* some format conversion */
+
+ lenp = buf;
+ len = size - sizeof(*lenp);
+ tagp = buf + sizeof(*lenp);
+
+ *tagp = htobe16(tag);
+
+ *newdata = buf;
+
+ return len;
+}
+
+
+static int internal_senddatato(mrp_transport_t *mu, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ mrp_data_descr_t *type = mrp_msg_find_type(tag);
+ void *newdata = NULL;
+ size_t size;
+ internal_message_t *msg;
+
+ if (type == NULL)
+ return FALSE;
+
+ msg = mrp_allocz(sizeof(internal_message_t));
+
+ if (!msg)
+ return FALSE;
+
+ size = encode_custom_data(data, &newdata, tag);
+
+ if (!newdata) {
+ mrp_log_error("custom data encoding failed");
+ mrp_free(msg);
+ return FALSE;
+ }
+
+ msg->addr = addr;
+ msg->addrlen = addrlen;
+ msg->data = newdata;
+ msg->free_data = TRUE;
+ msg->offset = 4;
+ msg->size = size;
+ msg->u = u;
+ msg->custom = TRUE;
+ msg->tag = tag;
+
+ mrp_list_init(&msg->hook);
+ mrp_list_append(&msg_queue, &msg->hook);
+
+ mrp_enable_deferred(d);
+
+ return TRUE;
+}
+
+
+static int internal_senddata(mrp_transport_t *mu, void *data, uint16_t tag)
+{
+ if (!mu->connected) {
+ return FALSE;
+ }
+
+ return internal_senddatato(mu, data, tag, NULL, 0);
+}
+
+
+
+
+MRP_REGISTER_TRANSPORT(internal, INTERNAL, internal_t, internal_resolve,
+ internal_open, NULL, internal_close, NULL,
+ internal_bind, internal_listen, internal_accept,
+ internal_connect, internal_disconnect,
+ internal_send, internal_sendto,
+ internal_sendraw, internal_sendrawto,
+ internal_senddata, internal_senddatato,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
diff --git a/src/common/json.c b/src/common/json.c
new file mode 100644
index 0000000..c275330
--- /dev/null
+++ b/src/common/json.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "murphy/config.h"
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/json.h>
+
+/** Type for a JSON parser. */
+typedef struct json_tokener mrp_json_parser_t;
+
+static mrp_json_parser_t *parser;
+
+mrp_json_t *mrp_json_create(mrp_json_type_t type, ...)
+{
+ mrp_json_t *o;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ o = json_object_new_string(s);
+ else
+ o = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ o = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ o = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ o = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ o = json_object_new_object();
+ break;
+ case MRP_JSON_ARRAY:
+ o = json_object_new_array();
+ break;
+ default:
+ o = NULL;
+ }
+ va_end(ap);
+
+ return o;
+}
+
+
+mrp_json_t *mrp_json_clone(mrp_json_t *o)
+{
+ if (o != NULL)
+ return mrp_json_string_to_object(mrp_json_object_to_string(o), -1);
+ else
+ return NULL;
+}
+
+
+mrp_json_t *mrp_json_string_to_object(const char *s, int len)
+{
+ if (parser == NULL) {
+ parser = json_tokener_new();
+
+ if (parser == NULL)
+ return NULL;
+ }
+ else
+ json_tokener_reset(parser);
+
+ if (len < 0)
+ len = strlen(s);
+
+ return json_tokener_parse_ex(parser, s, len);
+}
+
+
+const char *mrp_json_object_to_string(mrp_json_t *o)
+{
+ if (o != NULL)
+ return json_object_to_json_string(o);
+ else
+ return "{}";
+}
+
+
+mrp_json_t *mrp_json_ref(mrp_json_t *o)
+{
+ return json_object_get(o);
+}
+
+
+void mrp_json_unref(mrp_json_t *o)
+{
+ json_object_put(o);
+}
+
+
+mrp_json_type_t mrp_json_get_type(mrp_json_t *o)
+{
+ return json_object_get_type(o);
+}
+
+
+int mrp_json_is_type(mrp_json_t *o, mrp_json_type_t type)
+{
+ return json_object_is_type(o, type);
+}
+
+
+void mrp_json_add(mrp_json_t *o, const char *key, mrp_json_t *m)
+{
+ json_object_object_add(o, key, m);
+}
+
+
+mrp_json_t *mrp_json_add_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...)
+{
+ mrp_json_t *m;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ m = json_object_new_string(s);
+ else
+ m = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ m = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ m = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ m = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ m = json_object_new_object();
+ break;
+ case MRP_JSON_ARRAY:
+ m = json_object_new_array();
+ break;
+ default:
+ m = NULL;
+ errno = EINVAL;
+ }
+ va_end(ap);
+
+ if (m != NULL)
+ json_object_object_add(o, key, m);
+
+ return m;
+}
+
+
+mrp_json_t *mrp_json_add_array(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...)
+{
+ va_list ap;
+ void *arr;
+ size_t cnt, i;
+ mrp_json_t *a;
+
+ va_start(ap, type);
+ arr = va_arg(ap, void *);
+ cnt = va_arg(ap, size_t);
+ a = mrp_json_create(MRP_JSON_ARRAY);
+
+ if (a == NULL)
+ goto fail;
+
+ switch (type) {
+ case MRP_JSON_STRING:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_string(a, ((char **)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ case MRP_JSON_INTEGER:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_integer(a, ((int *)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ case MRP_JSON_DOUBLE:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_double(a, ((double *)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ case MRP_JSON_BOOLEAN:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_boolean(a, ((bool *)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+
+ }
+
+ va_end(ap);
+
+ mrp_json_add(o, key, a);
+ return a;
+
+ fail:
+ va_end(ap);
+ mrp_json_unref(a);
+
+ return NULL;
+}
+
+
+mrp_json_t *mrp_json_get(mrp_json_t *o, const char *key)
+{
+ mrp_json_iter_t it;
+ const char *k;
+ mrp_json_t *v;
+
+ mrp_json_foreach_member(o, k, v, it) {
+ if (!strcmp(k, key))
+ return v;
+ }
+
+ return NULL;
+}
+
+
+int mrp_json_get_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...)
+{
+ const char **s;
+ bool *b;
+ int *i;
+ double *d;
+ mrp_json_t *m, **mp;
+ int success;
+ va_list ap;
+
+ success = FALSE;
+ va_start(ap, type);
+
+ m = mrp_json_get(o, key);
+
+ if (m != NULL) {
+ if (json_object_is_type(m, type)) {
+ success = TRUE;
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char **);
+ *s = json_object_get_string(m);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, bool *);
+ *b = json_object_get_boolean(m);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int *);
+ *i = json_object_get_int(m);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double *);
+ *d = json_object_get_double(m);
+ break;
+ case MRP_JSON_OBJECT:
+ mp = va_arg(ap, mrp_json_t **);
+ *mp = m;
+ break;
+ case MRP_JSON_ARRAY:
+ mp = va_arg(ap, mrp_json_t **);
+ *mp = m;
+ break;
+ default:
+ success = FALSE;
+ }
+ }
+ else
+ errno = EINVAL;
+ }
+ else {
+ errno = ENOENT;
+ success = FALSE;
+ }
+
+ va_end(ap);
+
+ return success;
+}
+
+
+void mrp_json_del_member(mrp_json_t *o, const char *key)
+{
+ json_object_object_del(o, key);
+}
+
+
+int mrp_json_array_length(mrp_json_t *a)
+{
+ return json_object_array_length(a);
+}
+
+
+int mrp_json_array_append(mrp_json_t *a, mrp_json_t *v)
+{
+ return json_object_array_add(a, v) == 0;
+}
+
+
+mrp_json_t *mrp_json_array_append_item(mrp_json_t *a, mrp_json_type_t type, ...)
+{
+ mrp_json_t *v;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ v = json_object_new_string(s);
+ else
+ v = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ v = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ v = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ v = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ case MRP_JSON_ARRAY:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ default:
+ v = NULL;
+ errno = EINVAL;
+ }
+ va_end(ap);
+
+ if (v != NULL) {
+ if (json_object_array_add(a, v) == 0)
+ return v;
+ else {
+ mrp_json_unref(v);
+ errno = ENOMEM;
+ }
+ }
+
+ return NULL;
+}
+
+
+int mrp_json_array_set(mrp_json_t *a, int idx, mrp_json_t *v)
+{
+ return json_object_array_put_idx(a, idx, v);
+}
+
+
+int mrp_json_array_set_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...)
+{
+ mrp_json_t *v;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ v = json_object_new_string(s);
+ else
+ v = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ v = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ v = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ v = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ case MRP_JSON_ARRAY:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ default:
+ v = NULL;
+ errno = EINVAL;
+ }
+ va_end(ap);
+
+ if (v != NULL)
+ return json_object_array_put_idx(a, idx, v);
+ else {
+ errno = ENOMEM;
+ return FALSE;
+ }
+}
+
+
+mrp_json_t *mrp_json_array_get(mrp_json_t *a, int idx)
+{
+ return json_object_array_get_idx(a, idx);
+}
+
+
+int mrp_json_array_get_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...)
+{
+ const char **s;
+ bool *b;
+ int *i;
+ double *d;
+ mrp_json_t *v, **vp;
+ int success;
+ va_list ap;
+
+ success = FALSE;
+ va_start(ap, type);
+
+ v = json_object_array_get_idx(a, idx);
+
+ if (v != NULL) {
+ if (json_object_is_type(v, type)) {
+ success = TRUE;
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char **);
+ *s = json_object_get_string(v);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, bool *);
+ *b = json_object_get_boolean(v);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int *);
+ *i = json_object_get_int(v);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double *);
+ *d = json_object_get_double(v);
+ break;
+ case MRP_JSON_OBJECT:
+ vp = va_arg(ap, mrp_json_t **);
+ *vp = v;
+ break;
+ case MRP_JSON_ARRAY:
+ vp = va_arg(ap, mrp_json_t **);
+ *vp = v;
+ break;
+ default:
+ success = FALSE;
+ errno = EINVAL;
+ }
+ }
+ else
+ errno = EINVAL;
+ }
+ else
+ errno = ENOENT;
+
+ va_end(ap);
+
+ return success;
+}
+
+
+int mrp_json_parse_object(char **strp, int *lenp, mrp_json_t **op)
+{
+ char *str;
+ int len;
+ mrp_json_t *o = NULL;
+ json_tokener *tok = NULL;
+ int res = -1;
+
+ if (strp == NULL || *strp == NULL) {
+ *op = NULL;
+ if (lenp != NULL)
+ *lenp = 0;
+
+ return 0;
+ }
+
+ str = *strp;
+ len = lenp ? *lenp : 0;
+
+ if (len <= 0)
+ len = strlen(str);
+
+ tok = json_tokener_new();
+
+ if (tok != NULL) {
+ o = json_tokener_parse_ex(tok, str, len);
+
+ if (o != NULL) {
+ *strp += tok->char_offset;
+ if (lenp != NULL)
+ *lenp -= tok->char_offset;
+
+ res = 0;
+ }
+ else {
+#ifdef HAVE_JSON_TOKENER_GET_ERROR
+ if (json_tokener_get_error(tok) != json_tokener_success)
+ errno = EINVAL;
+#else
+ if (tok->err != json_tokener_success)
+ errno = EINVAL;
+#endif
+ else
+ res = 0;
+ }
+
+ json_tokener_free(tok);
+ }
+ else
+ errno = ENOMEM;
+
+ *op = o;
+ return res;
+}
diff --git a/src/common/json.h b/src/common/json.h
new file mode 100644
index 0000000..746a6ef
--- /dev/null
+++ b/src/common/json.h
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_JSON_H__
+#define __MURPHY_JSON_H__
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "murphy/config.h"
+
+#ifndef JSON_INCLUDE_PATH_JSONC
+# include <json/json.h>
+# include <json/linkhash.h>
+/* workaround for older broken json-c not exposing json_object_iter */
+# include <json/json_object_private.h>
+#else
+# include <json-c/json.h>
+# include <json-c/linkhash.h>
+/* workaround for older broken json-c not exposing json_object_iter */
+# include <json-c/json_object_private.h>
+#endif
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * We use json-c as the underlying json implementation, However, we do
+ * not want direct json-c dependencies to spread all over the code base
+ * (at least not yet). So we try to define here an envelop layer that
+ * hides json-c underneath.
+ */
+
+/** Type of a JSON object. */
+typedef json_object mrp_json_t;
+
+/** JSON object/member types. */
+typedef enum {
+ MRP_JSON_STRING = json_type_string,
+ MRP_JSON_BOOLEAN = json_type_boolean,
+ MRP_JSON_INTEGER = json_type_int,
+ MRP_JSON_DOUBLE = json_type_double,
+ MRP_JSON_OBJECT = json_type_object,
+ MRP_JSON_ARRAY = json_type_array
+} mrp_json_type_t;
+
+/** Type for a JSON member iterator. */
+typedef json_object_iter mrp_json_iter_t;
+
+/** Create a new JSON object of the given type. */
+mrp_json_t *mrp_json_create(mrp_json_type_t type, ...);
+
+/** Clone the given JSON object, creating a new private copy of it. */
+mrp_json_t *mrp_json_clone(mrp_json_t *o);
+
+/** Deserialize a string to a JSON object. */
+mrp_json_t *mrp_json_string_to_object(const char *str, int len);
+
+/** Serialize a JSON object to a string. */
+const char *mrp_json_object_to_string(mrp_json_t *o);
+
+/** Add a reference to the given JSON object. */
+mrp_json_t *mrp_json_ref(mrp_json_t *o);
+
+/** Remove a reference from the given JSON object, freeing if it was last. */
+void mrp_json_unref(mrp_json_t *o);
+
+/** Get the type of a JSON object. */
+mrp_json_type_t mrp_json_get_type(mrp_json_t *o);
+
+/** Check if a JSON object has the given type. */
+int mrp_json_is_type(mrp_json_t *o, mrp_json_type_t type);
+
+/** Convenience macros to get values of JSON objects of basic types. */
+#define mrp_json_string_value(o) json_object_get_string(o)
+#define mrp_json_integer_value(o) json_object_get_int(o)
+#define mrp_json_double_value(o) json_object_get_double(o)
+#define mrp_json_boolean_value(o) json_object_get_boolean(o)
+
+/** Set a member of a JSON object. */
+void mrp_json_add(mrp_json_t *o, const char *key, mrp_json_t *m);
+
+/** Create a new JSON object and set it as a member of another object. */
+mrp_json_t *mrp_json_add_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...);
+
+/** Convenience macros to add members of various basic types. */
+#define mrp_json_add_string(o, key, s) \
+ mrp_json_add_member(o, key, MRP_JSON_STRING, s, -1)
+
+#define mrp_json_add_string_slice(o, key, s, l) \
+ mrp_json_add_member(o, key, MRP_JSON_STRING, s, l)
+
+#define mrp_json_add_integer(o, key, i) \
+ mrp_json_add_member(o, key, MRP_JSON_INTEGER, i)
+
+#define mrp_json_add_double(o, key, d) \
+ mrp_json_add_member(o, key, MRP_JSON_DOUBLE, d)
+
+#define mrp_json_add_boolean(o, key, b) \
+ mrp_json_add_member(o, key, MRP_JSON_BOOLEAN, (int)b)
+
+/** Add an array member from a native C array of the given type. */
+mrp_json_t *mrp_json_add_array(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...);
+
+/** Convenience macros for adding arrays of various basic types. */
+#define mrp_json_add_string_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_STRING, arr, size)
+
+#define mrp_json_add_int_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_INTEGER, arr, size)
+
+#define mrp_json_add_double_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_DOUBLE, arr, size)
+
+#define mrp_json_add_boolean_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_BOOLEAN, arr, size)
+
+/** Get the member of a JSON object as a json object. */
+mrp_json_t *mrp_json_get(mrp_json_t *o, const char *key);
+
+/** Get the member of a JSON object in a type specific format. */
+int mrp_json_get_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...);
+
+/** Convenience macros to get members of various types. */
+#define mrp_json_get_string(o, key, sptr) \
+ mrp_json_get_member(o, key, MRP_JSON_STRING, sptr)
+
+#define mrp_json_get_integer(o, key, iptr) \
+ mrp_json_get_member(o, key, MRP_JSON_INTEGER, iptr)
+
+#define mrp_json_get_double(o, key, dptr) \
+ mrp_json_get_member(o, key, MRP_JSON_DOUBLE, dptr)
+
+#define mrp_json_get_boolean(o, key, bptr) \
+ mrp_json_get_member(o, key, MRP_JSON_BOOLEAN, bptr)
+
+#define mrp_json_get_array(o, key, aptr) \
+ mrp_json_get_member(o, key, MRP_JSON_ARRAY, aptr)
+
+#define mrp_json_get_object(o, key, optr) \
+ mrp_json_get_member(o, key, MRP_JSON_OBJECT, optr)
+
+/** Delete a member of a JSON object. */
+void mrp_json_del_member(mrp_json_t *o, const char *key);
+
+/** Get the length of a JSON array object. */
+int mrp_json_array_length(mrp_json_t *a);
+
+/** Append a JSON object to an array object. */
+int mrp_json_array_append(mrp_json_t *a, mrp_json_t *v);
+
+/** Create and append a new item to a JSON array object. */
+mrp_json_t *mrp_json_array_append_item(mrp_json_t *a, mrp_json_type_t type,
+ ...);
+
+/** Convenience macros for appending array items of basic types. */
+#define mrp_json_array_append_string(a, s) \
+ mrp_json_array_append_item(a, MRP_JSON_STRING, s, -1)
+
+#define mrp_json_array_append_string_slice(a, s, l) \
+ mrp_json_array_append_item(a, MRP_JSON_STRING, s, l)
+
+
+#define mrp_json_array_append_integer(a, i) \
+ mrp_json_array_append_item(a, MRP_JSON_INTEGER, (int)i)
+
+#define mrp_json_array_append_double(a, d) \
+ mrp_json_array_append_item(a, MRP_JSON_DOUBLE, 1.0*d)
+
+#define mrp_json_array_append_boolean(a, b) \
+ mrp_json_array_append_item(a, MRP_JSON_BOOLEAN, (int)b)
+
+/** Add a JSON object to a given index of an array object. */
+int mrp_json_array_set(mrp_json_t *a, int idx, mrp_json_t *v);
+
+/** Add a JSON object to a given index of an array object. */
+int mrp_json_array_set_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...);
+
+/** Get the object at a given index of a JSON array object. */
+mrp_json_t *mrp_json_array_get(mrp_json_t *a, int idx);
+
+/** Get the element of a JSON array object at an index. */
+int mrp_json_array_get_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...);
+
+/** Convenience macros to get items of certain types from an array. */
+#define mrp_json_array_get_string(a, idx, sptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_STRING, sptr)
+
+#define mrp_json_array_get_integer(a, idx, iptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_INTEGER, iptr)
+
+#define mrp_json_array_get_double(a, idx, dptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_DOUBLE, dptr)
+
+#define mrp_json_array_get_boolean(a, idx, bptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_BOOLEAN, bptr)
+
+#define mrp_json_array_get_array(a, idx, aptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_ARRAY, aptr)
+
+#define mrp_json_array_get_object(a, idx, optr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_OBJECT, optr)
+
+/** Iterate through the members of an object. */
+#define mrp_json_foreach_member(o, _k, _v, it) \
+ for (it.entry = json_object_get_object((o))->head; \
+ (it.entry ? \
+ (_k = it.key = it.entry->k, \
+ _v = it.val = (mrp_json_t *)it.entry->v, \
+ it.entry) : 0); \
+ it.entry = it.entry->next)
+
+/** Parse a JSON object from the given string. */
+int mrp_json_parse_object(char **str, int *len, mrp_json_t **op);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_JSON_H__ */
diff --git a/src/common/libdbus-glue.c b/src/common/libdbus-glue.c
new file mode 100644
index 0000000..d1bb093
--- /dev/null
+++ b/src/common/libdbus-glue.c
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dbus/dbus.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+
+typedef struct dbus_glue_s dbus_glue_t;
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_io_watch_t *mw;
+ DBusWatch *dw;
+ mrp_list_hook_t hook;
+} watch_t;
+
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_timer_t *mt;
+ DBusTimeout *dt;
+ mrp_list_hook_t hook;
+} timeout_t;
+
+
+struct dbus_glue_s {
+ DBusConnection *conn;
+ mrp_mainloop_t *ml;
+ mrp_list_hook_t watches;
+ mrp_list_hook_t timers;
+ mrp_deferred_t *pump;
+};
+
+
+static dbus_int32_t data_slot = -1;
+
+static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *watch = (watch_t *)user_data;
+ DBusConnection *conn = watch->glue->conn;
+ unsigned int mask = 0;
+
+ MRP_UNUSED(mw);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ mask |= DBUS_WATCH_READABLE;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= DBUS_WATCH_WRITABLE;
+ if (events & MRP_IO_EVENT_HUP)
+ mask |= DBUS_WATCH_HANGUP;
+ if (events & MRP_IO_EVENT_ERR)
+ mask |= DBUS_WATCH_ERROR;
+
+ dbus_connection_ref(conn);
+ dbus_watch_handle(watch->dw, mask);
+ dbus_connection_unref(conn);
+}
+
+
+static void watch_freed_cb(void *data)
+{
+ watch_t *watch = (watch_t *)data;
+
+ if (watch != NULL) {
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+ mrp_free(watch);
+ }
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *dw, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ watch_t *watch;
+ mrp_io_watch_t *mw;
+ mrp_io_event_t mask;
+ int fd;
+ unsigned int flags;
+
+ mrp_debug("adding D-BUS watch %p (%s)", dw,
+ dbus_watch_get_enabled(dw) ? "enabled" : "disabled");
+
+ if (!dbus_watch_get_enabled(dw))
+ return TRUE;
+
+ fd = dbus_watch_get_unix_fd(dw);
+ flags = dbus_watch_get_flags(dw);
+ mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= MRP_IO_EVENT_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= MRP_IO_EVENT_OUT;
+
+ mrp_debug("event mask for fd %d: %s%s", fd,
+ mask & MRP_IO_EVENT_IN ? "read" : "",
+ mask & MRP_IO_EVENT_OUT ? "write" : "");
+
+ if ((watch = mrp_allocz(sizeof(*watch))) != NULL) {
+ mrp_list_init(&watch->hook);
+ mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch);
+
+ if (mw != NULL) {
+ watch->glue = glue;
+ watch->mw = mw;
+ watch->dw = dw;
+ dbus_watch_set_data(dw, watch, watch_freed_cb);
+ mrp_list_append(&glue->watches, &watch->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(watch);
+ }
+
+ return FALSE;
+}
+
+
+static void del_watch(DBusWatch *dw, void *data)
+{
+ watch_t *watch = (watch_t *)dbus_watch_get_data(dw);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS watch %p...", dw);
+
+ if (watch != NULL) {
+ mrp_del_io_watch(watch->mw);
+ watch->mw = NULL;
+ }
+}
+
+
+static void toggle_watch(DBusWatch *dw, void *data)
+{
+ mrp_debug("Toggling D-BUS watch %p...", dw);
+
+ if (dbus_watch_get_enabled(dw))
+ add_watch(dw, data);
+ else
+ del_watch(dw, data);
+}
+
+
+static void dispatch_timeout(mrp_timer_t *mt, void *user_data)
+{
+ timeout_t *timer = (timeout_t *)user_data;
+
+ MRP_UNUSED(mt);
+
+ mrp_debug("dispatching D-BUS timeout %p...", timer->dt);
+
+ dbus_timeout_handle(timer->dt);
+}
+
+
+static void timeout_freed_cb(void *data)
+{
+ timeout_t *timer = (timeout_t *)data;
+
+ if (timer != NULL) {
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *dt, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ timeout_t *timer;
+ mrp_timer_t *mt;
+ unsigned int msecs;
+
+ mrp_debug("adding D-BUS timeout %p...", dt);
+
+ if ((timer = mrp_allocz(sizeof(*timer))) != NULL) {
+ mrp_list_init(&timer->hook);
+ msecs = dbus_timeout_get_interval(dt);
+ mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer);
+
+ if (mt != NULL) {
+ timer->glue = glue;
+ timer->mt = mt;
+ timer->dt = dt;
+ dbus_timeout_set_data(dt, timer, timeout_freed_cb);
+ mrp_list_append(&glue->timers, &timer->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(timer);
+ }
+
+ return FALSE;
+}
+
+
+static void del_timeout(DBusTimeout *dt, void *data)
+{
+ timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS timeout %p...", dt);
+
+ if (timer != NULL) {
+ mrp_del_timer(timer->mt);
+ timer->mt = NULL;
+ }
+}
+
+
+static void toggle_timeout(DBusTimeout *dt, void *data)
+{
+ mrp_debug("toggling D-BUS timeout %p...", dt);
+
+ if (dbus_timeout_get_enabled(dt))
+ add_timeout(dt, data);
+ else
+ del_timeout(dt, data);
+}
+
+
+static void wakeup_mainloop(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+
+ mrp_debug("waking up mainloop...");
+
+ mrp_enable_deferred(glue->pump);
+}
+
+
+static void glue_free_cb(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ mrp_list_hook_t *p, *n;
+ watch_t *watch;
+ timeout_t *timer;
+
+ mrp_list_foreach(&glue->watches, p, n) {
+ watch = mrp_list_entry(p, typeof(*watch), hook);
+
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+
+ mrp_free(watch);
+ }
+
+ mrp_list_foreach(&glue->timers, p, n) {
+ timer = mrp_list_entry(p, typeof(*timer), hook);
+
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+
+ mrp_free(glue);
+}
+
+
+static void pump_cb(mrp_deferred_t *d, void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ mrp_debug("dispatching dbus connection %p...", glue->conn);
+
+ if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE)
+ mrp_disable_deferred(d);
+}
+
+
+static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status,
+ void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ MRP_UNUSED(conn);
+
+ switch (status) {
+ case DBUS_DISPATCH_COMPLETE:
+ mrp_debug("dispatching status for %p: complete", conn);
+ mrp_disable_deferred(glue->pump);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ mrp_debug("dispatching status for %p: not complete yet", conn);
+ mrp_enable_deferred(glue->pump);
+ break;
+ }
+}
+
+
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn)
+{
+ dbus_glue_t *glue;
+
+ if (!dbus_connection_allocate_data_slot(&data_slot))
+ return FALSE;
+
+ if (dbus_connection_get_data(conn, data_slot) != NULL)
+ return FALSE;
+
+ if ((glue = mrp_allocz(sizeof(*glue))) != NULL) {
+ mrp_list_init(&glue->watches);
+ mrp_list_init(&glue->timers);
+ glue->pump = mrp_add_deferred(ml, pump_cb, glue);
+
+ if (glue->pump == NULL) {
+ mrp_free(glue);
+ return FALSE;
+ }
+
+ glue->ml = ml;
+ glue->conn = conn;
+ }
+ else
+ return FALSE;
+
+ if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb))
+ return FALSE;
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+ glue, NULL);
+
+ dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop,
+ glue, NULL);
+
+ return
+ dbus_connection_set_watch_functions(conn, add_watch, del_watch,
+ toggle_watch, glue, NULL) &&
+ dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout,
+ toggle_timeout, glue, NULL);
+}
diff --git a/src/common/libdbus.c b/src/common/libdbus.c
new file mode 100644
index 0000000..a4c7a24
--- /dev/null
+++ b/src/common/libdbus.c
@@ -0,0 +1,1471 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/libdbus.h>
+
+
+#define DBUS_ADMIN_SERVICE "org.freedesktop.DBus"
+#define DBUS_ADMIN_INTERFACE "org.freedesktop.DBus"
+#define DBUS_ADMIN_PATH "/org/freedesktop/DBus"
+#define DBUS_NAME_CHANGED "NameOwnerChanged"
+
+
+struct mrp_dbus_s {
+ char *address; /* bus address */
+ DBusConnection *conn; /* actual D-BUS connection */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_htbl_t *methods; /* method handler table */
+ mrp_htbl_t *signals; /* signal handler table */
+ mrp_list_hook_t name_trackers; /* peer (name) watchers */
+ mrp_list_hook_t calls; /* pending calls */
+ uint32_t call_id; /* next call id */
+ const char *unique_name; /* our unique D-BUS address */
+ int priv; /* whether a private connection */
+ int signal_filter; /* if signal dispatching is set up */
+ int register_fallback; /* if the fallback object is set up */
+ mrp_refcnt_t refcnt; /* reference count */
+};
+
+
+/*
+ * Notes:
+ *
+ * At the moment we administer DBUS method and signal handlers
+ * in a very primitive way (subject to be changed later). For
+ * every bus instance we maintain two hash tables, one for methods
+ * and another for signals. Each method and signal handler is
+ * hashed in only by it's method/signal name to a linked list of
+ * method or signal handlers.
+ *
+ * When dispatching a method, we look up the chain with a matching
+ * method name, or the chain for "" in case a matching chain is
+ * not found, and invoke the handler which best matches the
+ * received message (by looking at the path, interface and name).
+ * Only one such handler is invoked at most.
+ *
+ * For signals we look up both the chain with a matching name and
+ * the chain for "" and invoke all signal handlers that match the
+ * received message (regardless of their return value).
+ */
+
+
+typedef struct {
+ char *member; /* signal/method name */
+ mrp_list_hook_t handlers; /* handlers with matching member */
+} handler_list_t;
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *sender;
+ char *path;
+ char *interface;
+ char *member;
+ mrp_dbus_handler_t handler;
+ void *user_data;
+} handler_t;
+
+#define method_t handler_t
+#define signal_t handler_t
+
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to name tracker list */
+ char *name; /* name to track */
+ mrp_dbus_name_cb_t cb; /* status change callback */
+ void *user_data; /* opaque callback user data */
+ int32_t qid; /* initial query ID */
+} name_tracker_t;
+
+
+typedef struct {
+ mrp_dbus_t *dbus; /* DBUS connection */
+ int32_t id; /* call id */
+ mrp_dbus_reply_cb_t cb; /* completion notification callback */
+ void *user_data; /* opaque callback data */
+ DBusPendingCall *pend; /* pending DBUS call */
+ mrp_list_hook_t hook; /* hook to list of pending calls */
+} call_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml; /* mainloop for bus connection */
+ const char *address; /* address of bus */
+} bus_spec_t;
+
+static mrp_htbl_t *buses;
+
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static void purge_name_trackers(mrp_dbus_t *dbus);
+static void purge_calls(mrp_dbus_t *dbus);
+static void handler_list_free_cb(void *key, void *entry);
+static void handler_free(handler_t *h);
+static int name_owner_change_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *data);
+static void call_free(call_t *call);
+
+
+
+
+static int purge_filters(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ handler_list_t *l = (handler_list_t *)entry;
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ MRP_UNUSED(key);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_dbus_remove_filter(dbus,
+ h->sender, h->path, h->interface,
+ h->member, NULL);
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+void dbus_disconnect(mrp_dbus_t *dbus)
+{
+ if (dbus) {
+ mrp_htbl_remove(buses, dbus->conn, FALSE);
+
+ if (dbus->signals) {
+ mrp_htbl_foreach(dbus->signals, purge_filters, dbus);
+ mrp_htbl_destroy(dbus->signals, TRUE);
+ }
+ if (dbus->methods)
+ mrp_htbl_destroy(dbus->methods, TRUE);
+
+ if (dbus->conn != NULL) {
+ if (dbus->signal_filter)
+ dbus_connection_remove_filter(dbus->conn, dispatch_signal,
+ dbus);
+ if (dbus->register_fallback)
+ dbus_connection_unregister_object_path(dbus->conn, "/");
+ if (dbus->priv)
+ dbus_connection_close(dbus->conn);
+ dbus_connection_unref(dbus->conn);
+ }
+
+ purge_name_trackers(dbus);
+ purge_calls(dbus);
+
+ mrp_free(dbus->address);
+ dbus->conn = NULL;
+ dbus->ml = NULL;
+
+ mrp_free(dbus);
+ }
+}
+
+
+static int bus_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t bus_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (ptrdiff_t)key;
+ h >>= 2 * sizeof(key);
+
+ return h;
+}
+
+
+static int find_bus_by_spec(void *key, void *object, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)object;
+ bus_spec_t *spec = (bus_spec_t *)user_data;
+
+ MRP_UNUSED(key);
+
+ if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address)
+{
+ mrp_htbl_config_t hcfg;
+ bus_spec_t spec;
+
+ if (buses == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = bus_cmp;
+ hcfg.hash = bus_hash;
+ hcfg.free = NULL;
+
+ buses = mrp_htbl_create(&hcfg);
+
+ return NULL;
+ }
+ else {
+ spec.ml = ml;
+ spec.address = address;
+
+ return mrp_htbl_find(buses, find_bus_by_spec, &spec);
+ }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ DBusError *errp)
+{
+ static struct DBusObjectPathVTable vtable = {
+ .message_function = dispatch_method
+ };
+
+ mrp_htbl_config_t hcfg;
+ mrp_dbus_t *dbus;
+
+ if ((dbus = dbus_get(ml, address)) != NULL)
+ return mrp_dbus_ref(dbus);
+
+ if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL)
+ return NULL;
+
+ mrp_list_init(&dbus->calls);
+ mrp_list_init(&dbus->name_trackers);
+ mrp_refcnt_init(&dbus->refcnt);
+
+ dbus->ml = ml;
+
+
+ mrp_dbus_error_init(errp);
+
+ /*
+ * connect to the bus
+ */
+
+ if (!strcmp(address, "system"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SYSTEM, errp);
+ else if (!strcmp(address, "session"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SESSION, errp);
+ else {
+ dbus->conn = dbus_connection_open_private(address, errp);
+ dbus->priv = TRUE;
+
+ if (dbus->conn == NULL || !dbus_bus_register(dbus->conn, errp))
+ goto fail;
+ }
+
+ if (dbus->conn == NULL)
+ goto fail;
+
+ dbus->address = mrp_strdup(address);
+ dbus->unique_name = dbus_bus_get_unique_name(dbus->conn);
+
+ /*
+ * set up with mainloop
+ */
+
+ if (!mrp_dbus_setup_connection(ml, dbus->conn))
+ goto fail;
+
+ /*
+ * set up our message dispatchers and take our name on the bus
+ */
+
+ if (!dbus_connection_add_filter(dbus->conn, dispatch_signal, dbus, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up signal dispatching.");
+ goto fail;
+ }
+ dbus->signal_filter = TRUE;
+
+ if (!dbus_connection_register_fallback(dbus->conn, "/", &vtable, dbus)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up method dispatching.");
+ goto fail;
+ }
+ dbus->register_fallback = TRUE;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = handler_list_free_cb;
+
+ if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS method table.");
+ goto fail;
+ }
+
+ if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS signal table.");
+ goto fail;
+ }
+
+
+ /*
+ * install handler for NameOwnerChanged for tracking clients/peers
+ */
+
+ if (!mrp_dbus_add_signal_handler(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name_owner_change_cb, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to install NameOwnerChanged handler.");
+ goto fail;
+ }
+
+ /* install a 'safe' filter to avoid receiving all name change signals */
+ mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ DBUS_ADMIN_SERVICE, NULL);
+
+ mrp_list_init(&dbus->name_trackers);
+ dbus->call_id = 1;
+
+ if (mrp_htbl_insert(buses, dbus->conn, dbus))
+ return dbus;
+
+ fail:
+ dbus_disconnect(dbus);
+ return NULL;
+}
+
+
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus)
+{
+ return mrp_ref_obj(dbus, refcnt);
+}
+
+
+int mrp_dbus_unref(mrp_dbus_t *dbus)
+{
+ if (mrp_unref_obj(dbus, refcnt)) {
+ dbus_disconnect(dbus);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, DBusError *error)
+{
+ int flags, status;
+
+ mrp_dbus_error_init(error);
+
+ flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+ status = dbus_bus_request_name(dbus->conn, name, flags, error);
+
+ if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ return TRUE;
+ else {
+ if (status == DBUS_REQUEST_NAME_REPLY_EXISTS) {
+ if (error)
+ dbus_error_free(error);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "name already taken");
+ }
+ return FALSE;
+ }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, DBusError *error)
+{
+ mrp_dbus_error_init(error);
+
+ if (dbus_bus_release_name(dbus->conn, name, error) != -1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus)
+{
+ return dbus->unique_name;
+}
+
+static void name_owner_query_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *data)
+{
+ name_tracker_t *t = (name_tracker_t *)data;
+ const char *owner;
+ int state;
+
+ if (t->cb != NULL) { /* tracker still active */
+ t->qid = 0;
+ state = dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &owner,
+ DBUS_TYPE_INVALID))
+ owner = "<unknown>";
+
+ t->cb(dbus, t->name, state, owner, t->user_data);
+ }
+ else /* already requested to delete */
+ mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *data)
+{
+ const char *name, *prev, *next;
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ MRP_UNUSED(data);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &prev,
+ DBUS_TYPE_STRING, &next,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ /*
+ * Notes: XXX TODO
+ * In principle t->cb could call mrp_dbus_forget for some other D-BUS
+ * address than name. If that happened to be n (== p->hook.next) this
+ * would result in a crash or memory corruption in the next iteration
+ * of this loop (when handling n). We can easily get around this
+ * problem by
+ *
+ * 1) adminstering in mrp_dbus_t that we're handing a NameOwnerChange
+ * 2) checking for this in mrp_dbus_forget_name and if it is the case
+ * only marking the affected entry for deletion
+ * 3) removing entries marked for deletion in this loop (or just
+ * ignoring them and making another pass in the end removing any
+ * such entry).
+ */
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (!strcmp(name, t->name))
+ t->cb(dbus, name, next && *next, next, t->user_data);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ name_tracker_t *t;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ if ((t->name = mrp_strdup(name)) != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+
+ if (mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL)) {
+ mrp_list_append(&dbus->name_trackers, &t->hook);
+
+ t->qid = mrp_dbus_call(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, "GetNameOwner", 5000,
+ name_owner_query_cb, t,
+ DBUS_TYPE_STRING, &t->name,
+ DBUS_TYPE_INVALID);
+ return TRUE;
+ }
+ else {
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_dbus_remove_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL);
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) {
+ mrp_list_delete(&t->hook);
+ mrp_free(t->name);
+
+ if (!t->qid)
+ mrp_free(t);
+ else {
+ t->cb = NULL;
+ t->user_data = NULL;
+ t->name = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_name_trackers(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ mrp_list_delete(p);
+ mrp_dbus_remove_filter(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ t->name, NULL);
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+}
+
+
+static handler_t *handler_alloc(const char *sender, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_t *h;
+
+ if ((h = mrp_allocz(sizeof(*h))) != NULL) {
+ h->sender = mrp_strdup(sender);
+ h->path = mrp_strdup(path);
+ h->interface = mrp_strdup(interface);
+ h->member = mrp_strdup(member);
+
+ if ((path && !h->path) || !h->interface || !h->member) {
+ handler_free(h);
+ return NULL;
+ }
+
+ h->handler = handler;
+ h->user_data = user_data;
+
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static void handler_free(handler_t *h)
+{
+ if (h != NULL) {
+ mrp_free(h->sender);
+ mrp_free(h->path);
+ mrp_free(h->interface);
+ mrp_free(h->member);
+
+ mrp_free(h);
+ }
+}
+
+
+static handler_list_t *handler_list_alloc(const char *member)
+{
+ handler_list_t *l;
+
+ if ((l = mrp_allocz(sizeof(*l))) != NULL) {
+ if ((l->member = mrp_strdup(member)) != NULL)
+ mrp_list_init(&l->handlers);
+ else {
+ mrp_free(l);
+ l = NULL;
+ }
+ }
+
+ return l;
+}
+
+
+static inline void handler_list_free(handler_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_list_delete(p);
+ handler_free(h);
+ }
+
+ mrp_free(l->member);
+ mrp_free(l);
+}
+
+
+static void handler_list_free_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ handler_list_free((handler_list_t *)entry);
+}
+
+
+static inline int handler_specificity(handler_t *h)
+{
+ int score = 0;
+
+ if (h->path && *h->path)
+ score |= 0x4;
+ if (h->interface && *h->interface)
+ score |= 0x2;
+ if (h->member && *h->member)
+ score |= 0x1;
+
+ return score;
+}
+
+
+static void handler_list_insert(handler_list_t *l, handler_t *handler)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+ int score;
+
+ score = handler_specificity(handler);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (score >= handler_specificity(h)) {
+ mrp_list_append(h->hook.prev, &handler->hook); /* add before h */
+ return;
+ }
+ }
+
+ mrp_list_append(&l->handlers, &handler->hook);
+}
+
+
+static handler_t *handler_list_lookup(handler_list_t *l, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (h->handler == handler && user_data == h->user_data &&
+ path && !strcmp(path, h->path) &&
+ interface && !strcmp(interface, h->interface) &&
+ member && !strcmp(member, h->member))
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static handler_t *handler_list_find(handler_list_t *l, const char *path,
+ const char *interface, const char *member)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member))
+ return h;
+ }
+
+ return NULL;
+#undef MATCHES
+}
+
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+ if ((methods = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ mrp_htbl_insert(dbus->methods, methods->member, methods);
+ }
+
+ m = handler_alloc(NULL, path, interface, member, handler, user_data);
+ if (m != NULL) {
+ handler_list_insert(methods, m);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL)
+ return FALSE;
+
+ m = handler_list_lookup(methods, path, interface, member,
+ handler, user_data);
+ if (m != NULL) {
+ mrp_list_delete(&m->hook);
+ handler_free(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) {
+ if ((signals = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) {
+ handler_list_free(signals);
+ return FALSE;
+ }
+ }
+
+ s = handler_alloc(sender, path, interface, member, handler, user_data);
+ if (s != NULL) {
+ handler_list_insert(signals, s);
+ return TRUE;
+ }
+ else {
+ handler_free(s);
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, signals->member, TRUE);
+ return FALSE;
+ }
+}
+
+
+
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ MRP_UNUSED(sender);
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL)
+ return FALSE;
+
+ s = handler_list_lookup(signals, path, interface, member,
+ handler, user_data);
+ if (s != NULL) {
+ mrp_list_delete(&s->hook);
+ handler_free(s);
+
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, (void *)member, TRUE);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int success;
+
+
+ if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data)) {
+ va_start(ap, member);
+ success = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ if (success)
+ return TRUE;
+ else
+ mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ va_start(ap, member);
+ status &= mrp_dbus_remove_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+#define ADD_TAG(tag, value, ...) do { \
+ if (value != NULL) { \
+ l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \
+ tag, value); \
+ if (l >= n) \
+ do { __VA_ARGS__; } while (0); \
+ n -= l; \
+ p += l; \
+ } \
+ } while (0)
+
+ va_list ap;
+ DBusError error;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal" , return FALSE);
+ ADD_TAG("sender" , sender , return FALSE);
+ ADD_TAG("path" , path , return FALSE);
+ ADD_TAG("interface", interface, return FALSE);
+ ADD_TAG("member" , member , return FALSE);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val, { va_end(ap); return FALSE; });
+ i++;
+ }
+ va_end(ap);
+
+ dbus_error_init(&error);
+ dbus_bus_add_match(dbus->conn, filter, &error);
+
+ if (dbus_error_is_set(&error)) {
+ mrp_log_error("Failed to install filter '%s' (error: %s).", filter,
+ mrp_dbus_errmsg(&error));
+ dbus_error_free(&error);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+}
+
+
+int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal" , return FALSE);
+ ADD_TAG("sender" , sender , return FALSE);
+ ADD_TAG("path" , path , return FALSE);
+ ADD_TAG("interface", interface, return FALSE);
+ ADD_TAG("member" , member , return FALSE);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val, { va_end(ap); return FALSE; });
+ i++;
+ }
+ va_end(ap);
+
+ dbus_bus_remove_match(dbus->conn, filter, NULL);
+ return TRUE;
+#undef ADD_TAG
+}
+
+
+int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ handler_list_t *l;
+ handler_t *h;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+ retry:
+ if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+ if (h->handler(dbus, msg, h->user_data))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ }
+ else {
+ if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+ goto retry;
+ }
+
+ mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !h->field || !*h->field || \
+ !strcmp(field, h->field))
+
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_list_hook_t *p, *n;
+ handler_list_t *l;
+ handler_t *h;
+ int retried = FALSE;
+ int handled = FALSE;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("%s(path='%s', interface='%s', member='%s')...",
+ __FUNCTION__,
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) {
+ retry:
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) {
+ h->handler(dbus, msg, h->user_data);
+ handled = TRUE;
+ }
+ }
+ }
+
+ if (!retried) {
+ if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+ retried = TRUE;
+ goto retry;
+ }
+ }
+
+ if (!handled)
+ mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static void call_reply_cb(DBusPendingCall *pend, void *user_data)
+{
+ call_t *call = (call_t *)user_data;
+ DBusMessage *reply;
+
+ reply = dbus_pending_call_steal_reply(pend);
+
+ call->pend = NULL;
+ mrp_list_delete(&call->hook);
+
+ call->cb(call->dbus, reply, call->user_data);
+
+ dbus_message_unref(reply);
+ dbus_pending_call_unref(pend);
+
+ call_free(call);
+}
+
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, int type, ...)
+{
+ va_list ap;
+ int32_t id;
+ call_t *call;
+ DBusMessage *msg;
+ DBusPendingCall *pend;
+ int success;
+
+ call = NULL;
+ pend = NULL;
+
+ msg = dbus_message_new_method_call(dest, path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (cb == NULL) {
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ dbus_message_unref(msg);
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, DBusMessage *msg)
+{
+ int32_t id;
+ call_t *call;
+ DBusPendingCall *pend;
+ int method;
+
+ call = NULL;
+ pend = NULL;
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL) {
+ if (cb != NULL)
+ goto fail;
+ else
+ method = FALSE;
+ }
+ else
+ method = TRUE;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (!dbus_message_set_destination(msg, dest))
+ goto fail;
+ if (!dbus_message_set_path(msg, path))
+ goto fail;
+ if (!dbus_message_set_interface(msg, interface))
+ goto fail;
+ if (!dbus_message_set_member(msg, member))
+ goto fail;
+
+ if (cb == NULL) {
+ if (method)
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, DBusMessage *msg)
+{
+ return dbus_connection_send(dbus->conn, msg, NULL);
+}
+
+
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ if (call->id == id) {
+ mrp_list_delete(p);
+
+ dbus_pending_call_cancel(call->pend);
+ dbus_pending_call_unref(call->pend);
+ call->pend = NULL;
+
+ call_free(call);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, DBusMessage *msg, int type, ...)
+{
+ va_list ap;
+ DBusMessage *rpl;
+ int success;
+
+ rpl = dbus_message_new_method_return(msg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, DBusMessage *msg,
+ const char *errname, const char *errmsg, int type, ...)
+{
+ va_list ap;
+ DBusMessage *rpl;
+ int success;
+
+ rpl = dbus_message_new_error(msg, errname, errmsg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+ if (call != NULL)
+ mrp_free(call);
+}
+
+
+static void purge_calls(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ mrp_list_delete(&call->hook);
+
+ if (call->pend != NULL)
+ dbus_pending_call_unref(call->pend);
+
+ mrp_free(call);
+ }
+}
+
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg;
+ int success;
+
+ msg = dbus_message_new_signal(path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (dest && *dest && !dbus_message_set_destination(msg, dest))
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+
+ fail:
+ /*
+ * XXX TODO: Hmm... IIRC, libdbus unrefs messages upon failure. If it
+ * was really so, this would corrupt/crash. Check this from
+ * libdbus code.
+ */
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ return 0;
+}
diff --git a/src/common/libdbus.h b/src/common/libdbus.h
new file mode 100644
index 0000000..84f29b7
--- /dev/null
+++ b/src/common/libdbus.h
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_H__
+#define __MURPHY_DBUS_H__
+
+#include <murphy/common/mainloop.h>
+#include <dbus/dbus.h>
+
+#define MRP_AF_DBUS 0xDB
+
+/** Our D-BUS (connection) abstraction. */
+struct mrp_dbus_s;
+typedef struct mrp_dbus_s mrp_dbus_t;
+
+/** D-BUS method or signal callback type. */
+typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, DBusMessage *, void *);
+
+/** Create a new connection to the given bus. */
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ DBusError *errp);
+#define mrp_dbus_get mrp_dbus_connect
+
+
+/** Set up a DBusConnection with a mainloop. */
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn);
+
+/** Increase the reference count of the given DBus (connection). */
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus);
+
+/** Decrease the reference count of the given DBus (connection). */
+int mrp_dbus_unref(mrp_dbus_t *dbus);
+
+/** Acquire the given name on the given bus (connection). */
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, DBusError *error);
+
+/** Release the given name on the given bus (connection). */
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, DBusError *error);
+
+typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int,
+ const char *, void *);
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, DBusMessage *reply,
+ void *user_data);
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest,
+ const char *path, const char *interface,
+ const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ int dbus_type, ...);
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id);
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, DBusMessage *msg, int type, ...);
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, DBusMessage *msg,
+ const char *errname, const char *errmsg,
+ int type, ...);
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...);
+
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ DBusMessage *msg);
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, DBusMessage *msg);
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus);
+
+static inline void mrp_dbus_error_init(DBusError *error)
+{
+ /*
+ * Prevent libdbus error messages for NULL DBusError's...
+ */
+ if (error != NULL)
+ dbus_error_init(error);
+}
+
+
+static inline const char *mrp_dbus_errmsg(DBusError *err)
+{
+ if (err && dbus_error_is_set(err))
+ return err->message;
+ else
+ return "unknown DBUS error";
+}
+
+
+#endif /* __MURPHY_DBUS_H__ */
diff --git a/src/common/list.h b/src/common/list.h
new file mode 100644
index 0000000..16c0ae4
--- /dev/null
+++ b/src/common/list.h
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LIST_H__
+#define __MURPHY_LIST_H__
+
+#include <murphy/common/macros.h>
+
+
+MRP_CDECL_BEGIN
+
+
+/** \file
+ * A simple doubly-linked circular list implementation, obviously inspired
+ * by the linux kernel.
+ */
+
+
+/** A list hook. Used both a list head and to hook up objects to the list. */
+typedef struct mrp_list_hook_s mrp_list_hook_t;
+struct mrp_list_hook_s {
+ mrp_list_hook_t *prev;
+ mrp_list_hook_t *next;
+};
+
+/** Macro to initialize a list to be empty. */
+#define MRP_LIST_INIT(list) { .prev = &(list), .next = &(list) }
+
+/** Macro to define a list and initialize it to be empty. */
+#define MRP_LIST_HOOK(list) mrp_list_hook_t list = MRP_LIST_INIT(list)
+
+/** Initialize a list to be empty. */
+static inline void mrp_list_init(mrp_list_hook_t *list)
+{
+ list->prev = list->next = list;
+}
+
+/** Check if a list is empty. */
+static inline int mrp_list_empty(mrp_list_hook_t *list)
+{
+ if (list->next == list->prev) {
+ if (list->next == list)
+ return TRUE;
+
+#ifdef __MURPHY_LIST_ALLOW_NULL
+ if (!list->next)
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+/** Append a new item to a list (add it after the last item). */
+static inline void mrp_list_append(mrp_list_hook_t *list, mrp_list_hook_t *item)
+{
+ if (mrp_list_empty(list)) {
+ list->next = list->prev = item;
+ item->next = item->prev = list;
+ }
+ else {
+ mrp_list_hook_t *prev = list->prev;
+
+ prev->next = item;
+ item->prev = prev;
+ item->next = list;
+ list->prev = item;
+ }
+}
+
+/** Prepend a new item to a list (add it before the first item). */
+static inline void mrp_list_prepend(mrp_list_hook_t *list,
+ mrp_list_hook_t *item)
+{
+ if (mrp_list_empty(list)) {
+ list->next = list->prev = item;
+ item->next = item->prev = list;
+ }
+ else {
+ mrp_list_hook_t *next = list->next;
+
+ list->next = item;
+ item->prev = list;
+ item->next = next;
+ next->prev = item;
+ }
+}
+
+/** Insert a new item to the list before a given item. */
+static inline void mrp_list_insert_before(mrp_list_hook_t *next,
+ mrp_list_hook_t *item)
+{
+ mrp_list_append(next, item);
+}
+
+/** Insert a new item to the list after a given item. */
+static inline void mrp_list_insert_after(mrp_list_hook_t *prev,
+ mrp_list_hook_t *item)
+{
+ mrp_list_prepend(prev, item);
+}
+
+/** Delete the given item from the list. */
+static inline void mrp_list_delete(mrp_list_hook_t *item)
+{
+ mrp_list_hook_t *prev, *next;
+
+ if (!mrp_list_empty(item)) {
+ prev = item->prev;
+ next = item->next;
+
+ prev->next = next;
+ next->prev = prev;
+
+ item->prev = item->next = item;
+ }
+}
+
+/** Reattach a list to a new hook. Initialize old hook to be empty. */
+static inline void mrp_list_move(mrp_list_hook_t *new_hook,
+ mrp_list_hook_t *old_hook)
+{
+ *new_hook = *old_hook;
+
+ new_hook->next->prev = new_hook;
+ new_hook->prev->next = new_hook;
+
+ mrp_list_init(old_hook);
+}
+
+
+/** Update a list when the address of a hook has changed (eg. by realloc). */
+static inline void mrp_list_update_address(mrp_list_hook_t *new_addr,
+ mrp_list_hook_t *old_addr)
+{
+ mrp_list_hook_t *prev, *next;
+ ptrdiff_t diff;
+
+ diff = new_addr - old_addr;
+ prev = new_addr->prev;
+ next = new_addr->next;
+
+ prev->next += diff;
+ next->prev += diff;
+}
+
+
+/** Macro to iterate through a list (current item safe to remove). */
+#define mrp_list_foreach(list, p, n) \
+ if ((list)->next != NULL) \
+ for (p = (list)->next, n = p->next; p != (list); p = n, n = n->next)
+
+/** Macro to iterate through a list backwards (current item safe to remove). */
+#define mrp_list_foreach_back(list, p, n) \
+ if ((list)->prev != NULL) \
+ for (p = (list)->prev, n = p->prev; p != (list); p = n, n = n->prev)
+
+/** Macro to get a pointer to a embedding structure from a list pointer. */
+#ifndef __cplusplus
+# define PTR_ARITH_TYPE void
+#else
+# define PTR_ARITH_TYPE char
+#endif
+
+#define mrp_list_entry(ptr, type, member) \
+ (type *)(((PTR_ARITH_TYPE *)(ptr)) - MRP_OFFSET(type, member))
+
+
+MRP_CDECL_END
+
+
+#endif /* __MURPHY_LIST_H__ */
+
diff --git a/src/common/log.c b/src/common/log.c
new file mode 100644
index 0000000..e35c43a
--- /dev/null
+++ b/src/common/log.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *name;
+ mrp_logger_t logger;
+ void *data;
+ int builtin;
+} log_target_t;
+
+static log_target_t stderr_target;
+static log_target_t stdout_target;
+static log_target_t syslog_target;
+static log_target_t file_target;
+
+static MRP_LIST_HOOK(log_targets);
+static int log_mask = MRP_LOG_MASK_ERROR;
+static log_target_t *log_target = NULL;
+
+
+mrp_log_mask_t mrp_log_parse_levels(const char *levels)
+{
+ const char *p;
+ mrp_log_mask_t mask;
+
+ mask = 0;
+
+ if (levels == NULL) {
+ if (mask == 0)
+ mask = 1;
+ else {
+ mask <<= 1;
+ mask |= 1;
+ }
+ }
+ else {
+ p = levels;
+ while (p && *p) {
+# define MATCHES(s, l) (!strcmp(s, l) || \
+ !strncmp(s, l",", sizeof(l",") - 1))
+
+ if (MATCHES(p, "info"))
+ mask |= MRP_LOG_MASK_INFO;
+ else if (MATCHES(p, "error"))
+ mask |= MRP_LOG_MASK_ERROR;
+ else if (MATCHES(p, "warning"))
+ mask |= MRP_LOG_MASK_WARNING;
+ else if (MATCHES(p, "none") || MATCHES(p, "off"))
+ mask = 0;
+ else
+ return -1;
+
+ if ((p = strchr(p, ',')) != NULL)
+ p += 1;
+
+# undef MATCHES
+ }
+ }
+
+ return mask;
+}
+
+
+const char *mrp_log_parse_target(const char *target)
+{
+ return target;
+}
+
+
+const char *mrp_log_dump_mask(mrp_log_mask_t mask, char *buf, size_t size)
+{
+ char *p, *t;
+ int n, l;
+
+ if (!mask)
+ return "none";
+
+ p = buf;
+ l = size;
+
+ t = "";
+ *p = '\0';
+
+ if (mask & MRP_LOG_MASK_INFO) {
+ n = snprintf(p, l, "info");
+ p += n;
+ l -= n;
+ t = ",";
+ }
+ if (mask & MRP_LOG_MASK_WARNING) {
+ n = snprintf(p, l, "%swarning", t);
+ p += n;
+ l -= n;
+ t = ",";
+ }
+ if (mask & MRP_LOG_MASK_ERROR) {
+ n = snprintf(p, l, "%serror", t);
+ p += n;
+ l -= n;
+ t = ",";
+ }
+
+ return buf;
+}
+
+
+mrp_log_mask_t mrp_log_enable(mrp_log_mask_t enabled)
+{
+ mrp_log_mask_t old_mask = log_mask;
+
+ log_mask |= enabled;
+
+ return old_mask;
+}
+
+
+mrp_log_mask_t mrp_log_disable(mrp_log_mask_t disabled)
+{
+ mrp_log_mask_t old_mask = log_mask;
+
+ log_mask &= ~disabled;
+
+ return old_mask;
+}
+
+
+mrp_log_mask_t mrp_log_set_mask(mrp_log_mask_t enabled)
+{
+ mrp_log_mask_t old_mask = log_mask;
+
+ log_mask = enabled;
+
+ return old_mask;
+}
+
+
+static log_target_t *find_target(const char *name)
+{
+ log_target_t *t;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&log_targets, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (t->name == name || !strcmp(t->name, name))
+ return t;
+ }
+
+ return NULL;
+}
+
+
+int mrp_log_set_target(const char *name)
+{
+ log_target_t *target;
+ const char *path;
+
+ if (!strncmp(name, "file:", 5)) {
+ path = name + 5;
+ name = "file";
+ }
+ else
+ path = NULL;
+
+ target = find_target(name);
+
+ if (target == NULL || (target == &file_target && path == NULL))
+ return FALSE;
+
+ /* close files opened by us, if any */
+ if (log_target == &file_target) {
+ if (file_target.data != NULL) {
+ fclose(file_target.data);
+ file_target.data = NULL;
+ }
+ }
+
+ log_target = target;
+
+ /* open any new files if we have to */
+ if (target == &file_target) {
+ target->data = fopen(path, "a");
+
+ if (target->data == NULL) {
+ log_target = &syslog_target;
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+const char *mrp_log_get_target(void)
+{
+ return log_target->name;
+}
+
+
+int mrp_log_get_targets(const char **targets, size_t size)
+{
+ mrp_list_hook_t *p, *n;
+ log_target_t *t;
+ int cnt;
+
+ cnt = 0;
+ mrp_list_foreach(&log_targets, p, n) {
+ if (cnt == (int)size)
+ break;
+
+ t = mrp_list_entry(p, typeof(*t), hook);
+ targets[cnt++] = t->name;
+ }
+
+ return cnt;
+}
+
+
+int mrp_log_register_target(const char *name, mrp_logger_t logger, void *data)
+{
+ log_target_t *target;
+
+ if (find_target(name) != NULL)
+ return FALSE;
+
+ target = mrp_allocz(sizeof(*target));
+
+ mrp_list_init(&target->hook);
+ target->name = mrp_strdup(name);
+ target->logger = logger;
+ target->data = data;
+
+ if (target->name != NULL) {
+ mrp_list_append(&log_targets, &target->hook);
+
+ return TRUE;
+ }
+ else {
+ mrp_free(target);
+
+ return FALSE;
+ }
+}
+
+
+int mrp_log_unregister_target(const char *name)
+{
+ log_target_t *target;
+
+ target = find_target(name);
+
+ if (target == NULL || target->builtin)
+ return FALSE;
+
+ if (log_target == target)
+ log_target = &stderr_target;
+
+ mrp_list_delete(&target->hook);
+ mrp_free(target->name);
+ mrp_free(target);
+
+ return TRUE;
+}
+
+
+static void log_msgv(void *data, mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format,
+ va_list ap)
+{
+ FILE *fp = data;
+ int lvl;
+ const char *prefix;
+ char prfx[2*1024];
+
+ if (!(log_mask & (1 << level)))
+ return;
+
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+
+ switch (level) {
+ case MRP_LOG_ERROR: lvl = LOG_ERR; prefix = "E: "; break;
+ case MRP_LOG_WARNING: lvl = LOG_WARNING; prefix = "W: "; break;
+ case MRP_LOG_INFO: lvl = LOG_INFO; prefix = "I: "; break;
+ case MRP_LOG_DEBUG: lvl = LOG_INFO;
+ snprintf(prfx, sizeof(prfx) - 1, "D: [%s] ", func);
+ prfx[sizeof(prfx)-1] = '\0';
+ prefix = prfx;
+ break;
+ default:
+ return;
+ }
+
+ if (fp == NULL)
+ vsyslog(lvl, format, ap);
+ else {
+ fputs(prefix, fp);
+ vfprintf(fp, format, ap); fputs("\n", fp);
+ fflush(fp);
+ }
+}
+
+
+void mrp_log_msgv(mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format,
+ va_list ap)
+{
+ static int busy = 0;
+ mrp_logger_t logger = log_target->logger;
+ void *data = log_target->data;
+
+ if (MRP_UNLIKELY(busy != 0))
+ return;
+
+ if (!(log_mask & (1 << level)))
+ return;
+
+ busy++;
+ logger(data, level, file, line, func, format, ap);
+ busy--;
+}
+
+
+void mrp_log_msg(mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format, ...)
+{
+ va_list ap;
+
+ if (!(log_mask & (1 << level)))
+ return;
+
+ va_start(ap, format);
+ mrp_log_msgv(level, file, line, func, format, ap);
+ va_end(ap);
+}
+
+
+/*
+ * workaround for not being able to initialize log_fp to stderr
+ */
+
+static __attribute__((constructor)) void set_default_logging(void)
+{
+ mrp_list_init(&stderr_target.hook);
+ stderr_target.name = "stderr";
+ stderr_target.logger = log_msgv;
+ stderr_target.data = stderr;
+ stderr_target.builtin = TRUE;
+
+ mrp_list_init(&stdout_target.hook);
+ stdout_target.name = "stdout";
+ stdout_target.logger = log_msgv;
+ stdout_target.data = stdout;
+ stdout_target.builtin = TRUE;
+
+ mrp_list_init(&syslog_target.hook);
+ syslog_target.name = "syslog";
+ syslog_target.logger = log_msgv;
+ syslog_target.data = NULL;
+ syslog_target.builtin = TRUE;
+
+ mrp_list_init(&file_target.hook);
+ file_target.name = "file";
+ file_target.logger = log_msgv;
+ file_target.data = NULL;
+ file_target.builtin = TRUE;
+
+ mrp_list_prepend(&log_targets, &file_target.hook);
+ mrp_list_prepend(&log_targets, &syslog_target.hook);
+ mrp_list_prepend(&log_targets, &stderr_target.hook);
+ mrp_list_prepend(&log_targets, &stdout_target.hook);
+
+ log_target = &stderr_target;
+}
diff --git a/src/common/log.h b/src/common/log.h
new file mode 100644
index 0000000..61e69c5
--- /dev/null
+++ b/src/common/log.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LOG_H__
+#define __MURPHY_LOG_H__
+
+/** \file
+ * Logging functions and macros.
+ */
+
+#include <stdarg.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_LOG_NAME_ERROR "error" /**< name for error level */
+#define MRP_LOG_NAME_WARNING "warning" /**< name for warning level */
+#define MRP_LOG_NAME_INFO "info" /**< name for info level */
+#define MRP_LOG_NAME_DEBUG "debug" /**< name for debug level */
+
+
+/**
+ * Logging levels.
+ */
+typedef enum {
+ MRP_LOG_ERROR = 0, /**< error log level */
+ MRP_LOG_WARNING, /**< warning log level */
+ MRP_LOG_INFO, /**< info log level */
+ MRP_LOG_DEBUG, /**< debug log level */
+} mrp_log_level_t;
+
+
+/**
+ * Logging masks.
+ */
+typedef enum {
+ MRP_LOG_MASK_ERROR = 0x01, /**< error logging mask */
+ MRP_LOG_MASK_WARNING = 0x02, /**< warning logging mask */
+ MRP_LOG_MASK_INFO = 0x04, /**< info logging mask */
+ MRP_LOG_MASK_DEBUG = 0x08, /**< debug logging mask */
+} mrp_log_mask_t;
+
+#define MRP_LOG_MASK(level) (1 << ((level)-1)) /**< mask of level */
+#define MRP_LOG_UPTO(level) ((1 << (level+1))-1) /**< mask up to level */
+
+
+/** Parse a string of comma-separated log level names to a log mask. */
+mrp_log_mask_t mrp_log_parse_levels(const char *levels);
+
+/** Write the given log mask as a string to the given buffer. */
+const char *mrp_log_dump_mask(mrp_log_mask_t mask, char *buf, size_t size);
+
+/** Clear current logging level and enable levels in mask. */
+mrp_log_mask_t mrp_log_set_mask(mrp_log_mask_t mask);
+
+/** Enable logging for levels in mask. */
+mrp_log_mask_t mrp_log_enable(mrp_log_mask_t mask);
+
+/** Disable logging for levels in mask. */
+mrp_log_mask_t mrp_log_disable(mrp_log_mask_t mask);
+
+/** Get the current logging level mask. */
+#define mrp_log_get_mask() mrp_log_disable(0)
+
+/**
+ * Logging target names.
+ */
+#define MRP_LOG_NAME_STDOUT "stdout"
+#define MRP_LOG_NAME_STDERR "stderr"
+#define MRP_LOG_NAME_SYSLOG "syslog"
+
+/**
+ * Logging targets.
+ */
+#define MRP_LOG_TO_STDOUT "stdout"
+#define MRP_LOG_TO_STDERR "stderr"
+#define MRP_LOG_TO_SYSLOG "syslog"
+#define MRP_LOG_TO_FILE(path) ((const char *)(path))
+
+
+/** Parse a log target name to MRP_LOG_TO_*. */
+const char *mrp_log_parse_target(const char *target);
+
+/** Set logging target. */
+int mrp_log_set_target(const char *target);
+
+/** Get the current log target. */
+const char *mrp_log_get_target(void);
+
+/** Get all available logging targets. */
+int mrp_log_get_targets(const char **targets, size_t size);
+
+/** Log an error. */
+#define mrp_log_error(fmt, args...) \
+ mrp_log_msg(MRP_LOG_ERROR, __LOC__, fmt , ## args)
+
+/** Log a warning. */
+#define mrp_log_warning(fmt, args...) \
+ mrp_log_msg(MRP_LOG_WARNING, __LOC__, fmt , ## args)
+
+/** Log an informational message. */
+#define mrp_log_info(fmt, args...) \
+ mrp_log_msg(MRP_LOG_INFO, __LOC__, fmt , ## args)
+
+/** Generic logging function. */
+void mrp_log_msg(mrp_log_level_t level,
+ const char *file, int line, const char *func,
+ const char *format, ...) MRP_PRINTF_LIKE(5, 6);
+
+/** Generic logging function for easy wrapping. */
+void mrp_log_msgv(mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format, va_list ap);
+
+/** Type for custom logging functions. */
+typedef void (*mrp_logger_t)(void *user_data,
+ mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format,
+ va_list ap);
+
+/** Register a new logging target. */
+int mrp_log_register_target(const char *name, mrp_logger_t logger,
+ void *user_data);
+
+/** Unregister the given logging target. */
+int mrp_log_unregister_target(const char *name);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_LOG_H__ */
diff --git a/src/common/macros.h b/src/common/macros.h
new file mode 100644
index 0000000..1fde94a
--- /dev/null
+++ b/src/common/macros.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MACROS_H__
+#define __MURPHY_MACROS_H__
+
+#include <stddef.h>
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE (!FALSE)
+#endif
+
+#ifdef __cplusplus
+# define typeof(expr) decltype(expr)
+#endif
+
+/** Align ptr to multiple of align. */
+#define MRP_ALIGN(ptr, align) (((ptr) + ((align)-1)) & ~((align)-1))
+
+/** Get the offset of the given member in a struct/union of the given type. */
+#define MRP_OFFSET(type, member) ((ptrdiff_t)(&(((type *)0)->member)))
+
+/** Determine the dimension of the given array. */
+#define MRP_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+#ifndef __GNUC__
+# define __FUNCTION__ __func__
+#endif
+
+#ifdef __GNUC__
+ /** MAX that evalutes its arguments only once. */
+# define MRP_MAX(a, b) ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+
+ /** MIN that evalutes its arguments only once. */
+# define MRP_MIN(a, b) ({ \
+ typeof(a) _a = (a), _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+
+ /** Likeliness branch-prediction hint for the compiler. */
+# define MRP_LIKELY(cond) __builtin_expect((cond), 1)
+
+ /** Unlikeliness branch-prediction hint for the compiler. */
+# define MRP_UNLIKELY(cond) __builtin_expect((cond), 0)
+
+ /** Prevent symbol from being exported (overriden by linker scripts). */
+# define MRP_HIDDEN __attribute__ ((visibility ("hidden")))
+
+ /** Request a symbol to be exported (overriden by linker scripts). */
+# define MRP_EXPORT __attribute__ ((visibility ("default")))
+
+ /** Ask the compiler to check for the presence of a NULL-sentinel. */
+# define MRP_NULLTERM __attribute__((sentinel))
+
+ /** Ask for printf-like format string checks of calls to this function. */
+# define MRP_PRINTF_LIKE(format_idx, first_arg_idx) \
+ __attribute__ ((format (printf, format_idx, first_arg_idx)))
+
+ /** Mark a function to be called before main is entered. */
+# define MRP_INIT __attribute__((constructor(65535)))
+# define MRP_INIT_AT(prio) __attribute__ ((constructor(prio)))
+
+ /** Mark a function to be called after main returns, or exit is called. */
+# define MRP_EXIT __attribute__ ((destructor(65535)))
+# define MRP_EXIT_AT(prio) __attribute__ ((destructor(prio)))
+
+/** Mark a variable unused. */
+# define MRP_UNUSED(var) (void)var
+#else /* ! __GNUC__ */
+# define MRP_LIKELY(cond) (cond)
+# define MRP_UNLIKELY(cond) (cond)
+# define MRP_HIDDEN
+# define MRP_EXPORT
+# define MRP_NULLTERM
+# define MRP_PRINTF_LIKE(format_idx, first_arg_idx)
+# define __FUNCTION__ __func__
+# define MRP_INIT
+# define MRP_INIT_AT
+# define MRP_EXIT
+# define MRP_EXIT_AT
+# define MRP_UNUSED(var)
+#endif
+
+/** Macro that can be used to pass the location of its usage. */
+# define __LOC__ __FILE__, __LINE__, __FUNCTION__
+
+/** Assertions. */
+#ifndef NDEBUG
+# define MRP_ASSERT(expr, fmt, args...) do { \
+ if (!(expr)) { \
+ printf("assertion '%s' failed at %s@%s:%d: "fmt"\n", #expr, \
+ __FUNCTION__, __FILE__, __LINE__, ## args); \
+ abort(); \
+ } \
+ } while (0)
+#else
+# define MRP_ASSERT(expr, msg) do { } while (0)
+#endif
+
+/** Create a version integer from a (major, minor, micro) tuple. */
+#define MRP_VERSION_INT(maj, min, mic) \
+ ((((maj) & 0xff) << 16) | (((min) & 0xff) << 8) | ((mic) & 0xff))
+
+/** Create a version string from a (const) (major, minor, micro) tuple. */
+#define MRP_VERSION_STRING(maj, min, mic) #maj"."#min"."#mic
+
+/** Extract major version from a version integer. */
+#define MRP_VERSION_MAJOR(ver) (((ver) >> 16) & 0xff)
+
+/** Extract minor version from a version integer. */
+#define MRP_VERSION_MINOR(ver) (((ver) >> 8) & 0xff)
+
+/** Extract micro version from a version integer. */
+#define MRP_VERSION_MICRO(ver) ((ver) & 0xff)
+
+/** Macro to stringify a macro argument. */
+#define MRP_STRINGIFY(arg) #arg
+
+/** C++-compatibility macros. */
+#ifdef __cplusplus
+# define MRP_CDECL_BEGIN extern "C" {
+# define MRP_CDECL_END }
+#else
+# define MRP_CDECL_BEGIN
+# define MRP_CDECL_END
+#endif
+
+#endif /* __MURPHY_MACROS_H__ */
+
diff --git a/src/common/mainloop.c b/src/common/mainloop.c
new file mode 100644
index 0000000..5702c15
--- /dev/null
+++ b/src/common/mainloop.c
@@ -0,0 +1,2625 @@
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/json.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/mainloop.h>
+
+#define USECS_PER_SEC (1000 * 1000)
+#define USECS_PER_MSEC (1000)
+#define NSECS_PER_USEC (1000)
+
+/*
+ * I/O watches
+ */
+
+struct mrp_io_watch_s {
+ mrp_list_hook_t hook; /* to list of watches */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ int fd; /* file descriptor to watch */
+ mrp_io_event_t events; /* events of interest */
+ mrp_io_watch_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+ struct pollfd *pollfd; /* associated pollfd */
+ mrp_list_hook_t slave; /* watches with the same fd */
+ int wrhup; /* EPOLLHUPs delivered */
+};
+
+#define is_master(w) !mrp_list_empty(&(w)->hook)
+#define is_slave(w) !mrp_list_empty(&(w)->slave)
+
+
+/*
+ * timers
+ */
+
+struct mrp_timer_s {
+ mrp_list_hook_t hook; /* to list of timers */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ unsigned int msecs; /* timer interval */
+ uint64_t expire; /* next expiration time */
+ mrp_timer_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+};
+
+
+/*
+ * deferred callbacks
+ */
+
+struct mrp_deferred_s {
+ mrp_list_hook_t hook; /* to list of cbs */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ mrp_deferred_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+ int inactive : 1;
+};
+
+
+/*
+ * signal handlers
+ */
+
+struct mrp_sighandler_s {
+ mrp_list_hook_t hook; /* to list of handlers */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ int signum; /* signal number */
+ mrp_sighandler_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+};
+
+
+/*
+ * wakeup notifications
+ */
+
+struct mrp_wakeup_s {
+ mrp_list_hook_t hook; /* to list of wakeup cbs */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ mrp_wakeup_event_t events; /* wakeup event mask */
+ uint64_t lpf; /* wakeup at most this often */
+ uint64_t next; /* next wakeup time */
+ mrp_timer_t *timer; /* forced interval timer */
+ mrp_wakeup_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+};
+
+#define mark_deleted(o) do { \
+ (o)->cb = NULL; \
+ mrp_list_append(&(o)->ml->deleted, &(o)->deleted); \
+ } while (0)
+
+#define is_deleted(o) ((o)->cb == NULL)
+
+
+/*
+ * any of the above data structures linked to the list of deleted items
+ *
+ * When deleted, the above data structures are first unlinked from their
+ * native list and linked to the special list of deleted entries. At an
+ * appropriate point upon every iteration of the main loop this list is
+ * checked and all entries are freed. This structure is used to get a
+ * pointer to the real structure that we need free. For this to work link
+ * hooks in all of the above structures need to be kept at the same offset
+ * as it is in deleted_t.
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* unfreed deleted items */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+} deleted_t;
+
+
+/*
+ * file descriptor table
+ *
+ * We do not want to associate direct pointers to related data structures
+ * with epoll. We might get delivered pending events for deleted fds (at
+ * least for unix domain sockets this seems to be the case) and with direct
+ * pointers we'd get delivered a dangling pointer together with the event.
+ * Instead we keep these structures in an fd table and use the fd to look
+ * up the associated data structure for events. We ignore events for which
+ * no data structure is found. In the fd table we keep a fixed size direct
+ * table for a small amount of fds (we expect to be using at most in the
+ * vast majority of cases) and we hash in the rest.
+ */
+
+#define FDTBL_SIZE 64
+
+typedef struct {
+ void *t[FDTBL_SIZE];
+ mrp_htbl_t *h;
+} fdtbl_t;
+
+
+/*
+ * external mainloops
+ */
+
+struct mrp_subloop_s {
+ mrp_list_hook_t hook; /* to list of subloops */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* main loop */
+ mrp_subloop_ops_t *cb; /* subloop glue callbacks */
+ void *user_data; /* opaque subloop data */
+ int epollfd; /* epollfd for this subloop */
+ struct epoll_event *events; /* epoll event buffer */
+ int nevent; /* epoll event buffer size */
+ fdtbl_t *fdtbl; /* file descriptor table */
+ mrp_io_watch_t *w; /* watch for epollfd */
+ struct pollfd *pollfds; /* pollfds for this subloop */
+ int npollfd; /* number of pollfds */
+ int pending; /* pending events */
+ int poll; /* need to poll for events */
+};
+
+
+/*
+ * event busses
+ */
+
+struct mrp_event_bus_s {
+ char *name; /* bus name */
+ mrp_list_hook_t hook; /* to list of busses */
+ mrp_mainloop_t *ml; /* associated mainloop */
+ mrp_list_hook_t watches; /* event watches on this bus */
+ int busy; /* whether pumping events */
+ int dead;
+};
+
+
+/*
+ * event watches
+ */
+
+struct mrp_event_watch_s {
+ mrp_list_hook_t hook; /* to list of event watches */
+ mrp_event_bus_t *bus; /* associated event bus */
+ mrp_event_mask_t mask; /* mask of watched events */
+ mrp_event_watch_cb_t cb; /* notification callback */
+ void *user_data; /* opaque user data */
+ int dead : 1; /* marked for deletion */
+};
+
+
+/*
+ * pending events
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* to event queue */
+ mrp_event_bus_t *bus; /* bus for this event */
+ uint32_t id; /* event id */
+ int format; /* attached data format */
+ void *data; /* attached data */
+} pending_event_t;
+
+
+/*
+ * main loop
+ */
+
+struct mrp_mainloop_s {
+ int epollfd; /* our epoll descriptor */
+ struct epoll_event *events; /* epoll event buffer */
+ int nevent; /* epoll event buffer size */
+ fdtbl_t *fdtbl; /* file descriptor table */
+
+ mrp_list_hook_t iowatches; /* list of I/O watches */
+ int niowatch; /* number of I/O watches */
+ mrp_io_event_t iomode; /* default event trigger mode */
+
+ mrp_list_hook_t timers; /* list of timers */
+ mrp_timer_t *next_timer; /* next expiring timer */
+
+ mrp_list_hook_t deferred; /* list of deferred cbs */
+ mrp_list_hook_t inactive_deferred; /* inactive defferred cbs */
+
+ mrp_list_hook_t wakeups; /* list of wakeup cbs */
+
+ int poll_timeout; /* next poll timeout */
+ int poll_result; /* return value from poll */
+
+ int sigfd; /* signal polling fd */
+ sigset_t sigmask; /* signal mask */
+ mrp_io_watch_t *sigwatch; /* sigfd I/O watch */
+ mrp_list_hook_t sighandlers; /* signal handlers */
+
+ mrp_list_hook_t subloops; /* external main loops */
+
+ mrp_list_hook_t deleted; /* unfreed deleted items */
+ int quit; /* TRUE if _quit called */
+ int exit_code; /* returned from _run */
+
+ mrp_superloop_ops_t *super_ops; /* superloop options */
+ void *super_data; /* superloop glue data */
+ void *iow; /* superloop epollfd watch */
+ void *timer; /* superloop timer */
+ void *work; /* superloop deferred work */
+
+ mrp_list_hook_t busses; /* known event busses */
+ mrp_list_hook_t eventq; /* pending events */
+ mrp_deferred_t *eventd; /* deferred event pump cb */
+};
+
+
+static mrp_event_def_t *events; /* registered events */
+static int nevent; /* number of events */
+static MRP_LIST_HOOK (ewatches); /* global, synchronous 'bus' */
+
+
+static void dump_pollfds(const char *prefix, struct pollfd *fds, int nfd);
+static void adjust_superloop_timer(mrp_mainloop_t *ml);
+static size_t poll_events(void *id, mrp_mainloop_t *ml, void **bufp);
+static void pump_events(mrp_deferred_t *d, void *user_data);
+
+/*
+ * fd table manipulation
+ */
+
+static int fd_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t fd_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (uint32_t)(ptrdiff_t)key;
+
+ return h;
+}
+
+
+
+static fdtbl_t *fdtbl_create(void)
+{
+ fdtbl_t *ft;
+ mrp_htbl_config_t hcfg;
+
+ if ((ft = mrp_allocz(sizeof(*ft))) != NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = fd_cmp;
+ hcfg.hash = fd_hash;
+ hcfg.free = NULL;
+ hcfg.nbucket = 16;
+
+ ft->h = mrp_htbl_create(&hcfg);
+
+ if (ft->h != NULL)
+ return ft;
+ else
+ mrp_free(ft);
+ }
+
+ return NULL;
+}
+
+
+static void fdtbl_destroy(fdtbl_t *ft)
+{
+ if (ft != NULL) {
+ mrp_htbl_destroy(ft->h, FALSE);
+ mrp_free(ft);
+ }
+}
+
+
+static void *fdtbl_lookup(fdtbl_t *ft, int fd)
+{
+ if (fd >= 0 && ft != NULL) {
+ if (fd < FDTBL_SIZE)
+ return ft->t[fd];
+ else
+ return mrp_htbl_lookup(ft->h, (void *)(ptrdiff_t)fd);
+ }
+
+ return NULL;
+}
+
+
+static int fdtbl_insert(fdtbl_t *ft, int fd, void *ptr)
+{
+ if (fd >= 0 && ft != NULL) {
+ if (fd < FDTBL_SIZE) {
+ if (ft->t[fd] == NULL) {
+ ft->t[fd] = ptr;
+ return 0;
+ }
+ else
+ errno = EEXIST;
+ }
+ else {
+ if (mrp_htbl_insert(ft->h, (void *)(ptrdiff_t)fd, ptr))
+ return 0;
+ else
+ errno = EEXIST;
+ }
+ }
+ else
+ errno = EINVAL;
+
+ return -1;
+}
+
+
+static void fdtbl_remove(fdtbl_t *ft, int fd)
+{
+ if (fd >= 0 && ft != NULL) {
+ if (fd < FDTBL_SIZE)
+ ft->t[fd] = NULL;
+ else
+ mrp_htbl_remove(ft->h, (void *)(ptrdiff_t)fd, FALSE);
+ }
+}
+
+
+/*
+ * I/O watches
+ */
+
+static uint32_t epoll_event_mask(mrp_io_watch_t *master, mrp_io_watch_t *ignore)
+{
+ mrp_io_watch_t *w;
+ mrp_list_hook_t *p, *n;
+ uint32_t mask;
+
+ mask = (master != ignore ?
+ master->events : master->events & MRP_IO_TRIGGER_EDGE);
+
+ mrp_list_foreach(&master->slave, p, n) {
+ w = mrp_list_entry(p, typeof(*w), slave);
+
+ if (w != ignore)
+ mask |= w->events;
+ }
+
+ mrp_debug("epoll event mask for I/O watch %p: %d", master, mask);
+
+ return mask;
+}
+
+
+static int epoll_add_slave(mrp_io_watch_t *master, mrp_io_watch_t *slave)
+{
+ mrp_mainloop_t *ml = master->ml;
+ struct epoll_event evt;
+
+ evt.events = epoll_event_mask(master, NULL) | slave->events;
+ evt.data.u64 = 0;
+ evt.data.fd = master->fd;
+
+ if (epoll_ctl(ml->epollfd, EPOLL_CTL_MOD, master->fd, &evt) == 0) {
+ mrp_list_append(&master->slave, &slave->slave);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int epoll_add(mrp_io_watch_t *w)
+{
+ mrp_mainloop_t *ml = w->ml;
+ mrp_io_watch_t *master;
+ struct epoll_event evt;
+
+ if (fdtbl_insert(ml->fdtbl, w->fd, w) == 0) {
+ evt.events = w->events;
+ evt.data.u64 = 0; /* init full union for valgrind... */
+ evt.data.fd = w->fd;
+
+ if (epoll_ctl(ml->epollfd, EPOLL_CTL_ADD, w->fd, &evt) == 0) {
+ mrp_list_append(&ml->iowatches, &w->hook);
+ ml->niowatch++;
+
+ return 0;
+ }
+ else
+ fdtbl_remove(ml->fdtbl, w->fd);
+ }
+ else {
+ if (errno == EEXIST) {
+ master = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (master != NULL)
+ return epoll_add_slave(master, w);
+ }
+ }
+
+ return -1;
+}
+
+
+static int epoll_del(mrp_io_watch_t *w)
+{
+ mrp_mainloop_t *ml = w->ml;
+ mrp_io_watch_t *master;
+ struct epoll_event evt;
+ int status;
+
+ if (is_master(w))
+ master = w;
+ else
+ master = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (master != NULL) {
+ evt.events = epoll_event_mask(master, w);
+ evt.data.u64 = 0; /* init full union for valgrind... */
+ evt.data.fd = w->fd;
+
+ if ((evt.events & MRP_IO_EVENT_ALL) == 0) {
+ fdtbl_remove(ml->fdtbl, w->fd);
+ status = epoll_ctl(ml->epollfd, EPOLL_CTL_DEL, w->fd, &evt);
+
+ if (status == 0 || (errno == EBADF || errno == ENOENT))
+ ml->niowatch--;
+ }
+ else
+ status = epoll_ctl(ml->epollfd, EPOLL_CTL_MOD, w->fd, &evt);
+
+ if (status == 0 || (errno == EBADF || errno == ENOENT))
+ return 0;
+ else
+ mrp_log_error("Failed to update epoll for deleted I/O watch %p "
+ "(fd %d, %d: %s).", w, w->fd, errno, strerror(errno));
+ }
+ else {
+ mrp_log_error("Failed to find master for deleted I/O watch %p "
+ "(fd %d).", w, w->fd);
+ errno = EINVAL;
+ }
+
+ return -1;
+}
+
+
+static int free_io_watch(void *ptr)
+{
+ mrp_io_watch_t *w = (mrp_io_watch_t *)ptr;
+ mrp_mainloop_t *ml = w->ml;
+ mrp_io_watch_t *master;
+
+ master = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (master == w) {
+ fdtbl_remove(ml->fdtbl, w->fd);
+
+ if (!mrp_list_empty(&w->slave)) {
+ /* relink first slave as new master to mainloop */
+ master = mrp_list_entry(w->slave.next, typeof(*master), slave);
+ mrp_list_append(&ml->iowatches, &master->hook);
+
+ fdtbl_insert(ml->fdtbl, master->fd, master);
+ }
+ }
+
+ mrp_list_delete(&w->slave);
+ mrp_free(w);
+
+ return TRUE;
+}
+
+
+mrp_io_watch_t *mrp_add_io_watch(mrp_mainloop_t *ml, int fd,
+ mrp_io_event_t events,
+ mrp_io_watch_cb_t cb, void *user_data)
+{
+ mrp_io_watch_t *w;
+
+ if (fd < 0 || cb == NULL)
+ return NULL;
+
+ if ((w = mrp_allocz(sizeof(*w))) != NULL) {
+ mrp_list_init(&w->hook);
+ mrp_list_init(&w->deleted);
+ mrp_list_init(&w->slave);
+ w->ml = ml;
+ w->fd = fd;
+ w->events = events & MRP_IO_EVENT_ALL;
+
+ switch (events & MRP_IO_TRIGGER_MASK) {
+ case 0:
+ if (ml->iomode == MRP_IO_TRIGGER_EDGE)
+ w->events |= MRP_IO_TRIGGER_EDGE;
+ break;
+ case MRP_IO_TRIGGER_EDGE:
+ w->events |= MRP_IO_TRIGGER_EDGE;
+ break;
+ case MRP_IO_TRIGGER_LEVEL:
+ break;
+ default:
+ mrp_log_warning("Invalid I/O event trigger mode 0x%x.",
+ events & MRP_IO_TRIGGER_MASK);
+ break;
+ }
+
+ w->cb = cb;
+ w->user_data = user_data;
+ w->free = free_io_watch;
+
+ if (epoll_add(w) != 0) {
+ mrp_free(w);
+ w = NULL;
+ }
+ else
+ mrp_debug("added I/O watch %p (fd %d, events 0x%x)", w, w->fd, w->events);
+ }
+
+ return w;
+}
+
+
+void mrp_del_io_watch(mrp_io_watch_t *w)
+{
+ /*
+ * Notes: It is not safe to free the watch here as there might be
+ * a delivered but unprocessed epoll event with a pointer
+ * to the watch. We just mark it deleted and take care of
+ * the actual deletion in the dispatching loop.
+ */
+
+ if (w != NULL && !is_deleted(w)) {
+ mrp_debug("marking I/O watch %p (fd %d) deleted", w, w->fd);
+
+ mark_deleted(w);
+ w->events = 0;
+
+ epoll_del(w);
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_io_watch_mainloop(mrp_io_watch_t *w)
+{
+ return w ? w->ml : NULL;
+}
+
+
+int mrp_set_io_event_mode(mrp_mainloop_t *ml, mrp_io_event_t mode)
+{
+ if (mode == MRP_IO_TRIGGER_LEVEL || mode == MRP_IO_TRIGGER_EDGE) {
+ ml->iomode = mode;
+ return TRUE;
+ }
+ else {
+ mrp_log_error("Invalid I/O event mode 0x%x.", mode);
+ return FALSE;
+ }
+}
+
+
+mrp_io_event_t mrp_get_io_event_mode(mrp_mainloop_t *ml)
+{
+ return ml->iomode ? ml->iomode : MRP_IO_TRIGGER_LEVEL;
+}
+
+
+/*
+ * timers
+ */
+
+static uint64_t time_now(void)
+{
+ struct timespec ts;
+ uint64_t now;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ now = ts.tv_sec * USECS_PER_SEC;
+ now += ts.tv_nsec / NSECS_PER_USEC;
+
+ return now;
+}
+
+
+static inline int usecs_to_msecs(uint64_t usecs)
+{
+ int msecs;
+
+ msecs = (usecs + USECS_PER_MSEC - 1) / USECS_PER_MSEC;
+
+ return msecs;
+}
+
+
+static void insert_timer(mrp_timer_t *t)
+{
+ mrp_mainloop_t *ml = t->ml;
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t1, *next;
+ int inserted;
+
+ /*
+ * Notes:
+ * If there is ever a need to run a large number of
+ * simultaneous timers, we need to change this to a
+ * self-balancing data structure, eg. an red-black tree.
+ */
+
+ inserted = FALSE;
+ next = NULL;
+ mrp_list_foreach(&ml->timers, p, n) {
+ t1 = mrp_list_entry(p, mrp_timer_t, hook);
+
+ if (!is_deleted(t1)) {
+ if (t->expire <= t1->expire) {
+ mrp_list_prepend(p->prev, &t->hook);
+ inserted = TRUE;
+ break;
+ }
+ if (next == NULL)
+ next = t1;
+ }
+ }
+
+ if (!inserted)
+ mrp_list_append(&ml->timers, &t->hook);
+
+ if (next)
+ ml->next_timer = next;
+ else {
+ ml->next_timer = t;
+ adjust_superloop_timer(ml);
+ }
+}
+
+
+static inline void rearm_timer(mrp_timer_t *t)
+{
+ mrp_list_delete(&t->hook);
+ t->expire = time_now() + t->msecs * USECS_PER_MSEC;
+ insert_timer(t);
+}
+
+
+static mrp_timer_t *find_next_timer(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t = NULL;
+
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (!is_deleted(t))
+ break;
+ else
+ t = NULL;
+ }
+
+ ml->next_timer = t;
+ return t;
+}
+
+
+static int free_timer(void *ptr)
+{
+ mrp_timer_t *t = (mrp_timer_t *)ptr;
+
+ mrp_free(t);
+
+ return TRUE;
+}
+
+
+
+mrp_timer_t *mrp_add_timer(mrp_mainloop_t *ml, unsigned int msecs,
+ mrp_timer_cb_t cb, void *user_data)
+{
+ mrp_timer_t *t;
+
+ if (cb == NULL)
+ return NULL;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ mrp_list_init(&t->hook);
+ mrp_list_init(&t->deleted);
+ t->ml = ml;
+ t->expire = time_now() + msecs * USECS_PER_MSEC;
+ t->msecs = msecs;
+ t->cb = cb;
+ t->user_data = user_data;
+ t->free = free_timer;
+
+ insert_timer(t);
+ }
+
+ return t;
+}
+
+
+void mrp_mod_timer(mrp_timer_t *t, unsigned int msecs)
+{
+ if (t != NULL && !is_deleted(t)) {
+ if (msecs != MRP_TIMER_RESTART)
+ t->msecs = msecs;
+
+ rearm_timer(t);
+ }
+}
+
+
+void mrp_del_timer(mrp_timer_t *t)
+{
+ /*
+ * Notes: It is not safe to simply free this entry here as we might
+ * be dispatching with this entry being the next to process.
+ * We check for this and if it is not the case we relink this
+ * to the list of deleted items which will be then processed
+ * at end of the mainloop iteration. Otherwise we only mark the
+ * this entry for deletion and the rest will be taken care of in
+ * dispatch_timers().
+ */
+
+ if (t != NULL && !is_deleted(t)) {
+ mrp_debug("marking timer %p deleted", t);
+
+ mark_deleted(t);
+
+ if (t->ml->next_timer == t) {
+ find_next_timer(t->ml);
+ adjust_superloop_timer(t->ml);
+ }
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_timer_mainloop(mrp_timer_t *t)
+{
+ return t ? t->ml : NULL;
+}
+
+
+/*
+ * deferred/idle callbacks
+ */
+
+mrp_deferred_t *mrp_add_deferred(mrp_mainloop_t *ml, mrp_deferred_cb_t cb,
+ void *user_data)
+{
+ mrp_deferred_t *d;
+
+ if (cb == NULL)
+ return NULL;
+
+ if ((d = mrp_allocz(sizeof(*d))) != NULL) {
+ mrp_list_init(&d->hook);
+ mrp_list_init(&d->deleted);
+ d->ml = ml;
+ d->cb = cb;
+ d->user_data = user_data;
+
+ mrp_list_append(&ml->deferred, &d->hook);
+ adjust_superloop_timer(ml);
+ }
+
+ return d;
+}
+
+
+void mrp_del_deferred(mrp_deferred_t *d)
+{
+ /*
+ * Notes: It is not safe to simply free this entry here as we might
+ * be dispatching with this entry being the next to process.
+ * We just mark this here deleted and take care of the rest
+ * in the dispatching loop.
+ */
+
+ if (d != NULL && !is_deleted(d)) {
+ mrp_debug("marking deferred %p deleted", d);
+ mark_deleted(d);
+ }
+}
+
+
+void mrp_disable_deferred(mrp_deferred_t *d)
+{
+ if (d != NULL)
+ d->inactive = TRUE;
+}
+
+
+static inline void disable_deferred(mrp_deferred_t *d)
+{
+ if (MRP_LIKELY(d->inactive)) {
+ mrp_list_delete(&d->hook);
+ mrp_list_append(&d->ml->inactive_deferred, &d->hook);
+ }
+
+}
+
+
+void mrp_enable_deferred(mrp_deferred_t *d)
+{
+ if (d != NULL) {
+ if (!is_deleted(d)) {
+ d->inactive = FALSE;
+ mrp_list_delete(&d->hook);
+ mrp_list_append(&d->ml->deferred, &d->hook);
+ }
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_deferred_mainloop(mrp_deferred_t *d)
+{
+ return d ? d->ml : NULL;
+}
+
+
+/*
+ * signal notifications
+ */
+
+static void dispatch_signals(mrp_io_watch_t *w, int fd,
+ mrp_io_event_t events, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_io_watch_mainloop(w);
+ struct signalfd_siginfo sig;
+ mrp_list_hook_t *p, *n;
+ mrp_sighandler_t *h;
+ int signum;
+
+ MRP_UNUSED(events);
+ MRP_UNUSED(user_data);
+
+ while (read(fd, &sig, sizeof(sig)) > 0) {
+ signum = sig.ssi_signo;
+
+ mrp_list_foreach(&ml->sighandlers, p, n) {
+ h = mrp_list_entry(p, typeof(*h), hook);
+
+ if (!is_deleted(h)) {
+ if (h->signum == signum)
+ h->cb(h, signum, h->user_data);
+ }
+ }
+ }
+}
+
+
+static int setup_sighandlers(mrp_mainloop_t *ml)
+{
+ if (ml->sigfd == -1) {
+ sigemptyset(&ml->sigmask);
+
+ ml->sigfd = signalfd(-1, &ml->sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
+
+ if (ml->sigfd == -1)
+ return FALSE;
+
+ ml->sigwatch = mrp_add_io_watch(ml, ml->sigfd, MRP_IO_EVENT_IN,
+ dispatch_signals, NULL);
+
+ if (ml->sigwatch == NULL) {
+ close(ml->sigfd);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+mrp_sighandler_t *mrp_add_sighandler(mrp_mainloop_t *ml, int signum,
+ mrp_sighandler_cb_t cb, void *user_data)
+{
+ mrp_sighandler_t *s;
+
+ if (cb == NULL || ml->sigfd == -1)
+ return NULL;
+
+ if ((s = mrp_allocz(sizeof(*s))) != NULL) {
+ mrp_list_init(&s->hook);
+ mrp_list_init(&s->deleted);
+ s->ml = ml;
+ s->signum = signum;
+ s->cb = cb;
+ s->user_data = user_data;
+
+ mrp_list_append(&ml->sighandlers, &s->hook);
+ sigaddset(&ml->sigmask, s->signum);
+ signalfd(ml->sigfd, &ml->sigmask, SFD_NONBLOCK|SFD_CLOEXEC);
+ sigprocmask(SIG_BLOCK, &ml->sigmask, NULL);
+ }
+
+ return s;
+}
+
+
+static void recalc_sigmask(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_sighandler_t *h;
+
+ sigprocmask(SIG_UNBLOCK, &ml->sigmask, NULL);
+ sigemptyset(&ml->sigmask);
+
+ mrp_list_foreach(&ml->sighandlers, p, n) {
+ h = mrp_list_entry(p, typeof(*h), hook);
+ if (!is_deleted(h))
+ sigaddset(&ml->sigmask, h->signum);
+ }
+
+ sigprocmask(SIG_BLOCK, &ml->sigmask, NULL);
+}
+
+
+void mrp_del_sighandler(mrp_sighandler_t *h)
+{
+ if (h != NULL && !is_deleted(h)) {
+ mrp_debug("marking sighandler %p deleted", h);
+
+ mark_deleted(h);
+ recalc_sigmask(h->ml);
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_sighandler_mainloop(mrp_sighandler_t *h)
+{
+ return h ? h->ml : NULL;
+}
+
+
+/*
+ * wakeup notifications
+ */
+
+static void wakeup_cb(mrp_wakeup_t *w, mrp_wakeup_event_t event, uint64_t now)
+{
+ if (w->next > now) {
+ mrp_debug("skipping wakeup %p because of low-pass filter", w);
+ return;
+ }
+
+ w->cb(w, event, w->user_data);
+
+ if (w->lpf != MRP_WAKEUP_NOLIMIT)
+ w->next = now + w->lpf;
+
+ if (w->timer != NULL)
+ mrp_mod_timer(w->timer, MRP_TIMER_RESTART);
+}
+
+
+static void forced_wakeup_cb(mrp_timer_t *t, void *user_data)
+{
+ mrp_wakeup_t *w = (mrp_wakeup_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ if (is_deleted(w))
+ return;
+
+ mrp_debug("dispatching forced wakeup cb %p", w);
+
+ wakeup_cb(w, MRP_WAKEUP_EVENT_LIMIT, time_now());
+}
+
+
+mrp_wakeup_t *mrp_add_wakeup(mrp_mainloop_t *ml, mrp_wakeup_event_t events,
+ unsigned int lpf_msecs, unsigned int force_msecs,
+ mrp_wakeup_cb_t cb, void *user_data)
+{
+ mrp_wakeup_t *w;
+
+ if (cb == NULL)
+ return NULL;
+
+ if (lpf_msecs > force_msecs && force_msecs != MRP_WAKEUP_NOLIMIT)
+ return NULL;
+
+ if ((w = mrp_allocz(sizeof(*w))) != NULL) {
+ mrp_list_init(&w->hook);
+ mrp_list_init(&w->deleted);
+ w->ml = ml;
+ w->events = events;
+ w->cb = cb;
+ w->user_data = user_data;
+
+ w->lpf = lpf_msecs * USECS_PER_MSEC;
+
+ if (lpf_msecs != MRP_WAKEUP_NOLIMIT)
+ w->next = time_now() + w->lpf;
+
+ if (force_msecs != MRP_WAKEUP_NOLIMIT) {
+ w->timer = mrp_add_timer(ml, force_msecs, forced_wakeup_cb, w);
+
+ if (w->timer == NULL) {
+ mrp_free(w);
+ return NULL;
+ }
+ }
+
+ mrp_list_append(&ml->wakeups, &w->hook);
+ }
+
+ return w;
+}
+
+
+void mrp_del_wakeup(mrp_wakeup_t *w)
+{
+ /*
+ * Notes: It is not safe to simply free this entry here as we might
+ * be dispatching with this entry being the next to process.
+ * We just mark this here deleted and take care of the rest
+ * in the dispatching loop.
+ */
+
+ if (w != NULL && !is_deleted(w)) {
+ mrp_debug("marking wakeup %p deleted", w);
+ mark_deleted(w);
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_wakeup_mainloop(mrp_wakeup_t *w)
+{
+ return w ? w->ml : NULL;
+}
+
+
+/*
+ * external mainloops we pump
+ */
+
+static int free_subloop(void *ptr)
+{
+ mrp_subloop_t *sl = (mrp_subloop_t *)ptr;
+
+ mrp_debug("freeing subloop %p", sl);
+
+ mrp_free(sl->pollfds);
+ mrp_free(sl->events);
+ mrp_free(sl);
+
+ return TRUE;
+}
+
+
+static void subloop_event_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ mrp_subloop_t *sl = (mrp_subloop_t *)user_data;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(fd);
+ MRP_UNUSED(events);
+
+ mrp_debug("subloop %p has events, setting poll to TRUE", sl);
+
+ sl->poll = TRUE;
+}
+
+
+mrp_subloop_t *mrp_add_subloop(mrp_mainloop_t *ml, mrp_subloop_ops_t *ops,
+ void *user_data)
+{
+ mrp_subloop_t *sl;
+
+ if (ops == NULL || user_data == NULL)
+ return NULL;
+
+ if ((sl = mrp_allocz(sizeof(*sl))) != NULL) {
+ mrp_list_init(&sl->hook);
+ mrp_list_init(&sl->deleted);
+ sl->free = free_subloop;
+ sl->ml = ml;
+ sl->cb = ops;
+ sl->user_data = user_data;
+ sl->epollfd = epoll_create1(EPOLL_CLOEXEC);
+ sl->fdtbl = fdtbl_create();
+
+ if (sl->epollfd >= 0 && sl->fdtbl != NULL) {
+ sl->w = mrp_add_io_watch(ml, sl->epollfd, MRP_IO_EVENT_IN,
+ subloop_event_cb, sl);
+
+ if (sl->w != NULL)
+ mrp_list_append(&ml->subloops, &sl->hook);
+ else
+ goto fail;
+ }
+ else {
+ fail:
+ close(sl->epollfd);
+ fdtbl_destroy(sl->fdtbl);
+ mrp_free(sl);
+ sl = NULL;
+ }
+ }
+
+ return sl;
+}
+
+
+void mrp_del_subloop(mrp_subloop_t *sl)
+{
+ struct epoll_event dummy;
+ int i;
+
+ /*
+ * Notes: It is not safe to free the loop here as there might be
+ * a delivered but unprocessed epoll event with a pointers
+ * to the loops pollfds. However, since we do not dispatch
+ * loops by traversing the list of loops, it is safe to relink
+ * it to the list of data structures to be deleted at the
+ * end of the next main loop iteration. So we just remove the
+ * pollfds from epoll, mark this as deleted and relink it.
+ */
+
+ if (sl != NULL && !is_deleted(sl)) {
+ mrp_debug("deactivating and marking subloop %p deleted", sl);
+
+ mrp_del_io_watch(sl->w);
+
+ /* XXX TODO: Why ? close(sl->epollfd) should be enough... */
+ for (i = 0; i < sl->npollfd; i++)
+ epoll_ctl(sl->epollfd, EPOLL_CTL_DEL, sl->pollfds[i].fd, &dummy);
+
+ close(sl->epollfd);
+ sl->epollfd = -1;
+ fdtbl_destroy(sl->fdtbl);
+ sl->fdtbl = NULL;
+
+ mark_deleted(sl);
+ }
+}
+
+
+/*
+ * external mainloop that pumps us
+ */
+
+
+static void super_io_cb(void *super_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_superloop_ops_t *ops = ml->super_ops;
+
+ MRP_UNUSED(super_data);
+ MRP_UNUSED(id);
+ MRP_UNUSED(fd);
+ MRP_UNUSED(events);
+
+ ops->mod_defer(ml->super_data, ml->work, TRUE);
+}
+
+
+static void super_timer_cb(void *super_data, void *id, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_superloop_ops_t *ops = ml->super_ops;
+
+ MRP_UNUSED(super_data);
+ MRP_UNUSED(id);
+
+ ops->mod_defer(ml->super_data, ml->work, TRUE);
+}
+
+
+static void super_work_cb(void *super_data, void *id, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_superloop_ops_t *ops = ml->super_ops;
+ unsigned int timeout;
+
+ MRP_UNUSED(super_data);
+ MRP_UNUSED(id);
+
+ mrp_mainloop_poll(ml, FALSE);
+ mrp_mainloop_dispatch(ml);
+
+ if (!ml->quit) {
+ mrp_mainloop_prepare(ml);
+
+ /*
+ * Notes:
+ *
+ * Some mainloop abstractions (eg. the one in PulseAudio)
+ * have deferred callbacks that starve all other event
+ * processing until no more deferred callbacks are pending.
+ * For this reason, we cannot map our deferred callbacks
+ * directly to superloop deferred callbacks (in some cases
+ * this could starve the superloop indefinitely). Hence, if
+ * we have enabled deferred callbacks, we arm our timer with
+ * 0 timeout to let the superloop do one round of its event
+ * processing.
+ */
+
+ timeout = mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+ ops->mod_timer(ml->super_data, ml->timer, timeout);
+ ops->mod_defer(ml->super_data, ml->work, FALSE);
+ }
+ else {
+ ops->del_io(ml->super_data, ml->iow);
+ ops->del_timer(ml->super_data, ml->timer);
+ ops->del_defer(ml->super_data, ml->work);
+
+ ml->iow = NULL;
+ ml->timer = NULL;
+ ml->work = NULL;
+ }
+}
+
+
+static void adjust_superloop_timer(mrp_mainloop_t *ml)
+{
+ mrp_superloop_ops_t *ops = ml->super_ops;
+ unsigned int timeout;
+
+ if (ops == NULL)
+ return;
+
+ mrp_mainloop_prepare(ml);
+ timeout = mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+ ops->mod_timer(ml->super_data, ml->timer, timeout);
+}
+
+
+int mrp_set_superloop(mrp_mainloop_t *ml, mrp_superloop_ops_t *ops,
+ void *loop_data)
+{
+ mrp_io_event_t events;
+ int timeout;
+
+ if (ml->super_ops == NULL) {
+ if (ops->poll_io != NULL)
+ ops->poll_events = poll_events;
+
+ ml->super_ops = ops;
+ ml->super_data = loop_data;
+
+ mrp_mainloop_prepare(ml);
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_OUT | MRP_IO_EVENT_HUP;
+ ml->iow = ops->add_io(ml->super_data, ml->epollfd, events,
+ super_io_cb, ml);
+ ml->work = ops->add_defer(ml->super_data, super_work_cb, ml);
+
+ /*
+ * Notes:
+ *
+ * Some mainloop abstractions (eg. the one in PulseAudio)
+ * have deferred callbacks that starve all other event
+ * processing until no more deferred callbacks are pending.
+ * For this reason, we cannot map our deferred callbacks
+ * directly to superloop deferred callbacks (in some cases
+ * this could starve the superloop indefinitely). Hence, if
+ * we have enabled deferred callbacks, we arm our timer with
+ * 0 timeout to let the superloop do one round of its event
+ * processing.
+ */
+
+ timeout = mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+ ml->timer = ops->add_timer(ml->super_data, timeout, super_timer_cb, ml);
+
+ if (ml->iow != NULL && ml->timer != NULL && ml->work != NULL)
+ return TRUE;
+ else
+ mrp_clear_superloop(ml);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_clear_superloop(mrp_mainloop_t *ml)
+{
+ mrp_superloop_ops_t *ops = ml->super_ops;
+ void *data = ml->super_data;
+
+ if (ops != NULL) {
+ if (ml->iow != NULL) {
+ ops->del_io(data, ml->iow);
+ ml->iow = NULL;
+ }
+
+ if (ml->work != NULL) {
+ ops->del_defer(data, ml->work);
+ ml->work = NULL;
+ }
+
+ if (ml->timer != NULL) {
+ ops->del_timer(data, ml->timer);
+ ml->timer = NULL;
+ }
+
+ ml->super_ops = NULL;
+ ml->super_data = NULL;
+
+ ops->unregister(data);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_mainloop_unregister(mrp_mainloop_t *ml)
+{
+ return mrp_clear_superloop(ml);
+}
+
+
+/*
+ * mainloop
+ */
+
+static void purge_io_watches(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n, *sp, *sn;
+ mrp_io_watch_t *w, *s;
+
+ mrp_list_foreach(&ml->iowatches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+ mrp_list_delete(&w->hook);
+ mrp_list_delete(&w->deleted);
+
+ mrp_list_foreach(&w->slave, sp, sn) {
+ s = mrp_list_entry(sp, typeof(*s), slave);
+ mrp_list_delete(&s->slave);
+ mrp_free(s);
+ }
+
+ mrp_free(w);
+ }
+}
+
+
+static void purge_timers(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t;
+
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+ mrp_list_delete(&t->hook);
+ mrp_list_delete(&t->deleted);
+ mrp_free(t);
+ }
+}
+
+
+static void purge_deferred(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_deferred_t *d;
+
+ mrp_list_foreach(&ml->deferred, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ mrp_list_delete(&d->hook);
+ mrp_list_delete(&d->deleted);
+ mrp_free(d);
+ }
+
+ mrp_list_foreach(&ml->inactive_deferred, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ mrp_list_delete(&d->hook);
+ mrp_list_delete(&d->deleted);
+ mrp_free(d);
+ }
+}
+
+
+static void purge_sighandlers(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_sighandler_t *s;
+
+ mrp_list_foreach(&ml->sighandlers, p, n) {
+ s = mrp_list_entry(p, typeof(*s), hook);
+ mrp_list_delete(&s->hook);
+ mrp_list_delete(&s->deleted);
+ mrp_free(s);
+ }
+}
+
+
+static void purge_wakeups(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_wakeup_t *w;
+
+ mrp_list_foreach(&ml->wakeups, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+ mrp_list_delete(&w->hook);
+ mrp_list_delete(&w->deleted);
+ mrp_free(w);
+ }
+}
+
+
+static void purge_deleted(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ deleted_t *d;
+
+ mrp_list_foreach(&ml->deleted, p, n) {
+ d = mrp_list_entry(p, typeof(*d), deleted);
+ mrp_list_delete(&d->deleted);
+ mrp_list_delete(&d->hook);
+ if (d->free == NULL) {
+ mrp_debug("purging deleted object %p", d);
+ mrp_free(d);
+ }
+ else {
+ mrp_debug("purging deleted object %p (free cb: %p)", d, d->free);
+ if (!d->free(d)) {
+ mrp_log_error("Failed to free purged item %p.", d);
+ mrp_list_prepend(p, &d->deleted);
+ }
+ }
+ }
+}
+
+
+static void purge_subloops(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_subloop_t *sl;
+
+ mrp_list_foreach(&ml->subloops, p, n) {
+ sl = mrp_list_entry(p, typeof(*sl), hook);
+ mrp_list_delete(&sl->hook);
+ mrp_list_delete(&sl->deleted);
+ free_subloop(sl);
+ }
+}
+
+
+mrp_mainloop_t *mrp_mainloop_create(void)
+{
+ mrp_mainloop_t *ml;
+
+ if ((ml = mrp_allocz(sizeof(*ml))) != NULL) {
+ ml->epollfd = epoll_create1(EPOLL_CLOEXEC);
+ ml->sigfd = -1;
+ ml->fdtbl = fdtbl_create();
+
+ if (ml->epollfd >= 0 && ml->fdtbl != NULL) {
+ mrp_list_init(&ml->iowatches);
+ mrp_list_init(&ml->timers);
+ mrp_list_init(&ml->deferred);
+ mrp_list_init(&ml->inactive_deferred);
+ mrp_list_init(&ml->sighandlers);
+ mrp_list_init(&ml->wakeups);
+ mrp_list_init(&ml->deleted);
+ mrp_list_init(&ml->subloops);
+ mrp_list_init(&ml->busses);
+ mrp_list_init(&ml->eventq);
+
+ ml->eventd = mrp_add_deferred(ml, pump_events, ml);
+ if (ml->eventd == NULL)
+ goto fail;
+ mrp_disable_deferred(ml->eventd);
+
+ if (!setup_sighandlers(ml))
+ goto fail;
+ }
+ else {
+ fail:
+ close(ml->epollfd);
+ fdtbl_destroy(ml->fdtbl);
+ mrp_free(ml);
+ ml = NULL;
+ }
+ }
+
+
+
+ return ml;
+}
+
+
+void mrp_mainloop_destroy(mrp_mainloop_t *ml)
+{
+ if (ml != NULL) {
+ mrp_clear_superloop(ml);
+ purge_io_watches(ml);
+ purge_timers(ml);
+ purge_deferred(ml);
+ purge_sighandlers(ml);
+ purge_wakeups(ml);
+ purge_subloops(ml);
+ purge_deleted(ml);
+
+ close(ml->sigfd);
+ close(ml->epollfd);
+ fdtbl_destroy(ml->fdtbl);
+
+ mrp_free(ml->events);
+ mrp_free(ml);
+ }
+}
+
+
+static int prepare_subloop(mrp_subloop_t *sl)
+{
+ /*
+ * Notes:
+ *
+ * If we have a relatively large number of file descriptors to
+ * poll but typically only a small fraction of them has pending
+ * events per mainloop iteration epoll has significant advantages
+ * over poll. This is the main reason why our mainloop uses epoll.
+ * However, there is a considerable amount of pain one needs to
+ * go through to integrate an external poll-based (sub-)mainloop
+ * (e.g. glib's GMainLoop) with an epoll-based mainloop. I mean,
+ * just look at the code below !
+ *
+ * If it eventually turns out that we typically only have a small
+ * number of file descriptors while at the same time we practically
+ * always need to pump GMainLoop, it is probably a good idea to
+ * bite the bullet and change our mainloop to be poll-based as well.
+ * But let's not go there yet...
+ */
+
+
+ struct epoll_event evt;
+ struct pollfd *fds, *pollfds;
+ int timeout;
+ int nfd, npollfd, n, i;
+ int nmatch;
+ int fd, idx;
+
+ MRP_UNUSED(dump_pollfds);
+
+ mrp_debug("preparing subloop %p", sl);
+
+ pollfds = sl->pollfds;
+ npollfd = sl->npollfd;
+
+ if (sl->cb->prepare(sl->user_data)) {
+ mrp_debug("subloop %p prepare reported ready, dispatching it", sl);
+ sl->cb->dispatch(sl->user_data);
+ }
+ sl->poll = FALSE;
+
+ nfd = npollfd;
+ fds = nfd ? mrp_allocz(nfd * sizeof(*fds)) : NULL;
+
+ MRP_ASSERT(nfd == 0 || fds != NULL, "failed to allocate pollfd's");
+
+ while ((n = sl->cb->query(sl->user_data, fds, nfd, &timeout)) > nfd) {
+ fds = mrp_reallocz(fds, nfd, n);
+ nfd = n;
+ MRP_ASSERT(fds != NULL, "failed to allocate pollfd's");
+ }
+ nfd = n;
+
+
+#if 0
+ printf("-------------------------\n");
+ dump_pollfds("old: ", sl->pollfds, sl->npollfd);
+ dump_pollfds("new: ", fds, nfd);
+ printf("-------------------------\n");
+#endif
+
+
+ /*
+ * skip over the identical portion of the old and new pollfd's
+ */
+
+ for (i = nmatch = 0; i < npollfd && i < n; i++, nmatch++) {
+ if (fds[i].fd != pollfds[i].fd ||
+ fds[i].events != pollfds[i].events)
+ break;
+ else
+ fds[i].revents = pollfds[i].revents = 0;
+ }
+
+
+ if (nmatch == npollfd && npollfd == nfd) {
+ mrp_free(fds);
+ goto out;
+ }
+
+
+ /*
+ * replace file descriptors with the new set (remove old, add new)
+ */
+
+ for (i = 0; i < npollfd; i++) {
+ fd = pollfds[i].fd;
+ fdtbl_remove(sl->fdtbl, fd);
+ if (epoll_ctl(sl->epollfd, EPOLL_CTL_DEL, fd, &evt) < 0) {
+ if (errno != EBADF && errno != ENOENT)
+ mrp_log_error("Failed to delete subloop fd %d from epoll "
+ "(%d: %s)", fd, errno, strerror(errno));
+ }
+ }
+
+ for (i = 0; i < nfd; i++) {
+ fd = fds[i].fd;
+ idx = i + 1;
+
+ evt.events = fds[i].events;
+ evt.data.u64 = 0; /* init full union for valgrind... */
+ evt.data.fd = fd;
+
+ if (fdtbl_insert(sl->fdtbl, fd, (void *)(ptrdiff_t)idx) == 0) {
+ if (epoll_ctl(sl->epollfd, EPOLL_CTL_ADD, fd, &evt) != 0) {
+ mrp_log_error("Failed to add subloop fd %d to epoll "
+ "(%d: %s)", fd, errno, strerror(errno));
+ }
+ }
+ else {
+ mrp_log_error("Failed to add subloop fd %d to fd table "
+ "(%d: %s)", fd, errno, strerror(errno));
+ }
+
+ fds[i].revents = 0;
+ }
+
+ mrp_free(sl->pollfds);
+ sl->pollfds = fds;
+ sl->npollfd = nfd;
+
+
+ /*
+ * resize event buffer if needed
+ */
+
+ if (sl->nevent < nfd) {
+ sl->nevent = nfd;
+ sl->events = mrp_realloc(sl->events, sl->nevent * sizeof(*sl->events));
+
+ MRP_ASSERT(sl->events != NULL || sl->nevent == 0,
+ "can't allocate epoll event buffer");
+ }
+
+ out:
+ mrp_debug("subloop %p: fds: %d, timeout: %d, poll: %s",
+ sl, sl->npollfd, timeout, sl->poll ? "TRUE" : "FALSE");
+
+ return timeout;
+}
+
+
+static int prepare_subloops(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_subloop_t *sl;
+ int ext_timeout, min_timeout;
+
+ min_timeout = INT_MAX;
+
+ mrp_list_foreach(&ml->subloops, p, n) {
+ sl = mrp_list_entry(p, typeof(*sl), hook);
+
+ if (!is_deleted(sl)) {
+ ext_timeout = prepare_subloop(sl);
+ min_timeout = MRP_MIN(min_timeout, ext_timeout);
+ }
+ else
+ mrp_debug("skipping deleted subloop %p", sl);
+ }
+
+ return min_timeout;
+}
+
+
+#if 0
+static inline void dump_timers(mrp_mainloop_t *ml)
+{
+ mrp_timer_t *t;
+ mrp_list_hook_t *p, *n;
+ int i;
+ mrp_timer_t *next = NULL;
+
+ mrp_debug("timer dump:");
+ i = 0;
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ mrp_debug(" #%d: %p, @%u, next %llu (%s)", i, t, t->msecs, t->expire,
+ is_deleted(t) ? "DEAD" : "alive");
+
+ if (!is_deleted(t) && next == NULL)
+ next = t;
+
+ i++;
+ }
+
+ mrp_debug("next timer: %p", ml->next_timer);
+ mrp_debug("poll timer: %d", ml->poll_timeout);
+
+ if (next != NULL && ml->next_timer != NULL &&
+ !is_deleted(ml->next_timer) && next != ml->next_timer) {
+ mrp_debug("*** BUG ml->next_timer is not the nearest !!! ***");
+ if (getenv("__MURPHY_TIMER_CHECK_ABORT") != NULL)
+ abort();
+ }
+}
+#endif
+
+
+int mrp_mainloop_prepare(mrp_mainloop_t *ml)
+{
+ mrp_timer_t *next_timer;
+ int timeout, ext_timeout;
+ uint64_t now;
+
+ if (!mrp_list_empty(&ml->deferred)) {
+ timeout = 0;
+ }
+ else {
+ next_timer = ml->next_timer;
+
+ if (next_timer == NULL)
+ timeout = -1;
+ else {
+ now = time_now();
+ if (MRP_UNLIKELY(next_timer->expire <= now))
+ timeout = 0;
+ else
+ timeout = usecs_to_msecs(next_timer->expire - now);
+ }
+ }
+
+ ext_timeout = prepare_subloops(ml);
+
+ if (ext_timeout != -1 && timeout != -1)
+ ml->poll_timeout = MRP_MIN(timeout, ext_timeout);
+ else if (ext_timeout == -1)
+ ml->poll_timeout = timeout;
+ else
+ ml->poll_timeout = ext_timeout;
+
+ if (ml->nevent < ml->niowatch) {
+ ml->nevent = ml->niowatch;
+ ml->events = mrp_realloc(ml->events, ml->nevent * sizeof(*ml->events));
+
+ MRP_ASSERT(ml->events != NULL, "can't allocate epoll event buffer");
+ }
+
+ mrp_debug("mainloop %p prepared: %d I/O watches, timeout %d", ml,
+ ml->niowatch, ml->poll_timeout);
+
+ return TRUE;
+}
+
+
+static size_t poll_events(void *id, mrp_mainloop_t *ml, void **bufp)
+{
+ void *buf;
+ int n;
+
+ if (MRP_UNLIKELY(id != ml->iow)) {
+ mrp_log_error("superloop polling with invalid I/O watch (%p != %p)",
+ id, ml->iow);
+ *bufp = NULL;
+ return 0;
+ }
+
+ buf = mrp_allocz(ml->nevent * sizeof(ml->events[0]));
+
+ if (buf != NULL) {
+ n = epoll_wait(ml->epollfd, buf, ml->nevent, 0);
+
+ if (n < 0)
+ n = 0;
+ }
+ else
+ n = 0;
+
+ *bufp = buf;
+ return n * sizeof(ml->events[0]);
+}
+
+
+int mrp_mainloop_poll(mrp_mainloop_t *ml, int may_block)
+{
+ int n, timeout;
+
+ timeout = may_block && mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+
+ if (ml->nevent > 0) {
+ if (ml->super_ops == NULL || ml->super_ops->poll_io == NULL) {
+ mrp_debug("polling %d descriptors with timeout %d",
+ ml->nevent, timeout);
+
+ n = epoll_wait(ml->epollfd, ml->events, ml->nevent, timeout);
+
+ if (n < 0 && errno == EINTR)
+ n = 0;
+ }
+ else {
+ mrp_superloop_ops_t *super_ops = ml->super_ops;
+ void *super_data = ml->super_data;
+ void *id = ml->iow;
+ void *buf = ml->events;
+ size_t size = ml->nevent * sizeof(ml->events[0]);
+
+ size = super_ops->poll_io(super_data, id, buf, size);
+ n = size / sizeof(ml->events[0]);
+
+ MRP_ASSERT(n * sizeof(ml->events[0]) == size,
+ "superloop passed us a partial epoll_event");
+ }
+
+ mrp_debug("mainloop %p has %d/%d I/O events waiting", ml, n,
+ ml->nevent);
+
+ ml->poll_result = n;
+ }
+ else {
+ /*
+ * Notes: Practically we should never branch here because
+ * we always have at least ml->sigfd registered for epoll.
+ */
+ if (timeout > 0)
+ usleep(timeout * USECS_PER_MSEC);
+
+ ml->poll_result = 0;
+ }
+
+ return TRUE;
+}
+
+
+static int poll_subloop(mrp_subloop_t *sl)
+{
+ struct epoll_event *e;
+ struct pollfd *pfd;
+ int fd, idx, n, i;
+
+ if (sl->poll) {
+ n = epoll_wait(sl->epollfd, sl->events, sl->nevent, 0);
+
+ if (n < 0 && errno == EINTR)
+ n = 0;
+
+ for (i = 0, e = sl->events; i < n; i++, e++) {
+ fd = e->data.fd;
+ idx = ((int)(ptrdiff_t)fdtbl_lookup(sl->fdtbl, fd)) - 1;
+
+ if (0 <= idx && idx < sl->npollfd) {
+ pfd = sl->pollfds + idx;
+ pfd->revents = e->events;
+ }
+ }
+
+ mrp_debug("subloop %p has %d fds ready", sl, sl->npollfd);
+
+ return n;
+ }
+ else {
+ mrp_debug("subloop %p has poll flag off", sl);
+
+ return 0;
+ }
+}
+
+
+static void dispatch_wakeup(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_wakeup_t *w;
+ mrp_wakeup_event_t event;
+ uint64_t now;
+
+ if (ml->poll_timeout == 0) {
+ mrp_debug("skipping wakeup callbacks (poll timeout was 0)");
+ return;
+ }
+
+ if (ml->poll_result == 0) {
+ mrp_debug("woken up by timeout");
+ event = MRP_WAKEUP_EVENT_TIMER;
+ }
+ else {
+ mrp_debug("woken up by I/O (or signal)");
+ event = MRP_WAKEUP_EVENT_IO;
+ }
+
+ now = time_now();
+
+ mrp_list_foreach(&ml->wakeups, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+
+ if (!(w->events & event))
+ continue;
+
+ if (!is_deleted(w)) {
+ mrp_debug("dispatching wakeup cb %p", w);
+ wakeup_cb(w, event, now);
+ }
+ else
+ mrp_debug("skipping deleted wakeup cb %p", w);
+
+ if (ml->quit)
+ break;
+ }
+}
+
+
+static void dispatch_deferred(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_deferred_t *d;
+
+ mrp_list_foreach(&ml->deferred, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+
+ if (!is_deleted(d) && !d->inactive) {
+ mrp_debug("dispatching active deferred cb %p", d);
+ d->cb(d, d->user_data);
+ }
+ else
+ mrp_debug("skipping %s deferred cb %p",
+ is_deleted(d) ? "deleted" : "inactive", d);
+
+ if (!is_deleted(d) && d->inactive)
+ disable_deferred(d);
+
+ if (ml->quit)
+ break;
+ }
+}
+
+
+static void dispatch_timers(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t;
+ uint64_t now;
+
+ now = time_now();
+
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (!is_deleted(t)) {
+ if (t->expire <= now) {
+ mrp_debug("dispatching expired timer %p", t);
+
+ t->cb(t, t->user_data);
+
+ if (!is_deleted(t))
+ rearm_timer(t);
+ }
+ else
+ break;
+ }
+ else
+ mrp_debug("skipping deleted timer %p", t);
+
+ if (ml->quit)
+ break;
+ }
+}
+
+
+static void dispatch_subloops(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_subloop_t *sl;
+
+ mrp_list_foreach(&ml->subloops, p, n) {
+ sl = mrp_list_entry(p, typeof(*sl), hook);
+
+ if (!is_deleted(sl)) {
+ poll_subloop(sl);
+
+ if (sl->cb->check(sl->user_data, sl->pollfds,
+ sl->npollfd)) {
+ mrp_debug("dispatching subloop %p", sl);
+ sl->cb->dispatch(sl->user_data);
+ }
+ else
+ mrp_debug("skipping subloop %p, check said no", sl);
+ }
+ }
+}
+
+
+static void dispatch_slaves(mrp_io_watch_t *w, struct epoll_event *e)
+{
+ mrp_io_watch_t *s;
+ mrp_list_hook_t *p, *n;
+ mrp_io_event_t events;
+
+ events = e->events & ~(MRP_IO_EVENT_INOUT & w->events);
+
+ mrp_list_foreach(&w->slave, p, n) {
+ if (events == MRP_IO_EVENT_NONE)
+ break;
+
+ s = mrp_list_entry(p, typeof(*s), slave);
+
+ if (!is_deleted(s)) {
+ mrp_debug("dispatching slave I/O watch %p (fd %d)", s, s->fd);
+ s->cb(s, s->fd, events, s->user_data);
+ }
+ else
+ mrp_debug("skipping slave I/O watch %p (fd %d)", s, s->fd);
+
+ events &= ~(MRP_IO_EVENT_INOUT & s->events);
+ }
+}
+
+
+static void dispatch_poll_events(mrp_mainloop_t *ml)
+{
+ struct epoll_event *e;
+ mrp_io_watch_t *w, *tblw;
+ int i, fd;
+
+ for (i = 0, e = ml->events; i < ml->poll_result; i++, e++) {
+ fd = e->data.fd;
+ w = fdtbl_lookup(ml->fdtbl, fd);
+
+ if (w == NULL) {
+ mrp_debug("ignoring event for deleted fd %d", fd);
+ continue;
+ }
+
+ if (!is_deleted(w)) {
+ mrp_debug("dispatching I/O watch %p (fd %d)", w, fd);
+ w->cb(w, w->fd, e->events, w->user_data);
+ }
+ else
+ mrp_debug("skipping deleted I/O watch %p (fd %d)", w, fd);
+
+ if (!mrp_list_empty(&w->slave))
+ dispatch_slaves(w, e);
+
+ if (e->events & EPOLLRDHUP) {
+ tblw = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (tblw == w) {
+ mrp_debug("forcibly stop polling fd %d for watch %p", w->fd, w);
+ epoll_del(w);
+ }
+ else if (tblw != NULL)
+ mrp_debug("don't stop polling reused fd %d of watch %p",
+ w->fd, w);
+ }
+ else {
+ if ((e->events & EPOLLHUP) && !is_deleted(w)) {
+ /*
+ * Notes:
+ *
+ * If the user does not react to EPOLLHUPs delivered
+ * we stop monitoring the fd to avoid sitting in an
+ * infinite busy loop just delivering more EPOLLHUP
+ * notifications...
+ */
+
+ if (w->wrhup++ > 5) {
+ tblw = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (tblw == w) {
+ mrp_debug("forcibly stop polling fd %d for watch %p",
+ w->fd, w);
+ epoll_del(w);
+ }
+ else if (tblw != NULL)
+ mrp_debug("don't stop polling reused fd %d of watch %p",
+ w->fd, w);
+ }
+ }
+ }
+
+ if (ml->quit)
+ break;
+ }
+
+ if (ml->quit)
+ return;
+
+ dispatch_subloops(ml);
+
+ mrp_debug("done dispatching poll events");
+}
+
+
+int mrp_mainloop_dispatch(mrp_mainloop_t *ml)
+{
+ dispatch_wakeup(ml);
+
+ if (ml->quit)
+ goto quit;
+
+ dispatch_deferred(ml);
+
+ if (ml->quit)
+ goto quit;
+
+ dispatch_timers(ml);
+
+ if (ml->quit)
+ goto quit;
+
+ dispatch_poll_events(ml);
+
+ quit:
+ purge_deleted(ml);
+
+ return !ml->quit;
+}
+
+
+int mrp_mainloop_iterate(mrp_mainloop_t *ml)
+{
+ return
+ mrp_mainloop_prepare(ml) &&
+ mrp_mainloop_poll(ml, TRUE) &&
+ mrp_mainloop_dispatch(ml) &&
+ !ml->quit;
+}
+
+
+int mrp_mainloop_run(mrp_mainloop_t *ml)
+{
+ while (mrp_mainloop_iterate(ml))
+ ;
+
+ return ml->exit_code;
+}
+
+
+void mrp_mainloop_quit(mrp_mainloop_t *ml, int exit_code)
+{
+ ml->exit_code = exit_code;
+ ml->quit = TRUE;
+}
+
+
+/*
+ * debugging routines
+ */
+
+
+static void dump_pollfds(const char *prefix, struct pollfd *fds, int nfd)
+{
+ char *t;
+ int i;
+
+ printf("%s (%d): ", prefix, nfd);
+ for (i = 0, t = ""; i < nfd; i++, t = ", ")
+ printf("%s%d/0x%x", t, fds[i].fd, fds[i].events);
+ printf("\n");
+}
+
+
+/*
+ * event bus and events
+ */
+
+static inline void *ref_event_data(void *data, int format)
+{
+ switch (format & MRP_EVENT_FORMAT_MASK) {
+ case MRP_EVENT_FORMAT_JSON:
+ return mrp_json_ref((mrp_json_t *)data);
+ case MRP_EVENT_FORMAT_MSG:
+ return mrp_msg_ref((mrp_msg_t *)data);
+ default:
+ return data;
+ }
+}
+
+
+static inline void unref_event_data(void *data, int format)
+{
+ switch (format & MRP_EVENT_FORMAT_MASK) {
+ case MRP_EVENT_FORMAT_JSON:
+ mrp_json_unref((mrp_json_t *)data);
+ break;
+ case MRP_EVENT_FORMAT_MSG:
+ mrp_msg_unref((mrp_msg_t *)data);
+ break;
+ default:
+ break;
+ }
+}
+
+
+mrp_event_bus_t *mrp_event_bus_get(mrp_mainloop_t *ml, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_event_bus_t *bus;
+
+ if (name == NULL || !strcmp(name, MRP_GLOBAL_BUS_NAME))
+ return MRP_GLOBAL_BUS;
+
+ mrp_list_foreach(&ml->busses, p, n) {
+ bus = mrp_list_entry(p, typeof(*bus), hook);
+
+ if (!strcmp(bus->name, name))
+ return bus;
+ }
+
+ bus = mrp_allocz(sizeof(*bus));
+
+ if (bus == NULL)
+ return NULL;
+
+ bus->name = mrp_strdup(name);
+
+ if (bus->name == NULL) {
+ mrp_free(bus);
+ return NULL;
+ }
+
+ mrp_list_init(&bus->hook);
+ mrp_list_init(&bus->watches);
+ bus->ml = ml;
+
+ mrp_list_append(&ml->busses, &bus->hook);
+
+ return bus;
+}
+
+
+uint32_t mrp_event_id(const char *name)
+{
+ mrp_event_def_t *e;
+ int i;
+
+ if (events != NULL)
+ for (i = 0, e = events; i < nevent; i++, e++)
+ if (!strcmp(e->name, name))
+ return e->id;
+
+ if (!mrp_reallocz(events, nevent, nevent + 1))
+ return 0;
+
+ e = events + nevent;
+
+ e->id = nevent;
+ e->name = mrp_strdup(name);
+
+ if (e->name == NULL) {
+ mrp_reallocz(events, nevent + 1, nevent);
+ return 0;
+ }
+
+ nevent++;
+
+ return e->id;
+}
+
+
+const char *mrp_event_name(uint32_t id)
+{
+ if ((int)id < nevent)
+ return events[id].name;
+ else
+ return MRP_EVENT_UNKNOWN_NAME;
+}
+
+
+char *mrp_event_dump_mask(mrp_event_mask_t *mask, char *buf, size_t size)
+{
+ char *p, *t;
+ int l, n, id;
+
+ p = buf;
+ l = (int)size;
+ t = "";
+
+ MRP_MASK_FOREACH_SET(mask, id, 1) {
+ n = snprintf(p, l, "%s%s", t, mrp_event_name(id));
+ t = "|";
+
+ if (n >= l)
+ return "<insufficient mask dump buffer>";
+
+ p += n;
+ l -= n;
+ }
+
+ return buf;
+}
+
+
+mrp_event_watch_t *mrp_event_add_watch(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_watch_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *watches = bus ? &bus->watches : &ewatches;
+ mrp_event_watch_t *w;
+
+ w = mrp_allocz(sizeof(*w));
+
+ if (w == NULL)
+ return NULL;
+
+ mrp_list_init(&w->hook);
+ mrp_mask_init(&w->mask);
+ w->bus = bus;
+ w->cb = cb;
+ w->user_data = user_data;
+
+ if (!mrp_mask_set(&w->mask, id)) {
+ mrp_free(w);
+ return NULL;
+ }
+
+ mrp_list_append(watches, &w->hook);
+
+ mrp_debug("added event watch %p for event %d (%s) on bus %s", w, id,
+ mrp_event_name(id), bus ? bus->name : MRP_GLOBAL_BUS_NAME);
+
+ return w;
+}
+
+
+mrp_event_watch_t *mrp_event_add_watch_mask(mrp_event_bus_t *bus,
+ mrp_event_mask_t *mask,
+ mrp_event_watch_cb_t cb,
+ void *user_data)
+{
+ mrp_list_hook_t *watches = bus ? &bus->watches : &ewatches;
+ mrp_event_watch_t *w;
+ char events[512];
+
+ w = mrp_allocz(sizeof(*w));
+
+ if (w == NULL)
+ return NULL;
+
+ mrp_list_init(&w->hook);
+ mrp_mask_init(&w->mask);
+ w->bus = bus;
+ w->cb = cb;
+ w->user_data = user_data;
+
+ if (!mrp_mask_copy(&w->mask, mask)) {
+ mrp_free(w);
+ return NULL;
+ }
+
+ mrp_list_append(watches, &w->hook);
+
+ mrp_debug("added event watch %p for events <%s> on bus %s", w,
+ mrp_event_dump_mask(&w->mask, events, sizeof(events)),
+ bus ? bus->name : MRP_GLOBAL_BUS_NAME);
+
+ return w;
+}
+
+
+void mrp_event_del_watch(mrp_event_watch_t *w)
+{
+ if (w == NULL)
+ return;
+
+ if (w->bus != NULL && w->bus->busy) {
+ w->dead = TRUE;
+ w->bus->dead++;
+ return;
+ }
+
+ mrp_list_delete(&w->hook);
+ mrp_mask_reset(&w->mask);
+ mrp_free(w);
+}
+
+
+void bus_purge_dead(mrp_event_bus_t *bus)
+{
+ mrp_event_watch_t *w;
+ mrp_list_hook_t *p, *n;
+
+ if (!bus->dead)
+ return;
+
+ mrp_list_foreach(&bus->watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+
+ if (!w->dead)
+ continue;
+
+ mrp_list_delete(&w->hook);
+ mrp_mask_reset(&w->mask);
+ mrp_free(w);
+ }
+
+ bus->dead = 0;
+}
+
+
+static int queue_event(mrp_event_bus_t *bus, uint32_t id, void *data,
+ mrp_event_flag_t flags)
+{
+ pending_event_t *e;
+
+ e = mrp_allocz(sizeof(*e));
+
+ if (e == NULL)
+ return -1;
+
+ mrp_list_init(&e->hook);
+ e->bus = bus;
+ e->id = id;
+ e->format = flags & MRP_EVENT_FORMAT_MASK;
+ e->data = ref_event_data(data, e->format);
+ mrp_list_append(&bus->ml->eventq, &e->hook);
+
+ mrp_enable_deferred(bus->ml->eventd);
+
+ return 0;
+}
+
+
+static int emit_event(mrp_event_bus_t *bus, uint32_t id, void *data,
+ mrp_event_flag_t flags)
+{
+ mrp_list_hook_t *watches;
+ mrp_event_watch_t *w;
+ mrp_list_hook_t *p, *n;
+
+ if (bus)
+ watches = &bus->watches;
+ else {
+ if (!(flags & MRP_EVENT_SYNCHRONOUS)) {
+ errno = EINVAL;
+ return -1;
+ }
+ watches = &ewatches;
+ }
+
+ if (bus)
+ bus->busy++;
+
+ mrp_debug("emitting event 0x%x (%s) on bus <%s>", id, mrp_event_name(id),
+ bus ? bus->name : MRP_GLOBAL_BUS_NAME);
+
+ mrp_list_foreach(watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+
+ if (w->dead)
+ continue;
+
+ if (mrp_mask_test(&w->mask, id))
+ w->cb(w, id, flags & MRP_EVENT_FORMAT_MASK, data, w->user_data);
+ }
+
+ if (bus) {
+ bus->busy--;
+
+ if (!bus->busy)
+ bus_purge_dead(bus);
+ }
+
+ return 0;
+}
+
+
+static void pump_events(mrp_deferred_t *d, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_list_hook_t *p, *n;
+ pending_event_t *e;
+
+ pump:
+ mrp_list_foreach(&ml->eventq, p, n) {
+ e = mrp_list_entry(p, typeof(*e), hook);
+
+ emit_event(e->bus, e->id, e->data, e->format);
+
+ mrp_list_delete(&e->hook);
+ unref_event_data(e->data, e->format);
+
+ mrp_free(e);
+ }
+
+ if (!mrp_list_empty(&ml->eventq))
+ goto pump;
+
+ mrp_disable_deferred(d);
+}
+
+
+int mrp_emit_event(mrp_event_bus_t *bus, uint32_t id, mrp_event_flag_t flags,
+ void *data)
+{
+ int status;
+
+ if (flags & MRP_EVENT_SYNCHRONOUS) {
+ ref_event_data(data, flags);
+ status = emit_event(bus, id, data, flags);
+ unref_event_data(data, flags);
+
+ return status;
+ }
+ else {
+ if (bus != NULL)
+ return queue_event(bus, id, data, flags);
+
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+}
+
+
+int _mrp_event_emit_msg(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_flag_t flags, ...)
+{
+ mrp_msg_t *msg;
+ uint16_t tag;
+ va_list ap;
+ int status;
+
+ va_start(ap, flags);
+ tag = va_arg(ap, unsigned int);
+ msg = tag ? mrp_msg_createv(tag, ap) : NULL;
+ va_end(ap);
+
+ flags &= ~MRP_EVENT_FORMAT_MASK;
+ status = mrp_emit_event(bus, id, flags | MRP_EVENT_FORMAT_MSG, msg);
+ mrp_msg_unref(msg);
+
+ return status;
+}
+
+
+MRP_INIT static void init_events(void)
+{
+ MRP_ASSERT(mrp_event_id(MRP_EVENT_UNKNOWN_NAME) == MRP_EVENT_UNKNOWN,
+ "reserved id 0x%x for builtin event <%s> already taken",
+ MRP_EVENT_UNKNOWN, MRP_EVENT_UNKNOWN_NAME);
+}
diff --git a/src/common/mainloop.h b/src/common/mainloop.h
new file mode 100644
index 0000000..4e7eee2
--- /dev/null
+++ b/src/common/mainloop.h
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MAINLOOP_H__
+#define __MURPHY_MAINLOOP_H__
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_mainloop_s mrp_mainloop_t;
+
+/*
+ * I/O watches
+ */
+
+/** I/O events */
+typedef enum {
+ MRP_IO_EVENT_NONE = 0x0,
+ MRP_IO_EVENT_IN = EPOLLIN,
+ MRP_IO_EVENT_PRI = EPOLLPRI,
+ MRP_IO_EVENT_OUT = EPOLLOUT,
+ MRP_IO_EVENT_RDHUP = EPOLLRDHUP,
+ MRP_IO_EVENT_WRHUP = EPOLLHUP,
+ MRP_IO_EVENT_HUP = EPOLLRDHUP|EPOLLHUP,
+ MRP_IO_EVENT_ERR = EPOLLERR,
+ MRP_IO_EVENT_INOUT = EPOLLIN|EPOLLOUT,
+ MRP_IO_EVENT_ALL = EPOLLIN|EPOLLPRI|EPOLLOUT|EPOLLRDHUP|EPOLLERR,
+ /* event trigger modes */
+ MRP_IO_TRIGGER_LEVEL = 0x1U << 25,
+ MRP_IO_TRIGGER_EDGE = EPOLLET,
+ MRP_IO_TRIGGER_MASK = MRP_IO_TRIGGER_LEVEL|MRP_IO_TRIGGER_EDGE
+} mrp_io_event_t;
+
+typedef struct mrp_io_watch_s mrp_io_watch_t;
+
+/** I/O watch notification callback type. */
+typedef void (*mrp_io_watch_cb_t)(mrp_io_watch_t *w, int fd,
+ mrp_io_event_t events, void *user_data);
+/** Register a new file descriptor to watch. */
+mrp_io_watch_t *mrp_add_io_watch(mrp_mainloop_t *ml, int fd,
+ mrp_io_event_t events,
+ mrp_io_watch_cb_t cb, void *user_data);
+/** Unregister an I/O watch. */
+void mrp_del_io_watch(mrp_io_watch_t *watch);
+
+/** Get the mainloop of an I/O watch. */
+mrp_mainloop_t *mrp_get_io_watch_mainloop(mrp_io_watch_t *watch);
+
+/** Set the default event trigger mode for the given mainloop. */
+int mrp_set_io_event_mode(mrp_mainloop_t *ml, mrp_io_event_t mode);
+
+/** Get the default event trigger mode of the given mainloop. */
+mrp_io_event_t mrp_get_io_event_mode(mrp_mainloop_t *ml);
+
+/*
+ * timers
+ */
+
+typedef struct mrp_timer_s mrp_timer_t;
+
+/** Timer notification callback type. */
+typedef void (*mrp_timer_cb_t)(mrp_timer_t *t, void *user_data);
+
+/** Add a new timer. */
+mrp_timer_t *mrp_add_timer(mrp_mainloop_t *ml, unsigned int msecs,
+ mrp_timer_cb_t cb, void *user_data);
+/** Modify the timeout or rearm the given timer. */
+#define MRP_TIMER_RESTART (unsigned int)-1
+void mrp_mod_timer(mrp_timer_t *t, unsigned int msecs);
+
+/** Delete a timer. */
+void mrp_del_timer(mrp_timer_t *t);
+
+/** Get the mainloop of a timer. */
+mrp_mainloop_t *mrp_get_timer_mainloop(mrp_timer_t *t);
+
+
+/*
+ * deferred callbacks
+ */
+
+typedef struct mrp_deferred_s mrp_deferred_t;
+
+/** Deferred callback notification callback type. */
+typedef void (*mrp_deferred_cb_t)(mrp_deferred_t *d, void *user_data);
+
+/** Add a deferred callback. */
+mrp_deferred_t *mrp_add_deferred(mrp_mainloop_t *ml, mrp_deferred_cb_t cb,
+ void *user_data);
+/** Remove a deferred callback. */
+void mrp_del_deferred(mrp_deferred_t *d);
+
+/** Disable a deferred callback. */
+void mrp_disable_deferred(mrp_deferred_t *d);
+
+/** Enable a deferred callback. */
+void mrp_enable_deferred(mrp_deferred_t *d);
+
+/** Get the mainloop of a deferred callback. */
+mrp_mainloop_t *mrp_get_deferred_mainloop(mrp_deferred_t *d);
+
+
+/*
+ * signals
+ */
+
+typedef struct mrp_sighandler_s mrp_sighandler_t;
+
+/* Signal handler callback type. */
+typedef void (*mrp_sighandler_cb_t)(mrp_sighandler_t *h, int signum,
+ void *user_data);
+/** Register a signal handler. */
+mrp_sighandler_t *mrp_add_sighandler(mrp_mainloop_t *ml, int signum,
+ mrp_sighandler_cb_t cb, void *user_data);
+/** Unregister a signal handler. */
+void mrp_del_sighandler(mrp_sighandler_t *h);
+
+/** Get the mainloop of a signal handler. */
+mrp_mainloop_t *mrp_get_sighandler_mainloop(mrp_sighandler_t *h);
+
+
+/*
+ * wakeup callbacks
+ */
+
+/** I/O events */
+typedef enum {
+ MRP_WAKEUP_EVENT_NONE = 0x0, /* no event */
+ MRP_WAKEUP_EVENT_TIMER = 0x1, /* woken up by timeout */
+ MRP_WAKEUP_EVENT_IO = 0x2, /* woken up by I/O (or signal) */
+ MRP_WAKEUP_EVENT_ANY = 0x3, /* mask of all selectable events */
+ MRP_WAKEUP_EVENT_LIMIT = 0x4, /* woken up by forced trigger */
+} mrp_wakeup_event_t;
+
+typedef struct mrp_wakeup_s mrp_wakeup_t;
+
+/** Wakeup callback notification type. */
+typedef void (*mrp_wakeup_cb_t)(mrp_wakeup_t *w, mrp_wakeup_event_t event,
+ void *user_data);
+
+/** Add a wakeup callback for the specified events. lpf_msecs and
+ * force_msecs specifiy two limiting intervals in milliseconds.
+ * lpf_msecs is a low-pass filtering interval. It is guaranteed that
+ * the wakeup callback will not be invoked more ofter than this.
+ * force_msecs is a forced trigger interval. It is guaranteed that
+ * the wakeup callback will be triggered at least this often. You can
+ * MRP_WAKEUP_NOLIMIT to omit either or both limiting intervals. */
+#define MRP_WAKEUP_NOLIMIT ((unsigned int)0)
+mrp_wakeup_t *mrp_add_wakeup(mrp_mainloop_t *ml, mrp_wakeup_event_t events,
+ unsigned int lpf_msecs, unsigned int force_msecs,
+ mrp_wakeup_cb_t cb, void *user_data);
+
+/** Remove a wakeup callback. */
+void mrp_del_wakeup(mrp_wakeup_t *w);
+
+/** Get the mainloop of a wakeup callback. */
+mrp_mainloop_t *mrp_get_wakeup_mainloop(mrp_wakeup_t *w);
+
+
+/*
+ * subloops - external mainloops pumped by this mainloop
+ */
+
+typedef struct mrp_subloop_s mrp_subloop_t;
+
+typedef struct {
+ int (*prepare)(void *user_data);
+ int (*query)(void *user_data, struct pollfd *fds, int nfd, int *timeout);
+ int (*check)(void *user_data, struct pollfd *fds, int nfd);
+ void (*dispatch)(void *user_data);
+} mrp_subloop_ops_t;
+
+
+/** Register an external mainloop to be pumped by the given mainloop. */
+mrp_subloop_t *mrp_add_subloop(mrp_mainloop_t *ml, mrp_subloop_ops_t *ops,
+ void *user_data);
+
+/** Stop pumping a registered external mainloop. */
+void mrp_del_subloop(mrp_subloop_t *sl);
+
+
+/*
+ * superloops - external mainloop to pump murphy mainloops
+ */
+
+typedef struct {
+ void *(*add_io)(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+ void (*del_io)(void *glue_data, void *id);
+
+ void *(*add_timer)(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+ void (*del_timer)(void *glue_data, void *id);
+ void (*mod_timer)(void *glue_data, void *id, unsigned int msecs);
+
+ void *(*add_defer)(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+ void (*del_defer)(void *glue_data, void *id);
+ void (*mod_defer)(void *glue_data, void *id, int enabled);
+ void (*unregister)(void *glue_data);
+
+ /*
+ * Notes:
+ *
+ * This is a band-aid attempt to get our mainloop run under the
+ * threaded event loop of xwalk which has strict limitations about
+ * what (type of) thread can access which functionality of the
+ * event loop. In particular, the I/O watch equivalent is limited
+ * for the I/O thread. In the current media element resource infra
+ * integration code of xwalk, the related code is run in the UI
+ * thread. This makes interacting from there with the daemon using
+ * the stock resource libraries (in particular, pumping the mainloop)
+ * non-straightforward.
+ *
+ * The superloop glue code is supposed to use poll_events to trigger
+ * a nonblocking epoll_wait on our epoll fd to retrieve (and cache)
+ * pending epoll events from the kernel. poll_io is then used by us
+ * to retrieve pending epoll events from the glue code. The glue code
+ * needs to take care of any necessary locking to protect itself/us
+ * from potentially concurrent invocations of poll_io and poll_events
+ * from different threads.
+ *
+ * The superloop abstraction now became really really ugly. poll_io
+ * and poll_events implicitly assume/know that add_io/del_io is only
+ * used for adding a single superloop I/O watch for our epoll fd.
+ * The I/O watch id in the functions below is passed around only for
+ * doublechecing this. Probably we should change the I/O watch part
+ * of the superloop API to be actually less generic and only usable
+ * for the epoll fd to avoid further confusion.
+ */
+ size_t (*poll_events)(void *id, mrp_mainloop_t *ml, void **events);
+ size_t (*poll_io)(void *glue_data, void *id, void *buf, size_t size);
+} mrp_superloop_ops_t;
+
+
+/** Set a superloop to pump the given mainloop. */
+int mrp_set_superloop(mrp_mainloop_t *ml, mrp_superloop_ops_t *ops,
+ void *loop_data);
+
+/** Clear the superloop that pumps the given mainloop. */
+int mrp_clear_superloop(mrp_mainloop_t *ml);
+
+/** Unregister a mainloop from its superloop if it has one. */
+int mrp_mainloop_unregister(mrp_mainloop_t *ml);
+
+/*
+ * mainloop
+ */
+
+/** Create a new mainloop. */
+mrp_mainloop_t *mrp_mainloop_create(void);
+
+/** Destroy an existing mainloop, free all I/O watches, timers, etc. */
+void mrp_mainloop_destroy(mrp_mainloop_t *ml);
+
+/** Keep iterating the mainloop until mrp_mainloop_quit is called. */
+int mrp_mainloop_run(mrp_mainloop_t *ml);
+
+/** Prepare the mainloop for polling. */
+int mrp_mainloop_prepare(mrp_mainloop_t *ml);
+
+/** Poll the mainloop. */
+int mrp_mainloop_poll(mrp_mainloop_t *ml, int may_block);
+
+/** Dispatch pending events of the mainloop. */
+int mrp_mainloop_dispatch(mrp_mainloop_t *ml);
+
+/** Run a single prepare-poll-dispatch cycle of the mainloop. */
+int mrp_mainloop_iterate(mrp_mainloop_t *ml);
+
+/** Quit the mainloop. */
+void mrp_mainloop_quit(mrp_mainloop_t *ml, int exit_code);
+
+/*
+ * event bus and and events
+ */
+
+#include <murphy/common/mask.h>
+#include <murphy/common/msg.h>
+
+#define MRP_GLOBAL_BUS NULL /* global synchronous bus */
+#define MRP_GLOBAL_BUS_NAME "global" /* global synchronous bus name */
+
+/** event flags */
+typedef enum {
+ MRP_EVENT_ASYNCHRONOUS = 0x00, /* deliver asynchronously */
+ MRP_EVENT_SYNCHRONOUS = 0x01, /* deliver synchronously */
+ MRP_EVENT_FORMAT_JSON = 0x01 << 1, /* attached data is JSON */
+ MRP_EVENT_FORMAT_MSG = 0x02 << 1, /* attached data is mrp_msg_t */
+ MRP_EVENT_FORMAT_CUSTOM = 0x03 << 1, /* attached data is of custom format */
+ MRP_EVENT_FORMAT_MASK = 0x03 << 1,
+} mrp_event_flag_t;
+
+/** Reserved event id for unknown events. */
+#define MRP_EVENT_UNKNOWN 0
+
+/** Reserved name for unknown events. */
+#define MRP_EVENT_UNKNOWN_NAME "unknown"
+
+/**
+ * Event definition for registering event(name)s.
+ */
+typedef struct {
+ char *name; /* event name */
+ uint32_t id; /* event id assigned by murphy */
+} mrp_event_def_t;
+
+/** Opaque event watch type. */
+typedef struct mrp_event_watch_s mrp_event_watch_t;
+
+/** Event watch notification callback type. */
+typedef void (*mrp_event_watch_cb_t)(mrp_event_watch_t *w, uint32_t id,
+ int format, void *data, void *user_data);
+
+/** Opaque event bus type. */
+typedef struct mrp_event_bus_s mrp_event_bus_t;
+
+/** Event mask type (a bitmask of event ids). */
+typedef mrp_mask_t mrp_event_mask_t;
+
+/** Look up (or create) the event bus with the given name. */
+mrp_event_bus_t *mrp_event_bus_get(mrp_mainloop_t *ml, const char *name);
+#define mrp_event_bus_create mrp_event_bus_get
+
+/** Look up (or register) the id of the given event. */
+uint32_t mrp_event_id(const char *name);
+#define mrp_event_register mrp_event_id
+
+/** Look up the name of the given event. */
+const char *mrp_event_name(uint32_t id);
+
+/** Dump the names of the events set in mask. */
+char *mrp_event_dump_mask(mrp_event_mask_t *mask, char *buf, size_t size);
+
+/** Register an event watch for a single event on the given bus. */
+mrp_event_watch_t *mrp_event_add_watch(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_watch_cb_t cb, void *user_data);
+
+/** Register an event watch for a number of events on the given bus. */
+mrp_event_watch_t *mrp_event_add_watch_mask(mrp_event_bus_t *bus,
+ mrp_event_mask_t *mask,
+ mrp_event_watch_cb_t cb,
+ void *user_data);
+
+/** Unregister the given event watch. */
+void mrp_event_del_watch(mrp_event_watch_t *w);
+
+/** Emit the given event with the data attached on the given bus. */
+int mrp_event_emit(mrp_event_bus_t *bus, uint32_t id, mrp_event_flag_t flags,
+ void *data);
+
+/** Convenience macro to emit an event with JSON data attached. */
+#define mrp_event_emit_json(bus, id, flags, data) \
+ mrp_event_emit((bus), (id), (flags) | MRP_EVENT_FORMAT_JSON, (data))
+
+/** Convenience macro to emit an event with mrp_msg_t data attached. */
+#define mrp_event_emit_msg(bus, id, flags, ...) \
+ _mrp_event_emit_msg((bus), (id), (flags), __VA_ARGS__, MRP_MSG_END)
+
+/** Emit an event with mrp_msg_t data attached. */
+int _mrp_event_emit_msg(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_flag_t flags, ...);
+
+/** Convenience macro to emit an event with custom data attached. */
+#define mrp_event_emit_custom(bus, id, data, flags) \
+ mrp_event_emit((bus), (id), (data), (flags) | MRP_EVENT_FORMAT_CUSTOM)
+
+/** Convenience macros for autoregistering a table of events on startup. */
+#define MRP_EVENT(_name, _id) [_id] = { .name = _name, .id = 0 }
+#define MRP_REGISTER_EVENTS(_event_table, ...) \
+ static mrp_event_def_t _event_table[] = { \
+ __VA_ARGS__, \
+ { .name = NULL } \
+ }; \
+ \
+ MRP_INIT static void register_##_event_table(void) \
+ { \
+ mrp_event_def_t *e; \
+ \
+ for (e = _event_table; e->name != NULL; e++) { \
+ e->id = mrp_event_register(e->name); \
+ \
+ if (e->id != MRP_EVENT_UNKNOWN) \
+ mrp_log_info("Event '%s' registered as 0x%x.", \
+ e->name, e->id); \
+ else \
+ mrp_log_error("Failed to register event '%s'.", \
+ e->name); \
+ } \
+ } \
+ struct __mrp_allow_trailing_semicolon
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MAINLOOP_H__ */
diff --git a/src/common/mask.h b/src/common/mask.h
new file mode 100644
index 0000000..12ed336
--- /dev/null
+++ b/src/common/mask.h
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MASK_H__
+#define __MURPHY_MASK_H__
+
+#include <stdint.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+
+
+MRP_CDECL_BEGIN
+
+/** Type used to store bits in bitmasks. */
+typedef uint64_t _mask_t;
+
+
+/**
+ * trivial representation of a bitmask of arbitrary size
+ */
+
+typedef struct {
+ int nbit; /* number of bits in this mask */
+ union {
+ _mask_t bits; /* bits for nbit <= 64 */
+ _mask_t *bitp; /* bits for nbit > 64 */
+ };
+} mrp_mask_t;
+
+
+/** Macro to intialize a bitmask to empty. */
+#define MRP_MASK_EMPTY { .nbit = 64, .bits = 0 }
+#define MRP_MASK_INIT(m) do { (m)->nbit = 64; (m)->bits = 0; } while (0)
+
+/** Macro to declare a bitmask variable and initialize it. */
+#define MRP_MASK(m) mrp_mask_t m = MRP_MASK_EMPTY
+
+/* Various bit-fiddling macros. */
+#define MRP_MASK_BIT(bit) (1ULL << (bit))
+#define MRP_MASK_UPTO(bit) ((1ULL << (bit)) - 1)
+#define MRP_MASK_BELOW(bit) (MRP_MASK_UPTO(bit) >> 1)
+
+
+#define _BITS_PER_WORD ((int)(sizeof(_mask_t) * 8))
+#define _WORD(bit) ((bit) / _BITS_PER_WORD)
+#define _BIT(bit) ((bit) & (_BITS_PER_WORD - 1))
+#define _MASK(bit) (0x1ULL << (bit))
+
+
+/** Initialize the given mask. */
+static inline void mrp_mask_init(mrp_mask_t *m)
+{
+#ifndef __cplusplus
+ *m = (mrp_mask_t)MRP_MASK_EMPTY;
+#else
+ MRP_MASK_INIT(m);
+#endif
+}
+
+
+/** Reset the given mask. */
+static inline void mrp_mask_reset(mrp_mask_t *m)
+{
+ if (m->nbit > _BITS_PER_WORD)
+ mrp_free(m->bitp);
+
+ mrp_mask_init(m);
+}
+
+
+/** Ensure the given mask to accomodate the given number of bits. */
+static inline mrp_mask_t *mrp_mask_ensure(mrp_mask_t *m, int bits)
+{
+ _mask_t w;
+ int o, n;
+
+ if (bits <= m->nbit)
+ return m;
+
+ if (m->nbit == _BITS_PER_WORD) {
+ w = m->bits;
+ n = (bits + _BITS_PER_WORD - 1) / _BITS_PER_WORD;
+
+ m->bitp = (_mask_t *)mrp_allocz(n * sizeof(*m->bitp));
+
+ if (m->bitp == NULL) {
+ m->bits = w;
+
+ return NULL;
+ }
+
+ m->bitp[0] = w;
+ m->nbit = n * _BITS_PER_WORD;
+ }
+ else {
+ o = m->nbit / _BITS_PER_WORD;
+ n = (bits + _BITS_PER_WORD - 1) / _BITS_PER_WORD;
+
+ if (!mrp_reallocz(m->bitp, o, n + 1))
+ return NULL;
+
+ m->nbit = n * _BITS_PER_WORD;
+ }
+
+ return m;
+}
+
+
+/** Resize mask to accomodate the given number of bits, truncate if possible. */
+static inline mrp_mask_t *mrp_mask_trunc(mrp_mask_t *m, int bits)
+{
+ int n;
+ uint64_t *bitp;
+
+ if (m->nbit <= bits)
+ return mrp_mask_ensure(m, bits);
+
+ n = (bits + _BITS_PER_WORD - 1) / _BITS_PER_WORD;
+
+ if (n == 1) {
+ bitp = m->bitp;
+ m->bits = bitp[0];
+
+ mrp_free(bitp);
+ }
+ else
+ mrp_reallocz(m->bitp, m->nbit / _BITS_PER_WORD, n);
+
+ m->nbit = n * _BITS_PER_WORD;
+
+ return m;
+}
+
+
+/** Set the given bit in the mask. */
+static inline mrp_mask_t *mrp_mask_set(mrp_mask_t *m, int bit)
+{
+ int w, b;
+
+ if (!mrp_mask_ensure(m, bit))
+ return NULL;
+
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ m->bits |= _MASK(b);
+ else {
+ w = _WORD(bit);
+ m->bitp[w] |= _MASK(b);
+ }
+
+ return m;
+}
+
+
+/** Clear the given bit in the mask. */
+static inline mrp_mask_t *mrp_mask_clear(mrp_mask_t *m, int bit)
+{
+ int w, b;
+
+ if (bit >= m->nbit)
+ return m;
+
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ m->bits &= ~_MASK(b);
+ else {
+ w = _WORD(bit);
+ m->bitp[w] &= ~_MASK(b);
+ }
+
+ return m;
+}
+
+
+/** Test the given bit in the mask. */
+static inline int mrp_mask_test(mrp_mask_t *m, int bit)
+{
+ int w, b;
+
+ if (bit >= m->nbit)
+ return 0;
+
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ return !!(m->bits & _MASK(b));
+ else {
+ w = _WORD(bit);
+ return !!(m->bitp[w] & _MASK(b));
+ }
+}
+
+
+/** Copy the given mask, overwriting dst. */
+static inline mrp_mask_t *mrp_mask_copy(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ mrp_mask_reset(dst);
+
+ dst->nbit = src->nbit;
+
+ if (src->nbit == _BITS_PER_WORD)
+ *dst = *src;
+ else {
+ dst->bitp = (_mask_t *)mrp_alloc(dst->nbit * _BITS_PER_WORD);
+
+ if (dst->bitp == NULL)
+ return NULL;
+
+ memcpy(dst->bitp, src->bitp, dst->nbit * _BITS_PER_WORD);
+ }
+
+ return dst;
+}
+
+
+/** Set all bits in src into dst (dst |= src). */
+static inline mrp_mask_t *mrp_mask_or(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ int i, n;
+
+ if (!mrp_mask_ensure(dst, src->nbit))
+ return NULL;
+
+ if (src->nbit == _BITS_PER_WORD) {
+ if (dst->nbit == _BITS_PER_WORD)
+ dst->bits |= src->bits;
+ else
+ dst->bitp[0] |= src->bits;
+ }
+ else {
+ n = src->nbit / _BITS_PER_WORD;
+
+ for (i = 0; i < n; i++)
+ dst->bitp[i] |= src->bitp[i];
+ }
+
+ return dst;
+}
+
+
+/** Mask all bits in dst with the corresponding ones from src (dst &= src). */
+static inline mrp_mask_t *mrp_mask_and(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ int i, n;
+
+ n = MRP_MIN(dst->nbit, src->nbit);
+ mrp_mask_trunc(dst, n);
+
+ n /= _BITS_PER_WORD;
+
+ if (dst->nbit == _BITS_PER_WORD) {
+ if (src->nbit == _BITS_PER_WORD)
+ dst->bits &= src->bits;
+ else
+ dst->bits &= src->bitp[0];
+ }
+ else {
+ for (i = 0; i < n; i++)
+ dst->bitp[i] &= src->bitp[i];
+ }
+
+ return dst;
+}
+
+
+/** Set all bits in src into dst (dst ^= src). */
+static inline mrp_mask_t *mrp_mask_xor(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ int i, n;
+
+ if (!mrp_mask_ensure(dst, src->nbit))
+ return NULL;
+
+ if (src->nbit == _BITS_PER_WORD) {
+ if (dst->nbit == _BITS_PER_WORD)
+ dst->bits |= src->bits;
+ else
+ dst->bitp[0] |= src->bits;
+
+#if 0
+ /*
+ * Hmm... this would consider those bits in src which are not
+ * actually there but are in dst to be implicit 0's. However,
+ * I'm not sure if this really is a good idea... Needs a bit
+ * exposure to using this code to decide.
+ */
+
+ n = dst->nbit / _BITS_PER_WORD;
+ for (i = 1; i < n; i++)
+ dst->bitp[i] ^= 0;
+#endif
+ }
+ else {
+ n = src->nbit / _BITS_PER_WORD;
+ for (i = 0; i < n; i++)
+ dst->bitp[i] ^= src->bitp[i];
+
+#if 0
+ /*
+ * Hmm... ditto for this piece of code.
+ */
+
+ n = dst->nbit / _BITS_PER_WORD;
+ while (i < n)
+ dst->bitp[i] ^= 0;
+#endif
+ }
+
+ return dst;
+}
+
+
+/** Negate all bits in mask (~mask). */
+static inline mrp_mask_t *mrp_mask_neg(mrp_mask_t *m)
+{
+ int i, n;
+
+ if (m->nbit == _BITS_PER_WORD)
+ m->bits = ~m->bits;
+ else {
+ n = m->nbit / _BITS_PER_WORD;
+
+ for (i = 0; i < n; i++)
+ m->bitp[i] = ~m->bitp[i];
+ }
+
+ return m;
+}
+
+
+/** Find the first bit set (1-based indexing) in the given mask. */
+static inline int mrp_ffsll(_mask_t bits)
+{
+#ifdef __GNUC__
+ return __builtin_ffsll(bits);
+#else
+ _mask_t mask = 0xffffffff;
+ int w, n;
+
+ if (!bits)
+ return 0;
+
+ n = 0;
+ w = _BITS_PER_WORD / 2;
+ while (w) {
+ if (!(bits & mask)) {
+ bits >>= w;
+ mask >>= w / 2;
+ n += w;
+ w /= 2;
+ }
+ else {
+ bits &= mask;
+ mask >>= w / 2;
+ w /= 2;
+ }
+ }
+
+ return n + 1;
+#endif
+}
+
+
+/** Get the first bit set starting at the given bit. */
+static inline int mrp_mask_next_set(mrp_mask_t *m, int bit)
+{
+ _mask_t wrd, clr;
+ int w, b, n;
+
+ while (bit < m->nbit - 1) {
+ w = _WORD(bit);
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ wrd = m->bits;
+ else
+ wrd = m->bitp[w];
+
+ clr = ~(_MASK(b) - 1);
+ n = mrp_ffsll(wrd & clr);
+
+ if (n > 0)
+ return w * _BITS_PER_WORD + n - 1;
+
+ bit = (bit + _BITS_PER_WORD) & ~(_BITS_PER_WORD - 1);
+ }
+
+ return -1;
+}
+
+
+/** Get the first bit cleared starting at the given bit. */
+static inline int mrp_mask_next_clear(mrp_mask_t *m, int bit)
+{
+ _mask_t wrd, clr;
+ int w, b, n;
+
+ while (bit < m->nbit - 1) {
+ w = _WORD(bit);
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ wrd = m->bits;
+ else
+ wrd = m->bitp[w];
+
+ clr = _MASK(b) - 1;
+ n = mrp_ffsll(~(wrd | clr));
+
+ if (n > 0)
+ return w * _BITS_PER_WORD + n - 1;
+
+ bit = (bit + _BITS_PER_WORD) & ~(_BITS_PER_WORD - 1);
+ }
+
+ return -1;
+}
+
+
+/** Loop through all bits set in a mask. */
+#define MRP_MASK_FOREACH_SET(m, bit, start) \
+ for (bit = mrp_mask_next_set(m, start); \
+ bit >= 0; \
+ bit = mrp_mask_next_set(m, bit + 1))
+
+
+/** Loop through all bits cleared in a mask. */
+#define MRP_MASK_FOREACH_CLEAR(m, bit, start) \
+ for (bit = mrp_mask_next_clear(m, start); \
+ bit >= 0; \
+ bit = mrp_mask_next_clear(m, bit + 1))
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MASK_H__ */
diff --git a/src/common/mm.c b/src/common/mm.c
new file mode 100644
index 0000000..b461241
--- /dev/null
+++ b/src/common/mm.c
@@ -0,0 +1,1414 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <execinfo.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/hashtbl.h>
+
+
+#define DEFAULT_DEPTH 8 /* default backtrace depth */
+#define MAX_DEPTH 128 /* max. backtrace depth */
+
+/*
+ * memory allocator state
+ */
+
+typedef struct {
+ mrp_list_hook_t blocks; /* list of allocated blocks */
+ size_t hdrsize; /* header size */
+ int depth; /* backtrace depth */
+ uint32_t cur_blocks; /* currently allocated blocks */
+ uint32_t max_blocks; /* max allocated blocks */
+ uint64_t cur_alloc; /* currently allocated memory */
+ uint64_t max_alloc; /* max allocated memory */
+ int poison; /* poisoning pattern */
+ size_t chunk_size; /* object pool chunk size */
+ mrp_mm_type_t mode; /* passthru/debug mode */
+
+ void *(*alloc)(size_t size, const char *file, int line, const char *func);
+ void *(*realloc)(void *ptr, size_t size, const char *file,
+ int line, const char *func);
+ int (*memalign)(void **ptr, size_t align, size_t size,
+ const char *file, int line, const char *func);
+ void (*free)(void *ptr, const char *file, int line, const char *func);
+} mm_t;
+
+
+/*
+ * memory block
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* to allocated blocks */
+ mrp_list_hook_t more; /* with the same backtrace */
+ const char *file; /* file of immediate caller */
+ int line; /* line of immediate caller */
+ const char *func; /* name of immediate caller */
+ size_t size; /* requested size */
+ void *bt[]; /* for accessing backtrace */
+} memblk_t;
+
+
+
+static mm_t __mm = { /* allocator state */
+ .hdrsize = MRP_ALIGN(MRP_OFFSET(memblk_t, bt[DEFAULT_DEPTH]),
+ MRP_MM_ALIGN),
+ .depth = DEFAULT_DEPTH,
+ .poison = 0xdeadbeef,
+};
+
+
+static const char *get_config_key(const char *config, const char *key)
+{
+ const char *beg;
+ int len;
+
+ if (config != NULL) {
+ len = strlen(key);
+
+ beg = config;
+ while (beg != NULL) {
+ beg = strstr(beg, key);
+
+ if (beg != NULL) {
+ if ((beg == config || beg[-1] == ':') &&
+ (beg[len] == '=' || beg[len] == ':' || beg[len] == '\0'))
+ return (beg[len] == '=' ? beg + len + 1 : "");
+ else
+ beg++;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+static int32_t get_config_int32(const char *cfg, const char *key,
+ int32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = get_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+static uint32_t get_config_uint32(const char *cfg, const char *key,
+ uint32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = get_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+static int get_config_bool(const char *config, const char *key, int defval)
+{
+ const char *v;
+
+ v = get_config_key(config, key);
+
+ if (v != NULL) {
+ if (*v) {
+
+ if ((!strncasecmp(v, "false", 5) && (v[5] == ':' || !v[5])) ||
+ (!strncasecmp(v, "true" , 4) && (v[4] == ':' || !v[4])))
+ return (v[0] == 't' || v[0] == 'T');
+ }
+ else if (*v == '\0')
+ return TRUE;
+ }
+
+ return defval;
+}
+
+
+static int get_config_string(const char *cfg, const char *key,
+ const char *defval, char *buf, size_t size)
+{
+ const char *v;
+ char *end;
+ int len;
+
+ v = get_config_key(cfg, key);
+
+ if (v == NULL)
+ v = defval;
+
+ end = strchr(v, ':');
+
+ if (end != NULL)
+ len = end - v;
+ else
+ len = strlen(v);
+
+ len = snprintf(buf, size, "%*.*s", len, len, v);
+
+ if (len >= (int)size - 1)
+ buf[size - 1] = '\0';
+
+ return len;
+}
+
+
+
+MRP_INIT_AT(101) static void setup(void)
+{
+ char *config = getenv(MRP_MM_CONFIG_ENVVAR);
+
+ mrp_list_init(&__mm.blocks);
+
+ __mm.depth = get_config_int32(config, "depth", DEFAULT_DEPTH);
+
+ if (__mm.depth > MAX_DEPTH)
+ __mm.depth = MAX_DEPTH;
+
+ __mm.hdrsize = MRP_ALIGN(MRP_OFFSET(memblk_t, bt[__mm.depth]),
+ MRP_MM_ALIGN);
+
+ __mm.cur_blocks = 0;
+ __mm.max_blocks = 0;
+ __mm.cur_alloc = 0;
+ __mm.max_alloc = 0;
+
+ __mm.poison = get_config_uint32(config, "poison", 0xdeadbeef);
+ __mm.chunk_size = sysconf(_SC_PAGESIZE) * 2;
+
+ if (config == NULL || !get_config_bool(config, "debug", FALSE))
+ mrp_mm_config(MRP_MM_PASSTHRU);
+ else
+ mrp_mm_config(MRP_MM_DEBUG);
+}
+
+
+static void __attribute__((destructor)) cleanup(void)
+{
+ if (__mm.mode == MRP_MM_DEBUG) {
+ mrp_mm_dump(stdout);
+ /*mrp_mm_check(stdout);*/
+ }
+}
+
+
+
+int32_t mrp_mm_config_int32(const char *key, int32_t defval)
+{
+ return get_config_int32(getenv(MRP_MM_CONFIG_ENVVAR), key, defval);
+}
+
+
+uint32_t mrp_mm_config_uint32(const char *key, uint32_t defval)
+{
+ return get_config_uint32(getenv(MRP_MM_CONFIG_ENVVAR), key, defval);
+}
+
+
+int mrp_mm_config_bool(const char *key, int defval)
+{
+ return get_config_bool(getenv(MRP_MM_CONFIG_ENVVAR), key, defval);
+}
+
+
+int mrp_mm_config_string(const char *key, const char *defval,
+ char *buf, size_t size)
+{
+ return get_config_string(getenv(MRP_MM_CONFIG_ENVVAR), key, defval,
+ buf, size);
+}
+
+
+/*
+ * memblk handling
+ */
+
+static memblk_t *memblk_alloc(size_t size, const char *file, int line,
+ const char *func, void **bt)
+{
+ memblk_t *blk;
+
+ if (MRP_UNLIKELY(size == 0))
+ blk = NULL;
+ else {
+ if ((blk = malloc(__mm.hdrsize + size)) != NULL) {
+ mrp_list_init(&blk->hook);
+ mrp_list_init(&blk->more);
+ mrp_list_append(&__mm.blocks, &blk->hook);
+
+ blk->file = file;
+ blk->line = line;
+ blk->func = func;
+ blk->size = size;
+
+ memcpy(blk->bt, bt, __mm.depth * sizeof(*bt));
+
+ __mm.cur_blocks++;
+ __mm.cur_alloc += size;
+
+ __mm.max_blocks = MRP_MAX(__mm.max_blocks, __mm.cur_blocks);
+ __mm.max_alloc = MRP_MAX(__mm.max_alloc , __mm.cur_alloc);
+ }
+ }
+
+ return blk;
+}
+
+
+static void memblk_free(memblk_t *blk, const char *file, int line,
+ const char *func, void **bt)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+ MRP_UNUSED(bt);
+
+ if (blk != NULL) {
+ mrp_list_delete(&blk->hook);
+
+ __mm.cur_blocks--;
+ __mm.cur_alloc -= blk->size;
+
+ if (__mm.poison != 0)
+ memset(&blk->bt[__mm.depth], __mm.poison, blk->size);
+
+ free(blk);
+ }
+}
+
+
+static memblk_t *memblk_resize(memblk_t *blk, size_t size, const char *file,
+ int line, const char *func, void **bt)
+{
+ memblk_t *resized;
+
+ if (blk != NULL) {
+ mrp_list_delete(&blk->hook);
+
+ if (size != 0) {
+ resized = realloc(blk, __mm.hdrsize + size);
+
+ if (resized != NULL) {
+ mrp_list_init(&resized->hook);
+
+ mrp_list_append(&__mm.blocks, &resized->hook);
+
+ __mm.cur_alloc -= resized->size;
+ __mm.cur_alloc += size;
+ __mm.max_alloc = MRP_MAX(__mm.max_alloc, __mm.cur_alloc);
+
+ resized->file = file;
+ resized->line = line;
+ resized->func = func;
+
+ memcpy(resized->bt, bt, __mm.depth * sizeof(*bt));
+
+ resized->size = size;
+ }
+ else
+ mrp_list_append(&__mm.blocks, &blk->hook);
+ }
+ else {
+ resized = NULL;
+ memblk_free(blk, file, line, func, bt);
+ }
+
+ return resized;
+ }
+ else
+ return memblk_alloc(size, file, line, func, bt);
+}
+
+
+static inline void *memblk_to_ptr(memblk_t *blk)
+{
+ if (blk != NULL)
+ return (void *)&blk->bt[__mm.depth];
+ else
+ return NULL;
+}
+
+
+static inline memblk_t *ptr_to_memblk(void *ptr)
+{
+ /*
+ * XXX Hmm... maybe we should also add pre- and post-sentinels
+ * and check them here to have minimal protection/detection of
+ * trivial buffer overflow bugs when running in debug mode.
+ */
+
+ if (ptr != NULL)
+ return ptr - MRP_OFFSET(memblk_t, bt[__mm.depth]);
+ else
+ return NULL;
+}
+
+
+/*
+ * debugging allocator
+ */
+
+static inline int __mm_backtrace(void **bt, size_t size)
+{
+ int i, n;
+
+ n = backtrace(bt, (int)size);
+ for (i = n; i < (int)size; i++)
+ bt[i] = NULL;
+
+ return n;
+}
+
+
+static void *__mm_alloc(size_t size, const char *file, int line,
+ const char *func)
+{
+ memblk_t *blk;
+ void *bt[__mm.depth + 1];
+
+ __mm_backtrace(bt, MRP_ARRAY_SIZE(bt));
+ blk = memblk_alloc(size, file, line, func, bt + 1);
+
+ return memblk_to_ptr(blk);
+}
+
+
+static void *__mm_realloc(void *ptr, size_t size, const char *file,
+ int line, const char *func)
+{
+ memblk_t *blk;
+ void *bt[__mm.depth + 1];
+
+ __mm_backtrace(bt, MRP_ARRAY_SIZE(bt));
+ blk = ptr_to_memblk(ptr);
+
+ if (blk != NULL)
+ blk = memblk_resize(blk, size, file, line, func, bt + 1);
+ else
+ blk = memblk_alloc(size, file, line, func, bt + 1);
+
+ return memblk_to_ptr(blk);
+}
+
+
+static int __mm_memalign(void **ptr, size_t align, size_t size,
+ const char *file, int line, const char *func)
+{
+ MRP_UNUSED(align);
+ MRP_UNUSED(size);
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ *ptr = NULL;
+ errno = ENOSYS;
+
+ mrp_log_error("XXX %s not implemented!!!", __FUNCTION__);
+ return -1;
+}
+
+
+static void __mm_free(void *ptr, const char *file, int line,
+ const char *func)
+{
+ memblk_t *blk;
+ void *bt[__mm.depth + 1];
+
+ if (ptr != NULL) {
+ __mm_backtrace(bt, MRP_ARRAY_SIZE(bt));
+ blk = ptr_to_memblk(ptr);
+
+ if (blk != NULL)
+ memblk_free(blk, file, line, func, bt + 1);
+ }
+}
+
+
+/*
+ * passthru allocator
+ */
+
+static void *__passthru_alloc(size_t size, const char *file, int line,
+ const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ if (MRP_UNLIKELY(size == 0))
+ return NULL;
+ else
+ return malloc(size);
+}
+
+
+static void *__passthru_realloc(void *ptr, size_t size, const char *file,
+ int line, const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ return realloc(ptr, size);
+}
+
+
+static int __passthru_memalign(void **ptr, size_t align, size_t size,
+ const char *file, int line, const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ return posix_memalign(ptr, align, size);
+}
+
+
+static void __passthru_free(void *ptr, const char *file, int line,
+ const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ free(ptr);
+}
+
+
+/*
+ * common public interface - uses either passthru or debugging
+ */
+
+void *mrp_mm_alloc(size_t size, const char *file, int line, const char *func)
+{
+ return __mm.alloc(size, file, line, func);
+}
+
+
+void *mrp_mm_realloc(void *ptr, size_t size, const char *file, int line,
+ const char *func)
+{
+ return __mm.realloc(ptr, size, file, line, func);
+}
+
+
+char *mrp_mm_strdup(const char *s, const char *file, int line, const char *func)
+{
+ char *p;
+ size_t size;
+
+ if (s != NULL) {
+ size = strlen(s) + 1;
+ p = mrp_mm_alloc(size, file, line, func);
+
+ if (p != NULL)
+ strcpy(p, s);
+ }
+ else
+ p = NULL;
+
+ return p;
+}
+
+
+int mrp_mm_memalign(void **ptr, size_t align, size_t size, const char *file,
+ int line, const char *func)
+{
+ return __mm.memalign(ptr, align, size, file, line, func);
+}
+
+
+void mrp_mm_free(void *ptr, const char *file, int line, const char *func)
+{
+ return __mm.free(ptr, file, line, func);
+}
+
+
+int mrp_mm_config(mrp_mm_type_t type)
+{
+ if (__mm.cur_blocks != 0)
+ return FALSE;
+
+ switch (type) {
+ case MRP_MM_PASSTHRU:
+ __mm.alloc = __passthru_alloc;
+ __mm.realloc = __passthru_realloc;
+ __mm.memalign = __passthru_memalign;
+ __mm.free = __passthru_free;
+ __mm.mode = MRP_MM_PASSTHRU;
+ return TRUE;
+
+ case MRP_MM_DEBUG:
+ __mm.alloc = __mm_alloc;
+ __mm.realloc = __mm_realloc;
+ __mm.memalign = __mm_memalign;
+ __mm.free = __mm_free;
+ __mm.mode = MRP_MM_DEBUG;
+ return TRUE;
+
+ default:
+ mrp_log_error("Invalid memory allocator type 0x%x requested.", type);
+ return FALSE;
+ }
+}
+
+
+#define NBUCKET 1024
+
+static int btcmp(void **bt1, void **bt2)
+{
+ ptrdiff_t diff;
+ int i;
+
+ for (i = 0; i < __mm.depth; i++) {
+ diff = bt1[i] - bt2[i];
+
+ if (diff < 0)
+ return -1;
+ else if (diff > 0)
+ return +1;
+ }
+
+ return 0;
+}
+
+
+static uint32_t blkhash(memblk_t *blk)
+{
+ uint32_t h;
+ int i;
+
+ h = 0;
+ for (i = 0; i < __mm.depth; i++)
+ h ^= (blk->bt[i] - NULL) & 0xffffffffUL;
+
+ return h % NBUCKET;
+}
+
+
+static memblk_t *blkfind(mrp_list_hook_t *buckets, memblk_t *blk)
+{
+ uint32_t h = blkhash(blk);
+ mrp_list_hook_t *head = buckets + h;
+ mrp_list_hook_t *p, *n;
+ memblk_t *b;
+
+ mrp_list_foreach(head, p, n) {
+ b = mrp_list_entry(p, typeof(*b), hook);
+ if (!btcmp(&b->bt[0], &blk->bt[0]))
+ return b;
+ }
+
+ return NULL;
+}
+
+
+static void collect_blocks(mrp_list_hook_t *buckets)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *head, *blk;
+ uint32_t h;
+ int i;
+
+ for (i = 0; i < NBUCKET; i++)
+ mrp_list_init(buckets + i);
+
+ mrp_list_foreach(&__mm.blocks, p, n) {
+ blk = mrp_list_entry(p, typeof(*blk), hook);
+
+ mrp_list_init(&blk->more);
+ head = blkfind(buckets, blk);
+
+ if (head != NULL) {
+ mrp_list_append(&head->more, &blk->more);
+ head->size += blk->size;
+ }
+ else {
+ h = blkhash(blk);
+ mrp_list_delete(&blk->hook);
+ mrp_list_append(buckets + h, &blk->hook);
+ }
+ }
+}
+
+
+static uint32_t group_usage(memblk_t *head, int exclude_head)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *blk;
+ uint32_t total;
+
+ total = exclude_head ? 0 : head->size;
+ mrp_list_foreach(&head->more, p, n) {
+ blk = mrp_list_entry(p, typeof(*blk), more);
+ total += blk->size;
+ }
+
+ return total;
+}
+
+
+static void dump_group(FILE *fp, memblk_t *head)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *blk;
+ char **syms, *sym;
+ uint32_t total;
+ int nblk, i;
+
+ fprintf(fp, "Allocations with call stack fingerprint:\n");
+ syms = backtrace_symbols(head->bt, __mm.depth);
+ for (i = 0; i < __mm.depth && head->bt[i]; i++) {
+ sym = syms && syms[i] ? strrchr(syms[i], '/') : NULL;
+ fprintf(fp, " %p (%s)\n", head->bt[i], sym ? sym + 1 : "<unknown>");
+ }
+ free(syms);
+
+ total = head->size - group_usage(head, TRUE);
+ nblk = 1;
+
+ fprintf(fp, " %lu bytes at %p\n", (unsigned long)total,
+ memblk_to_ptr(head));
+
+ mrp_list_foreach(&head->more, p, n) {
+ blk = mrp_list_entry(p, typeof(*blk), more);
+
+ total += blk->size;
+ nblk++;
+
+ fprintf(fp, " %zd bytes at %p\n", blk->size, memblk_to_ptr(blk));
+ }
+
+ if (nblk > 1)
+ fprintf(fp, " total %lu bytes in %d blocks\n",
+ (unsigned long)total, nblk);
+}
+
+
+static void sort_blocks(mrp_list_hook_t *buckets, mrp_list_hook_t *sorted)
+{
+ mrp_list_hook_t *bp, *bn, *sp, *sn;
+ memblk_t *head, *entry, *next;
+ int i;
+
+ mrp_list_init(sorted);
+
+ for (i = 0; i < NBUCKET; i++) {
+ mrp_list_foreach(buckets + i, bp, bn) {
+ head = mrp_list_entry(bp, typeof(*head), hook);
+
+ next = NULL;
+ mrp_list_foreach(sorted, sp, sn) {
+ entry = mrp_list_entry(sp, typeof(*entry), hook);
+
+ if (head->size <= entry->size) {
+ next = entry;
+ break;
+ }
+ }
+
+ mrp_list_delete(&head->hook);
+
+ if (next != NULL)
+ mrp_list_insert_before(&next->hook, &head->hook);
+ else
+ mrp_list_append(sorted, &head->hook);
+ }
+ }
+}
+
+
+static void dump_blocks(FILE *fp, mrp_list_hook_t *sorted)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *head;
+
+ mrp_list_foreach(sorted, p, n) {
+ head = mrp_list_entry(p, typeof(*head), hook);
+ dump_group(fp, head);
+ }
+}
+
+
+static void relink_blocks(mrp_list_hook_t *sorted)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *head;
+ uint32_t rest;
+
+ mrp_list_foreach(sorted, p, n) {
+ head = mrp_list_entry(p, typeof(*head), hook);
+ mrp_list_delete(&head->hook);
+ mrp_list_append(&__mm.blocks, &head->hook);
+
+ rest = group_usage(head, TRUE);
+ head->size -= rest;
+ }
+}
+
+
+void mrp_mm_dump(FILE *fp)
+{
+ mrp_list_hook_t buckets[NBUCKET];
+ mrp_list_hook_t sorted;
+
+ mrp_list_init(&sorted);
+
+ collect_blocks(buckets);
+ sort_blocks(buckets, &sorted);
+ dump_blocks(fp, &sorted);
+ relink_blocks(&sorted);
+
+ fprintf(fp, "Max: %llu bytes (%.2f M, %.2f G), %ld blocks\n",
+ (unsigned long long)__mm.max_alloc,
+ 1.0 * __mm.max_alloc / (1024 * 1024),
+ 1.0 * __mm.max_alloc / (1024 * 1024 * 1024),
+ (unsigned long)__mm.max_blocks);
+ fprintf(fp, "Current: %llu bytes (%.2f M, %.2f G) in %ld blocks.\n",
+ (unsigned long long)__mm.cur_alloc,
+ 1.0 * __mm.cur_alloc / (1024 * 1024),
+ 1.0 * __mm.cur_alloc / (1024 * 1024 * 1024),
+ (unsigned long)__mm.cur_blocks);
+}
+
+
+void mrp_mm_check(FILE *fp)
+{
+ mrp_mm_dump(fp);
+}
+
+
+
+
+
+/*
+ * object pool interface
+ */
+
+typedef unsigned int mask_t;
+
+#define W sizeof(mask_t)
+#define B (W * 8)
+#define MASK_BYTES (sizeof(mask_t))
+#define MASK_BITS (MASK_BYTES * 8)
+#define MASK_EMPTY ((mask_t)-1)
+#define MASK_FULL ((mask_t) 0)
+
+typedef struct pool_chunk_s pool_chunk_t;
+
+static int pool_calc_sizes(mrp_objpool_t *pool);
+static int pool_grow(mrp_objpool_t *pool, int nobj);
+static int pool_shrink(mrp_objpool_t *pool, int nobj);
+static pool_chunk_t *chunk_alloc(int nperchunk);
+static void chunk_free(pool_chunk_t *chunk);
+static inline int chunk_empty(pool_chunk_t *chunk);
+static void pool_foreach_object(mrp_objpool_t *pool,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data);
+static void chunk_foreach_object(pool_chunk_t *chunk,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data);
+
+
+/*
+ * an object pool
+ */
+
+struct mrp_objpool_s {
+ char *name; /* verbose pool name */
+ size_t limit; /* max. number of objects */
+ size_t objsize; /* size of a single object */
+ size_t prealloc; /* preallocate this many */
+ size_t nobj; /* currently allocated */
+ int (*setup)(void *); /* object setup callback */
+ void (*cleanup)(void *); /* object cleanup callback */
+ uint32_t flags; /* pool flags */
+ int poison; /* poisoning pattern */
+
+ size_t nperchunk; /* objects per chunk */
+ size_t dataidx; /* data */
+ mrp_list_hook_t space; /* chunk with frees slots */
+ size_t nspace; /* number of such chunks */
+ mrp_list_hook_t full; /* fully allocated chunks */
+ size_t nfull; /* number of such chunks */
+};
+
+
+/*
+ * a chunk of memory allocated to an object pool
+ */
+
+struct pool_chunk_s {
+ mrp_objpool_t *pool; /* pool we're alloced to */
+ mrp_list_hook_t hook; /* hook to chunk list */
+ mask_t cache; /* cache bits */
+ mask_t used[]; /* allocation mask */
+};
+
+
+
+mrp_objpool_t *mrp_objpool_create(mrp_objpool_config_t *cfg)
+{
+ mrp_objpool_t *pool;
+
+ if ((pool = mrp_allocz(sizeof(*pool))) != NULL) {
+ if ((pool->name = mrp_strdup(cfg->name)) == NULL)
+ goto fail;
+
+ pool->limit = cfg->limit;
+ pool->objsize = MRP_MAX(cfg->objsize, (size_t)MRP_MM_OBJSIZE_MIN);
+ pool->prealloc = cfg->prealloc;
+ pool->setup = cfg->setup;
+ pool->cleanup = cfg->cleanup;
+ pool->flags = cfg->flags;
+ pool->poison = cfg->poison;
+
+ mrp_list_init(&pool->space);
+ mrp_list_init(&pool->full);
+ pool->nspace = 0;
+ pool->nfull = 0;
+
+ if (!pool_calc_sizes(pool))
+ goto fail;
+
+ if (!mrp_objpool_grow(pool, pool->prealloc))
+ goto fail;
+
+ mrp_debug("pool <%s> created, with %zd/%zd objects.", pool->name,
+ pool->prealloc, pool->limit);
+
+ return pool;
+ }
+
+
+ fail:
+ mrp_objpool_destroy(pool);
+ return NULL;
+}
+
+
+static void free_object(void *obj, void *user_data)
+{
+ mrp_objpool_t *pool = (mrp_objpool_t *)user_data;
+
+ printf("Releasing unfreed object %p from pool <%s>.\n", obj, pool->name);
+ mrp_objpool_free(obj);
+}
+
+
+void mrp_objpool_destroy(mrp_objpool_t *pool)
+{
+ if (pool != NULL) {
+ if (pool->cleanup != NULL)
+ pool_foreach_object(pool, free_object, pool);
+
+ mrp_free(pool->name);
+ mrp_free(pool);
+ }
+}
+
+
+void *mrp_objpool_alloc(mrp_objpool_t *pool)
+{
+ pool_chunk_t *chunk;
+ void *obj;
+ unsigned int cidx, uidx, sidx;
+
+ if (pool->limit && pool->nobj >= pool->limit)
+ return NULL;
+
+ if (mrp_list_empty(&pool->space)) {
+ if (!pool_grow(pool, 1))
+ return NULL;
+ }
+
+ chunk = mrp_list_entry(pool->space.next, pool_chunk_t, hook);
+ cidx = ffs(chunk->cache);
+
+ if (!cidx) {
+ mrp_log_error("object pool bug: no free slots in cache mask.");
+ return NULL;
+ }
+ else
+ cidx--;
+
+ uidx = ffs(chunk->used[cidx]);
+
+ if (!uidx) {
+ mrp_log_error("object pool bug: no free slots in used mask.");
+ return NULL;
+ }
+ else
+ uidx--;
+
+ sidx = cidx * MASK_BITS + uidx;
+ obj = ((void *)&chunk->used[pool->dataidx]) + (sidx * pool->objsize);
+
+ mrp_debug("%p: %u/%u: %u, offs %zd\n", obj, cidx, uidx, sidx,
+ sidx * pool->objsize);
+
+ chunk->used[cidx] &= ~(1 << uidx);
+
+ if (chunk->used[cidx] == MASK_FULL) {
+ chunk->cache &= ~(1 << cidx);
+
+ if (chunk->cache == MASK_FULL) { /* chunk exhausted */
+ mrp_list_delete(&chunk->hook);
+ pool->nspace--;
+ mrp_list_append(&pool->full, &chunk->hook);
+ pool->nfull++;
+ }
+ }
+
+ if (pool->setup == NULL || pool->setup(obj)) {
+ pool->nobj++;
+ return obj;
+ }
+ else {
+ mrp_objpool_free(obj);
+ return NULL;
+ }
+}
+
+
+void mrp_objpool_free(void *obj)
+{
+ pool_chunk_t *chunk;
+ mrp_objpool_t *pool;
+ unsigned int cidx, uidx, sidx;
+ mask_t cache, used;
+ void *base;
+
+ if (obj == NULL)
+ return;
+
+ chunk = (pool_chunk_t *)(((ptrdiff_t)obj) & ~(__mm.chunk_size - 1));
+ pool = chunk->pool;
+
+ base = (void *)&chunk->used[pool->dataidx];
+ sidx = (obj - base) / pool->objsize;
+ cidx = sidx / MASK_BITS;
+ uidx = sidx & (MASK_BITS - 1);
+
+ mrp_debug("%p: %u/%u: %u, offs %zd\n", obj, cidx, uidx, sidx,
+ sidx * pool->objsize);
+
+ cache = chunk->cache;
+ used = chunk->used[cidx];
+
+ if (used & (1 << uidx)) {
+ mrp_log_error("Trying to free unallocated object %p of pool <%s>.",
+ obj, pool->name);
+ return;
+ }
+
+ if (pool->cleanup != NULL)
+ pool->cleanup(obj);
+
+ if (pool->flags & MRP_OBJPOOL_FLAG_POISON)
+ memset(obj, pool->poison, pool->objsize);
+
+ chunk->used[cidx] |= (1 << uidx);
+ chunk->cache |= (1 << cidx);
+
+ if (cache == MASK_FULL) { /* chunk was full */
+ mrp_list_delete(&chunk->hook);
+ pool->nfull--;
+ mrp_list_append(&pool->space, &chunk->hook);
+ pool->nspace++;
+ }
+
+ pool->nobj--;
+}
+
+
+int mrp_objpool_grow(mrp_objpool_t *pool, int nobj)
+{
+ int nchunk = (nobj + pool->nperchunk - 1) / pool->nperchunk;
+
+ return pool_grow(pool, nchunk) == nchunk;
+}
+
+
+int mrp_objpool_shrink(mrp_objpool_t *pool, int nobj)
+{
+ int nchunk = (nobj + pool->nperchunk - 1) / pool->nperchunk;
+
+ return pool_shrink(pool, nchunk) == nchunk;
+}
+
+
+static int pool_calc_sizes(mrp_objpool_t *pool)
+{
+ size_t S, C, Hf, Hv, P;
+ size_t n, T;
+
+ if (!pool->objsize)
+ return FALSE;
+
+ pool->objsize = MRP_ALIGN(pool->objsize, MRP_MM_ALIGN);
+
+ /*
+ * Pool chunks consist of an administrative header followed by object
+ * slots each of which can be either claimed/allocated or free. The
+ * header contains a back pointer to the pool, a hook to one of the
+ * chunk lists and a two-level bit-mask for slot allocation status.
+ * The two-level mask consists of a 32-bit cache word and actual slot
+ * status words. The nth bit of the cache word caches whether there are
+ * any free among the nth - (n + 31)th slots. The slot status words keep
+ * the status of the actual slots. To find a free slot we find the idx
+ * of the 1st word with a free slot from the cache and then the free
+ * slot index in that word. To be able to use FFS we use inverted bit
+ * semantics (0=allocated, 1=free) and we populate the words starting
+ * at the LSB.
+ *
+ * Here we calculate how many objects we'll be able to squeeze into a
+ * single pool chunk and how many mask bits we'll need to administer
+ * the status of these. To do this we use the following equations:
+ *
+ * 1) Hf + Hv + n * S = C
+ * 2) Hv = W + (n + B - 1) / B * W
+ * where
+ * C: chunk size
+ * S: object size (aligned to our minimum alignment)
+ * Hf: header size, fixed part
+ * Hv: Header size, variable part (allocation mask)
+ * n: number of objects / chunk
+ * W: bitmask word size in bytes
+ * B: bitmask word size in bits
+ *
+ * Solving the equations for n gives us
+ * n = (B*C - B*Hf - W*(2*B - 1)) / (B*S + W)
+ *
+ * If any, the only non-obvious thing below is that instead of trying
+ * to express padding as part of the equation system (which seems to be
+ * way beyond my abilities in math nowadays), we initally assume no
+ * padding then check and compensate for it in the end if necessary.
+ */
+
+ Hf = sizeof(pool_chunk_t);
+ C = __mm.chunk_size;
+ P = 0;
+
+ S = MRP_ALIGN(pool->objsize, MRP_MM_ALIGN);
+ n = (B * C - B * Hf - W * (2*B - 1)) / (B * S + W);
+ Hv = W + W * (n + B - 1) / B;
+
+ P = (Hf + Hv) % sizeof(void *);
+ if (P != 0) {
+ P = sizeof(void *) - P;
+
+ if (Hv + Hf + P + n * S > C) {
+ n--;
+ Hv = W + W * (n + B - 1) / B;
+ }
+ }
+
+ T = Hf + Hv + P + n * S;
+
+ if (T > C) {
+ mrp_log_error("Could not size pool '%s' properly.", pool->name);
+ return FALSE;
+ }
+
+ pool->nperchunk = n;
+ pool->dataidx = (n + B - 1) / B;
+
+ if (pool->limit && (pool->limit % pool->nperchunk) != 0)
+ pool->limit += (pool->nperchunk - (pool->limit % pool->nperchunk));
+
+ return TRUE;
+}
+
+
+static int pool_grow(mrp_objpool_t *pool, int nchunk)
+{
+ pool_chunk_t *chunk;
+ int cnt;
+
+ for (cnt = 0; cnt < nchunk; cnt++) {
+ chunk = chunk_alloc(pool->nperchunk);
+
+ if (chunk != NULL) {
+ chunk->pool = pool;
+ mrp_list_append(&pool->space, &chunk->hook);
+ pool->nspace++;
+ }
+ else
+ break;
+ }
+
+ return cnt;
+}
+
+
+static int pool_shrink(mrp_objpool_t *pool, int nchunk)
+{
+ mrp_list_hook_t *p, *n;
+ pool_chunk_t *chunk;
+ int cnt;
+
+ cnt = 0;
+ mrp_list_foreach(&pool->space, p, n) {
+ chunk = mrp_list_entry(p, pool_chunk_t, hook);
+
+ if (chunk_empty(chunk)) {
+ mrp_list_delete(&chunk->hook);
+ chunk_free(chunk);
+ pool->nspace--;
+ cnt++;
+ }
+
+ if (cnt >= nchunk)
+ break;
+ }
+
+ return cnt;
+}
+
+
+static void pool_foreach_object(mrp_objpool_t *pool,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ pool_chunk_t *chunk;
+
+ mrp_list_foreach(&pool->full, p, n) {
+ chunk = mrp_list_entry(p, pool_chunk_t, hook);
+ chunk_foreach_object(chunk, cb, user_data);
+ }
+
+ mrp_list_foreach(&pool->space, p, n) {
+ chunk = mrp_list_entry(p, pool_chunk_t, hook);
+ chunk_foreach_object(chunk, cb, user_data);
+ }
+}
+
+
+static void chunk_foreach_object(pool_chunk_t *chunk,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data)
+{
+ mrp_objpool_t *pool = chunk->pool;
+ void *obj;
+ int sidx, cidx, uidx;
+ mask_t used;
+
+ sidx = 0;
+ while (sidx < (int)pool->nperchunk) {
+ cidx = sidx / MASK_BITS;
+ uidx = sidx & (MASK_BITS - 1);
+ used = chunk->used[cidx];
+
+ if (!(used & (1 << uidx))) {
+ obj = ((void *)&chunk->used[pool->dataidx]) + (sidx*pool->objsize);
+ cb(obj, user_data);
+ sidx++;
+ }
+ else {
+ if (used == MASK_EMPTY)
+ sidx = (sidx + MASK_BITS) & ~(MASK_BITS - 1);
+ else
+ sidx++;
+ }
+ }
+}
+
+
+static inline int chunk_empty(pool_chunk_t *chunk)
+{
+ mask_t mask;
+ int i, n;
+
+ if (chunk->cache != (MASK_EMPTY & 0xffff))
+ return FALSE;
+ else {
+ for (n = chunk->pool->nperchunk, i = 0; n > 0; n -= MASK_BITS, i++) {
+ if (n >= (int)MASK_BITS)
+ mask = MASK_EMPTY;
+ else
+ mask = (1 << n) - 1;
+
+ if ((chunk->used[i] & mask) != mask)
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+
+static void chunk_init(pool_chunk_t *chunk, int nperchunk)
+{
+ int nword, left, i;
+
+ mrp_list_init(&chunk->hook);
+
+ left = nperchunk;
+ nword = (nperchunk + MASK_BITS - 1) / MASK_BITS;
+
+ /*
+ * initialize allocation bitmask
+ *
+ * Note that every bit that corresponds to a non-allocatable slots
+ * we mark as reserved. This keeps both the allocation and freeing
+ * code paths simpler.
+ */
+
+ chunk->cache = (1 << nword) - 1;
+
+ for (i = 0; left > 0; i++) {
+ if (left >= (int)MASK_BITS)
+ chunk->used[i] = MASK_EMPTY;
+ else
+ chunk->used[i] = (((mask_t)1) << left) - 1;
+
+ left -= B;
+ }
+}
+
+
+static pool_chunk_t *chunk_alloc(int nperchunk)
+{
+ void *chunk;
+ int err;
+
+ err = posix_memalign(&chunk, __mm.chunk_size, __mm.chunk_size);
+
+ if (err == 0) {
+ memset(chunk, 0, __mm.chunk_size);
+ chunk_init((pool_chunk_t *)chunk, nperchunk);
+ }
+ else
+ chunk = NULL;
+
+ return chunk;
+}
+
+
+static void chunk_free(pool_chunk_t *chunk)
+{
+ free(chunk);
+}
+
+
+#if 0
+static void test_sizes(void)
+{
+ size_t S, C, Hf, Hv, P;
+ size_t i, n, T, Hv1, n1, T1;
+ int ok, ok1;
+
+ Hf = sizeof(pool_chunk_t);
+ C = __mm.chunk_size;
+ P = 0;
+
+ printf(" C: %zd\n", C);
+ printf("Hf: %zd\n", Hf);
+
+ for (i = 1; i < __mm.chunk_size / 8; i++) {
+ S = MRP_ALIGN(i, MRP_MM_ALIGN);
+ n = (B * C - B * Hf - W * (2*B - 1)) / (B * S + W);
+ Hv = W + W * (n + B - 1) / B;
+
+ P = (Hf + Hv) % sizeof(void *);
+ if (P != 0) {
+ P = sizeof(void *) - P;
+
+ if (Hv + Hf + P + n * S > C) {
+ n--;
+ Hv = W + W * (n + B - 1) / B;
+ }
+ }
+
+ T = Hf + Hv + P + n * S;
+ ok = T <= C;
+
+ n1 = n + 1;
+ Hv1 = W + W * (n1 + B - 1) / B;
+ T1 = Hf + Hv1 + P + n1 * S;
+ ok1 = T1 > C;
+
+ printf(" i = %zd: %zd * %zd + %zd (%zd: %s, %zd: %s)\n", i, n, S, P,
+ T , ok ? "OK" : "FAIL",
+ T1, ok1 ? "OK" : "FAIL");
+ {
+ size_t hs, us;
+
+ us = sizeof(uint32_t);
+ hs = (Hf + Hv + P) / us;
+
+ printf(" H+P: %zd (%zd * %zd = %zd)\n", Hf + Hv + P,
+ hs, us, hs * us);
+
+ if (((Hf + Hv + P) % sizeof(void *)) != 0) {
+ printf("Padding error!\n");
+ exit(1);
+ }
+ }
+
+ if (!ok || !ok1)
+ exit(1);
+ }
+}
+#endif
diff --git a/src/common/mm.h b/src/common/mm.h
new file mode 100644
index 0000000..73ffacb
--- /dev/null
+++ b/src/common/mm.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MM_H__
+#define __MURPHY_MM_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_MM_ALIGN 8 /* object alignment */
+#define MRP_MM_CONFIG_ENVVAR "__MURPHY_MM_CONFIG"
+
+#define mrp_alloc(size) mrp_mm_alloc((size), __LOC__)
+#define mrp_free(ptr) mrp_mm_free((ptr), __LOC__)
+#define mrp_strdup(s) mrp_mm_strdup((s), __LOC__)
+#define mrp_datadup(ptr, size) ({ \
+ typeof(ptr) _ptr = mrp_alloc(size); \
+ \
+ if (_ptr != NULL) \
+ memcpy(_ptr, ptr, size); \
+ \
+ _ptr; \
+ })
+
+#define mrp_allocz(size) ({ \
+ void *_ptr; \
+ \
+ if ((_ptr = mrp_mm_alloc(size, __LOC__)) != NULL) \
+ memset(_ptr, 0, size); \
+ \
+ _ptr;})
+
+#define mrp_calloc(n, size) mrp_allocz((n) * (size))
+
+#define mrp_reallocz(ptr, o, n) ({ \
+ typeof(ptr) _ptr; \
+ typeof(o) _o; \
+ typeof(n) _n = (n); \
+ size_t _size = sizeof(*_ptr) * (_n); \
+ \
+ if ((ptr) != NULL) \
+ _o = o; \
+ else \
+ _o = 0; \
+ \
+ _ptr = (typeof(ptr))mrp_mm_realloc(ptr, _size, __LOC__); \
+ if (_ptr != NULL || _n == 0) { \
+ if ((unsigned)(_n) > (unsigned)(_o)) \
+ memset(_ptr + (_o), 0, \
+ ((_n)-(_o)) * sizeof(*_ptr)); \
+ ptr = _ptr; \
+ } \
+ _ptr; })
+
+#define mrp_realloc(ptr, size) ({ \
+ typeof(ptr) _ptr; \
+ size_t _size = size; \
+ \
+ _ptr = (typeof(ptr))mrp_mm_realloc(ptr, _size, __LOC__); \
+ if (_ptr != NULL || _size == 0) \
+ ptr = _ptr; \
+ \
+ _ptr; })
+
+#define mrp_clear(obj) memset((obj), 0, sizeof(*(obj)))
+
+
+#define mrp_alloc_array(type, n) ((type *)mrp_alloc(sizeof(type) * (n)))
+#define mrp_allocz_array(type, n) ((type *)mrp_allocz(sizeof(type) * (n)))
+
+typedef enum {
+ MRP_MM_PASSTHRU = 0, /* passthru allocator */
+ MRP_MM_DEFAULT = MRP_MM_PASSTHRU, /* default is passthru */
+ MRP_MM_DEBUG /* debugging allocator */
+} mrp_mm_type_t;
+
+
+int mrp_mm_config(mrp_mm_type_t type);
+void mrp_mm_check(FILE *fp);
+void mrp_mm_dump(FILE *fp);
+
+void *mrp_mm_alloc(size_t size, const char *file, int line, const char *func);
+void *mrp_mm_realloc(void *ptr, size_t size, const char *file, int line,
+ const char *func);
+char *mrp_mm_strdup(const char *s, const char *file, int line,
+ const char *func);
+int mrp_mm_memalign(void **ptr, size_t align, size_t size, const char *file,
+ int line, const char *func);
+void mrp_mm_free(void *ptr, const char *file, int line, const char *func);
+
+
+
+
+#define MRP_MM_OBJSIZE_MIN 16 /* minimum object size */
+
+enum {
+ MRP_OBJPOOL_FLAG_POISON = 0x1, /* poison free'd objects */
+};
+
+
+/*
+ * object pool configuration
+ */
+
+typedef struct {
+ char *name; /* verbose pool name */
+ size_t limit; /* max. number of objects */
+ size_t objsize; /* size of a single object */
+ size_t prealloc; /* preallocate this many */
+ int (*setup)(void *); /* object setup callback */
+ void (*cleanup)(void *); /* object cleanup callback */
+ uint32_t flags; /* MRP_OBJPOOL_FLAG_* */
+ int poison; /* poisoning pattern */
+} mrp_objpool_config_t;
+
+
+typedef struct mrp_objpool_s mrp_objpool_t;
+
+/** Create a new object pool with the given configuration. */
+mrp_objpool_t *mrp_objpool_create(mrp_objpool_config_t *cfg);
+
+/** Destroy an object pool, freeing all associated memory. */
+void mrp_objpool_destroy(mrp_objpool_t *pool);
+
+/** Allocate a new object from the pool. */
+void *mrp_objpool_alloc(mrp_objpool_t *pool);
+
+/** Free the given object. */
+void mrp_objpool_free(void *obj);
+
+/** Grow @pool to accomodate @nobj new objects. */
+int mrp_objpool_grow(mrp_objpool_t *pool, int nobj);
+
+/** Shrink @pool by @nobj new objects, if possible. */
+int mrp_objpool_shrink(mrp_objpool_t *pool, int nobj);
+
+/** Get the value of a boolean key from the configuration. */
+int mrp_mm_config_bool(const char *key, int defval);
+
+/** Get the value of a boolean key from the configuration. */
+int32_t mrp_mm_config_int32(const char *key, int32_t defval);
+
+/** Get the value of a boolean key from the configuration. */
+uint32_t mrp_mm_config_uint32(const char *key, uint32_t defval);
+
+/** Get the value of a string key from the configuration. */
+int mrp_mm_config_string(const char *key, const char *defval,
+ char *buf, size_t size);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MM_H__ */
+
diff --git a/src/common/msg.c b/src/common/msg.c
new file mode 100644
index 0000000..2db8214
--- /dev/null
+++ b/src/common/msg.c
@@ -0,0 +1,2222 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/msg.h>
+
+#define NDIRECT_TYPE 256 /* directly indexed types */
+
+static mrp_data_descr_t **direct_types; /* directly indexed types */
+static mrp_data_descr_t **other_types; /* linearly searched types */
+static int nother_type;
+
+
+static inline void destroy_field(mrp_msg_field_t *f)
+{
+ uint32_t i;
+
+ if (f != NULL) {
+ mrp_list_delete(&f->hook);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ mrp_free(f->str);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ mrp_free(f->blb);
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ if ((f->type & ~MRP_MSG_FIELD_ARRAY) == MRP_MSG_FIELD_STRING) {
+ for (i = 0; i < f->size[0]; i++) {
+ mrp_free(f->astr[i]);
+ }
+ }
+
+ mrp_free(f->aany);
+ }
+ break;
+ }
+
+ mrp_free(f);
+ }
+}
+
+
+static inline mrp_msg_field_t *create_field(uint16_t tag, va_list *ap)
+{
+ mrp_msg_field_t *f;
+ uint16_t type, base;
+ uint32_t size;
+ void *blb;
+
+ type = va_arg(*ap, uint32_t);
+
+#define CREATE(_f, _tag, _type, _fldtype, _fld, _last, _errlbl) do { \
+ \
+ (_f) = mrp_allocz(MRP_OFFSET(typeof(*_f), _last) + \
+ sizeof(_f->_last)); \
+ \
+ if ((_f) != NULL) { \
+ mrp_list_init(&(_f)->hook); \
+ (_f)->tag = _tag; \
+ (_f)->type = _type; \
+ (_f)->_fld = va_arg(*ap, _fldtype); \
+ } \
+ else { \
+ goto _errlbl; \
+ } \
+ } while (0)
+
+#define CREATE_ARRAY(_f, _tag, _type, _fld, _fldtype, _errlbl) do { \
+ uint16_t _base; \
+ uint32_t _i; \
+ \
+ (_f) = mrp_allocz(MRP_OFFSET(typeof(*_f), size[1])); \
+ \
+ if ((_f) != NULL) { \
+ mrp_list_init(&(_f)->hook); \
+ (_f)->tag = _tag; \
+ (_f)->type = _type | MRP_MSG_FIELD_ARRAY; \
+ _base = _type & ~MRP_MSG_FIELD_ARRAY; \
+ \
+ _f->size[0] = va_arg(*ap, uint32_t); \
+ _f->_fld = mrp_allocz_array(typeof(*_f->_fld), \
+ _f->size[0]); \
+ \
+ if (_f->_fld == NULL && _f->size[0] != 0) \
+ goto _errlbl; \
+ else \
+ memcpy(_f->_fld, va_arg(*ap, typeof(_f->_fld)), \
+ _f->size[0] * sizeof(_f->_fld[0])); \
+ \
+ if (_base == MRP_MSG_FIELD_STRING) { \
+ for (_i = 0; _i < _f->size[0]; _i++) { \
+ _f->astr[_i] = mrp_strdup(_f->astr[_i]); \
+ if (_f->astr[_i] == NULL) \
+ goto _errlbl; \
+ } \
+ } \
+ } \
+ else \
+ goto _errlbl; \
+ } while (0)
+
+ f = NULL;
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ CREATE(f, tag, type, char *, str, str, fail);
+ f->str = mrp_strdup(f->str);
+ if (f->str == NULL)
+ goto fail;
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ CREATE(f, tag, type, int, bln, bln, fail);
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ CREATE(f, tag, type, unsigned int, u8, u8, fail);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ CREATE(f, tag, type, signed int, s8, s8, fail);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ CREATE(f, tag, type, unsigned int, u16, u16, fail);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ CREATE(f, tag, type, signed int, s16, s16, fail);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ CREATE(f, tag, type, unsigned int, u32, u32, fail);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ CREATE(f, tag, type, signed int, s32, s32, fail);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ CREATE(f, tag, type, uint64_t, u64, u64, fail);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ CREATE(f, tag, type, int64_t, s64, s64, fail);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ CREATE(f, tag, type, double, dbl, dbl, fail);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ size = va_arg(*ap, uint32_t);
+ CREATE(f, tag, type, void *, blb, size[0], fail);
+
+ blb = f->blb;
+ f->size[0] = size;
+ f->blb = mrp_allocz(size);
+
+ if (f->blb != NULL) {
+ memcpy(f->blb, blb, size);
+ f->size[0] = size;
+ }
+ else
+ goto fail;
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ base = type & ~MRP_MSG_FIELD_ARRAY;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ CREATE_ARRAY(f, tag, base, astr, char *, fail);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ CREATE_ARRAY(f, tag, base, abln, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ CREATE_ARRAY(f, tag, base, au8, unsigned int, fail);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ CREATE_ARRAY(f, tag, base, as8, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ CREATE_ARRAY(f, tag, base, au16, unsigned int, fail);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ CREATE_ARRAY(f, tag, base, as16, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ CREATE_ARRAY(f, tag, base, au32, unsigned int, fail);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ CREATE_ARRAY(f, tag, base, as32, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ CREATE_ARRAY(f, tag, base, au64, unsigned long long, fail);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ CREATE_ARRAY(f, tag, base, as64, long long, fail);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ CREATE_ARRAY(f, tag, base, adbl, double, fail);
+ break;
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+ break;
+ }
+
+ return f;
+
+ fail:
+ destroy_field(f);
+ return NULL;
+
+#undef CREATE
+#undef CREATE_ARRAY
+}
+
+
+static void msg_destroy(mrp_msg_t *msg)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+
+ if (msg != NULL) {
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+ destroy_field(f);
+ }
+
+ mrp_free(msg);
+ }
+}
+
+
+mrp_msg_t *mrp_msg_createv(uint16_t tag, va_list ap)
+{
+ mrp_msg_t *msg;
+ mrp_msg_field_t *f;
+ va_list aq;
+
+ va_copy(aq, ap);
+ if ((msg = mrp_allocz(sizeof(*msg))) != NULL) {
+ mrp_list_init(&msg->fields);
+ mrp_refcnt_init(&msg->refcnt);
+
+ while (tag != MRP_MSG_FIELD_INVALID) {
+ f = create_field(tag, &aq);
+
+ if (f != NULL) {
+ mrp_list_append(&msg->fields, &f->hook);
+ msg->nfield++;
+ }
+ else {
+ msg_destroy(msg);
+ msg = NULL;
+ goto out;
+ }
+ tag = va_arg(aq, uint32_t);
+ }
+ }
+ out:
+ va_end(aq);
+
+ return msg;
+}
+
+
+mrp_msg_t *mrp_msg_create(uint16_t tag, ...)
+{
+ mrp_msg_t *msg;
+ va_list ap;
+
+ va_start(ap, tag);
+ msg = mrp_msg_createv(tag, ap);
+ va_end(ap);
+
+ return msg;
+}
+
+
+mrp_msg_t *mrp_msg_ref(mrp_msg_t *msg)
+{
+ return mrp_ref_obj(msg, refcnt);
+}
+
+
+void mrp_msg_unref(mrp_msg_t *msg)
+{
+ if (mrp_unref_obj(msg, refcnt))
+ msg_destroy(msg);
+}
+
+
+int mrp_msg_append(mrp_msg_t *msg, uint16_t tag, ...)
+{
+ mrp_msg_field_t *f;
+ va_list ap;
+
+ va_start(ap, tag);
+ f = create_field(tag, &ap);
+ va_end(ap);
+
+ if (f != NULL) {
+ mrp_list_append(&msg->fields, &f->hook);
+ msg->nfield++;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_msg_prepend(mrp_msg_t *msg, uint16_t tag, ...)
+{
+ mrp_msg_field_t *f;
+ va_list ap;
+
+ va_start(ap, tag);
+ f = create_field(tag, &ap);
+ va_end(ap);
+
+ if (f != NULL) {
+ mrp_list_prepend(&msg->fields, &f->hook);
+ msg->nfield++;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_msg_set(mrp_msg_t *msg, uint16_t tag, ...)
+{
+ mrp_msg_field_t *of, *nf;
+ va_list ap;
+
+ of = mrp_msg_find(msg, tag);
+
+ if (of != NULL) {
+ va_start(ap, tag);
+ nf = create_field(tag, &ap);
+ va_end(ap);
+
+ if (nf != NULL) {
+ mrp_list_append(&of->hook, &nf->hook);
+ destroy_field(of);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_msg_iterate(mrp_msg_t *msg, void **it, uint16_t *tagp, uint16_t *typep,
+ mrp_msg_value_t *valp, size_t *sizep)
+{
+ mrp_list_hook_t *p = *(mrp_list_hook_t **)it;
+ mrp_msg_field_t *f;
+
+ if (p == NULL)
+ p = msg->fields.next;
+
+ if (p == &msg->fields)
+ return FALSE;
+
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ *tagp = f->tag;
+ *typep = f->type;
+
+ switch (f->type) {
+#define HANDLE_TYPE(type, member) \
+ case MRP_MSG_FIELD_##type: \
+ valp->member = f->member; \
+ if (sizep != NULL) \
+ *sizep = sizeof(typeof(f->member)); \
+ break
+
+ HANDLE_TYPE(BOOL , bln);
+ HANDLE_TYPE(UINT8 , u8);
+ HANDLE_TYPE(SINT8 , s8);
+ HANDLE_TYPE(UINT16, u16);
+ HANDLE_TYPE(SINT16, s16);
+ HANDLE_TYPE(UINT32, u32);
+ HANDLE_TYPE(SINT32, s32);
+ HANDLE_TYPE(UINT64, u64);
+ HANDLE_TYPE(SINT64, s64);
+ HANDLE_TYPE(DOUBLE, dbl);
+
+ case MRP_MSG_FIELD_STRING:
+ valp->str = f->str;
+ if (sizep != NULL)
+ *sizep = strlen(f->str);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ valp->blb = f->blb;
+ if (sizep != NULL)
+ *sizep = (size_t)f->size[0];
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ valp->aany = f->aany;
+ if (sizep != NULL)
+ *sizep = f->size[0];
+ }
+ else
+ return FALSE;
+#undef HANDLE_TYPE
+ }
+
+ *it = p->next;
+
+ return TRUE;
+}
+
+
+mrp_msg_field_t *mrp_msg_find(mrp_msg_t *msg, uint16_t tag)
+{
+ mrp_msg_field_t *f;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+ if (f->tag == tag)
+ return f;
+ }
+
+ return NULL;
+}
+
+
+int mrp_msg_get(mrp_msg_t *msg, ...)
+{
+#define HANDLE_TYPE(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ valp = va_arg(ap, typeof(valp)); \
+ valp->_member = f->_member; \
+ break
+
+#define HANDLE_ARRAY(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ cntp = va_arg(ap, typeof(cntp)); \
+ valp = va_arg(ap, typeof(valp)); \
+ *cntp = f->size[0]; \
+ valp->_member = f->_member; \
+ break
+
+
+ mrp_msg_field_t *f;
+ mrp_msg_value_t *valp;
+ uint32_t *cntp;
+ mrp_list_hook_t *start, *p;
+ uint16_t tag, type;
+ int found;
+ va_list ap;
+
+ va_start(ap, msg);
+
+ /*
+ * Okay... this might look a bit weird at first sight. This is
+ * mostly because we don't use the standard list iterating macros
+ * in the inner loop. There is a good reason for that: we want to
+ * minimise the number of times we scan the message which is just
+ * a linked list of fields. We do this by arranging the nested
+ * loops below in such a way that if the order of fields to fetch
+ * in the argument list matches the order of fields in the message
+ * we end up running the outer and inner loops in a 'phase lock'.
+ * So if the caller fetches the fields in the correct order we end
+ * up scanning the message at most once but only up to the last
+ * field to fetch.
+ */
+
+ start = msg->fields.next;
+ found = FALSE;
+
+ while ((tag = va_arg(ap, unsigned int)) != MRP_MSG_FIELD_INVALID) {
+ type = va_arg(ap, unsigned int);
+ found = FALSE;
+
+ for (p = start; p != start->prev; p = p->next) {
+ if (p == &msg->fields)
+ continue;
+
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (f->tag != tag)
+ continue;
+
+ if (f->type != type)
+ goto out;
+
+ switch (type) {
+ HANDLE_TYPE(STRING, str);
+ HANDLE_TYPE(BOOL , bln);
+ HANDLE_TYPE(UINT8 , u8 );
+ HANDLE_TYPE(SINT8 , s8 );
+ HANDLE_TYPE(UINT16, u16);
+ HANDLE_TYPE(SINT16, s16);
+ HANDLE_TYPE(UINT32, u32);
+ HANDLE_TYPE(SINT32, s32);
+ HANDLE_TYPE(UINT64, u64);
+ HANDLE_TYPE(SINT64, s64);
+ HANDLE_TYPE(DOUBLE, dbl);
+ default:
+ if (type & MRP_MSG_FIELD_ARRAY) {
+ switch (type & ~MRP_MSG_FIELD_ARRAY) {
+ HANDLE_ARRAY(STRING, astr);
+ HANDLE_ARRAY(BOOL , abln);
+ HANDLE_ARRAY(UINT8 , au8 );
+ HANDLE_ARRAY(SINT8 , as8 );
+ HANDLE_ARRAY(UINT16, au16);
+ HANDLE_ARRAY(SINT16, as16);
+ HANDLE_ARRAY(UINT32, au32);
+ HANDLE_ARRAY(SINT32, as32);
+ HANDLE_ARRAY(UINT64, au64);
+ HANDLE_ARRAY(SINT64, as64);
+ HANDLE_ARRAY(DOUBLE, adbl);
+ default:
+ goto out;
+
+ }
+ }
+ else
+ goto out;
+ }
+
+ start = p->next;
+ found = TRUE;
+ break;
+ }
+
+ if (!found)
+ break;
+ }
+
+ out:
+ va_end(ap);
+
+ return found;
+
+#undef HANDLE_TYPE
+#undef HANDLE_ARRAY
+
+}
+
+
+int mrp_msg_iterate_get(mrp_msg_t *msg, void **it, ...)
+{
+#define HANDLE_TYPE(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ valp = va_arg(ap, typeof(valp)); \
+ valp->_member = f->_member; \
+ break
+
+#define HANDLE_ARRAY(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ cntp = va_arg(ap, typeof(cntp)); \
+ valp = va_arg(ap, typeof(valp)); \
+ *cntp = f->size[0]; \
+ valp->_member = f->_member; \
+ break
+
+#define ANY_TYPE(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ valp->_member = f->_member; \
+ break
+
+ mrp_msg_field_t *f;
+ mrp_msg_value_t *valp;
+ uint32_t *cntp;
+ mrp_list_hook_t *start, *p;
+ uint16_t tag, type, *typep;
+ int found;
+ va_list ap;
+
+ va_start(ap, it);
+
+ /*
+ * Okay... this might look a bit weird at first sight. This is
+ * mostly because we don't use the standard list iterating macros
+ * in the inner loop. There is a good reason for that: we want to
+ * minimise the number of times we scan the message which is just
+ * a linked list of fields. We do this by arranging the nested
+ * loops below in such a way that if the order of fields to fetch
+ * in the argument list matches the order of fields in the message
+ * we end up running the outer and inner loops in a 'phase lock'.
+ * So if the caller fetches the fields in the correct order we end
+ * up scanning the message at most once but only up to the last
+ * field to fetch.
+ */
+
+ start = (*it) ? (mrp_list_hook_t *)*it : msg->fields.next;
+ found = FALSE;
+
+ while ((tag = va_arg(ap, unsigned int)) != MRP_MSG_FIELD_INVALID) {
+ type = va_arg(ap, unsigned int);
+ found = FALSE;
+
+ if (type == MRP_MSG_FIELD_ANY) {
+ typep = va_arg(ap, uint16_t *);
+ valp = va_arg(ap, mrp_msg_value_t *);
+ }
+ else {
+ typep = NULL;
+ valp = NULL;
+ }
+
+ for (p = start; p != start->prev; p = p->next) {
+ if (p == &msg->fields)
+ continue;
+
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (f->tag != tag)
+ continue;
+
+ if (type == MRP_MSG_FIELD_ANY) {
+ *typep = f->type;
+ switch (f->type) {
+ ANY_TYPE(STRING, str);
+ ANY_TYPE(BOOL , bln);
+ ANY_TYPE(UINT8 , u8 );
+ ANY_TYPE(SINT8 , s8 );
+ ANY_TYPE(UINT16, u16);
+ ANY_TYPE(SINT16, s16);
+ ANY_TYPE(UINT32, u32);
+ ANY_TYPE(SINT32, s32);
+ ANY_TYPE(UINT64, u64);
+ ANY_TYPE(SINT64, s64);
+ ANY_TYPE(DOUBLE, dbl);
+ default:
+ mrp_log_error("XXX TODO: currently cannot fetch array "
+ "message fields with iterators.");
+ }
+
+ goto next;
+ }
+
+ if (f->type != type)
+ goto out;
+
+ switch (type) {
+ HANDLE_TYPE(STRING, str);
+ HANDLE_TYPE(BOOL , bln);
+ HANDLE_TYPE(UINT8 , u8 );
+ HANDLE_TYPE(SINT8 , s8 );
+ HANDLE_TYPE(UINT16, u16);
+ HANDLE_TYPE(SINT16, s16);
+ HANDLE_TYPE(UINT32, u32);
+ HANDLE_TYPE(SINT32, s32);
+ HANDLE_TYPE(UINT64, u64);
+ HANDLE_TYPE(SINT64, s64);
+ HANDLE_TYPE(DOUBLE, dbl);
+ default:
+ if (type & MRP_MSG_FIELD_ARRAY) {
+ switch (type & ~MRP_MSG_FIELD_ARRAY) {
+ HANDLE_ARRAY(STRING, astr);
+ HANDLE_ARRAY(BOOL , abln);
+ HANDLE_ARRAY(UINT8 , au8 );
+ HANDLE_ARRAY(SINT8 , as8 );
+ HANDLE_ARRAY(UINT16, au16);
+ HANDLE_ARRAY(SINT16, as16);
+ HANDLE_ARRAY(UINT32, au32);
+ HANDLE_ARRAY(SINT32, as32);
+ HANDLE_ARRAY(UINT64, au64);
+ HANDLE_ARRAY(SINT64, as64);
+ HANDLE_ARRAY(DOUBLE, adbl);
+ default:
+ goto out;
+
+ }
+ }
+ else
+ goto out;
+ }
+
+ next:
+ start = p->next;
+ found = TRUE;
+ break;
+ }
+
+ if (!found)
+ break;
+ }
+
+ out:
+ va_end(ap);
+
+ if (found)
+ *it = start;
+
+ return found;
+
+#undef HANDLE_TYPE
+#undef HANDLE_ARRAY
+
+}
+
+
+static const char *field_type_name(uint16_t type)
+{
+#define BASIC(t, n) [MRP_MSG_FIELD_##t] = n
+#define ARRAY(t, n) [MRP_MSG_FIELD_##t] = "array of "n"s"
+ static const char *basic[] = {
+ BASIC(STRING, "string" ),
+ BASIC(BOOL , "boolean"),
+ BASIC(UINT8 , "uint8" ),
+ BASIC(SINT8 , "sint8" ),
+ BASIC(UINT16, "uint16" ),
+ BASIC(SINT16, "sint16" ),
+ BASIC(UINT32, "uint32" ),
+ BASIC(SINT32, "sint32" ),
+ BASIC(UINT64, "uint64" ),
+ BASIC(SINT64, "sint64" ),
+ BASIC(DOUBLE, "double" ),
+ BASIC(BLOB , "blob" )
+ };
+
+ static const char *array[] = {
+ ARRAY(STRING, "string" ),
+ ARRAY(BOOL , "boolean"),
+ ARRAY(UINT8 , "uint8" ),
+ ARRAY(SINT8 , "sint8" ),
+ ARRAY(UINT16, "uint16" ),
+ ARRAY(SINT16, "sint16" ),
+ ARRAY(UINT32, "uint32" ),
+ ARRAY(SINT32, "sint32" ),
+ ARRAY(UINT64, "uint64" ),
+ ARRAY(SINT64, "sint64" ),
+ ARRAY(DOUBLE, "double" ),
+ ARRAY(BLOB , "blob" )
+ };
+#undef BASIC
+#undef ARRAY
+
+ uint16_t base;
+
+ if (MRP_MSG_FIELD_INVALID < type && type <= MRP_MSG_FIELD_MAX)
+ return basic[type];
+ else {
+ if (type & MRP_MSG_FIELD_ARRAY) {
+ base = type & ~MRP_MSG_FIELD_ARRAY;
+
+ if (MRP_MSG_FIELD_INVALID < base && base <= MRP_MSG_FIELD_MAX)
+ return array[base];
+ }
+ }
+
+ return "unknown type";
+}
+
+
+int mrp_msg_dump(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_field_t *f;
+ mrp_list_hook_t *p, *n;
+ int l;
+ uint32_t i;
+ uint16_t base;
+ const char *tname;
+
+ if (msg == NULL)
+ return fprintf(fp, "{\n <no message>\n}\n");
+
+ l = fprintf(fp, "{\n");
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ l += fprintf(fp, " 0x%x ", f->tag);
+
+#define DUMP(_indent, _fmt, _typename, _val) \
+ l += fprintf(fp, "%*.*s= <%s> "_fmt"\n", _indent, _indent, "", \
+ _typename, _val)
+
+ tname = field_type_name(f->type);
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(0, "'%s'", tname, f->str);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(0, "%s", tname, f->bln ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(0, "%u", tname, f->u8);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(0, "%d", tname, f->s8);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(0, "%u", tname, f->u16);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(0, "%d", tname, f->s16);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(0, "%u", tname, f->u32);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(0, "%d", tname, f->s32);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(0, "%Lu", tname, (long long unsigned)f->u64);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(0, "%Ld", tname, (long long signed)f->s64);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(0, "%f", tname, f->dbl);
+ break;
+ case MRP_MSG_FIELD_BLOB: {
+ char *p;
+ uint32_t i;
+
+ fprintf(fp, "= <%s> <%u bytes, ", tname, f->size[0]);
+
+ for (i = 0, p = f->blb; i < f->size[0]; i++, p++) {
+ if (isprint(*p) && *p != '\n' && *p != '\t' && *p != '\r')
+ fprintf(fp, "%c", *p);
+ else
+ fprintf(fp, ".");
+ }
+ fprintf(fp, ">\n");
+ }
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~MRP_MSG_FIELD_ARRAY;
+ tname = field_type_name(base);
+
+ fprintf(fp, "\n");
+ for (i = 0; i < f->size[0]; i++) {
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(8, "'%s'", tname, f->astr[i]);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(8, "%s", tname, f->abln[i] ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(8, "%u", tname, f->au8[i]);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(8, "%d", tname, f->as8[i]);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(8, "%u", tname, f->au16[i]);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(8, "%d", tname, f->as16[i]);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(8, "%u", tname, f->au32[i]);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(8, "%d", tname, f->as32[i]);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(8, "%Lu", tname,
+ (unsigned long long)f->au64[i]);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(8, "%Ld", tname,
+ (long long)f->as64[i]);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(8, "%f", tname, f->adbl[i]);
+ break;
+ default:
+ fprintf(fp, "%*.*s= <%s>\n", 8, 8, "", tname);
+ break;
+ }
+ }
+ }
+ else
+ fprintf(fp, "= <%s>\n", tname);
+ }
+ }
+ l += fprintf(fp, "}\n");
+
+ return l;
+#undef DUMP
+}
+
+
+#define MSG_MIN_CHUNK 32
+
+ssize_t mrp_msg_default_encode(mrp_msg_t *msg, void **bufp)
+{
+ mrp_msg_field_t *f;
+ mrp_list_hook_t *p, *n;
+ mrp_msgbuf_t mb;
+ uint32_t len, asize, i;
+ uint16_t type;
+ size_t size;
+
+ size = msg->nfield * (2 * sizeof(uint16_t) + sizeof(uint64_t));
+
+ if (mrp_msgbuf_write(&mb, size)) {
+ MRP_MSGBUF_PUSH(&mb, htobe16(MRP_MSG_TAG_DEFAULT), 1, nomem);
+ MRP_MSGBUF_PUSH(&mb, htobe16(msg->nfield), 1, nomem);
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->tag) , 1, nomem);
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->type), 1, nomem);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(f->str) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, f->str, len, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->bln ? TRUE : FALSE), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, f->u8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, f->s8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->u16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->s16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->u32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->s32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->u64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->s64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, f->dbl, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ len = f->size[0];
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, f->blb, len, 1, nomem);
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ type = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ MRP_MSGBUF_PUSH(&mb, htobe32(asize), 1, nomem);
+
+ for (i = 0; i < asize; i++) {
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(f->astr[i]) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, f->astr[i], len,
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->abln[i]?TRUE:FALSE),
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, f->au8[i], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, f->as8[i], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->au16[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->as16[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->au32[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->as32[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->au64[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->as64[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, f->adbl[i], 1, nomem);
+ break;
+
+ default:
+ goto invalid_type;
+ }
+ }
+ }
+ else {
+ invalid_type:
+ errno = EINVAL;
+ mrp_msgbuf_cancel(&mb);
+ nomem:
+ *bufp = NULL;
+ return -1;
+ }
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ return mb.p - mb.buf;
+}
+
+
+mrp_msg_t *mrp_msg_default_decode(void *buf, size_t size)
+{
+ mrp_msg_t *msg;
+ mrp_msgbuf_t mb;
+ mrp_msg_value_t v;
+ void *value;
+ uint16_t nfield, tag, type, base;
+ uint32_t len, n, i, j;
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ return NULL;
+
+ mrp_msgbuf_read(&mb, buf, size);
+
+ nfield = be16toh(MRP_MSGBUF_PULL(&mb, typeof(nfield), 1, nodata));
+
+ for (i = 0; i < nfield; i++) {
+ tag = be16toh(MRP_MSGBUF_PULL(&mb, typeof(tag) , 1, nodata));
+ type = be16toh(MRP_MSGBUF_PULL(&mb, typeof(type), 1, nodata));
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ if (len > 0)
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ value = "";
+ if (!mrp_msg_append(msg, tag, type, value))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v.bln = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.bln))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v.u8 = MRP_MSGBUF_PULL(&mb, typeof(v.u8), 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, v.u8))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v.s8 = MRP_MSGBUF_PULL(&mb, typeof(v.s8), 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, v.s8))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v.u16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.u16), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.u16))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v.s16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.s16), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.s16))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v.u32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.u32), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.u32))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v.s32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.s32), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.s32))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v.u64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.u64), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.u64))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v.s64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.s64), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.s64))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v.dbl = MRP_MSGBUF_PULL(&mb, typeof(v.dbl), 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, v.dbl))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, len, value))
+ goto fail;
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ base = type & ~MRP_MSG_FIELD_ARRAY;
+ n = be32toh(MRP_MSGBUF_PULL(&mb, typeof(n), 1, nodata));
+ {
+ char *astr[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len),
+ 1, nodata));
+ if (len > 0)
+ astr[j] = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ astr[j] = "";
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ abln[j] = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1,
+ nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ au8[j] = MRP_MSGBUF_PULL(&mb, typeof(v.u8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ as8[j] = MRP_MSGBUF_PULL(&mb, typeof(v.s8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ au16[j] = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.u16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ as16[j] = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.s16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ au32[j] = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.u32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ as32[j] = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.s32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ au64[j] = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.u64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ as64[j] = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.s64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ adbl[j] = MRP_MSGBUF_PULL(&mb, typeof(v.dbl),
+ 1, nodata);
+ break;
+
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+ }
+
+#define HANDLE_TYPE(_type, _var) \
+ case _type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY |_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ switch (base) {
+ HANDLE_TYPE(MRP_MSG_FIELD_STRING, astr);
+ HANDLE_TYPE(MRP_MSG_FIELD_BOOL , abln);
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT8 , au8 );
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT8 , as8 );
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT16, au16);
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT16, as16);
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT32, au32);
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT32, as32);
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT64, au64);
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT64, as64);
+ HANDLE_TYPE(MRP_MSG_FIELD_DOUBLE, adbl);
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+#undef HANDLE_TYPE
+ }
+ }
+ }
+
+ return msg;
+
+
+ fail:
+ nodata:
+ mrp_msg_unref(msg);
+ return NULL;
+}
+
+
+static int guarded_array_size(void *data, mrp_data_member_t *array)
+{
+#define MAX_ITEMS (32 * 1024)
+ uint16_t base;
+ void *value, *guard;
+ size_t size;
+ int cnt;
+
+ if (array->type & MRP_MSG_FIELD_ARRAY) {
+ base = array->type & ~MRP_MSG_FIELD_ARRAY;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size = sizeof(array->str); break;
+ case MRP_MSG_FIELD_BOOL: size = sizeof(array->bln); break;
+ case MRP_MSG_FIELD_UINT8: size = sizeof(array->u8); break;
+ case MRP_MSG_FIELD_SINT8: size = sizeof(array->s8); break;
+ case MRP_MSG_FIELD_UINT16: size = sizeof(array->u16); break;
+ case MRP_MSG_FIELD_SINT16: size = sizeof(array->s16); break;
+ case MRP_MSG_FIELD_UINT32: size = sizeof(array->u32); break;
+ case MRP_MSG_FIELD_SINT32: size = sizeof(array->s32); break;
+ case MRP_MSG_FIELD_UINT64: size = sizeof(array->u64); break;
+ case MRP_MSG_FIELD_SINT64: size = sizeof(array->s64); break;
+ case MRP_MSG_FIELD_DOUBLE: size = sizeof(array->dbl); break;
+ default: return -1;
+ }
+
+ guard = &array->str;
+ value = *(void **)(data + array->offs);
+ for (cnt = 0; cnt < MAX_ITEMS; cnt++, value += size) {
+ if (!memcmp(value, guard, size))
+ return cnt + 1;
+ }
+ }
+
+ return -1;
+#undef MAX_ITEMS
+}
+
+
+static int counted_array_size(void *data, mrp_data_member_t *cnt)
+{
+ void *val = data + cnt->offs;
+
+ switch (cnt->type) {
+ case MRP_MSG_FIELD_UINT8: return (int)*(uint8_t *)val;
+ case MRP_MSG_FIELD_SINT8: return (int)*( int8_t *)val;
+ case MRP_MSG_FIELD_UINT16: return (int)*(uint16_t *)val;
+ case MRP_MSG_FIELD_SINT16: return (int)*( int16_t *)val;
+ case MRP_MSG_FIELD_UINT32: return (int)*(uint32_t *)val;
+ case MRP_MSG_FIELD_SINT32: return (int)*( int32_t *)val;
+ }
+
+ return -1;
+}
+
+
+static int get_array_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ mrp_data_member_t *arr;
+
+ if (0 < idx && idx < type->nfield) {
+ arr = type->fields + idx;
+
+ if (arr->type & MRP_MSG_FIELD_ARRAY) {
+ if (arr->guard)
+ return guarded_array_size(data, arr);
+ else {
+ if ((int)arr->u32 < type->nfield)
+ return counted_array_size(data, type->fields + arr->u32);
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+int mrp_data_get_array_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ return get_array_size(data, type, idx);
+}
+
+
+static int get_blob_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ mrp_data_member_t *blb, *cnt;
+ void *val;
+
+ if (0 < idx && idx < type->nfield) {
+ blb = type->fields + idx;
+
+ if ((int)blb->u32 < type->nfield) {
+ cnt = type->fields + blb->u32;
+ val = data + cnt->offs;
+
+ switch (cnt->type) {
+ case MRP_MSG_FIELD_UINT8: return (int)*(uint8_t *)val;
+ case MRP_MSG_FIELD_SINT8: return (int)*( int8_t *)val;
+ case MRP_MSG_FIELD_UINT16: return (int)*(uint16_t *)val;
+ case MRP_MSG_FIELD_SINT16: return (int)*( int16_t *)val;
+ case MRP_MSG_FIELD_UINT32: return (int)*(uint32_t *)val;
+ case MRP_MSG_FIELD_SINT32: return (int)*( int32_t *)val;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+int mrp_data_get_blob_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ return get_blob_size(data, type, idx);
+}
+
+
+static int check_and_init_array_descr(mrp_data_descr_t *type, int idx)
+{
+ mrp_data_member_t *array, *cnt, *m;
+ int i;
+
+ array = type->fields + idx;
+
+ if (!array->guard) {
+ cnt = NULL;
+
+ for (i = 0, m = type->fields; i < type->nfield; i++, m++) {
+ if (m->offs == array->u32) {
+ cnt = m;
+ break;
+ }
+ }
+
+ if (cnt == NULL || cnt >= array)
+ return FALSE;
+
+ if (cnt->type < MRP_MSG_FIELD_UINT8 || cnt->type > MRP_MSG_FIELD_SINT32)
+ return FALSE;
+
+ array->u32 = i;
+
+ return TRUE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+int mrp_msg_register_type(mrp_data_descr_t *type)
+{
+ mrp_data_member_t *f;
+ int idx, i;
+
+ if (direct_types == NULL) {
+ direct_types = mrp_allocz_array(typeof(*direct_types), NDIRECT_TYPE);
+
+ if (direct_types == NULL)
+ return FALSE;
+ }
+
+ if (type->tag == MRP_MSG_TAG_DEFAULT) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ mrp_list_init(&type->allocated);
+
+ /* enumerate fields, check arrays, collect extra allocations */
+ for (i = 0, f = type->fields; i < type->nfield; i++, f++) {
+ f->tag = (uint16_t)i + 1;
+
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ if (!check_and_init_array_descr(type, i))
+ return FALSE;
+
+ mrp_list_append(&type->allocated, &f->hook);
+ }
+ else {
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ case MRP_MSG_FIELD_BLOB:
+ mrp_list_append(&type->allocated, &f->hook);
+ }
+ }
+ }
+
+ if (type->tag <= NDIRECT_TYPE) {
+ idx = type->tag - 1;
+
+ if (direct_types[idx] == NULL)
+ direct_types[idx] = type;
+ else
+ return FALSE;
+ }
+ else {
+ if (mrp_reallocz(other_types, nother_type, nother_type + 1) != NULL)
+ other_types[nother_type++] = type;
+ else
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+mrp_data_descr_t *mrp_msg_find_type(uint16_t tag)
+{
+ int i;
+
+ if (MRP_UNLIKELY(tag == MRP_MSG_TAG_DEFAULT))
+ return NULL;
+
+ if (tag <= NDIRECT_TYPE)
+ return direct_types[tag - 1];
+ else {
+ for (i = 0; i < nother_type; i++) {
+ if (other_types[i] != NULL && other_types[i]->tag == tag)
+ return other_types[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+static __attribute__((destructor)) void cleanup_types(void)
+{
+ mrp_free(direct_types);
+ mrp_free(other_types);
+ nother_type = 0;
+}
+
+
+size_t mrp_data_encode(void **bufp, void *data, mrp_data_descr_t *descr,
+ size_t reserve)
+{
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type;
+ mrp_msgbuf_t mb;
+ mrp_msg_value_t *v;
+ uint32_t len, asize, blblen, j;
+ int i, cnt;
+ size_t size;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+ size = reserve + nfield * (2 * sizeof(uint16_t) + sizeof(uint64_t));
+
+ if (mrp_msgbuf_write(&mb, size)) {
+ if (reserve)
+ mrp_msgbuf_reserve(&mb, reserve, 1);
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->tag) , 1, nomem);
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(v->str) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->str, len, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->bln ? TRUE : FALSE), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, v->u8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, v->s8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->u16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->s16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->u32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->s32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->u64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->s64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, v->dbl, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ blblen = (uint32_t)get_blob_size(data, descr, i);
+
+ if (blblen == (uint32_t)-1)
+ goto invalid_type;
+
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->u32), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->blb, blblen, 1, nomem);
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ type = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ cnt = get_array_size(data, descr, i);
+
+ if (cnt < 0)
+ goto invalid_type;
+
+ asize = (uint32_t)cnt;
+ MRP_MSGBUF_PUSH(&mb, htobe32(asize), 1, nomem);
+
+ for (j = 0; j < asize; j++) {
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(v->astr[j]) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->astr[j], len,
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->abln[j]?TRUE:FALSE),
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, v->au8[j], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, v->as8[j], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->au16[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->as16[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->au32[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->as32[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->au64[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->as64[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, v->adbl[j], 1, nomem);
+ break;
+
+ default:
+ goto invalid_type;
+ }
+ }
+ }
+ else {
+ invalid_type:
+ errno = EINVAL;
+ mrp_msgbuf_cancel(&mb);
+ nomem:
+ *bufp = NULL;
+ return 0;
+ }
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ return (size_t)(mb.p - mb.buf);
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+void *mrp_data_decode(void **bufp, size_t *sizep, mrp_data_descr_t *descr)
+{
+ void *data;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ mrp_msgbuf_t mb;
+ uint16_t tag, base;
+ mrp_msg_value_t *v;
+ void *value;
+ uint32_t len, n, j, size;
+ int i;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ return NULL;
+
+ mrp_msgbuf_read(&mb, *bufp, *sizep);
+
+ for (i = 0; i < nfield; i++) {
+ tag = be16toh(MRP_MSGBUF_PULL(&mb, typeof(tag) , 1, nodata));
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto unknown_field;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ if (len > 0)
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ value = "";
+ v->str = mrp_strdup((char *)value);
+ if (v->str == NULL)
+ goto nomem;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v->bln = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v->u8 = MRP_MSGBUF_PULL(&mb, typeof(v->u8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v->s8 = MRP_MSGBUF_PULL(&mb, typeof(v->s8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v->u16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->u16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v->s16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->s16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v->u32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->u32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v->s32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->s32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v->u64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->u64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v->s64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->s64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v->dbl = MRP_MSGBUF_PULL(&mb, typeof(v->dbl), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ v->blb = mrp_datadup(value, len);
+ if (v->blb == NULL)
+ goto nomem;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY)) {
+ unknown_field:
+ errno = EINVAL;
+ goto fail;
+ }
+
+ base = f->type & ~MRP_MSG_FIELD_ARRAY;
+ n = be32toh(MRP_MSGBUF_PULL(&mb, typeof(n), 1, nodata));
+
+ if (!f->guard && get_array_size(data, descr, i) != (int)n) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto nomem;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len),
+ 1, nodata));
+ if (len > 0)
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ value = "";
+
+ v->astr[j] = mrp_strdup(value);
+ if (v->astr[j] == NULL)
+ goto nomem;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v->abln[j] = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t,
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v->au8[j] = MRP_MSGBUF_PULL(&mb, typeof(v->u8),
+ 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v->as8[j] = MRP_MSGBUF_PULL(&mb, typeof(v->s8),
+ 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v->au16[j] = be16toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->u16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v->as16[j] = be16toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->s16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v->au32[j] = be32toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->u32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v->as32[j] = be32toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->s32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v->au64[j] = be64toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->u64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v->as64[j] = be64toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->s64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v->adbl[j] = MRP_MSGBUF_PULL(&mb, typeof(v->dbl),
+ 1, nodata);
+ break;
+
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ *sizep -= mb.p - mb.buf;
+ return data;
+
+ nodata:
+ nomem:
+ fail:
+ if (data != NULL) {
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ case MRP_MSG_FIELD_BLOB:
+ mrp_free(*(void **)(data + f->offs));
+ }
+ }
+
+ mrp_free(data);
+ }
+
+ return NULL;
+}
+
+
+int mrp_data_dump(void *data, mrp_data_descr_t *descr, FILE *fp)
+{
+#define DUMP(_indent, _fmt, _typename, _val) \
+ l += fprintf(fp, "%*.*s= <%s> "_fmt"\n", _indent, _indent, "", \
+ _typename, _val)
+
+ mrp_data_member_t *dm;
+ mrp_msg_value_t *v;
+ uint16_t base;
+ int i, j, l, cnt;
+ const char *tname;
+
+
+ l = fprintf(fp, "{\n");
+ for (i = 0, dm = descr->fields; i < descr->nfield; i++, dm++) {
+ l += fprintf(fp, " @%d ", dm->offs);
+ v = (mrp_msg_value_t *)(data + dm->offs);
+ tname = field_type_name(dm->type);
+
+ switch (dm->type) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(0, "'%s'", tname, v->str);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(0, "%s", tname, v->bln ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(0, "%u", tname, v->u8);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(0, "%d", tname, v->s8);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(0, "%u", tname, v->u16);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(0, "%d", tname, v->s16);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(0, "%u", tname, v->u32);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(0, "%d", tname, v->s32);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(0, "%Lu", tname, (long long unsigned)v->u64);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(0, "%Ld", tname, (long long signed)v->s64);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(0, "%f", tname, v->dbl);
+ break;
+ default:
+ if (dm->type & MRP_MSG_FIELD_ARRAY) {
+ base = dm->type & ~MRP_MSG_FIELD_ARRAY;
+ cnt = get_array_size(data, descr, i);
+
+ if (cnt < 0) {
+ fprintf(fp, "= <%s> ???\n", tname);
+ continue;
+ }
+
+ fprintf(fp, "= <%s> (%d)\n", tname, cnt);
+ tname = field_type_name(base);
+
+ for (j = 0; j < cnt; j++) {
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(8, "'%s'", tname, v->astr[j]);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(8, "%s", tname, v->abln[j] ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(8, "%u", tname, v->au8[j]);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(8, "%d", tname, v->as8[j]);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(8, "%u", tname, v->au16[j]);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(8, "%d", tname, v->as16[j]);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(8, "%u", tname, v->au32[j]);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(8, "%d", tname, v->as32[j]);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(8, "%Lu", tname, (long long unsigned)v->au64[j]);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(8, "%Ld", tname, (long long signed)v->as64[j]);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(8, "%f", tname, v->adbl[j]);
+ break;
+ default:
+ fprintf(fp, "%*.*s<%s>\n", 8, 8, "", tname);
+ break;
+ }
+ }
+ }
+ }
+ }
+ l += fprintf(fp, "}\n");
+
+ return l;
+}
+
+
+int mrp_data_free(void *data, uint16_t tag)
+{
+ mrp_data_descr_t *type;
+ mrp_list_hook_t *p, *n;
+ mrp_data_member_t *f;
+ void *ptr;
+ int i, idx, cnt;
+
+ if (data == NULL)
+ return TRUE;
+
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ mrp_list_foreach(&type->allocated, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+ ptr = *(void **)(data + f->offs);
+
+ if (f->type == (MRP_MSG_FIELD_ARRAY | MRP_MSG_FIELD_STRING)) {
+ idx = f - type->fields;
+ cnt = get_array_size(data, type, idx);
+
+ for (i = 0; i < cnt; i++)
+ mrp_free(((char **)ptr)[i]);
+ }
+
+ mrp_free(ptr);
+ }
+
+ mrp_free(data);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void *mrp_msgbuf_write(mrp_msgbuf_t *mb, size_t size)
+{
+ mrp_clear(mb);
+
+ mb->buf = mrp_allocz(size);
+
+ if (mb->buf != NULL) {
+ mb->size = size;
+ mb->p = mb->buf;
+ mb->l = size;
+
+ return mb->p;
+ }
+ else
+ return NULL;
+}
+
+
+void mrp_msgbuf_read(mrp_msgbuf_t *mb, void *buf, size_t size)
+{
+ mb->buf = mb->p = buf;
+ mb->size = mb->l = size;
+}
+
+
+void mrp_msgbuf_cancel(mrp_msgbuf_t *mb)
+{
+ mrp_free(mb->buf);
+ mb->buf = mb->p = NULL;
+}
+
+
+void *mrp_msgbuf_ensure(mrp_msgbuf_t *mb, size_t size)
+{
+ int diff;
+
+ if (MRP_UNLIKELY(size > mb->l)) {
+ diff = size - mb->l;
+
+ if (diff < MSG_MIN_CHUNK)
+ diff = MSG_MIN_CHUNK;
+
+ mb->p -= (ptrdiff_t)mb->buf;
+
+ if (mrp_realloc(mb->buf, mb->size + diff)) {
+ memset(mb->buf + mb->size, 0, diff);
+ mb->size += diff;
+ mb->p += (ptrdiff_t)mb->buf;
+ mb->l += diff;
+ }
+ else
+ mrp_msgbuf_cancel(mb);
+ }
+
+ return mb->p;
+}
+
+
+void *mrp_msgbuf_reserve(mrp_msgbuf_t *mb, size_t size, size_t align)
+{
+ void *reserved;
+ ptrdiff_t offs, pad;
+ size_t len;
+
+ len = size;
+ offs = mb->p - mb->buf;
+
+ if (offs % align != 0) {
+ pad = align - (offs % align);
+ len += pad;
+ }
+ else
+ pad = 0;
+
+ if (mrp_msgbuf_ensure(mb, len)) {
+ if (pad != 0)
+ memset(mb->p, 0, pad);
+
+ reserved = mb->p + pad;
+
+ mb->p += len;
+ mb->l -= len;
+ }
+ else
+ reserved = NULL;
+
+ return reserved;
+}
+
+
+void *mrp_msgbuf_pull(mrp_msgbuf_t *mb, size_t size, size_t align)
+{
+ void *pulled;
+ ptrdiff_t offs, pad;
+ size_t len;
+
+ len = size;
+ offs = mb->p - mb->buf;
+
+ if (offs % align != 0) {
+ pad = align - (offs % align);
+ len += pad;
+ }
+ else
+ pad = 0;
+
+ if (mb->l >= len) {
+ pulled = mb->p + pad;
+
+ mb->p += len;
+ mb->l -= len;
+ }
+ else
+ pulled = NULL;
+
+ return pulled;
+}
diff --git a/src/common/msg.h b/src/common/msg.h
new file mode 100644
index 0000000..e61805b
--- /dev/null
+++ b/src/common/msg.h
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MSG_H__
+#define __MURPHY_MSG_H__
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include <murphy/common/list.h>
+#include <murphy/common/refcnt.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * message field types
+ */
+
+#define A(t) MRP_MSG_FIELD_##t
+typedef enum {
+ MRP_MSG_FIELD_INVALID = 0x00, /* defined invalid type */
+ MRP_MSG_FIELD_STRING = 0x01, /* mqi_varchar */
+ MRP_MSG_FIELD_INTEGER = 0x02, /* mqi_integer */
+ MRP_MSG_FIELD_UNSIGNED = 0x03, /* mqi_unsignd */
+ MRP_MSG_FIELD_DOUBLE = 0x04, /* mqi_floating */
+ MRP_MSG_FIELD_BOOL = 0x05, /* boolean */
+ MRP_MSG_FIELD_UINT8 = 0x06, /* unsigned 8-bit integer */
+ MRP_MSG_FIELD_SINT8 = 0x07, /* signed 8-bit integer */
+ MRP_MSG_FIELD_INT8 = A(SINT8), /* alias for SINT8 */
+ MRP_MSG_FIELD_UINT16 = 0x08, /* unsigned 16-bit integer */
+ MRP_MSG_FIELD_SINT16 = 0x09, /* signed 16-bit integer */
+ MRP_MSG_FIELD_INT16 = A(SINT16), /* alias for SINT16 */
+ MRP_MSG_FIELD_UINT32 = 0x0a, /* unsigned 32-bit integer */
+ MRP_MSG_FIELD_SINT32 = 0x0b, /* signed 32-bit integer */
+ MRP_MSG_FIELD_INT32 = A(SINT32), /* alias for SINT32 */
+ MRP_MSG_FIELD_UINT64 = 0x0c, /* unsigned 64-bit integer */
+ MRP_MSG_FIELD_SINT64 = 0x0d, /* signed 64-bit integer */
+ MRP_MSG_FIELD_INT64 = A(SINT64), /* alias for SINT64 */
+ MRP_MSG_FIELD_BLOB = 0x0e, /* a blob (not allowed in arrays) */
+ MRP_MSG_FIELD_MAX = 0x0e,
+ MRP_MSG_FIELD_ANY = 0x0f, /* any type of field when querying */
+
+ MRP_MSG_FIELD_ARRAY = 0x80, /* bit-mask to mark arrays */
+} mrp_msg_field_type_t;
+#undef A
+
+#define MRP_MSG_END ((char *)MRP_MSG_FIELD_INVALID) /* NULL */
+
+#define MRP_MSG_FIELD_ARRAY_OF(t) (MRP_MSG_FIELD_ARRAY | MRP_MSG_FIELD_##t)
+#define MRP_MSG_FIELD_IS_ARRAY(t) ((t) & MRP_MSG_FIELD_ARRAY)
+#define MRP_MSG_FIELD_ARRAY_TYPE(t) ((t) & ~MRP_MSG_FIELD_ARRAY)
+
+#define MRP_MSG_TAG_STRING(tag, arg) (tag), MRP_MSG_FIELD_STRING, (arg)
+#define MRP_MSG_TAG_BOOL(tag, arg) (tag), MRP_MSG_FIELD_BOOL , (arg)
+#define MRP_MSG_TAG_UINT8(tag, arg) (tag), MRP_MSG_FIELD_UINT8 , (arg)
+#define MRP_MSG_TAG_SINT8(tag, arg) (tag), MRP_MSG_FIELD_SINT8 , (arg)
+#define MRP_MSG_TAG_UINT16(tag, arg) (tag), MRP_MSG_FIELD_UINT16, (arg)
+#define MRP_MSG_TAG_SINT16(tag, arg) (tag), MRP_MSG_FIELD_SINT16, (arg)
+#define MRP_MSG_TAG_UINT32(tag, arg) (tag), MRP_MSG_FIELD_UINT32, (arg)
+#define MRP_MSG_TAG_SINT32(tag, arg) (tag), MRP_MSG_FIELD_SINT32, (arg)
+#define MRP_MSG_TAG_UINT64(tag, arg) (tag), MRP_MSG_FIELD_UINT64, (arg)
+#define MRP_MSG_TAG_SINT64(tag, arg) (tag), MRP_MSG_FIELD_SINT64, (arg)
+#define MRP_MSG_TAG_DOUBLE(tag, arg) (tag), MRP_MSG_FIELD_DOUBLE, (arg)
+#define MRP_MSG_TAG_BLOB(tag, arg) (tag), MRP_MSG_FIELD_BLOB , (arg)
+
+#define MRP_MSG_TAGGED(tag, type, ...) (tag), (type), __VA_ARGS__
+#define MRP_MSG_TAG_ARRAY(tag, type, cnt, arr) \
+ (tag), MRP_MSG_FIELD_ARRAY | MRP_MSG_FIELD_##type, (cnt), (arr)
+#define MRP_MSG_TAG_STRING_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), STRING, (cnt), (arr))
+#define MRP_MSG_TAG_BOOL_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), BOOL, (cnt), (arr))
+#define MRP_MSG_TAG_UINT8_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT8, (cnt), (arr))
+#define MRP_MSG_TAG_SINT8_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT8, (cnt), (arr))
+#define MRP_MSG_TAG_UINT16_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT16, (cnt), (arr))
+#define MRP_MSG_TAG_SINT16_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT16, (cnt), (arr))
+#define MRP_MSG_TAG_UINT32_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT32, (cnt), (arr))
+#define MRP_MSG_TAG_SINT32_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT32, (cnt), (arr))
+#define MRP_MSG_TAG_UINT64_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT64, (cnt), (arr))
+#define MRP_MSG_TAG_SINT64_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT64, (cnt), (arr))
+#define MRP_MSG_TAG_DOUBLE_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), DOUBLE, (cnt), (arr))
+#define MRP_MSG_TAG_BLOB_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), BLOB, (cnt), (arr))
+
+#define MRP_MSG_TAG_ANY(tag, typep, valuep) \
+ (tag), MRP_MSG_FIELD_ANY, (typep), (valuep)
+
+
+/** Sentinel to pass in as the last argument to mrp_msg_create. */
+#define MRP_MSG_FIELD_END NULL
+
+
+/*
+ * generic messages
+ *
+ * A generic message is just a collection of message fields. By default
+ * transports are in generic messaging mode in which case they take messages
+ * as input (for transmission) and provide messages as events (for receiption).
+ * A generic message field consists of a field tag, a field type, the actual
+ * type-specific field value, and for certain types a size.
+ *
+ * The field tag is used by the communicating parties to attach semantic
+ * meaning to the field data. One can think of it as the 'name' of the field
+ * within a message. It is not interpreted by the messaging layer in any way.
+ * The field type defines what kind of data the field contains contains and
+ * it must be one of the predefined MRP_MSG_FIELD_* types. The actual field
+ * data then depends on the type. size is only used for those data types that
+ * require a size (blobs and arrays).
+ */
+
+#define MRP_MSG_VALUE_UNION union { \
+ char *str; \
+ bool bln; \
+ uint8_t u8; \
+ int8_t s8; \
+ uint16_t u16; \
+ int16_t s16; \
+ uint32_t u32; \
+ int32_t s32; \
+ uint64_t u64; \
+ int64_t s64; \
+ double dbl; \
+ void *blb; \
+ void *aany; \
+ char **astr; \
+ bool *abln; \
+ uint8_t *au8; \
+ int8_t *as8; \
+ uint16_t *au16; \
+ int16_t *as16; \
+ uint32_t *au32; \
+ int32_t *as32; \
+ uint64_t *au64; \
+ int64_t *as64; \
+ double *adbl; \
+ }
+
+typedef MRP_MSG_VALUE_UNION mrp_msg_value_t;
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to list of fields */
+ uint16_t tag; /* message field tag */
+ uint16_t type; /* message field type */
+ MRP_MSG_VALUE_UNION; /* message field value */
+ uint32_t size[0]; /* size, if an array or a blob */
+} mrp_msg_field_t;
+
+
+typedef struct {
+ mrp_list_hook_t fields; /* list of message fields */
+ size_t nfield; /* number of fields */
+ mrp_refcnt_t refcnt; /* reference count */
+} mrp_msg_t;
+
+
+/** Create a new message. */
+mrp_msg_t *mrp_msg_create(uint16_t tag, ...) MRP_NULLTERM;
+
+/** Create a new message. */
+mrp_msg_t *mrp_msg_createv(uint16_t tag, va_list ap);
+
+/** Macro to create an empty message. */
+#define mrp_msg_create_empty() mrp_msg_create(MRP_MSG_FIELD_INVALID, NULL)
+
+/** Increase refcount of the given message. */
+mrp_msg_t *mrp_msg_ref(mrp_msg_t *msg);
+
+/** Decrease the refcount, free the message if refcount drops to zero. */
+void mrp_msg_unref(mrp_msg_t *msg);
+
+/** Append a field to a message. */
+int mrp_msg_append(mrp_msg_t *msg, uint16_t tag, ...);
+
+/** Prepend a field to a message. */
+int mrp_msg_prepend(mrp_msg_t *msg, uint16_t tag, ...);
+
+/** Set a field in a message to the given value. */
+int mrp_msg_set(mrp_msg_t *msg, uint16_t tag, ...);
+
+/** Iterate through the fields of a message. You must not any of the
+ fields while iterating. */
+int mrp_msg_iterate(mrp_msg_t *msg, void **it, uint16_t *tagp,
+ uint16_t *typep, mrp_msg_value_t *valp, size_t *sizep);
+
+/** Iterate through the matching fields of a message. You should not delete
+ * any of the fields while iterating through the message. */
+int mrp_msg_iterate_matching(mrp_msg_t *msg, void **it, uint16_t *tagp,
+ uint16_t *typep, mrp_msg_value_t *valp,
+ size_t *sizep);
+
+/** Find a field in a message. */
+mrp_msg_field_t *mrp_msg_find(mrp_msg_t *msg, uint16_t tag);
+
+/** Get the given fields (with matching tags and types) from the message. */
+int mrp_msg_get(mrp_msg_t *msg, ...) MRP_NULLTERM;
+
+/** Iterate through the message getting the given fields. */
+int mrp_msg_iterate_get(mrp_msg_t *msg, void **it, ...);
+
+/** Dump a message. */
+int mrp_msg_dump(mrp_msg_t *msg, FILE *fp);
+
+/** Encode the given message using the default message encoder. */
+ssize_t mrp_msg_default_encode(mrp_msg_t *msg, void **bufp);
+
+/** Decode the given message using the default message decoder. */
+mrp_msg_t *mrp_msg_default_decode(void *buf, size_t size);
+
+
+/*
+ * custom data types
+ *
+ * In addition to generic messages, you can instruct the messaging and
+ * transport layers to encode/decode messages directly from/to custom data
+ * structures. To do so you need to describe your data structures and register
+ * them using data descriptors. A descriptor basically consists of a type
+ * tag, structure size, number of members and and array of structure member
+ * descriptors.
+ *
+ * The data type tag is used to identify the descriptor and consequently
+ * the custom data type both during sending and receiving (ie. encoding and
+ * decoding). It is assigned by the registering entity, it must be unique,
+ * and it cannot be MRP_MSG_TAG_DEFAULT (0x0), or else registration will
+ * fail. The size is used to allocate necessary memory for the data on the
+ * receiving end. The member descriptors are used to describe the offset
+ * and types of the members within the custom data type.
+ */
+
+#define MRP_MSG_TAG_DEFAULT 0x0 /* tag for default encode/decoder */
+
+typedef struct {
+ uint16_t offs; /* offset within structure */
+ uint16_t tag; /* tag for this member */
+ uint16_t type; /* type of this member */
+ bool guard; /* whether sentinel-terminated */
+ MRP_MSG_VALUE_UNION; /* sentinel or offset of count field */
+ mrp_list_hook_t hook; /* hook to list of extra allocations */
+} mrp_data_member_t;
+
+
+typedef struct {
+ mrp_refcnt_t refcnt; /* reference count */
+ uint16_t tag; /* structure tag */
+ size_t size; /* size of this structure */
+ int nfield; /* number of members */
+ mrp_data_member_t *fields; /* member descriptors */
+ mrp_list_hook_t allocated; /* fields needing extra allocation */
+} mrp_data_descr_t;
+
+
+/** Convenience macro to declare a custom data type (and its members). */
+#define MRP_DATA_DESCRIPTOR(_var, _tag, _type, ...) \
+ static mrp_data_member_t _var##_members[] = { \
+ __VA_ARGS__ \
+ }; \
+ \
+ static mrp_data_descr_t _var = { \
+ .size = sizeof(_type), \
+ .tag = _tag, \
+ .fields = _var##_members, \
+ .nfield = MRP_ARRAY_SIZE(_var##_members) \
+ }
+
+/** Convenience macro to declare a data member. */
+#define MRP_DATA_MEMBER(_data_type, _member, _member_type) { \
+ .offs = MRP_OFFSET(_data_type, _member), \
+ .type = _member_type, \
+ .guard = FALSE \
+ }
+
+/** Convenience macro to declare an array data member with a count field. */
+#define MRP_DATA_ARRAY_COUNT(_data_type, _array, _count, _base_type) { \
+ .offs = MRP_OFFSET(_data_type, _array), \
+ .type = MRP_MSG_FIELD_ARRAY | _base_type, \
+ .guard = FALSE, \
+ { .u32 = MRP_OFFSET(_data_type, _count) } \
+ }
+
+/** Convenience macro to declare an array data member with a sentinel value. */
+#define MRP_DATA_ARRAY_GUARD(_data_type, _array, _guard_member, _guard_val, \
+ _base_type) { \
+ .offs = MRP_OFFSET(_data_type, _array), \
+ .type = MRP_MSG_FIELD_ARRAY | _base_type, \
+ .guard = TRUE, \
+ { ._guard_member = _guard_val } \
+ }
+
+/** Convenience macro to declare a blob data member with a count field. */
+#define MRP_DATA_BLOB_MEMBER(_data_type, _blob, _count) { \
+ .offs = MRP_OFFSET(_data_type, _blob), \
+ .type = MRP_MSG_FIELD_BLOB, \
+ .guard = FALSE, \
+ .u32 = MRP_OFFSET(_data_type, _count) \
+ }
+
+
+/** Encode a structure using the given message descriptor. */
+size_t mrp_data_encode(void **bufp, void *data, mrp_data_descr_t *descr,
+ size_t reserve);
+
+/** Decode a structure using the given message descriptor. */
+void *mrp_data_decode(void **bufp, size_t *sizep, mrp_data_descr_t *descr);
+
+/** Dump the given data buffer. */
+int mrp_data_dump(void *data, mrp_data_descr_t *descr, FILE *fp);
+
+/** Get the size of a data array member. */
+int mrp_data_get_array_size(void *data, mrp_data_descr_t *type, int idx);
+
+/** Get the size of a data blob member. */
+int mrp_data_get_blob_size(void *data, mrp_data_descr_t *type, int idx);
+
+/** Register a new custom data type with the messaging/transport layer. */
+int mrp_msg_register_type(mrp_data_descr_t *type);
+
+/** Look up the data type descriptor corresponding to the given tag. */
+mrp_data_descr_t *mrp_msg_find_type(uint16_t tag);
+
+/** Free the given custom data allocated by the messaging layer. */
+int mrp_data_free(void *data, uint16_t tag);
+
+/*
+ * message encoding/decoding buffer
+ *
+ * This message buffer and the associated functions and macros can be
+ * used to write message encoding/decoding functions for bitpipe-type
+ * transports, ie. for transports where the underlying IPC just provides
+ * a raw data connection between the communication endpoints and does not
+ * impose/expect any structure on/from the data being transmitted.
+ *
+ * Practically all the basic stream and datagram socket transports are
+ * such. They use the default encoding/decoding functions provided by
+ * the messaging layer together with a very simple transport frame scheme,
+ * where each frame consists of the amount a size indicating the size of
+ * the encoded message in the bitpipe and the actual encoded message data.
+ *
+ * Note that at the moment this framing scheme is rather implicit in the
+ * sense that you won't find a data type representing a frame. Rather the
+ * framing is simply done in the sending/receiving code of the individual
+ * transports.
+ */
+
+typedef struct {
+ void *buf; /* buffer to encode to/decode from */
+ size_t size; /* size of the buffer */
+ void *p; /* encoding/decoding pointer */
+ size_t l; /* space left in the buffer */
+} mrp_msgbuf_t;
+
+
+
+/** Initialize the given message buffer for writing. */
+void *mrp_msgbuf_write(mrp_msgbuf_t *mb, size_t size);
+
+/** Initialize the given message buffer for reading. */
+void mrp_msgbuf_read(mrp_msgbuf_t *mb, void *buf, size_t size);
+
+/** Deinitialize the given message buffer, usually due to some error. */
+void mrp_msgbuf_cancel(mrp_msgbuf_t *mb);
+
+/** Reallocate the buffer if needed to accomodate size bytes of data. */
+void *mrp_msgbuf_ensure(mrp_msgbuf_t *mb, size_t size);
+
+/** Reserve the given amount of space from the buffer. */
+void *mrp_msgbuf_reserve(mrp_msgbuf_t *mb, size_t size, size_t align);
+
+/** Pull the given amount of data from the buffer. */
+void *mrp_msgbuf_pull(mrp_msgbuf_t *mb, size_t size, size_t align);
+
+/** Push data with alignment to the buffer, jumping to errlbl on errors. */
+#define MRP_MSGBUF_PUSH(mb, data, align, errlbl) do { \
+ size_t _size = sizeof(data); \
+ typeof(data) *_ptr; \
+ \
+ _ptr = mrp_msgbuf_reserve((mb), _size, (align)); \
+ \
+ if (_ptr != NULL) \
+ *_ptr = data; \
+ else \
+ goto errlbl; \
+ } while (0)
+
+/** Push aligned data to the buffer, jumping to errlbl on errors. */
+#define MRP_MSGBUF_PUSH_DATA(mb, data, size, align, errlbl) do { \
+ size_t _size = (size); \
+ void *_ptr; \
+ \
+ _ptr = mrp_msgbuf_reserve((mb), _size, (align)); \
+ \
+ if (_ptr != NULL) \
+ memcpy(_ptr, data, _size); \
+ else \
+ goto errlbl; \
+ } while (0)
+
+/** Pull aligned data of type from the buffer, jump to errlbl on errors. */
+#define MRP_MSGBUF_PULL(mb, type, align, errlbl) ({ \
+ size_t _size = sizeof(type); \
+ type *_ptr; \
+ \
+ _ptr = mrp_msgbuf_pull((mb), _size, (align)); \
+ \
+ if (_ptr == NULL) \
+ goto errlbl; \
+ \
+ *_ptr; \
+ })
+
+/** Pull aligned data of type from the buffer, jump to errlbl on errors. */
+#define MRP_MSGBUF_PULL_DATA(mb, size, align, errlbl) ({ \
+ size_t _size = size; \
+ void *_ptr; \
+ \
+ _ptr = mrp_msgbuf_pull((mb), _size, (align)); \
+ \
+ if (_ptr == NULL) \
+ goto errlbl; \
+ \
+ _ptr; \
+ })
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MSG_H__ */
diff --git a/src/common/murphy-common.pc.in b/src/common/murphy-common.pc.in
new file mode 100644
index 0000000..0b75762
--- /dev/null
+++ b/src/common/murphy-common.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-common
+Description: Murphy policy framework, common library.
+Requires:
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-common @JSON_LIBS@
+Cflags: -I${includedir} @JSON_CFLAGS@
diff --git a/src/common/murphy-dbus-libdbus.pc.in b/src/common/murphy-dbus-libdbus.pc.in
new file mode 100644
index 0000000..d6efb6f
--- /dev/null
+++ b/src/common/murphy-dbus-libdbus.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-dbus
+Description: Murphy policy framework, libdbus based dbus library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-dbus-libdbus @LIBDBUS_LIBS@
+Cflags: -I${includedir} @LIBDBUS_CFLAGS@
diff --git a/src/common/murphy-dbus-sdbus.pc.in b/src/common/murphy-dbus-sdbus.pc.in
new file mode 100644
index 0000000..94008ee
--- /dev/null
+++ b/src/common/murphy-dbus-sdbus.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-sd-bus
+Description: Murphy policy framework, systemd-bus based dbus library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-sd-bus @SDBUS_LIBS@
+Cflags: -I${includedir} @SDBUS_CFLAGS@
diff --git a/src/common/murphy-ecore.pc.in b/src/common/murphy-ecore.pc.in
new file mode 100644
index 0000000..626a07b
--- /dev/null
+++ b/src/common/murphy-ecore.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-ecore
+Description: Murphy policy framework, EFL/ecore mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-ecore @ECORE_LIBS@
+Cflags: -I${includedir} @ECORE_CFLAGS@
diff --git a/src/common/murphy-glib.pc.in b/src/common/murphy-glib.pc.in
new file mode 100644
index 0000000..5193219
--- /dev/null
+++ b/src/common/murphy-glib.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-glib
+Description: Murphy policy framework, GLIB mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-glib @GLIB_LIBS@
+Cflags: -I${includedir} @GLIB_CFLAGS@
diff --git a/src/common/murphy-libdbus.pc.in b/src/common/murphy-libdbus.pc.in
new file mode 100644
index 0000000..bcda7b4
--- /dev/null
+++ b/src/common/murphy-libdbus.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-dbus
+Description: Murphy policy framework, dbus library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-libdbus @LIBDBUS_LIBS@
+Cflags: -I${includedir} @LIBDBUS_CFLAGS@
diff --git a/src/common/murphy-pulse.pc.in b/src/common/murphy-pulse.pc.in
new file mode 100644
index 0000000..2655307
--- /dev/null
+++ b/src/common/murphy-pulse.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-pulse
+Description: Murphy policy framework, PulseAudio mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-pulse
+Cflags: -I${includedir} @PULSE_CFLAGS@
diff --git a/src/common/murphy-qt.pc.in b/src/common/murphy-qt.pc.in
new file mode 100644
index 0000000..95c6aba
--- /dev/null
+++ b/src/common/murphy-qt.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-qt
+Description: Murphy policy framework, Qt mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-qt @QTCORE_LIBS@
+Cflags: -I${includedir} @QTCORE_CFLAGS@
diff --git a/src/common/native-types.c b/src/common/native-types.c
new file mode 100644
index 0000000..d9890e3
--- /dev/null
+++ b/src/common/native-types.c
@@ -0,0 +1,1626 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/tlv.h>
+#include <murphy/common/native-types.h>
+
+
+/*
+ * TLV tags we use when encoding/decoding our native types
+ */
+
+typedef enum {
+ TAG_NONE = MRP_TLV_UNTAGGED, /* untagged data */
+ TAG_STRUCT, /* a native structure */
+ TAG_MEMBER, /* a native structure member */
+ TAG_ARRAY, /* an array */
+ TAG_NELEM, /* size of an array (in elements) */
+} tag_t;
+
+
+/*
+ * extra header we use to keep track of memory while decoding
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to chunk list */
+ char data[0]; /* user-visible data */
+} chunk_t;
+
+
+static int encode_struct(mrp_tlv_t *tlv, void *data, mrp_native_type_t *t,
+ mrp_typemap_t *idmap);
+static int decode_struct(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ void **datap, uint32_t *idp, mrp_typemap_t *idmap);
+static int print_struct(char **buf, size_t *size, int level,
+ void *data, mrp_native_type_t *t);
+static void free_native(mrp_native_type_t *t);
+
+static void *alloc_chunk(mrp_list_hook_t **chunks, size_t size);
+static void free_chunks(mrp_list_hook_t *chunks);
+
+
+/*
+ * list and table of registered native types
+ */
+
+static MRP_LIST_HOOK(types);
+static int ntype;
+
+static mrp_native_type_t **typetbl;
+
+
+static mrp_native_member_t *native_member(mrp_native_type_t *t, int idx)
+{
+ if (0 <= idx && idx < (int)t->nmember)
+ return t->members + idx;
+ else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+
+static int member_index(mrp_native_type_t *t, const char *name)
+{
+ mrp_native_member_t *m;
+ size_t i;
+
+ for (i = 0, m = t->members; i < t->nmember; i++, m++)
+ if (!strcmp(m->any.name, name))
+ return m - t->members;
+
+ return -1;
+}
+
+
+static int copy_member(mrp_native_type_t *t, mrp_native_member_t *m)
+{
+ mrp_native_member_t *tm;
+ size_t size;
+
+ if ((tm = native_member(t, member_index(t, m->any.name))) != NULL)
+ return tm - t->members;
+ else
+ tm = t->members + t->nmember;
+
+ *tm = *m;
+
+ if (*m->any.name != '"')
+ tm->any.name = mrp_strdup(m->any.name);
+ else {
+ size = strlen(m->any.name) + 1 - 2;
+
+ if ((tm->any.name = mrp_allocz(size)) != NULL)
+ strncpy(tm->any.name, m->any.name + 1, size - 1);
+ }
+
+ if (tm->any.name != NULL) {
+ t->nmember++;
+ return tm - t->members;
+ }
+ else
+ return -1;
+}
+
+
+static mrp_native_type_t *find_type(const char *type_name)
+{
+ mrp_native_type_t *t;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&types, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (!strcmp(t->name, type_name))
+ return t;
+ }
+
+ return NULL;
+}
+
+
+static mrp_native_type_t *lookup_type(uint32_t id)
+{
+ mrp_native_type_t *t;
+ mrp_list_hook_t *p, *n;
+
+ /* XXX TODO: turn this into a real lookup instead of linear search */
+
+ if (1 <= id && id <= (uint32_t)ntype)
+ if ((t = typetbl[id]) != NULL && t->id == id)
+ return t;
+
+ mrp_log_warning("Type lookup for %u failed, doing linear search...\n", id);
+
+ mrp_list_foreach(&types, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (t->id == id)
+ return t;
+ }
+
+ return NULL;
+}
+
+
+static mrp_native_type_t *member_type(mrp_native_member_t *m)
+{
+ mrp_native_type_t *t;
+
+ if (m->any.type != MRP_TYPE_STRUCT)
+ t = lookup_type(m->any.type);
+ else
+ t = lookup_type(m->strct.data_type.id);
+
+ if (t == NULL)
+ errno = EINVAL;
+
+ return t;
+}
+
+
+static inline uint32_t map_type(uint32_t id, mrp_typemap_t *idmap)
+{
+ uint32_t mapped = MRP_INVALID_TYPE;
+
+ if (id < MRP_TYPE_STRUCT || idmap == NULL)
+ mapped = id;
+ else {
+ while (idmap->type_id != MRP_INVALID_TYPE) {
+ if (idmap->type_id == id) {
+ mapped = MRP_TYPE_STRUCT + idmap->mapped;
+ break;
+ }
+ else
+ idmap++;
+ }
+ }
+
+ return mapped;
+}
+
+
+static inline uint32_t mapped_type(uint32_t mapped, mrp_typemap_t *idmap)
+{
+ uint32_t id = MRP_INVALID_TYPE;
+
+ if (mapped < MRP_TYPE_STRUCT || idmap == NULL)
+ id = mapped;
+ else {
+ while (idmap->type_id != MRP_INVALID_TYPE) {
+ if (MRP_TYPE_STRUCT + idmap->mapped == mapped) {
+ id = idmap->type_id;
+ break;
+ }
+ else
+ idmap++;
+ }
+ }
+
+ return id;
+}
+
+
+uint32_t mrp_type_id(const char *type_name)
+{
+ mrp_native_type_t *t;
+
+ if ((t = find_type(type_name)) != NULL)
+ return t->id;
+ else
+ return MRP_INVALID_TYPE;
+}
+
+
+static size_t type_size(uint32_t id)
+{
+ mrp_native_type_t *t = lookup_type(id);
+
+ if (t != NULL)
+ return t->size;
+ else
+ return 0;
+}
+
+
+static int matching_types(mrp_native_type_t *t1, mrp_native_type_t *t2)
+{
+ MRP_UNUSED(t1);
+ MRP_UNUSED(t2);
+
+ /* XXX TODO */
+ return 0;
+}
+
+
+static void register_default_types(void)
+{
+#define DEFAULT_NTYPE (MRP_TYPE_STRUCT + 1)
+
+#define DECLARE_TYPE(_ctype, _mtype) \
+ static mrp_native_type_t _mtype##_type = { \
+ .name = #_ctype, \
+ .id = MRP_TYPE_##_mtype, \
+ .size = sizeof(_ctype), \
+ .members = NULL, \
+ .nmember = 0, \
+ .hook = { NULL, NULL } \
+ }
+
+#define REGISTER_TYPE(_type) \
+ mrp_list_init(&(_type)->hook); \
+ mrp_list_append(&types, &(_type)->hook); \
+ typetbl[(_type)->id] = (_type)
+
+ if (mrp_reallocz(typetbl, 0, DEFAULT_NTYPE) == NULL) {
+ mrp_log_error("Failed to initialize native type table.");
+ abort();
+ }
+
+ DECLARE_TYPE( int8_t , INT8 );
+ DECLARE_TYPE(uint8_t , UINT8 );
+ DECLARE_TYPE(int16_t , INT16 );
+ DECLARE_TYPE(uint16_t , UINT16);
+ DECLARE_TYPE(int32_t , INT32 );
+ DECLARE_TYPE(uint32_t , UINT32);
+ DECLARE_TYPE(int64_t , INT64 );
+ DECLARE_TYPE(uint64_t , UINT64);
+ DECLARE_TYPE(float , FLOAT );
+ DECLARE_TYPE(double , DOUBLE);
+ DECLARE_TYPE(bool , BOOL );
+ DECLARE_TYPE(int , INT );
+ DECLARE_TYPE(unsigned int , UINT );
+ DECLARE_TYPE(short , SHORT );
+ DECLARE_TYPE(unsigned short, USHORT);
+ DECLARE_TYPE(size_t , SIZET );
+ DECLARE_TYPE(ssize_t , SSIZET);
+ DECLARE_TYPE(char * , STRING);
+ DECLARE_TYPE(void * , BLOB );
+ DECLARE_TYPE(void * , ARRAY );
+ DECLARE_TYPE(void * , STRUCT);
+
+ REGISTER_TYPE(&INT8_type);
+ REGISTER_TYPE(&UINT8_type);
+ REGISTER_TYPE(&INT16_type);
+ REGISTER_TYPE(&UINT16_type);
+ REGISTER_TYPE(&INT32_type);
+ REGISTER_TYPE(&UINT32_type);
+ REGISTER_TYPE(&INT64_type);
+ REGISTER_TYPE(&UINT64_type);
+ REGISTER_TYPE(&FLOAT_type);
+ REGISTER_TYPE(&DOUBLE_type);
+ REGISTER_TYPE(&BOOL_type);
+ REGISTER_TYPE(&INT_type);
+ REGISTER_TYPE(&UINT_type);
+ REGISTER_TYPE(&SHORT_type);
+ REGISTER_TYPE(&USHORT_type);
+ REGISTER_TYPE(&SIZET_type);
+ REGISTER_TYPE(&SSIZET_type);
+ REGISTER_TYPE(&STRING_type);
+ REGISTER_TYPE(&BLOB_type);
+ REGISTER_TYPE(&ARRAY_type);
+ REGISTER_TYPE(&STRUCT_type);
+
+ ntype = DEFAULT_NTYPE;
+
+#undef DECLARE_TYPE
+#undef REGISTER_TYPE
+}
+
+
+uint32_t mrp_register_native(mrp_native_type_t *type)
+{
+ mrp_native_type_t *existing = find_type(type->name);
+ mrp_native_type_t *t, *elemt;
+ mrp_native_member_t *s, *d, *m;
+ int idx;
+
+ (void)member_type;
+
+ if (existing != NULL && !matching_types(existing, type)) {
+ errno = EEXIST;
+ return MRP_INVALID_TYPE;
+ }
+
+ if (ntype == 0)
+ register_default_types();
+
+ if ((t = mrp_allocz(sizeof(*t))) == NULL)
+ return MRP_INVALID_TYPE;
+
+ mrp_list_init(&t->hook);
+ t->name = mrp_strdup(type->name);
+
+ if (t->name == NULL)
+ goto fail;
+
+ t->size = type->size;
+ t->members = mrp_allocz_array(mrp_native_member_t, type->nmember);
+
+ if (t->members == NULL && type->nmember != 0)
+ goto fail;
+
+ /*
+ * Notes:
+ *
+ * While we copy the members, we also take care of reordering them
+ * so that any member that another one depends on ('size' members)
+ * get registered (and consequently encoded and decoded) before the
+ * dependant members.
+ */
+
+ s = type->members;
+ d = t->members;
+ while (t->nmember < type->nmember) {
+ /* make sure there are no duplicate members */
+ if (native_member(type, member_index(type, s->any.name)) != s) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ /* skip already copied members */
+ while (member_index(t, s->any.name) >= 0)
+ s++;
+
+ switch (s->any.type) {
+ case MRP_TYPE_BLOB:
+ m = native_member(t, member_index(t, s->blob.size.name));
+
+ if (m == NULL) {
+ m = native_member(type,
+ member_index(type, s->blob.size.name));
+
+ if (m == NULL)
+ goto fail;
+ else
+ idx = copy_member(t, m);
+
+ if (idx < 0)
+ goto fail;
+ }
+ else
+ idx = m - t->members;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+
+ d = t->members + t->nmember;
+ d->blob.size.idx = idx;
+
+ break;
+
+ case MRP_TYPE_ARRAY:
+ if (s->array.kind == MRP_ARRAY_SIZE_EXPLICIT) {
+ m = native_member(t, member_index(t, s->array.size.name));
+
+ if (m == NULL) {
+ m = native_member(type,
+ member_index(type, s->array.size.name));
+
+ if (m == NULL)
+ goto fail;
+ else
+ idx = copy_member(t, m);
+
+ if (idx < 0)
+ goto fail;
+
+ }
+ else
+ idx = m - t->members;
+
+ d = t->members + t->nmember;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+
+ d->array.size.idx = idx;
+ }
+ else {
+ d = t->members + t->nmember;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+ }
+
+ d->array.elem.id = mrp_type_id(d->array.elem.name);
+
+ if (d->array.elem.id == MRP_INVALID_TYPE)
+ goto fail;
+
+ if (s->array.kind == MRP_ARRAY_SIZE_GUARDED) {
+ elemt = lookup_type(d->array.elem.id);
+
+ if (elemt == NULL)
+ goto fail;
+
+ if (elemt->id < MRP_TYPE_ARRAY)
+ idx = 0;
+ else {
+ idx = member_index(elemt, s->array.size.name);
+ d->array.size.idx = member_index(elemt, s->array.size.name);
+
+ if (d->array.size.idx == (uint32_t)-1)
+ goto fail;
+ }
+ }
+
+ break;
+
+ case MRP_TYPE_STRUCT:
+ d = t->members + t->nmember;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+
+ d->strct.data_type.id = mrp_type_id(d->strct.data_type.name);
+
+ if (d->strct.data_type.id == MRP_INVALID_TYPE)
+ goto fail;
+ break;
+
+ default:
+ if (copy_member(t, s) < 0)
+ goto fail;
+ }
+ }
+
+ if (mrp_reallocz(typetbl, ntype, ntype + 1) == NULL)
+ goto fail;
+
+ t->id = ntype;
+ mrp_list_append(&types, &t->hook);
+ typetbl[ntype] = t;
+ ntype++;
+
+ return t->id;
+
+ fail:
+ free_native(t);
+
+ return MRP_INVALID_TYPE;
+}
+
+
+static void free_native(mrp_native_type_t *t)
+{
+ mrp_native_member_t *m;
+ size_t i;
+
+ if (t == NULL)
+ return;
+
+ mrp_list_delete(&t->hook);
+
+ mrp_free(t->name);
+ for (i = 0, m = t->members; i < t->nmember; i++, m++)
+ mrp_free(m->any.name);
+ mrp_free(t);
+}
+
+
+static int encode_basic(mrp_tlv_t *tlv, mrp_type_t type, mrp_value_t *v)
+{
+ switch (type) {
+ case MRP_TYPE_INT8: return mrp_tlv_push_int8 (tlv, TAG_NONE, v->s8);
+ case MRP_TYPE_UINT8: return mrp_tlv_push_uint8 (tlv, TAG_NONE, v->u8);
+ case MRP_TYPE_INT16: return mrp_tlv_push_int16 (tlv, TAG_NONE, v->s16);
+ case MRP_TYPE_UINT16: return mrp_tlv_push_uint16(tlv, TAG_NONE, v->u16);
+ case MRP_TYPE_INT32: return mrp_tlv_push_int32 (tlv, TAG_NONE, v->s32);
+ case MRP_TYPE_UINT32: return mrp_tlv_push_uint32(tlv, TAG_NONE, v->u32);
+ case MRP_TYPE_INT64: return mrp_tlv_push_int64 (tlv, TAG_NONE, v->s64);
+ case MRP_TYPE_UINT64: return mrp_tlv_push_uint64(tlv, TAG_NONE, v->u64);
+ case MRP_TYPE_FLOAT: return mrp_tlv_push_float (tlv, TAG_NONE, v->flt);
+ case MRP_TYPE_DOUBLE: return mrp_tlv_push_double(tlv, TAG_NONE, v->dbl);
+ case MRP_TYPE_BOOL: return mrp_tlv_push_bool (tlv, TAG_NONE, v->bln);
+ case MRP_TYPE_STRING: return mrp_tlv_push_string(tlv, TAG_NONE, v->str);
+
+ case MRP_TYPE_INT:
+ return mrp_tlv_push_int32 (tlv, TAG_NONE, (int32_t)v->i);
+ case MRP_TYPE_UINT:
+ return mrp_tlv_push_uint32(tlv, TAG_NONE, (uint32_t)v->ui);
+ case MRP_TYPE_SHORT:
+ return mrp_tlv_push_int32 (tlv, TAG_NONE, (int32_t)v->si);
+ case MRP_TYPE_USHORT:
+ return mrp_tlv_push_uint32(tlv, TAG_NONE, (uint32_t)v->usi);
+ case MRP_TYPE_SIZET:
+ return mrp_tlv_push_uint32(tlv, TAG_NONE, (uint32_t)v->sz);
+ case MRP_TYPE_SSIZET:
+ return mrp_tlv_push_int32 (tlv, TAG_NONE, (int32_t)v->ssz);
+
+ default:
+ return -1;
+ }
+}
+
+
+static inline int get_blob_size(void *base, mrp_native_type_t *t,
+ mrp_native_blob_t *m, size_t *sizep)
+{
+ mrp_native_member_t *sizem;
+ mrp_value_t *v;
+
+ if ((sizem = native_member(t, m->size.idx)) == NULL)
+ return -1;
+
+ if (sizem->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)base;
+ else
+ v = base;
+
+ switch (sizem->any.type) {
+ case MRP_TYPE_INT8: *sizep = v->s8; return 0;
+ case MRP_TYPE_UINT8: *sizep = v->u8; return 0;
+ case MRP_TYPE_INT16: *sizep = v->s16; return 0;
+ case MRP_TYPE_UINT16: *sizep = v->u16; return 0;
+ case MRP_TYPE_INT32: *sizep = v->s32; return 0;
+ case MRP_TYPE_UINT32: *sizep = v->u32; return 0;
+ case MRP_TYPE_INT64: *sizep = (size_t)v->s32; return 0;
+ case MRP_TYPE_UINT64: *sizep = (size_t)v->u32; return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+
+static int guard_offset_and_size(mrp_native_array_t *m, size_t *offsp,
+ size_t *sizep)
+{
+ mrp_native_type_t *t = lookup_type(m->elem.id);
+ mrp_native_member_t *g;
+
+ if (t == NULL)
+ return -1;
+
+ switch (t->id) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_STRING:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ *offsp = 0;
+ *sizep = t->size;
+ return 0;
+
+ default:
+ if ((g = native_member(t, m->size.idx)) == NULL)
+ return -1;
+
+ *offsp = g->any.offs;
+ *sizep = type_size(g->any.type);
+ return 0;
+ }
+}
+
+
+static inline int get_explicit_array_size(void *base, mrp_native_type_t *t,
+ mrp_native_array_t *m)
+{
+ mrp_native_member_t *nelemm;
+ mrp_value_t *v;
+ int n;
+
+ if ((nelemm = native_member(t, m->size.idx)) == NULL)
+ return -1;
+ if (nelemm->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)(base + nelemm->any.offs);
+ else
+ v = base + nelemm->any.offs;
+
+ switch (nelemm->any.type) {
+ case MRP_TYPE_INT8: n = v->s8; break;
+ case MRP_TYPE_UINT8: n = v->u8; break;
+ case MRP_TYPE_INT16: n = v->s16; break;
+ case MRP_TYPE_UINT16: n = v->u16; break;
+ case MRP_TYPE_INT32: n = v->s32; break;
+ case MRP_TYPE_UINT32: n = v->u32; break;
+ case MRP_TYPE_INT64: n = (int)v->s64; break;
+ case MRP_TYPE_UINT64: n = (int)v->u64; break;
+
+ case MRP_TYPE_INT: n = (int) v->i; break;
+ case MRP_TYPE_UINT: n = (unsigned int) v->ui; break;
+ case MRP_TYPE_SHORT: n = (short) v->si; break;
+ case MRP_TYPE_USHORT: n = (unsigned short)v->usi; break;
+ case MRP_TYPE_SIZET: n = (size_t) v->sz; break;
+ case MRP_TYPE_SSIZET: n = (ssize_t) v->ssz; break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return n;
+}
+
+
+static inline int get_guarded_array_size(void *arrp, mrp_native_array_t *m)
+{
+ mrp_value_t *guard;
+ size_t goffs, gsize, esize;
+ int n;
+
+ if ((esize = type_size(m->elem.id)) == 0)
+ return -1;
+
+ if (guard_offset_and_size(m, &goffs, &gsize) < 0)
+ return -1;
+
+ guard = &m->sentinel;
+
+ for (n = 0; memcmp(arrp + n * esize + goffs, guard, gsize); n++)
+ ;
+ return n;
+}
+
+
+static int get_array_size(void *base, mrp_native_type_t *t, void *arrp,
+ mrp_native_array_t *m, size_t *nelemp,
+ size_t *esizep)
+{
+ int n;
+
+ if ((*esizep = type_size(m->elem.id)) == 0)
+ return -1;
+
+ switch (m->kind) {
+ case MRP_ARRAY_SIZE_FIXED:
+ *nelemp = m->size.nelem;
+ return 0;
+
+ case MRP_ARRAY_SIZE_EXPLICIT:
+ if ((n = get_explicit_array_size(base, t, m)) < 0)
+ return -1;
+
+ *nelemp = (size_t)n;
+ return 0;
+
+ case MRP_ARRAY_SIZE_GUARDED:
+ if ((n = get_guarded_array_size(arrp, m)) < 0)
+ return -1;
+
+ *nelemp = (size_t)n;
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+
+static int terminate_guarded_array(void *elem, mrp_native_array_t *m,
+ mrp_native_type_t *mt)
+{
+ mrp_native_member_t *g;
+
+ if (m->elem.id <= MRP_TYPE_STRING)
+ memcpy(elem, &m->sentinel, mt->size);
+ else if (m->elem.id > MRP_TYPE_STRUCT) {
+ if ((g = native_member(mt, m->size.idx)) == NULL)
+ return -1;
+
+ memcpy(elem + g->any.offs, &m->sentinel, type_size(g->any.type));
+ }
+
+ return 0;
+}
+
+
+static int encode_array(mrp_tlv_t *tlv, void *arrp, mrp_native_array_t *m,
+ size_t nelem, size_t elem_size, mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *t;
+ mrp_value_t *v;
+ void *elem;
+ size_t i;
+
+ if (mrp_tlv_push_uint32(tlv, TAG_ARRAY, map_type(m->elem.id, idmap)) < 0)
+ return -1;
+
+ if (mrp_tlv_push_uint32(tlv, TAG_NELEM, nelem) < 0)
+ return -1;
+
+ if ((t = lookup_type(m->elem.id)) == NULL)
+ return -1;
+
+ for (i = 0, elem = arrp; i < nelem; i++, elem += elem_size) {
+ v = elem;
+
+ switch (t->id) {
+ case MRP_TYPE_STRING:
+ v = *(void **)elem;
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (encode_basic(tlv, t->id, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ return -1;
+
+ default:
+ /* an MRP_TYPE_STRUCT */
+ if (encode_struct(tlv, elem, t, idmap) < 0)
+ return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int encode_struct(mrp_tlv_t *tlv, void *data, mrp_native_type_t *t,
+ mrp_typemap_t *idmap)
+{
+ mrp_native_member_t *m;
+ mrp_native_type_t *mt;
+ mrp_value_t *v;
+ uint32_t idx;
+ size_t size, nelem;
+
+ if (t == NULL)
+ return -1;
+
+ if (mrp_tlv_push_uint32(tlv, TAG_STRUCT, map_type(t->id, idmap)) < 0)
+ return -1;
+
+ for (idx = 0, m = t->members; idx < t->nmember; idx++, m++) {
+ if (mrp_tlv_push_uint32(tlv, TAG_MEMBER, idx) < 0)
+ return -1;
+
+ if (m->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)(data + m->any.offs);
+ else
+ v = data + m->any.offs;
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_STRING:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (encode_basic(tlv, m->any.type, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ if (get_blob_size(data, t, &m->blob, &size) < 0)
+ return -1;
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ if (get_array_size(data, t, v->ptr, &m->array, &nelem, &size) < 0)
+ return -1;
+ if (encode_array(tlv, v->ptr, &m->array, nelem,
+ size, idmap) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRUCT:
+ if ((mt = lookup_type(m->strct.data_type.id)) == NULL)
+ return -1;
+ if (encode_struct(tlv, v->ptr, mt, idmap) < 0)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int mrp_encode_native(void *data, uint32_t id, size_t reserve, void **bufp,
+ size_t *sizep, mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *t = lookup_type(id);
+ mrp_tlv_t tlv;
+
+ *bufp = NULL;
+ *sizep = 0;
+
+ if (t == NULL)
+ return -1;
+
+ if (mrp_tlv_setup_write(&tlv, reserve + 4096) < 0)
+ return -1;
+
+ if (reserve > 0)
+ if (mrp_tlv_reserve(&tlv, reserve, 1) == NULL)
+ goto fail;
+
+ if (encode_struct(&tlv, data, t, idmap) < 0)
+ goto fail;
+
+ mrp_tlv_trim(&tlv);
+ mrp_tlv_steal(&tlv, bufp, sizep);
+
+ return 0;
+
+ fail:
+ mrp_tlv_cleanup(&tlv);
+ return -1;
+}
+
+
+static void *allocate_indirect(mrp_list_hook_t **chunks, mrp_value_t *v,
+ mrp_native_member_t *m, mrp_typemap_t *idmap)
+{
+ size_t size;
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int8_t)));
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int16_t)));
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int32_t)));
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int64_t)));
+ case MRP_TYPE_FLOAT:
+ return (v->ptr = alloc_chunk(chunks, sizeof(float)));
+ case MRP_TYPE_DOUBLE:
+ return (v->ptr = alloc_chunk(chunks, sizeof(double)));
+ case MRP_TYPE_BOOL:
+ return (v->ptr = alloc_chunk(chunks, sizeof(bool)));
+ case MRP_TYPE_STRING:
+ return v; /* will be allocated by TLV pull */
+ case MRP_TYPE_BLOB:
+ return v; /* will be allocated by decoder */
+ case MRP_TYPE_ARRAY:
+ return v; /* will be allocated by decoder */
+ case MRP_TYPE_STRUCT:
+ if ((size = type_size(mapped_type(m->strct.data_type.id, idmap))) == 0)
+ return NULL;
+ return (v->ptr = alloc_chunk(chunks, size));
+ default:
+ return NULL;
+ }
+}
+
+
+static void *alloc_str_chunk(size_t size, void *chunksp)
+{
+ return alloc_chunk((mrp_list_hook_t **)chunksp, size);
+}
+
+
+static int decode_basic(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ mrp_type_t type, mrp_value_t *v)
+{
+ int32_t i;
+ uint32_t u;
+
+ switch (type) {
+ case MRP_TYPE_INT8: return mrp_tlv_pull_int8 (tlv, TAG_NONE, &v->s8);
+ case MRP_TYPE_UINT8: return mrp_tlv_pull_uint8 (tlv, TAG_NONE, &v->u8);
+ case MRP_TYPE_INT16: return mrp_tlv_pull_int16 (tlv, TAG_NONE, &v->s16);
+ case MRP_TYPE_UINT16: return mrp_tlv_pull_uint16(tlv, TAG_NONE, &v->u16);
+ case MRP_TYPE_INT32: return mrp_tlv_pull_int32 (tlv, TAG_NONE, &v->s32);
+ case MRP_TYPE_UINT32: return mrp_tlv_pull_uint32(tlv, TAG_NONE, &v->u32);
+ case MRP_TYPE_INT64: return mrp_tlv_pull_int64 (tlv, TAG_NONE, &v->s64);
+ case MRP_TYPE_UINT64: return mrp_tlv_pull_uint64(tlv, TAG_NONE, &v->u64);
+ case MRP_TYPE_FLOAT: return mrp_tlv_pull_float (tlv, TAG_NONE, &v->flt);
+ case MRP_TYPE_DOUBLE: return mrp_tlv_pull_double(tlv, TAG_NONE, &v->dbl);
+ case MRP_TYPE_BOOL: return mrp_tlv_pull_bool (tlv, TAG_NONE, &v->bln);
+ case MRP_TYPE_STRING:
+ return mrp_tlv_pull_string(tlv, TAG_NONE, &v->strp,
+ -1, alloc_str_chunk, chunks);
+
+ case MRP_TYPE_INT:
+ if (mrp_tlv_pull_int32(tlv, TAG_NONE, &i) < 0)
+ return -1;
+ v->i = (int)i;
+ return 0;
+
+ case MRP_TYPE_UINT:
+ if (mrp_tlv_pull_uint32(tlv, TAG_NONE, &u) < 0)
+ return -1;
+ v->ui = (unsigned int)u;
+ return 0;
+
+ case MRP_TYPE_SHORT:
+ if (mrp_tlv_pull_int32(tlv, TAG_NONE, &i) < 0)
+ return -1;
+ v->si = (short)i;
+ return 0;
+
+ case MRP_TYPE_USHORT:
+ if (mrp_tlv_pull_uint32(tlv, TAG_NONE, &u) < 0)
+ return -1;
+ v->usi = (unsigned short)u;
+ return 0;
+
+ case MRP_TYPE_SIZET:
+ if (mrp_tlv_pull_uint32(tlv, TAG_NONE, &u) < 0)
+ return -1;
+ v->sz = (size_t)u;
+ return 0;
+
+ case MRP_TYPE_SSIZET:
+ if (mrp_tlv_pull_int32(tlv, TAG_NONE, &i) < 0)
+ return -1;
+ v->ssz = (ssize_t)i;
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+
+static int decode_array(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ void **arrp, mrp_native_array_t *m,
+ void *data, mrp_native_type_t *t,
+ mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *mt;
+ mrp_value_t *v;
+ void *elem, *base;
+ size_t elem_size, i;
+ uint32_t id, nelem;
+ int n, guard;
+
+ if (mrp_tlv_pull_uint32(tlv, TAG_ARRAY, &id) < 0)
+ return -1;
+
+ if ((id = mapped_type(id, idmap)) != m->elem.id)
+ return -1;
+
+ if ((elem_size = type_size(id)) == 0)
+ return -1;
+
+ if (mrp_tlv_pull_uint32(tlv, TAG_NELEM, &nelem) < 0)
+ return -1;
+
+ if ((mt = lookup_type(m->elem.id)) == NULL)
+ return -1;
+
+ switch (m->kind) {
+ case MRP_ARRAY_SIZE_EXPLICIT:
+ if ((n = get_explicit_array_size(data, t, m)) < 0)
+ return -1;
+ guard = 0;
+ break;
+ case MRP_ARRAY_SIZE_FIXED:
+ n = m->size.nelem;
+ guard = 0;
+ break;
+ case MRP_ARRAY_SIZE_GUARDED:
+ n = nelem;
+ guard = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ if (n != (int)nelem)
+ return -1;
+
+ switch (m->layout) {
+ case MRP_LAYOUT_INLINED:
+ base = (void *)arrp;
+ break;
+ case MRP_LAYOUT_INDIRECT:
+ case MRP_LAYOUT_DEFAULT:
+ if ((*arrp = alloc_chunk(chunks, (nelem + guard) * elem_size)) == NULL)
+ return (nelem + guard) ? -1 : 0;
+ base = *arrp;
+ break;
+ default:
+ return -1;
+ }
+
+ for (i = 0, elem = base; i < nelem; i++, elem += elem_size) {
+ v = elem;
+
+ switch (mt->id) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_STRING:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (decode_basic(tlv, chunks, mt->id, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ return -1;
+
+ default:
+ /* an MRP_TYPE_STRUCT */
+ if (decode_struct(tlv, chunks, &elem, &id, idmap) < 0)
+ return -1;
+ }
+ }
+
+ if (guard) {
+ if (terminate_guarded_array(elem, m, mt) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int decode_struct(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ void **datap, uint32_t *idp, mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *t;
+ mrp_native_member_t *m;
+ mrp_value_t *v;
+ char *str, **strp;
+ size_t max, i;
+ uint32_t idx, id;
+
+ if (datap == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (mrp_tlv_pull_uint32(tlv, TAG_STRUCT, &id) < 0)
+ return -1;
+ else
+ id = mapped_type(id, idmap);
+
+ if (*idp) {
+ if (*idp != id) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ else
+ *idp = id;
+
+ if ((t = lookup_type(id)) == NULL)
+ return -1;
+
+ if (*datap == NULL)
+ if ((*datap = alloc_chunk(chunks, t->size)) == NULL)
+ return -1;
+
+ for (i = 0, m = t->members; i < t->nmember; i++, m++) {
+ if (mrp_tlv_pull_uint32(tlv, TAG_MEMBER, &idx) < 0)
+ return -1;
+
+ v = *datap + m->any.offs;
+
+ if (m->any.layout == MRP_LAYOUT_INDIRECT) {
+ if ((v = allocate_indirect(chunks, v, m, idmap)) == NULL)
+ return -1;
+ }
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (decode_basic(tlv, chunks, m->any.type, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRING:
+ if (m->any.layout == MRP_LAYOUT_INLINED) {
+ max = m->str.size;
+ str = v->str;
+ strp = &str;
+ }
+ else {
+ max = (size_t)-1;
+ strp = &v->strp;
+ }
+ if (mrp_tlv_pull_string(tlv, TAG_NONE, strp, max,
+ alloc_str_chunk, chunks) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ if (decode_array(tlv, chunks, &v->ptr, &m->array,
+ *datap, t, idmap) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRUCT:
+ id = m->strct.data_type.id;
+ if (decode_struct(tlv, chunks, &v->ptr, &id, idmap) < 0)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int mrp_decode_native(void **bufp, size_t *sizep, void **datap, uint32_t *idp,
+ mrp_typemap_t *idmap)
+{
+ mrp_tlv_t tlv;
+ mrp_list_hook_t *chunks;
+ void *data;
+ size_t diff;
+
+ chunks = NULL;
+ data = NULL;
+
+ if (mrp_tlv_setup_read(&tlv, *bufp, *sizep) < 0)
+ return -1;
+
+ if (decode_struct(&tlv, &chunks, &data, idp, idmap) == 0) {
+ diff = mrp_tlv_offset(&tlv);
+
+ if (diff <= *sizep) {
+ *bufp += diff;
+ *sizep -= diff;
+ *datap = data;
+
+ return 0;
+ }
+ }
+
+ free_chunks(chunks);
+
+ return -1;
+}
+
+
+void mrp_free_native(void *data, uint32_t id)
+{
+ mrp_list_hook_t *chunks;
+
+ MRP_UNUSED(id);
+
+ if (data != NULL) {
+ chunks = ((void *)data) - MRP_OFFSET(chunk_t, data);
+ free_chunks(chunks);
+ }
+}
+
+
+#define INDENT(_level, _fmt) "%*.*s"_fmt, _level * 4, _level * 4, ""
+
+#define PRINT(_l, _p, _size, fmt, args...) do { \
+ ssize_t _n; \
+ _n = snprintf((_p), (_size), INDENT(_l, fmt), ## args); \
+ if (_n >= (ssize_t)(_size)) \
+ return -1; \
+ (_p) += _n; \
+ (_size) -= _n; \
+ } while (0)
+
+
+static int print_basic(int level, char **bufp, size_t *sizep, int type,
+ const char *name, mrp_value_t *v)
+{
+#define NAME name ? name : "", name ? " = " : ""
+ char *p = *bufp;
+ size_t size = *sizep;
+
+ if (type >= MRP_TYPE_BLOB)
+ return -1;
+
+ switch (type) {
+ case MRP_TYPE_INT8:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->s8);
+ break;
+ case MRP_TYPE_UINT8:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->u8);
+ break;
+
+ case MRP_TYPE_INT16:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->s16);
+ break;
+ case MRP_TYPE_UINT16:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->u16);
+ break;
+
+ case MRP_TYPE_INT32:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->s32);
+ break;
+ case MRP_TYPE_UINT32:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->u32);
+ break;
+
+ case MRP_TYPE_INT64:
+ PRINT(level, p, size, "%s%s%lld\n", NAME, (long long)v->s64);
+ break;
+ case MRP_TYPE_UINT64:
+ PRINT(level, p, size, "%s%s%llu\n", NAME,
+ (unsigned long long)v->s64);
+ break;
+
+ case MRP_TYPE_FLOAT:
+ PRINT(level, p, size, "%s%s%f\n", NAME, v->flt);
+ break;
+ case MRP_TYPE_DOUBLE:
+ PRINT(level, p, size, "%s%s%f\n", NAME, v->dbl);
+ break;
+
+ case MRP_TYPE_BOOL:
+ PRINT(level, p, size, "%s%s%s\n", NAME,
+ v->bln ? "<true>" : "<false>");
+ break;
+
+ case MRP_TYPE_STRING:
+ PRINT(level, p, size, "%s%s%s\n", NAME,
+ v->str ? v->str : "<null>");
+ break;
+
+ case MRP_TYPE_INT:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->i);
+ break;
+ case MRP_TYPE_UINT:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->ui);
+ break;
+
+ case MRP_TYPE_SHORT:
+ PRINT(level, p, size, "%s%s%hd\n", NAME, v->si);
+ break;
+ case MRP_TYPE_USHORT:
+ PRINT(level, p, size, "%s%s%hu\n", NAME, v->usi);
+ break;
+
+ case MRP_TYPE_SIZET:
+ PRINT(level, p, size, "%s%s%zu\n", NAME, v->sz);
+ break;
+ case MRP_TYPE_SSIZET:
+ PRINT(level, p, size, "%s%s%zd\n", NAME, v->ssz);
+ break;
+
+ default:
+ PRINT(level, p, size, "%s%s%s\n", NAME, "<unknown>");
+ }
+
+ *bufp = p;
+ *sizep = size;
+
+ return 0;
+
+#undef NAME
+}
+
+
+static int print_array(char **bufp, size_t *sizep, int level,
+ void *arrp, mrp_native_array_t *a, size_t nelem,
+ size_t elem_size)
+{
+ mrp_native_type_t *et;
+ mrp_value_t *v;
+ void *elem;
+ size_t i;
+ char *p;
+ size_t size;
+
+ p = *bufp;
+ size = *sizep;
+
+ if ((et = lookup_type(a->elem.id)) == NULL)
+ return -1;
+
+ PRINT(level, p, size, "%s = [%s", a->name, nelem == 0 ? "]" : "\n");
+ level++;
+
+ for (i = 0, elem = arrp; i < nelem; i++, elem += elem_size) {
+ v = elem;
+
+ switch (et->id) {
+ case MRP_TYPE_STRING:
+ v = *(void **)elem;
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (print_basic(level, &p, &size, et->id, NULL, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB:
+ PRINT(level, p, size, "<blob>\n");
+ break;
+
+ case MRP_TYPE_ARRAY:
+ return -1;
+
+ default:
+ /* an MRP_TYPE_STRUCT */
+ if (print_struct(&p, &size, level, elem, et) < 0)
+ return -1;
+ break;
+ }
+ }
+
+ level--;
+ PRINT(level, p, size, "%s\n", nelem == 0 ? "" : "]");
+
+ *bufp = p;
+ *sizep = size;
+
+ return 0;
+}
+
+
+static int print_struct(char **bufp, size_t *sizep, int level,
+ void *data, mrp_native_type_t *t)
+{
+ mrp_native_member_t *m;
+ mrp_native_type_t *mt;
+ mrp_value_t *v;
+ uint32_t idx;
+ size_t esize, nelem;
+ char *p;
+ size_t size;
+
+ if (data == NULL) {
+ **bufp = '\0';
+
+ return 0;
+ }
+
+ if (t == NULL)
+ return -1;
+
+ p = *bufp;
+ size = *sizep;
+ PRINT(level, p, size, "{\n");
+ level++;
+
+ for (idx = 0, m = t->members; idx < t->nmember; idx++, m++) {
+ if (m->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)(data + m->any.offs);
+ else
+ v = data + m->any.offs;
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ case MRP_TYPE_STRING:
+ if (print_basic(level, &p, &size, m->any.type, m->any.name, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ PRINT(level, p, size, "%s = <blob>\n", m->any.name);
+ break;
+
+ case MRP_TYPE_ARRAY:
+ if (get_array_size(data, t, v->ptr, &m->array, &nelem, &esize) < 0)
+ return -1;
+ if (print_array(&p, &size, level, v->ptr, &m->array,
+ nelem, esize) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRUCT:
+ if ((mt = lookup_type(m->strct.data_type.id)) == NULL)
+ return -1;
+ if (print_struct(&p, &size, level, v->ptr, mt) < 0)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ level--;
+ PRINT(level, p, size, "}\n");
+
+ *bufp = p;
+ *sizep = size;
+
+ return 0;
+}
+
+
+ssize_t mrp_print_native(char *buf, size_t size, void *data, uint32_t id)
+{
+ mrp_native_type_t *t;
+ char *p;
+
+ p = buf;
+
+ if (id < MRP_TYPE_STRUCT || (t = lookup_type(id)) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (print_struct(&p, &size, 0, data, t) == 0)
+ return (ssize_t)(p - buf);
+ else
+ return -1;
+}
+
+
+static inline size_t chunk_size(size_t size)
+{
+ return MRP_OFFSET(chunk_t, data[size]);
+}
+
+
+static void *alloc_chunk(mrp_list_hook_t **chunks, size_t size)
+{
+ chunk_t *chunk;
+
+ if (size == 0)
+ return NULL;
+
+ if (*chunks == NULL) {
+ if ((*chunks = mrp_allocz(sizeof(*chunks))) == NULL)
+ return NULL;
+ else
+ mrp_list_init(*chunks);
+ }
+
+ if ((chunk = mrp_allocz(chunk_size(size))) == NULL)
+ return NULL;
+
+ mrp_list_init(&chunk->hook);
+ mrp_list_append(*chunks, &chunk->hook);
+
+ return &chunk->data[0];
+}
+
+
+static void free_chunks(mrp_list_hook_t *chunks)
+{
+ mrp_list_hook_t *p, *n;
+
+ if (chunks != NULL) {
+ mrp_list_foreach(chunks, p, n) {
+ mrp_list_delete(p);
+
+ if (p != chunks)
+ mrp_free(p);
+ }
+
+ mrp_free(chunks);
+ }
+}
diff --git a/src/common/native-types.h b/src/common/native-types.h
new file mode 100644
index 0000000..ac6dacf
--- /dev/null
+++ b/src/common/native-types.h
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_COMMON_NATIVE_TYPES_H__
+#define __MURPHY_COMMON_NATIVE_TYPES_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_INVALID_TYPE ((uint32_t)-1)
+
+
+/**
+ * pre-defined native type ids
+ */
+
+typedef enum {
+ MRP_TYPE_UNKNOWN = 0,
+ MRP_TYPE_INT8,
+ MRP_TYPE_UINT8,
+ MRP_TYPE_INT16,
+ MRP_TYPE_UINT16,
+ MRP_TYPE_INT32,
+ MRP_TYPE_UINT32,
+ MRP_TYPE_INT64,
+ MRP_TYPE_UINT64,
+ MRP_TYPE_FLOAT,
+ MRP_TYPE_DOUBLE,
+ MRP_TYPE_BOOL,
+ MRP_TYPE_INT,
+ MRP_TYPE_UINT,
+ MRP_TYPE_SHORT,
+ MRP_TYPE_USHORT,
+ MRP_TYPE_SIZET,
+ MRP_TYPE_SSIZET,
+ MRP_TYPE_STRING,
+ MRP_TYPE_BLOB,
+ MRP_TYPE_ARRAY,
+ MRP_TYPE_STRUCT,
+ MRP_TYPE_MAX
+} mrp_type_t;
+
+
+/**
+ * data type values
+ */
+
+typedef union {
+ int8_t s8;
+ int8_t *s8p;
+ uint8_t u8;
+ uint8_t *u8p;
+ int16_t s16;
+ int16_t *s16p;
+ uint16_t u16;
+ uint16_t *u16p;
+ int32_t s32;
+ int32_t *s32p;
+ uint32_t u32;
+ uint32_t *u32p;
+ int64_t s64;
+ int64_t *s64p;
+ uint64_t u64;
+ uint64_t *u64p;
+ float flt;
+ float *fltp;
+ double dbl;
+ double *dblp;
+ bool bln;
+ bool *blnp;
+ void *blb;
+ char str[0];
+ char *strp;
+ int i;
+ int *ip;
+ unsigned int ui;
+ unsigned int *uip;
+ short si;
+ short *sip;
+ unsigned short usi;
+ unsigned short *usip;
+ size_t sz;
+ size_t *szp;
+ ssize_t ssz;
+ ssize_t *sszp;
+ void *ptr;
+ void **ptrp;
+} mrp_value_t;
+
+
+/**
+ * type id map (for transport-specific mapping of type ids)
+ */
+
+typedef struct {
+ uint32_t type_id; /* native type id */
+ uint32_t mapped; /* mapped type id */
+} mrp_typemap_t;
+
+
+/** Macro to initialize a typemap entry. */
+#define MRP_TYPEMAP(_mapped_id, _type_id) \
+ { .type_id = _type_id, .mapped = _mapped_id }
+
+/** Macro to set a typemap termination entry. */
+#define MRP_TYPEMAP_END \
+ { MRP_INVALID_TYPE, MRP_INVALID_TYPE }
+
+/**
+ * type and member descriptors
+ */
+
+typedef enum {
+ MRP_LAYOUT_DEFAULT = 0, /* default, type-specific layout */
+ MRP_LAYOUT_INLINED, /* inlined/embedded layout */
+ MRP_LAYOUT_INDIRECT, /* indirect layout */
+} mrp_layout_t;
+
+#define MRP_NATIVE_COMMON_FIELDS /* fields common to all members */ \
+ char *name; /* name of this member */ \
+ uint32_t type; /* type id of this member */ \
+ size_t offs; /* offset from base pointer */ \
+ mrp_layout_t layout /* member layout */
+
+typedef struct {
+ MRP_NATIVE_COMMON_FIELDS; /* common fields to all members */
+} mrp_native_any_t;
+
+typedef struct { /* a blob member */
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ union { /* size-indicating member */
+ char *name; /* name */
+ uint32_t idx; /* or index */
+ } size;
+} mrp_native_blob_t;
+
+typedef enum {
+ MRP_ARRAY_SIZE_EXPLICIT, /* explicitly sized array */
+ MRP_ARRAY_SIZE_GUARDED, /* sentinel-guarded array */
+ MRP_ARRAY_SIZE_FIXED, /* a fixed size array */
+} mrp_array_size_t;
+
+typedef struct {
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ size_t size; /* inlined buffer size */
+} mrp_native_string_t;
+
+typedef struct { /* an array member */
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ mrp_array_size_t kind; /* which kind of array */
+ union { /* contained element type */
+ char *name; /* name */
+ uint32_t id; /* or type id */
+ } elem;
+ union { /* size or guard member */
+ char *name; /* name */
+ uint32_t idx; /* or index */
+ size_t nelem; /* or number of elements */
+ } size;
+ mrp_value_t sentinel; /* sentinel value, if guarded */
+} mrp_native_array_t;
+
+typedef struct { /* member of type struct */
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ union { /* struct type */
+ char *name; /* name */
+ uint32_t id; /* or type id */
+ } data_type;
+} mrp_native_struct_t;
+
+typedef union {
+ mrp_native_any_t any;
+ mrp_native_string_t str;
+ mrp_native_blob_t blob;
+ mrp_native_array_t array;
+ mrp_native_struct_t strct;
+} mrp_native_member_t;
+
+typedef struct {
+ char *name; /* name of this type */
+ uint32_t id; /* assigned id for this type */
+ size_t size; /* size of this type */
+ mrp_native_member_t *members; /* members of this type if any */
+ size_t nmember; /* number of members */
+ mrp_list_hook_t hook; /* to list of registered types */
+} mrp_native_type_t;
+
+
+/** Helper macro to initialize native member fields. */
+#define __MRP_MEMBER_INIT(_objtype, _member, _type) \
+ .name = #_member, \
+ .type = _type, \
+ .offs = MRP_OFFSET(_objtype, _member)
+
+/** Helper macro to declare a native member with a given type an layout. */
+#define __MRP_MEMBER(_objtype, _type, _member, _layout) \
+ { \
+ .any = { \
+ __MRP_MEMBER_INIT(_objtype, _member, _type), \
+ .layout = MRP_LAYOUT_##_layout, \
+ } \
+ }
+
+/** Declare an indirect string member of the native type. */
+#define MRP_INDIRECT_STRING(_objtype, _member, _size) \
+ __MRP_MEMBER(_objtype, _member, MRP_TYPE_STRING, INDIRECT)
+
+/** Declare an inlined string member of the native type. */
+#define MRP_INLINED_STRING(_objtype, _member, _size) \
+ { \
+ .str = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_STRING), \
+ .layout = MRP_LAYOUT_INLINED, \
+ .size = _size, \
+ } \
+ }
+
+/** By default declare a string members indirect. */
+#define MRP_DEFAULT_STRING(_objtype, _member, _size) \
+ __MRP_MEMBER(_objtype, MRP_TYPE_STRING, _member, INDIRECT)
+
+/** Declare an explicitly sized array member of the native typet. */
+#define MRP_SIZED_ARRAY(_objtype, _member, _layout, _type, _size) \
+ { \
+ .array = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY), \
+ .layout = MRP_LAYOUT_##_layout, \
+ .kind = MRP_ARRAY_SIZE_EXPLICIT, \
+ .elem = { .name = #_type, }, \
+ .size = { .name = #_size, }, \
+ } \
+ }
+
+/** Declare a sentinel-guarded array member of the native type. */
+#define MRP_GUARDED_ARRAY(_objtype, _member, _layout, _type, _guard, \
+ ...) \
+ { \
+ .array = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY), \
+ .layout = MRP_LAYOUT_##_layout, \
+ .kind = MRP_ARRAY_SIZE_GUARDED, \
+ .elem = { .name = #_type, }, \
+ .size = { .name = #_guard, }, \
+ .sentinel = { __VA_ARGS__ }, \
+ } \
+ }
+
+/** Declare a fixed array member of the native type. */
+#define MRP_FIXED_ARRAY(_objtype, _member, _layout, _type) \
+ { \
+ .array = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY), \
+ .layout = MRP_LAYOUT_##_layout, \
+ .kind = MRP_ARRAY_SIZE_FIXED, \
+ .elem = { .name = #_type, }, \
+ .size = { \
+ .nelem = MRP_ARRAY_SIZE(((_objtype *)0x0)->_member) \
+ }, \
+ } \
+ }
+
+/** Declare a struct member of the native type. */
+#define MRP_STRUCT(_objtype, _member, _layout, _type) \
+ { \
+ .strct = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_STRUCT), \
+ .data_type = { .name = #_type }, \
+ } \
+ }
+
+/** Macros for declaring basic members of the native type. */
+#define MRP_INT8(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT8 , _m, _l)
+#define MRP_UINT8(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT8 , _m, _l)
+#define MRP_INT16(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT16 , _m, _l)
+#define MRP_UINT16(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT16, _m, _l)
+#define MRP_INT32(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT32 , _m, _l)
+#define MRP_UINT32(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT32, _m, _l)
+#define MRP_INT64(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT64 , _m, _l)
+#define MRP_UINT64(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT64, _m, _l)
+#define MRP_FLOAT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_FLOAT , _m, _l)
+#define MRP_DOUBLE(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_DOUBLE, _m, _l)
+#define MRP_BOOL(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_BOOL , _m, _l)
+
+#define MRP_INT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT , _m, _l)
+#define MRP_UINT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT , _m, _l)
+#define MRP_SHORT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_SHORT , _m, _l)
+#define MRP_USHORT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_USHORT, _m, _l)
+#define MRP_SIZET(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_SIZET , _m, _l)
+#define MRP_SSIZET(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_SSIZET, _m, _l)
+
+/** Macro for declaring string members of the native type. */
+#define MRP_STRING(_objtype, _member, _layout) \
+ MRP_##_layout##_STRING(_objtype, _member, \
+ sizeof(((_objtype *)0x0)->_member))
+
+/** Macro for declaring array members of the native type. */
+#define MRP_ARRAY(_objtype, _member, _layout, _kind, ...) \
+ MRP_##_kind##_ARRAY(_objtype, _member, _layout, __VA_ARGS__)
+
+/** Macro to declare a native type. */
+#define MRP_NATIVE_TYPE(_var, _type, ...) \
+ mrp_native_member_t _var##_members[] = { \
+ __VA_ARGS__ \
+ }; \
+ mrp_native_type_t _var = { \
+ .id = -1, \
+ .name = #_type, \
+ .size = sizeof(_type), \
+ .members = _var##_members, \
+ .nmember = MRP_ARRAY_SIZE(_var##_members), \
+ .hook = { NULL, NULL }, \
+ }
+
+/** Declare and register the given native type. */
+uint32_t mrp_register_native(mrp_native_type_t *type);
+
+/** Look up the type id of the given native type name. */
+uint32_t mrp_native_id(const char *type_name);
+
+/** Encode data of the given native type. */
+int mrp_encode_native(void *data, uint32_t id, size_t reserve, void **bufp,
+ size_t *sizep, mrp_typemap_t *idmap);
+
+/** Decode data of (the given) native type (if specified). */
+int mrp_decode_native(void **bufp, size_t *sizep, void **datap, uint32_t *idp,
+ mrp_typemap_t *idmap);
+
+/** Free data of the given native type, obtained from mrp_decode_native. */
+void mrp_free_native(void *data, uint32_t id);
+
+/** Print data of the given native type. */
+ssize_t mrp_print_native(char *buf, size_t size, void *data, uint32_t id);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_COMMON_NATIVE_TYPES_H__ */
diff --git a/src/common/process.c b/src/common/process.c
new file mode 100644
index 0000000..71a10e1
--- /dev/null
+++ b/src/common/process.c
@@ -0,0 +1,1077 @@
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/inotify.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <linux/cn_proc.h>
+#include <linux/netlink.h>
+#include <linux/connector.h>
+#include <linux/filter.h>
+
+#include <murphy/common/process.h>
+#include <murphy/common.h>
+
+
+#define MURPHY_PROCESS_INOTIFY_DIR "/var/run/murphy/processes"
+
+struct mrp_pid_watch_s {
+ pid_t pid;
+};
+
+typedef struct {
+ char *path; /* file name token */
+ pid_t pid;
+ char *filename;
+
+ /* either this ... */
+ mrp_process_watch_handler_t process_cb;
+ /* ... or this is set. */
+ mrp_pid_watch_handler_t pid_cb;
+
+ void *userdata;
+ mrp_io_watch_t *inotify_cb;
+} i_watch_t;
+
+typedef struct {
+ mrp_pid_watch_handler_t cb;
+ void *user_data;
+ mrp_pid_watch_t *w; /* identify the client */
+
+ mrp_list_hook_t hook;
+} nl_pid_client_t;
+
+typedef struct {
+ pid_t pid;
+ char pid_s[16]; /* memory for hashing */
+
+ mrp_list_hook_t clients;
+ int n_clients;
+ int busy : 1;
+ int dead : 1;
+} nl_pid_watch_t;
+
+/* murphy pid file directory notify */
+static int dir_fd;
+static int i_n_process_watches;
+
+/* inotify */
+static int i_fd;
+static mrp_htbl_t *i_watches;
+static mrp_io_watch_t *i_wd;
+
+/* netlink process listening */
+static int nl_sock;
+static bool subscribed;
+static mrp_io_watch_t *nl_wd;
+static mrp_htbl_t *nl_watches;
+static int nl_n_pid_watches;
+
+static bool id_ok(const char *id)
+{
+ int i, len;
+ /* restrict the input */
+
+ len = strlen(id);
+
+ for (i = 0; i < len; i++) {
+ bool character, number, special;
+
+ character = ((id[i] >= 'A' && id[i] <= 'Z') ||
+ (id[i] >= 'a' && id[i] <= 'z'));
+
+ number = (id[i] >= '0' && id[i] <= '9');
+
+ special = (id[i] == '-' || id[i] == '_');
+
+ if (!(character || number || special))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static char *path_from_id(const char *id)
+{
+ char buf[PATH_MAX];
+ int ret;
+
+ if (!id || !id_ok(id))
+ return NULL;
+
+ ret = snprintf(buf, PATH_MAX, "%s/%s", MURPHY_PROCESS_INOTIFY_DIR, id);
+
+ if (ret < 0 || ret >= PATH_MAX)
+ return NULL;
+
+ return mrp_strdup(buf);
+}
+
+
+static void htbl_free_nl_watch(void *key, void *object)
+{
+ nl_pid_watch_t *w = (nl_pid_watch_t *) object;
+
+ MRP_UNUSED(key);
+
+ if (!w->busy)
+ mrp_free(w);
+ else
+ w->dead = TRUE;
+}
+
+
+static void htbl_free_i_watch(void *key, void *object)
+{
+ i_watch_t *w = (i_watch_t *) object;
+
+ MRP_UNUSED(key);
+
+ mrp_free(w->path);
+ mrp_free(w->filename);
+ mrp_free(w);
+}
+
+
+static int initialize_dir()
+{
+ /* TODO: check if the directory is present; if not, create it */
+
+ return 0;
+}
+
+
+static void process_change(mrp_io_watch_t *wd, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ struct inotify_event *is;
+ int bufsize = sizeof(struct inotify_event) + PATH_MAX + 1;
+ char buf[bufsize];
+ i_watch_t *w;
+ FILE *f;
+
+ MRP_UNUSED(wd);
+ MRP_UNUSED(user_data);
+
+ if (!i_watches)
+ return;
+
+ if (events & MRP_IO_EVENT_IN) {
+ int read_bytes;
+ int processed_bytes = 0;
+
+ read_bytes = read(fd, buf, bufsize - 1);
+
+ if (read_bytes < 0) {
+ mrp_log_error("Failed to read event from inotify: %s",
+ strerror(errno));
+ return;
+ }
+
+ buf[read_bytes] = '\0';
+
+ while (processed_bytes < read_bytes) {
+ char *filename = NULL;
+
+ /* the kernel doesn't allow to read incomplete events */
+ is = (struct inotify_event *) (buf + processed_bytes);
+
+ processed_bytes += sizeof(struct inotify_event) + is->len;
+
+ if (is->len == 0) {
+ /* no file name */
+ continue;
+ }
+
+ if (is->wd != dir_fd) {
+ /* wrong descriptor? */
+ continue;
+ }
+
+ filename = path_from_id(is->name);
+
+ if (!filename)
+ continue;
+
+ w = (i_watch_t *) mrp_htbl_lookup(i_watches, filename);
+
+ if (w) {
+ f = fopen(filename, "r");
+
+ if (f) {
+ fclose(f);
+ mrp_log_info("Received inotify event for %s, READY", w->path);
+ w->process_cb(w->path, MRP_PROCESS_STATE_READY, w->userdata);
+ }
+ else {
+ mrp_log_info("Received inotify event for %s, NOT READY", w->path);
+ w->process_cb(w->path, MRP_PROCESS_STATE_NOT_READY, w->userdata);
+ }
+ }
+ mrp_free(filename);
+ }
+ }
+}
+
+
+static int send_proc_cmd(enum proc_cn_mcast_op cmd)
+{
+ int data_size = sizeof(enum proc_cn_mcast_op);
+
+ /* connector message size */
+ int cn_size = sizeof(struct cn_msg);
+
+ /* total size of bytes we need */
+ int message_size = NLMSG_SPACE(cn_size + data_size);
+
+ /* aligned size */
+ int payload_size = NLMSG_LENGTH(message_size - sizeof(struct nlmsghdr));
+
+ /* helper pointers */
+ struct nlmsghdr *nl;
+ struct cn_msg *cn;
+
+ /* message data */
+ char buf[message_size];
+
+ if (nl_sock <= 0) {
+ mrp_log_error("invalid netlink socket %d", nl_sock);
+ return -1;
+ }
+
+ /* structs point to the aligned memory */
+ nl = (struct nlmsghdr *) buf;
+ cn = (struct cn_msg *) NLMSG_DATA(nl);
+
+ /* fill the structures */
+ nl->nlmsg_len = payload_size;
+ nl->nlmsg_seq = 0;
+ nl->nlmsg_pid = getpid();
+ nl->nlmsg_type = NLMSG_DONE;
+ nl->nlmsg_flags = 0;
+
+ cn->len = data_size;
+ cn->seq = 0;
+ cn->ack = 0;
+ cn->id.idx = CN_IDX_PROC;
+ cn->id.val = CN_VAL_PROC;
+ cn->flags = 0;
+
+ /* all this was done for this data */
+ memcpy(cn->data, &cmd, data_size);
+
+ return send(nl_sock, buf, message_size, 0);
+}
+
+
+static int subscribe_proc_events()
+{
+ int ret = send_proc_cmd(PROC_CN_MCAST_LISTEN);
+
+ if (ret >= 0)
+ subscribed = TRUE;
+
+ return ret;
+}
+
+
+static int unsubscribe_proc_events()
+{
+ int ret = send_proc_cmd(PROC_CN_MCAST_IGNORE);
+
+ if (ret >= 0)
+ subscribed = FALSE;
+
+ return ret;
+}
+
+
+static void nl_watch(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ char buf[4096];
+ struct nlmsghdr *nl;
+ struct sockaddr_nl addr;
+ unsigned int sockaddr_len;
+ ssize_t len;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(events);
+ MRP_UNUSED(user_data);
+
+ sockaddr_len = sizeof(struct sockaddr_nl);
+
+ len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &addr,
+ &sockaddr_len);
+
+ if (len < 0) {
+ mrp_log_error("failed to read data from socket");
+ return;
+ }
+
+ if (addr.nl_pid != 0) {
+ mrp_log_error("message wasn't from the kernel");
+ return;
+ }
+
+ /* set pointer to the first message in the buffer */
+ nl = (struct nlmsghdr *) buf;
+
+ while (NLMSG_OK(nl, (unsigned int) len)) {
+ struct cn_msg *cn;
+
+ /* we are expecting a non-multipart message -- filter errors and
+ * others away */
+ if (nl->nlmsg_type != NLMSG_DONE) {
+ if (nl->nlmsg_type == NLMSG_ERROR) {
+ /* TODO: error processing, resynchronization */
+ }
+ nl = NLMSG_NEXT(nl, len);
+ continue;
+ }
+
+ cn = (struct cn_msg *) NLMSG_DATA(nl);
+
+ if (cn->id.idx == CN_IDX_PROC && cn->id.val == CN_VAL_PROC) {
+ struct proc_event *ev = (struct proc_event *) cn->data;
+
+ switch (ev->what) {
+ case PROC_EVENT_EXIT:
+ {
+ mrp_list_hook_t *p, *n;
+ nl_pid_watch_t *nl_w;
+ char pid_s[16];
+ int ret;
+
+ mrp_log_info("process %d exited",
+ ev->event_data.exit.process_pid);
+
+ ret = snprintf(pid_s, sizeof(pid_s), "%u",
+ (unsigned int) ev->event_data.exit.process_pid);
+
+ if (ret < 0 || ret >= (int) sizeof(pid_s))
+ break;
+
+ /* check the pid */
+ nl_w = (nl_pid_watch_t *) mrp_htbl_lookup(nl_watches, pid_s);
+
+ if (!nl_w) {
+ mrp_log_error("pid %s exited but no-one was following it", pid_s);
+ break;
+ }
+
+ nl_w->busy = TRUE;
+ mrp_list_foreach(&nl_w->clients, p, n) {
+ nl_pid_client_t *client;
+
+ client = mrp_list_entry(p, typeof(*client), hook);
+ client->cb(nl_w->pid, MRP_PROCESS_STATE_NOT_READY,
+ client->user_data);
+ }
+ if (nl_w->dead)
+ mrp_free(nl_w);
+ else
+ nl_w->busy = FALSE;
+
+ /* TODO: should we automatically free the wathces? Or let
+ * client do that to preserver symmetricity? */
+ break;
+ }
+ default:
+ /* the filter isn't working for some reason */
+ mrp_log_error("some other message!\n");
+ break;
+ }
+ }
+
+ nl = NLMSG_NEXT(nl, len);
+ }
+}
+
+
+static int initialize(mrp_mainloop_t *ml, bool process, bool pid)
+{
+ if (process) {
+
+ if (initialize_dir() < 0)
+ goto error;
+
+ if (i_fd <= 0) {
+ i_fd = inotify_init();
+
+ if (i_fd <= 0)
+ goto error;
+ }
+
+ if (dir_fd <= 0) {
+
+ dir_fd = inotify_add_watch(i_fd, MURPHY_PROCESS_INOTIFY_DIR,
+ IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_MODIFY);
+
+ if (dir_fd < 0)
+ goto error;
+ }
+
+ if (!i_wd) {
+ i_wd = mrp_add_io_watch(ml, i_fd, MRP_IO_EVENT_IN, process_change, NULL);
+
+ if (!i_wd)
+ goto error;
+ }
+
+ if (!i_watches) {
+ mrp_htbl_config_t watches_conf;
+
+ watches_conf.comp = mrp_string_comp;
+ watches_conf.hash = mrp_string_hash;
+ watches_conf.free = htbl_free_i_watch;
+ watches_conf.nbucket = 0;
+ watches_conf.nentry = 10;
+
+ i_watches = mrp_htbl_create(&watches_conf);
+
+ if (!i_watches)
+ goto error;
+ }
+ }
+
+ if (pid) {
+ if (nl_sock <= 0) {
+ struct sockaddr_nl nl_addr;
+ int nl_options = SOCK_NONBLOCK | SOCK_DGRAM | SOCK_CLOEXEC;
+ struct sock_filter block[] = {
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+ };
+ struct sock_fprog fp;
+
+ /* socket creation */
+
+ nl_sock = socket(PF_NETLINK, nl_options, NETLINK_CONNECTOR);
+
+ if (nl_sock <= 0)
+ goto error;
+
+ memset(&nl_addr, 0, sizeof(struct sockaddr_nl));
+ memset(&fp, 0, sizeof(struct sock_fprog));
+
+ /* bind the socket to the address */
+
+ nl_addr.nl_pid = getpid();
+ nl_addr.nl_family = AF_NETLINK;
+ nl_addr.nl_groups = CN_IDX_PROC;
+
+ if (bind(nl_sock, (struct sockaddr *) &nl_addr,
+ sizeof(struct sockaddr_nl)) < 0)
+ goto error;
+
+ fp.filter = block;
+ fp.len = 1;
+
+ /* set socket filter that blocks everything */
+ if (setsockopt(nl_sock, SOL_SOCKET, SO_ATTACH_FILTER, &fp,
+ sizeof(struct sock_fprog)) < 0) {
+ mrp_log_error("setting blocking socket filter failed: %s",
+ strerror(errno));
+ goto error;
+ }
+
+ nl_wd = mrp_add_io_watch(ml, nl_sock, MRP_IO_EVENT_IN, nl_watch, NULL);
+ }
+
+ if (!nl_watches) {
+ mrp_htbl_config_t watches_conf;
+
+ watches_conf.comp = mrp_string_comp;
+ watches_conf.hash = mrp_string_hash;
+ watches_conf.free = htbl_free_nl_watch;
+ watches_conf.nbucket = 0;
+ watches_conf.nentry = 10;
+
+ nl_watches = mrp_htbl_create(&watches_conf);
+
+ if (!nl_watches)
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ mrp_log_error("initialization error");
+
+ if (process) {
+
+ if (i_watches) {
+ mrp_htbl_destroy(i_watches, FALSE);
+ i_watches = NULL;
+ }
+
+ if (i_wd) {
+ mrp_del_io_watch(i_wd);
+ i_wd = NULL;
+ }
+
+ if (i_fd && dir_fd) {
+ inotify_rm_watch(i_fd, dir_fd);
+ dir_fd = -1;
+ }
+
+ i_n_process_watches = 0;
+ }
+
+ if (pid) {
+
+ if (nl_sock > 0) {
+ close(nl_sock);
+ nl_sock = -1;
+ }
+
+ nl_n_pid_watches = 0;
+ }
+
+ return -1;
+}
+
+
+static int initialize_process(mrp_mainloop_t *ml)
+{
+ return initialize(ml, TRUE, FALSE);
+}
+
+
+static int initialize_pid(mrp_mainloop_t *ml)
+{
+ return initialize(ml, FALSE, TRUE);
+}
+
+
+int mrp_process_set_state(const char *id, mrp_process_state_t state)
+{
+ char *path = NULL;
+ FILE *f;
+ int ret = -1;
+
+ if (initialize_dir() < 0)
+ goto end;
+
+ path = path_from_id(id);
+
+ if (!path)
+ goto end;
+
+ switch (state) {
+ case MRP_PROCESS_STATE_UNKNOWN:
+ case MRP_PROCESS_STATE_NOT_READY:
+ if (unlink(path) < 0) {
+ if (errno != ENOENT) {
+ goto end;
+ }
+ }
+ break;
+ case MRP_PROCESS_STATE_READY:
+ f = fopen(path, "w");
+ if (!f)
+ goto end;
+
+ fclose(f);
+ break;
+ }
+
+ ret = 0;
+
+end:
+ mrp_free(path);
+ return ret;
+}
+
+
+static void filter_add_pid(struct sock_filter *p, pid_t pid, int proc_offset)
+{
+ int proc_event_data_offset = proc_offset +
+ offsetof(struct proc_event, event_data);
+ /* event_data is an union, leaving out */
+ int proc_event_data_exit_pid_offset = proc_event_data_offset +
+ offsetof(struct exit_proc_event, process_pid);
+
+ struct sock_filter bpf[] = {
+ /* load the pid value ... */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, proc_event_data_exit_pid_offset),
+
+ /* ... if it is the pid we're comparing it to ... */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(pid), 0, 1),
+
+ /* ... return success immediately ... */
+ BPF_STMT(BPF_RET | BPF_K, 0xffffffff),
+
+ /* ... otherwise proceed to the next comparison or exit. */
+ };
+
+ mrp_debug("adding pid %d to filter\n", pid);
+
+ memcpy(p, bpf, 3*sizeof(struct sock_filter));
+}
+
+
+static int filter_update(pid_t pids[], int len_pids)
+{
+ int nl_type_offset = offsetof(struct nlmsghdr, nlmsg_type);
+ int cn_offset = NLMSG_LENGTH(0);
+ int cn_id_offset = cn_offset + offsetof(struct cn_msg, id);
+ int cn_idx_offset = cn_id_offset + offsetof(struct cb_id, idx);
+ int cn_val_offset = cn_id_offset + offsetof(struct cb_id, val);
+ int proc_offset = cn_offset + offsetof(struct cn_msg, data);
+ int proc_what_offset = proc_offset + offsetof(struct proc_event, what);
+
+ struct sock_fprog fp;
+ struct sock_filter *bpf, *iter;
+
+ struct sock_filter bpf_header[] = {
+
+ /* check that the message has only one part or is an error
+ * ( NLMSG_DONE || NLMSG_ERROR) -- return error immediately */
+
+ /* load the message type */
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, nl_type_offset),
+
+ /* check the error type */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(NLMSG_ERROR), 0, 1),
+
+ /* return if error */
+ BPF_STMT(BPF_RET | BPF_K, 0xffffffff),
+
+ /* check the done type */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(NLMSG_DONE), 1, 0),
+
+ /* filter the packet if not NLMSG_DONE */
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+
+ /* check that the message is from the proc connector
+ * ( CN_IDX_PROC && CN_VAL_PROC ) */
+
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, cn_idx_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(CN_IDX_PROC), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, cn_val_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(CN_VAL_PROC), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+
+ /* check that the message is a PROC_EVENT_EXIT message */
+
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, proc_what_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(PROC_EVENT_EXIT), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+ };
+
+ struct sock_filter bpf_footer[] = {
+ /* if there was no pid match then filter the packet */
+ BPF_STMT (BPF_RET | BPF_K, 0x0),
+ };
+
+ int len_bpf_header = sizeof(bpf_header);
+ int len_bpf_footer = sizeof(bpf_footer);
+ /* three statements */
+ int len_bpf_pids = sizeof(struct sock_filter) * len_pids * 3;
+ int len = len_bpf_header + len_bpf_pids + len_bpf_footer;
+ int i;
+
+ if (nl_sock <= 0) {
+ mrp_log_error("invalid netlink socket %d", nl_sock);
+ goto error;
+ }
+
+ /* build the filter */
+
+ bpf = (struct sock_filter *) mrp_allocz(len);
+
+ if (!bpf)
+ goto error;
+
+ iter = bpf;
+
+ memcpy(iter, bpf_header, len_bpf_header);
+
+ iter = iter + (len_bpf_header / sizeof(struct sock_filter));
+
+ /* check that the PID is one that we are following */
+ for (i = 0; i < len_pids; i++, iter=iter+3) {
+ filter_add_pid(iter, pids[i], proc_offset);
+ }
+
+ memcpy(iter, bpf_footer, len_bpf_footer);
+
+ memset(&fp, 0, sizeof(struct sock_fprog));
+ fp.filter = bpf;
+ fp.len = len / sizeof(struct sock_filter);
+
+ if (setsockopt(nl_sock, SOL_SOCKET, SO_ATTACH_FILTER, &fp,
+ sizeof(struct sock_fprog)) < 0)
+ mrp_log_error("setting socket filter failed: %s", strerror(errno));
+
+ mrp_free(bpf);
+
+error:
+ return -1;
+}
+
+
+struct key_data_s {
+ int index;
+ pid_t *pids;
+};
+
+
+static int gather_pids_cb(void *key, void *object, void *user_data)
+{
+ struct key_data_s *kd = (struct key_data_s *) user_data;
+ nl_pid_watch_t *w = (nl_pid_watch_t *) object;
+
+ MRP_UNUSED(key);
+
+ kd->pids[kd->index] = w->pid;
+ kd->index++;
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static int pid_filter_update()
+{
+ pid_t pids[nl_n_pid_watches];
+
+ struct key_data_s kd;
+
+ kd.index = 0;
+ kd.pids = pids;
+
+ mrp_htbl_foreach(nl_watches, gather_pids_cb, &kd);
+
+ return filter_update(pids, kd.index);
+}
+
+
+mrp_process_state_t mrp_process_query_state(const char *id)
+{
+ char *path;
+ FILE *f;
+
+ if (initialize_dir() < 0)
+ return MRP_PROCESS_STATE_UNKNOWN;
+
+ path = path_from_id(id);
+
+ if (!path)
+ return MRP_PROCESS_STATE_UNKNOWN;
+
+ f = fopen(path, "r");
+
+ mrp_free(path);
+
+ if (f) {
+ fclose(f);
+ return MRP_PROCESS_STATE_READY;
+ }
+
+ return MRP_PROCESS_STATE_NOT_READY;
+}
+
+
+mrp_process_state_t mrp_pid_query_state(pid_t pid)
+{
+ char path[64];
+ struct stat s;
+ mrp_process_state_t state = MRP_PROCESS_STATE_UNKNOWN;
+ int ret;
+
+ ret = snprintf(path, sizeof(path), "/proc/%u", (unsigned int) pid);
+
+ if (ret < 0 || ret >= (int) sizeof(path))
+ goto end;
+
+ ret = stat(path, &s);
+
+ if (ret < 0 && (errno == ENOENT || errno == ENOTDIR)) {
+ state = MRP_PROCESS_STATE_NOT_READY;
+ }
+ else if (ret == 0 && S_ISDIR(s.st_mode)) {
+ state = MRP_PROCESS_STATE_READY;
+ }
+
+end:
+ return state;
+}
+
+
+int mrp_process_set_watch(const char *id, mrp_mainloop_t *ml,
+ mrp_process_watch_handler_t cb, void *userdata)
+{
+ i_watch_t *w = NULL;
+
+ if (initialize_process(ml) < 0)
+ goto error;
+
+ w = (i_watch_t *) mrp_allocz(sizeof(i_watch_t));
+
+ if (!w)
+ goto error;
+
+ w->inotify_cb = i_wd;
+ w->userdata = userdata;
+ w->process_cb = cb;
+
+ w->path = mrp_strdup(id);
+ if (!w->path)
+ goto error;
+
+ w->filename = path_from_id(id);
+ if (!w->filename)
+ goto error;
+
+ if (mrp_htbl_insert(i_watches, w->filename, w) < 0)
+ goto error;
+
+ i_n_process_watches++;
+
+ return 0;
+
+error:
+ if (w) {
+ mrp_free(w->path);
+ mrp_free(w->filename);
+ mrp_free(w);
+ }
+
+ return -1;
+}
+
+
+mrp_pid_watch_t *mrp_pid_set_watch(pid_t pid, mrp_mainloop_t *ml,
+ mrp_pid_watch_handler_t cb, void *userdata)
+{
+ nl_pid_watch_t *nl_w = NULL;
+ nl_pid_client_t *client = NULL;
+ char pid_s[16];
+ bool already_inserted = TRUE;
+ int ret;
+
+ if (initialize_pid(ml) < 0)
+ goto error;
+
+ ret = snprintf(pid_s, sizeof(pid_s), "%u", (unsigned int) pid);
+
+ if (ret < 0 || ret >= (int) sizeof(pid_s))
+ goto error;
+
+ nl_w = (nl_pid_watch_t *) mrp_htbl_lookup(nl_watches, pid_s);
+
+ if (!nl_w) {
+
+ nl_w = (nl_pid_watch_t *) mrp_allocz(sizeof(nl_pid_watch_t));
+
+ if (!nl_w)
+ goto error;
+
+ mrp_list_init(&nl_w->clients);
+ nl_w->pid = pid;
+ memcpy(nl_w->pid_s, pid_s, sizeof(nl_w->pid_s));
+
+ already_inserted = FALSE;
+ }
+
+ client = (nl_pid_client_t *) mrp_allocz(sizeof(nl_pid_client_t));
+
+ if (!client) {
+ if (!already_inserted)
+ mrp_free(nl_w);
+ goto error;
+ }
+
+ client->cb = cb;
+ client->user_data = userdata;
+ client->w = (mrp_pid_watch_t *) mrp_allocz(sizeof(mrp_pid_watch_t));
+
+ if (!client->w) {
+ mrp_free(nl_w);
+ goto error;
+ }
+
+ client->w->pid = pid;
+
+ mrp_list_init(&client->hook);
+ mrp_list_append(&nl_w->clients, &client->hook);
+ nl_w->n_clients++;
+
+ if (!already_inserted) {
+ if (mrp_htbl_insert(nl_watches, nl_w->pid_s, nl_w) < 0) {
+ mrp_list_delete(&client->hook);
+ mrp_free(nl_w);
+ goto error;
+ }
+
+ nl_n_pid_watches++;
+ }
+
+ pid_filter_update();
+
+ if (!subscribed)
+ subscribe_proc_events();
+
+ /* check that the pid is still there -- return error if not */
+
+ if (mrp_pid_query_state(pid) != MRP_PROCESS_STATE_READY)
+ goto error_process;
+
+ return client->w;
+
+error_process:
+ mrp_pid_remove_watch(client->w);
+ client = NULL;
+
+error:
+ if (client) {
+ mrp_free(client);
+ mrp_free(client->w);
+ }
+
+ return NULL;
+}
+
+
+static void update_map()
+{
+ if (i_n_process_watches <= 0) {
+ if (i_fd > 0 && dir_fd > 0) {
+ inotify_rm_watch(i_fd, dir_fd);
+ dir_fd = -1;
+ }
+ i_n_process_watches = 0;
+ }
+
+ if ((i_n_process_watches) == 0) {
+ mrp_htbl_destroy(i_watches, TRUE);
+ i_watches = NULL;
+ }
+}
+
+
+int mrp_process_remove_watch(const char *id)
+{
+ i_watch_t *w;
+ char *filename;
+
+ if (!i_watches)
+ return -1;
+
+ filename = path_from_id(id);
+
+ w = (i_watch_t *) mrp_htbl_lookup(i_watches, (void *)filename);
+
+ if (!w) {
+ mrp_free(filename);
+ return -1;
+ }
+
+ mrp_htbl_remove(i_watches, (void *) filename, TRUE);
+ i_n_process_watches--;
+
+ update_map();
+
+ mrp_free(filename);
+ return 0;
+}
+
+
+int mrp_pid_remove_watch(mrp_pid_watch_t *w)
+{
+ nl_pid_watch_t *nl_w = NULL;
+ nl_pid_client_t *client = NULL;
+ char pid_s[16];
+ mrp_list_hook_t *p, *n;
+ bool found = FALSE;
+ int ret;
+
+ if (!w)
+ goto error;
+
+ ret = snprintf(pid_s, sizeof(pid_s), "%u", (unsigned int) w->pid);
+
+ if (ret < 0 || ret >= (int) sizeof(pid_s))
+ goto error;
+
+ nl_w = (nl_pid_watch_t *) mrp_htbl_lookup(nl_watches, pid_s);
+
+ if (!nl_w) {
+ mrp_log_error("no corresponding pid watch found");
+ goto error;
+ }
+
+ mrp_list_foreach(&nl_w->clients, p, n) {
+ client = mrp_list_entry(p, typeof(*client), hook);
+ if (client->w == w) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ mrp_log_error("not registered to watch the pid");
+ goto error;
+ }
+
+ mrp_list_delete(&client->hook);
+ mrp_free(client);
+ nl_w->n_clients--;
+
+ if (nl_w->n_clients == 0) {
+ /* no-one is interested in this pid anymore */
+ mrp_htbl_remove(nl_watches, pid_s, TRUE);
+ nl_n_pid_watches--;
+
+ pid_filter_update();
+
+ if (nl_n_pid_watches == 0) {
+ /* no-one is following pids anymore */
+ if (subscribed)
+ unsubscribe_proc_events();
+ }
+ }
+
+ mrp_free(w);
+ return 0;
+
+error:
+ mrp_free(w);
+ return -1;
+}
diff --git a/src/common/process.h b/src/common/process.h
new file mode 100644
index 0000000..956a367
--- /dev/null
+++ b/src/common/process.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_PROCESS_H__
+#define __MURPHY_PROCESS_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+/* functions for the murphy family of processes */
+
+typedef enum {
+ MRP_PROCESS_STATE_UNKNOWN,
+ MRP_PROCESS_STATE_READY,
+ MRP_PROCESS_STATE_NOT_READY
+} mrp_process_state_t;
+
+typedef void (*mrp_process_watch_handler_t)(const char *,
+ mrp_process_state_t, void *);
+
+int mrp_process_set_state(const char *id, mrp_process_state_t state);
+
+mrp_process_state_t mrp_process_query_state(const char *id);
+
+int mrp_process_set_watch(const char *id, mrp_mainloop_t *ml,
+ mrp_process_watch_handler_t cb, void *userdata);
+
+int mrp_process_remove_watch(const char *id);
+
+/* functions to track external processes by pid */
+
+typedef struct mrp_pid_watch_s mrp_pid_watch_t;
+
+typedef void (*mrp_pid_watch_handler_t)(pid_t,
+ mrp_process_state_t, void *);
+
+mrp_process_state_t mrp_pid_query_state(pid_t pid);
+
+mrp_pid_watch_t *mrp_pid_set_watch(pid_t pid, mrp_mainloop_t *ml,
+ mrp_pid_watch_handler_t cb, void *userdata);
+
+int mrp_pid_remove_watch(mrp_pid_watch_t *w);
+
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_PROCESS_H__ */
diff --git a/src/common/pulse-glue.c b/src/common/pulse-glue.c
new file mode 100644
index 0000000..98dbf24
--- /dev/null
+++ b/src/common/pulse-glue.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <pulse/mainloop-api.h>
+#include <pulse/timeval.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+#include <murphy/common/pulse-glue.h>
+
+
+typedef struct {
+ pa_mainloop_api *pa;
+} pulse_glue_t;
+
+
+typedef struct {
+ pa_io_event *pa_io;
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ void *user_data;
+ void *glue_data;
+} io_t;
+
+
+typedef struct {
+ pa_time_event *pa_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} tmr_t;
+
+
+typedef struct {
+ pa_defer_event *pa_d;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} dfr_t;
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+static void del_io(void *glue_data, void *id);
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_timer(void *glue_data, void *id);
+static void mod_timer(void *glue_data, void *id, unsigned int msecs);
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_defer(void *glue_data, void *id);
+static void mod_defer(void *glue_data, void *id, int enabled);
+
+
+static void io_cb(pa_mainloop_api *pa, pa_io_event *e, int fd,
+ pa_io_event_flags_t mask, void *user_data)
+{
+ io_t *io = (io_t *)user_data;
+ mrp_io_event_t events = MRP_IO_EVENT_NONE;
+
+ MRP_UNUSED(pa);
+ MRP_UNUSED(e);
+
+ if (mask & PA_IO_EVENT_INPUT) events |= MRP_IO_EVENT_IN;
+ if (mask & PA_IO_EVENT_OUTPUT) events |= MRP_IO_EVENT_OUT;
+ if (mask & PA_IO_EVENT_HANGUP) events |= MRP_IO_EVENT_HUP;
+ if (mask & PA_IO_EVENT_ERROR) events |= MRP_IO_EVENT_ERR;
+
+ io->cb(io->glue_data, io, fd, events, io->user_data);
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ pa_io_event_flags_t mask = PA_IO_EVENT_NULL;
+ io_t *io;
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io != NULL) {
+ if (events & MRP_IO_EVENT_IN) mask |= PA_IO_EVENT_INPUT;
+ if (events & MRP_IO_EVENT_OUT) mask |= PA_IO_EVENT_OUTPUT;
+ if (events & MRP_IO_EVENT_HUP) mask |= PA_IO_EVENT_HANGUP;
+ if (events & MRP_IO_EVENT_ERR) mask |= PA_IO_EVENT_ERROR;
+
+ io->pa_io = pa->io_new(pa, fd, mask, io_cb, io);
+
+ if (io->pa_io != NULL) {
+ io->cb = cb;
+ io->user_data = user_data;
+ io->glue_data = glue_data;
+
+ return io;
+ }
+ else
+ mrp_free(io);
+ }
+
+ return NULL;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ io_t *io = (io_t *)id;
+
+ pa->io_free(io->pa_io);
+ mrp_free(io);
+}
+
+
+static void timer_cb(pa_mainloop_api *pa, pa_time_event *e,
+ const struct timeval *tv, void *user_data)
+{
+ tmr_t *t = (tmr_t *)user_data;
+
+ MRP_UNUSED(pa);
+ MRP_UNUSED(e);
+ MRP_UNUSED(tv);
+
+ t->cb(t->glue_data, t, t->user_data);
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ struct timeval tv;
+ tmr_t *t;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ pa_gettimeofday(&tv);
+
+ tv.tv_sec += msecs / 1000;
+ tv.tv_usec += 1000 * (msecs % 1000);
+
+ t->pa_t = pa->time_new(pa, &tv, timer_cb, t);
+
+ if (t->pa_t != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->glue_data = glue_data;
+
+ return t;
+ }
+ else
+ mrp_free(t);
+ }
+
+ return NULL;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ tmr_t *t = (tmr_t *)id;
+
+ pa->time_free(t->pa_t);
+ mrp_free(t);
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ tmr_t *t = (tmr_t *)id;
+ struct timeval tv;
+
+ if (t != NULL) {
+ pa_gettimeofday(&tv);
+
+ tv.tv_sec += msecs / 1000;
+ tv.tv_usec += 1000 * (msecs % 1000);
+
+ pa->time_restart(t->pa_t, &tv);
+ }
+}
+
+
+static void defer_cb(pa_mainloop_api *pa, pa_defer_event *e, void *user_data)
+{
+ dfr_t *d = (dfr_t *)user_data;
+
+ MRP_UNUSED(pa);
+ MRP_UNUSED(e);
+
+ d->cb(d->glue_data, d, d->user_data);
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ dfr_t *d;
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d != NULL) {
+ d->pa_d = pa->defer_new(pa, defer_cb, d);
+
+ if (d->pa_d != NULL) {
+ d->cb = cb;
+ d->user_data = user_data;
+ d->glue_data = glue_data;
+
+ return d;
+ }
+ else
+ mrp_free(d);
+ }
+
+ return NULL;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ dfr_t *d = (dfr_t *)id;
+
+ pa->defer_free(d->pa_d);
+ mrp_free(d);
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ dfr_t *d = (dfr_t *)id;
+
+ pa->defer_enable(d->pa_d, !!enabled);
+}
+
+
+static void unregister(void *data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)data;
+
+ mrp_free(glue);
+}
+
+
+static mrp_superloop_ops_t pa_ops = {
+ .add_io = add_io,
+ .del_io = del_io,
+ .add_timer = add_timer,
+ .del_timer = del_timer,
+ .mod_timer = mod_timer,
+ .add_defer = add_defer,
+ .del_defer = del_defer,
+ .mod_defer = mod_defer,
+ .unregister = unregister,
+};
+
+
+int mrp_mainloop_register_with_pulse(mrp_mainloop_t *ml, pa_mainloop_api *pa)
+{
+ pulse_glue_t *glue;
+
+ glue = mrp_allocz(sizeof(*glue));
+
+ if (glue != NULL) {
+ glue->pa = pa;
+
+ if (mrp_set_superloop(ml, &pa_ops, glue))
+ return TRUE;
+ else
+ mrp_free(glue);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_mainloop_unregister_from_pulse(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+
+static mrp_mainloop_t *pulse_ml;
+
+mrp_mainloop_t *mrp_mainloop_pulse_get(pa_mainloop_api *pa)
+{
+ if (pulse_ml == NULL) {
+ pulse_ml = mrp_mainloop_create();
+
+ if (pulse_ml != NULL) {
+ if (!mrp_mainloop_register_with_pulse(pulse_ml, pa)) {
+ mrp_mainloop_destroy(pulse_ml);
+ pulse_ml = NULL;
+ }
+ }
+ }
+
+ return pulse_ml;
+}
diff --git a/src/common/pulse-glue.h b/src/common/pulse-glue.h
new file mode 100644
index 0000000..24fa3e8
--- /dev/null
+++ b/src/common/pulse-glue.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_PULSE_H__
+#define __MURPHY_PULSE_H__
+
+#include <murphy/common/mainloop.h>
+#include <pulse/mainloop.h>
+
+/** Register the given murphy mainloop with the given pulse mainloop. */
+int mrp_mainloop_register_with_pulse(mrp_mainloop_t *ml, pa_mainloop_api *pa);
+
+/** Unrgister the given murphy mainloop from the given pulse mainloop. */
+int mrp_mainloop_unregister_from_pulse(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the given pulse mainloop. */
+mrp_mainloop_t *mrp_mainloop_pulse_get(pa_mainloop_api *pa);
+
+#endif /* __MURPHY_PULSE_H__ */
diff --git a/src/common/pulse-subloop.c b/src/common/pulse-subloop.c
new file mode 100644
index 0000000..7e08b6c
--- /dev/null
+++ b/src/common/pulse-subloop.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include <murphy/common/debug.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/pulse-subloop.h>
+
+
+struct pa_murphy_mainloop {
+ mrp_mainloop_t *ml;
+ pa_mainloop_api api;
+ mrp_list_hook_t io_events;
+ mrp_list_hook_t time_events;
+ mrp_list_hook_t defer_events;
+ mrp_list_hook_t io_dead;
+ mrp_list_hook_t time_dead;
+ mrp_list_hook_t defer_dead;
+};
+
+
+struct pa_io_event {
+ pa_murphy_mainloop *m;
+ int fd;
+ mrp_io_watch_t *w;
+ pa_io_event_cb_t cb;
+ pa_io_event_destroy_cb_t destroy;
+ void *userdata;
+ mrp_list_hook_t hook;
+ int busy : 1;
+ int dead : 1;
+};
+
+
+struct pa_time_event {
+ pa_murphy_mainloop *m;
+ mrp_timer_t *t;
+ struct timeval tv;
+ pa_time_event_cb_t cb;
+ pa_time_event_destroy_cb_t destroy;
+ void *userdata;
+ mrp_list_hook_t hook;
+ int busy : 1;
+ int dead : 1;
+};
+
+
+struct pa_defer_event {
+ pa_murphy_mainloop *m;
+ mrp_deferred_t *d;
+ pa_defer_event_cb_t cb;
+ pa_defer_event_destroy_cb_t destroy;
+ void *userdata;
+ mrp_list_hook_t hook;
+ int busy : 1;
+ int dead : 1;
+};
+
+
+pa_murphy_mainloop *pa_murphy_mainloop_new(mrp_mainloop_t *ml)
+{
+ pa_murphy_mainloop *m;
+
+ if (ml == NULL)
+ return NULL;
+
+ m = mrp_allocz(sizeof(*m));
+
+ if (m == NULL)
+ return NULL;
+
+ m->ml = ml;
+ mrp_list_init(&m->io_events);
+ mrp_list_init(&m->time_events);
+ mrp_list_init(&m->defer_events);
+ mrp_list_init(&m->io_dead);
+ mrp_list_init(&m->time_dead);
+ mrp_list_init(&m->defer_dead);
+
+ return m;
+}
+
+
+static void cleanup_io_events(pa_murphy_mainloop *m)
+{
+ mrp_list_hook_t *p, *n;
+ pa_io_event *io;
+
+ mrp_list_foreach(&m->io_events, p, n) {
+ io = mrp_list_entry(p, typeof(*io), hook);
+
+ mrp_list_delete(&io->hook);
+ mrp_del_io_watch(io->w);
+ io->w = NULL;
+
+ if (io->destroy != NULL) {
+ io->dead = true;
+ io->destroy(&io->m->api, io, io->userdata);
+ }
+
+ mrp_free(io);
+ }
+
+ mrp_list_foreach(&m->io_dead, p, n) {
+ io = mrp_list_entry(p, typeof(*io), hook);
+ mrp_list_delete(&io->hook);
+ mrp_free(io);
+ }
+}
+
+
+static void cleanup_time_events(pa_murphy_mainloop *m)
+{
+ mrp_list_hook_t *p, *n;
+ pa_time_event *t;
+
+ mrp_list_foreach(&m->time_events, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ mrp_list_delete(&t->hook);
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ if (t->destroy != NULL) {
+ t->dead = true;
+ t->destroy(&t->m->api, t, t->userdata);
+ }
+
+ mrp_free(t);
+ }
+
+ mrp_list_foreach(&m->time_dead, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+ mrp_list_delete(&t->hook);
+ mrp_free(t);
+ }
+}
+
+
+static void cleanup_defer_events(pa_murphy_mainloop *m)
+{
+ mrp_list_hook_t *p, *n;
+ pa_defer_event *d;
+
+ mrp_list_foreach(&m->defer_events, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+
+ mrp_list_delete(&d->hook);
+ mrp_del_deferred(d->d);
+ d->d = NULL;
+
+ if (d->destroy != NULL) {
+ d->dead = true;
+ d->destroy(&d->m->api, d, d->userdata);
+ }
+
+ mrp_free(d);
+ }
+
+ mrp_list_foreach(&m->defer_dead, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ mrp_list_delete(&d->hook);
+ mrp_free(d);
+ }
+}
+
+
+void pa_murphy_mainloop_free(pa_murphy_mainloop *m)
+{
+ if (m == NULL)
+ return;
+
+ cleanup_io_events(m);
+ cleanup_time_events(m);
+ cleanup_defer_events(m);
+}
+
+
+static void io_event_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *userdata)
+{
+ pa_io_event *io = (pa_io_event *)userdata;
+ pa_io_event_flags_t flags = 0;
+
+ MRP_UNUSED(w);
+
+ mrp_debug("PA I/O event 0x%x for watch %p (fd %d)", events, io, fd);
+
+ if (events & MRP_IO_EVENT_IN) flags |= PA_IO_EVENT_INPUT;
+ if (events & MRP_IO_EVENT_OUT) flags |= PA_IO_EVENT_OUTPUT;
+ if (events & MRP_IO_EVENT_HUP) flags |= PA_IO_EVENT_HANGUP;
+ if (events & MRP_IO_EVENT_ERR) flags |= PA_IO_EVENT_ERROR;
+
+ io->busy = true;
+ io->cb(&io->m->api, io, fd, flags, io->userdata);
+ io->busy = false;
+
+ if (io->dead) {
+ mrp_list_delete(&io->hook);
+ mrp_free(io);
+ }
+}
+
+
+static pa_io_event *io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t e,
+ pa_io_event_cb_t cb, void *userdata)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+ mrp_io_event_t events = 0;
+ pa_io_event *io;
+
+ mrp_debug("PA create I/O watch for fd %d, events 0x%x", fd, e);
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io == NULL)
+ return NULL;
+
+ mrp_list_init(&io->hook);
+
+ if (e & PA_IO_EVENT_INPUT) events |= MRP_IO_EVENT_IN;
+ if (e & PA_IO_EVENT_OUTPUT) events |= MRP_IO_EVENT_OUT;
+ if (e & PA_IO_EVENT_HANGUP) events |= MRP_IO_EVENT_HUP; /* RDHUP ? */
+ if (e & PA_IO_EVENT_ERROR) events |= MRP_IO_EVENT_ERR;
+
+ io->m = m;
+ io->fd = fd;
+ io->cb = cb;
+ io->userdata = userdata;
+ io->w = mrp_add_io_watch(m->ml, fd, events, io_event_cb, io);
+
+ if (io->w != NULL)
+ mrp_list_append(&m->io_events, &io->hook);
+ else {
+ mrp_free(io);
+ io = NULL;
+ }
+
+ return io;
+}
+
+
+static void io_enable(pa_io_event *io, pa_io_event_flags_t e)
+{
+ pa_murphy_mainloop *m = io->m;
+ mrp_io_event_t events = 0;
+
+ mrp_debug("PA enable events 0x%x for I/O watch %p (fd %d)", e, io, io->fd);
+
+ mrp_del_io_watch(io->w);
+ io->w = NULL;
+
+ if (e & PA_IO_EVENT_INPUT) events |= MRP_IO_EVENT_IN;
+ if (e & PA_IO_EVENT_OUTPUT) events |= MRP_IO_EVENT_OUT;
+ if (e & PA_IO_EVENT_HANGUP) events |= MRP_IO_EVENT_HUP; /* RDHUP ? */
+ if (e & PA_IO_EVENT_ERROR) events |= MRP_IO_EVENT_ERR;
+
+ io->w = mrp_add_io_watch(m->ml, io->fd, events, io_event_cb, io);
+}
+
+
+static void io_free(pa_io_event *io)
+{
+ pa_murphy_mainloop *m = io->m;
+
+ mrp_debug("PA free I/O watch %p (fd %d)", io, io->fd);
+
+ mrp_list_delete(&io->hook);
+ mrp_del_io_watch(io->w);
+ io->w = NULL;
+
+ io->dead = true;
+
+ if (!io->busy && !io->dead) {
+ io->busy = true;
+ if (io->destroy != NULL)
+ io->destroy(&io->m->api, io, io->userdata);
+ mrp_free(io);
+ }
+ else
+ mrp_list_append(&m->io_dead, &io->hook);
+}
+
+
+static void io_set_destroy(pa_io_event *io, pa_io_event_destroy_cb_t cb)
+{
+ mrp_debug("PA set I/O watch destroy callback for %p (fd %d) to %p",
+ io, io->fd, cb);
+
+ io->destroy = cb;
+}
+
+
+
+
+static void time_event_cb(mrp_timer_t *tmr, void *userdata)
+{
+ pa_time_event *t = (pa_time_event *)userdata;
+
+ MRP_UNUSED(tmr);
+
+ mrp_debug("PA time event for timer %p", t);
+
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ t->busy = true;
+ t->cb(&t->m->api, t, &t->tv, t->userdata);
+ t->busy = false;
+
+ if (t->dead) {
+ mrp_del_timer(t->t);
+ mrp_list_delete(&t->hook);
+ mrp_free(t);
+ }
+}
+
+
+static unsigned int timeval_diff(const struct timeval *from,
+ const struct timeval *to)
+{
+ int msecs, musecs, diff;
+
+ msecs = (to->tv_sec - from->tv_sec) * 1000;
+ musecs = ((int)to->tv_usec - (int)from->tv_usec) / 1000;
+
+ diff = msecs + musecs;
+
+ if (diff >= 0)
+ return (unsigned int)diff;
+ else
+ return 0;
+}
+
+
+static pa_time_event *time_new(pa_mainloop_api *api, const struct timeval *tv,
+ pa_time_event_cb_t cb, void *userdata)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+ pa_time_event *t;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ mrp_debug("PA create timer for %u msecs", timeval_diff(&now, tv));
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t == NULL)
+ return NULL;
+
+ mrp_list_init(&t->hook);
+
+ t->m = m;
+ t->cb = cb;
+ t->userdata = userdata;
+ t->t = mrp_add_timer(m->ml, timeval_diff(&now, tv), time_event_cb, t);
+
+ if (t->t != NULL)
+ mrp_list_append(&m->time_events, &t->hook);
+ else {
+ mrp_free(t);
+ t = NULL;
+ }
+
+ return t;
+}
+
+
+static void time_restart(pa_time_event *t, const struct timeval *tv)
+{
+ pa_murphy_mainloop *m = t->m;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ mrp_debug("PA restart timer %p with %u msecs", t, timeval_diff(&now, tv));
+
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ t->t = mrp_add_timer(m->ml, timeval_diff(&now, tv), time_event_cb, t);
+}
+
+
+static void time_free(pa_time_event *t)
+{
+ pa_murphy_mainloop *m = t->m;
+
+ mrp_debug("PA free timer %p", t);
+
+ mrp_list_delete(&t->hook);
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ t->dead = true;
+
+ if (!t->busy && !t->dead) {
+ t->busy = true;
+ if (t->destroy != NULL)
+ t->destroy(&t->m->api, t, t->userdata);
+ mrp_free(t);
+ }
+ else
+ mrp_list_append(&m->time_dead, &t->hook);
+}
+
+
+static void time_set_destroy(pa_time_event *t, pa_time_event_destroy_cb_t cb)
+{
+ mrp_debug("PA set timer destroy callback for %p to %p", t, cb);
+
+ t->destroy = cb;
+}
+
+
+
+
+static void defer_event_cb(mrp_deferred_t *def, void *userdata)
+{
+ pa_defer_event *d = (pa_defer_event *)userdata;
+
+ MRP_UNUSED(def);
+
+ mrp_debug("PA defer event for %p", d);
+
+ d->busy = true;
+ d->cb(&d->m->api, d, d->userdata);
+ d->busy = false;
+
+ if (d->dead) {
+ mrp_del_deferred(d->d);
+ mrp_list_delete(&d->hook);
+ mrp_free(d);
+ }
+}
+
+
+static pa_defer_event *defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb,
+ void *userdata)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+ pa_defer_event *d;
+
+ mrp_debug("PA create defer event");
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d == NULL)
+ return NULL;
+
+ mrp_list_init(&d->hook);
+
+ d->m = m;
+ d->cb = cb;
+ d->userdata = userdata;
+ d->d = mrp_add_deferred(m->ml, defer_event_cb, d);
+
+ if (d->d != NULL)
+ mrp_list_append(&m->defer_events, &d->hook);
+ else {
+ mrp_free(d);
+ d = NULL;
+ }
+
+ return d;
+}
+
+
+static void defer_enable(pa_defer_event *d, int enable)
+{
+ mrp_debug("PA %s defer event %p", enable ? "enable" : "disable", d);
+
+ if (enable)
+ mrp_enable_deferred(d->d);
+ else
+ mrp_disable_deferred(d->d);
+}
+
+
+static void defer_free(pa_defer_event *d)
+{
+ pa_murphy_mainloop *m = d->m;
+
+ mrp_debug("PA free defer event %p", d);
+
+ mrp_list_delete(&d->hook);
+ mrp_del_deferred(d->d);
+ d->d = NULL;
+
+ d->dead = true;
+
+ if (!d->busy && !d->dead) {
+ d->busy = true;
+ if (d->destroy != NULL)
+ d->destroy(&d->m->api, d, d->userdata);
+ mrp_free(d);
+ }
+ else
+ mrp_list_append(&m->defer_dead, &d->hook);
+}
+
+
+static void defer_set_destroy(pa_defer_event *d, pa_defer_event_destroy_cb_t cb)
+{
+ mrp_debug("PA set defer event destroy callback for %p to %p", d, cb);
+
+ d->destroy = cb;
+}
+
+
+static void quit(pa_mainloop_api *api, int retval)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+
+ mrp_mainloop_quit(m->ml, retval);
+}
+
+
+
+pa_mainloop_api *pa_murphy_mainloop_get_api(pa_murphy_mainloop *m)
+{
+ pa_mainloop_api api = {
+ .userdata = m,
+ .io_new = io_new,
+ .io_enable = io_enable,
+ .io_free = io_free,
+ .io_set_destroy = io_set_destroy,
+ .time_new = time_new,
+ .time_restart = time_restart,
+ .time_free = time_free,
+ .time_set_destroy = time_set_destroy,
+ .defer_new = defer_new,
+ .defer_enable = defer_enable,
+ .defer_free = defer_free,
+ .defer_set_destroy = defer_set_destroy,
+ .quit = quit
+ };
+
+ m->api = api;
+
+ return &m->api;
+}
diff --git a/src/common/pulse-subloop.h b/src/common/pulse-subloop.h
new file mode 100644
index 0000000..f2fbe17
--- /dev/null
+++ b/src/common/pulse-subloop.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_PULSE_SUBLOOP_H__
+#define __MURPHY_PULSE_SUBLOOP_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+#include <pulse/mainloop-api.h>
+
+MRP_CDECL_BEGIN
+
+/** An opaque Murphy main loop object. */
+typedef struct pa_murphy_mainloop pa_murphy_mainloop;
+
+/** Create a new Murphy PA main loop object for the specified main loop. */
+pa_murphy_mainloop *pa_murphy_mainloop_new(mrp_mainloop_t *ml);
+
+/** Free the Murphy PA main loop object. */
+void pa_murphy_mainloop_free(pa_murphy_mainloop *m);
+
+/** Return the abstract main loop API for the PA Murphy main loop object. */
+pa_mainloop_api *pa_murphy_mainloop_get_api(pa_murphy_mainloop *m);
+
+MRP_CDECL_END
+
+#endif /* __PULSE_SUBLOOP_H__ */
diff --git a/src/common/qt-glue-priv.h b/src/common/qt-glue-priv.h
new file mode 100644
index 0000000..cb31531
--- /dev/null
+++ b/src/common/qt-glue-priv.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MURPHY_QT_GLUE_PRIV_H_
+#define __MURPHY_QT_GLUE_PRIV_H_
+
+#include <murphy/config.h>
+
+#ifdef QT_ENABLED
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <QSocketNotifier>
+#include <QTimer>
+#include <murphy/common/mainloop.h>
+
+class QtGlue : public QObject
+{
+Q_OBJECT
+
+public:
+ QtGlue(QObject *parent = NULL);
+};
+
+class QtIO : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Event {
+ Read = 0x01,
+ Write = 0x02,
+ Exception = 0x04
+ };
+ Q_DECLARE_FLAGS(EventMask, Event)
+
+ QtIO (int fd, EventMask events, QObject *parent = NULL);
+ ~QtIO();
+
+public Q_SLOTS:
+ void readyRead (int fd);
+ void readyWrite (int fd);
+ void exception (int fd);
+
+private:
+ EventMask m_events;
+ QSocketNotifier *m_fdIn;
+ QSocketNotifier *m_fdOut;
+ QSocketNotifier *m_fdExcep;
+
+public:
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ void *user_data;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS (QtIO::EventMask)
+
+class QtTimer : public QObject
+{
+Q_OBJECT
+
+public:
+ QtTimer (int msecs, QObject *parent = NULL);
+ ~QtTimer ();
+
+ void setInterval (int msecs);
+ void start ();
+ void stop ();
+ void enable ();
+ void disable();
+
+private Q_SLOTS:
+ void timedout();
+
+private:
+ QTimer *m_timer;
+ int m_interval;
+ bool m_disabled;
+
+public:
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+};
+
+#endif
+
+#endif /* __MURPHY_QT_GLUE_PRIV_H_ */
diff --git a/src/common/qt-glue.cpp b/src/common/qt-glue.cpp
new file mode 100644
index 0000000..8fbf636
--- /dev/null
+++ b/src/common/qt-glue.cpp
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qt-glue-priv.h"
+#include "qt-glue-priv.moc.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <QObject>
+#include <QSocketNotifier>
+#include <QTimer>
+
+#include <murphy/config.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/qt-glue.h>
+
+static mrp_mainloop_t *qt_ml;
+
+
+/*
+ * QtGlue
+ */
+
+QtGlue::QtGlue(QObject *parent)
+ : QObject(parent)
+{
+}
+
+
+/*
+ * QtIO
+ */
+
+QtIO::QtIO (int fd, EventMask events, QObject *parent)
+ : QObject (parent)
+ , m_events(events), m_fdIn(0), m_fdOut(0), m_fdExcep(0)
+ , cb(0), user_data(0)
+{
+ if (events & Read) {
+ m_fdIn = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+
+ m_fdIn->setEnabled (true);
+
+ QObject::connect (m_fdIn, SIGNAL(activated(int)), this,
+ SLOT(readyRead(int)));
+ }
+ if (events & Write) {
+ m_fdOut = new QSocketNotifier(fd, QSocketNotifier::Write, this);
+
+ m_fdOut->setEnabled (true);
+
+ QObject::connect (m_fdOut, SIGNAL(activated(int)), this,
+ SLOT(readyWrite(int)));
+ }
+ if (events & Exception) {
+ m_fdExcep = new QSocketNotifier(fd, QSocketNotifier::Exception, this);
+
+ m_fdExcep->setEnabled (true);
+
+ QObject::connect (m_fdExcep, SIGNAL(activated(int)), this,
+ SLOT(exception(int)));
+ }
+}
+
+
+QtIO::~QtIO() {
+ if (m_fdIn)
+ delete m_fdIn;
+ if (m_fdOut)
+ delete m_fdOut;
+ if (m_fdExcep)
+ delete m_fdExcep;
+}
+
+
+void QtIO::readyRead (int fd)
+{
+ if(cb)
+ cb(parent(), this, fd, MRP_IO_EVENT_IN, user_data);
+}
+
+
+void QtIO::readyWrite (int fd)
+{
+ if (cb)
+ cb(parent(), this, fd, MRP_IO_EVENT_OUT, user_data);
+}
+
+
+static bool check_hup(int fd)
+{
+ char buf[1];
+ ssize_t n;
+
+ n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_PEEK);
+
+ if (n == 0)
+ return true;
+ else
+ return false;
+}
+
+
+void QtIO::exception (int fd)
+{
+ mrp_io_event_t events;
+
+ if (!check_hup(fd))
+ events = MRP_IO_EVENT_HUP;
+ else
+ events = (mrp_io_event_t)(MRP_IO_EVENT_ERR | MRP_IO_EVENT_HUP);
+
+ if (cb)
+ cb(parent(), this, fd, events, user_data);
+}
+
+
+/*
+ * QtTimer
+ */
+
+QtTimer::QtTimer (int msecs, QObject *parent)
+ : QObject (parent)
+ , m_timer(new QTimer(this)), m_interval(msecs >= 0 ? msecs : 0)
+ , cb(0), user_data(0)
+{
+ m_timer->setInterval (m_interval);
+ m_timer->setSingleShot (false);
+
+ QObject::connect (m_timer, SIGNAL(timeout()), this, SLOT(timedout()));
+}
+
+
+QtTimer::~QtTimer ()
+{
+ delete m_timer;
+}
+
+
+void QtTimer::setInterval (int msecs)
+{
+ m_interval = (msecs >= 0 ? msecs : 0);
+
+ m_timer->setInterval (m_interval);
+
+ if (m_timer->isActive()) {
+ m_timer->stop ();
+ m_timer->start ();
+ }
+}
+
+
+void QtTimer::start ()
+{
+ if (!m_timer->isActive())
+ m_timer->start ();
+}
+
+
+void QtTimer::stop ()
+{
+ if (m_timer->isActive())
+ m_timer->stop();
+}
+
+
+void QtTimer::disable()
+{
+ if (!m_disabled) {
+ delete m_timer;
+
+ m_timer = 0;
+ m_disabled = true;
+ }
+}
+
+
+void QtTimer::enable()
+{
+ if (m_disabled) {
+ m_timer = new QTimer(this);
+
+ setInterval (m_interval);
+
+ connect (m_timer, SIGNAL(timeout()), this, SLOT(timedout()));
+
+ m_timer->start ();
+ m_disabled = false;
+ }
+}
+
+
+void QtTimer::timedout()
+{
+ mrp_debug("timer %p latched", this);
+
+ if (cb)
+ cb(parent(), this, user_data);
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+ QtIO *io;
+ QtIO::EventMask mask;
+
+ mask = 0;
+ if (events & MRP_IO_EVENT_IN)
+ mask |= QtIO::Read;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= QtIO::Write;
+ if (events & (MRP_IO_EVENT_ERR | MRP_IO_EVENT_HUP))
+ mask |= QtIO::Exception;
+
+ io = new QtIO (fd, mask, qt_glue);
+
+ if (io) {
+ mrp_debug("added I/O watch %p (events 0x%x) on fd %d", io, events, fd);
+
+ io->cb = cb;
+ io->user_data = user_data;
+ }
+
+ return io;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ QtIO *io = (QtIO *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("deleting I/O watch %p", io);
+
+ delete io;
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+ QtTimer *t = new QtTimer(msecs, qt_glue);
+
+ mrp_debug("created timer %p with %d msecs interval", t, msecs);
+
+ if (t) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->start();
+ }
+
+ return t;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("deleting timer %p", t);
+
+ delete t;
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("setting timer %p to %d msecs interval", t, msecs);
+
+ if (t != NULL)
+ t->setInterval(msecs);
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+ QtTimer *t = new QtTimer(0, qt_glue);
+
+ mrp_debug("created timer %p", t);
+
+ if (t) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->start();
+ }
+
+ return t;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("deleting timer %p", t);
+
+ delete t;
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("%s timer %p", enabled ? "enabling" : "disabling", t);
+
+ if (enabled)
+ t->enable();
+ else
+ t->disable();
+}
+
+
+static void unregister(void *glue_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+
+ mrp_debug("unregistering mainloop");
+
+ delete qt_glue;
+}
+
+
+int mrp_mainloop_register_with_qt(mrp_mainloop_t *ml)
+{
+ static mrp_superloop_ops_t qt_ops;
+ QtGlue *qt_glue;
+
+ qt_ops.add_io = add_io;
+ qt_ops.del_io = del_io;
+ qt_ops.add_timer = add_timer;
+ qt_ops.del_timer = del_timer;
+ qt_ops.mod_timer = mod_timer;
+ qt_ops.add_defer = add_defer;
+ qt_ops.del_defer = del_defer;
+ qt_ops.mod_defer = mod_defer;
+ qt_ops.unregister = unregister;
+
+ qt_glue = new QtGlue ();
+
+ return mrp_set_superloop(ml, &qt_ops, (void *)qt_glue);
+}
+
+
+int mrp_mainloop_unregister_from_qt(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+mrp_mainloop_t *mrp_mainloop_qt_get(void)
+{
+ if (qt_ml == NULL) {
+ qt_ml = mrp_mainloop_create();
+
+ if (qt_ml != NULL) {
+ if (!mrp_mainloop_register_with_qt(qt_ml)) {
+ mrp_mainloop_destroy(qt_ml);
+ qt_ml = NULL;
+ }
+ }
+ }
+
+ return qt_ml;
+}
diff --git a/src/common/qt-glue.h b/src/common/qt-glue.h
new file mode 100644
index 0000000..8b2bda5
--- /dev/null
+++ b/src/common/qt-glue.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_QT_H__
+#define __MURPHY_QT_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+/** Register the given murphy mainloop with the qt mainloop. */
+int mrp_mainloop_register_with_qt(mrp_mainloop_t *ml);
+
+/** Unrgister the given murphy mainloop from the qt mainloop. */
+int mrp_mainloop_unregister_from_qt(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the qt mainloop. */
+mrp_mainloop_t *mrp_mainloop_qt_get(void);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_QT_H__ */
diff --git a/src/common/refcnt.h b/src/common/refcnt.h
new file mode 100644
index 0000000..dbaf8c2
--- /dev/null
+++ b/src/common/refcnt.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_REFCNT_H__
+#define __MURPHY_REFCNT_H__
+
+/*
+ * A place/typeholder, so we can switch easily to atomic type
+ * if/when necessary.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+
+#define __MURPHY_REFCNT_CHECK__
+
+MRP_CDECL_BEGIN
+
+typedef int mrp_refcnt_t;
+
+static inline void *_mrp_ref_obj(void *obj, off_t offs)
+{
+ mrp_refcnt_t *refcnt;
+
+ if (obj != NULL) {
+ refcnt = (mrp_refcnt_t *) ((char *) obj + offs);
+ (*refcnt)++;
+ }
+
+ return obj;
+}
+
+static inline int _mrp_unref_obj(void *obj, off_t offs
+#ifdef __MURPHY_REFCNT_CHECK__
+ , const char *file
+ , int line
+ , const char *func
+#endif
+ )
+{
+ mrp_refcnt_t *refcnt;
+
+ if (obj != NULL) {
+ refcnt = (mrp_refcnt_t *) ((char *) obj + offs);
+ --(*refcnt);
+
+ if (*refcnt == 0)
+ return TRUE;
+
+#ifdef __MURPHY_REFCNT_CHECK__
+# define W mrp_log_error
+
+ if (*refcnt < 0) {
+ W("****************** REFCOUNTING BUG WARNING ******************");
+ W("* Reference-counting bug detected. The reference count of");
+ W("* object %p (@offs %d) has dropped to %d.", obj, (int)offs,
+ (int)*refcnt);
+ W("* The offending unref call was made at:");
+ W("* %s@%s:%d", func ? func : "<unkown>",
+ file ? file : "<unknown>", line);
+ W("*************************************************************");
+ }
+
+#undef W
+#endif
+ }
+
+ return FALSE;
+}
+
+
+static inline void mrp_refcnt_init(mrp_refcnt_t *refcnt)
+{
+ *refcnt = 1;
+}
+
+#define mrp_ref_obj(obj, member) \
+ (typeof(obj))_mrp_ref_obj(obj, MRP_OFFSET(typeof(*(obj)), member))
+
+#ifndef __MURPHY_REFCNT_CHECK__
+# define mrp_unref_obj(obj, member) \
+ _mrp_unref_obj(obj, MRP_OFFSET(typeof(*(obj)), member))
+#else
+# define mrp_unref_obj(obj, member) \
+ _mrp_unref_obj(obj, MRP_OFFSET(typeof(*(obj)), member), __LOC__)
+#endif
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_REFCNT_H__ */
diff --git a/src/common/socket-utils.c b/src/common/socket-utils.c
new file mode 100644
index 0000000..4f7d957
--- /dev/null
+++ b/src/common/socket-utils.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <murphy/common/macros.h>
+
+static int reject_fd = -1;
+
+
+static inline int reserve_reject_fd(void)
+{
+ if (reject_fd < 0)
+ reject_fd = open("/dev/null", O_RDONLY);
+
+ return reject_fd;
+}
+
+
+static void MRP_INIT reserve_reject(void)
+{
+ reserve_reject_fd();
+}
+
+
+int mrp_reject_connection(int sock, struct sockaddr *addr, socklen_t *alen)
+{
+ struct sockaddr *a, buf;
+ socklen_t *l, len;
+ int fd;
+
+ if (addr != NULL) {
+ a = addr;
+ l = alen;
+ }
+ else {
+ len = sizeof(buf);
+ a = &buf;
+ l = &len;
+ }
+
+ fd = accept(sock, a, l);
+
+ if (fd >= 0) {
+ close(fd);
+
+ return 0;
+ }
+
+ if (errno != EMFILE)
+ return -1;
+
+ if (reject_fd < 0) {
+ errno = ENOENT;
+
+ return -1;
+ }
+
+ close(reject_fd);
+ reject_fd = -1;
+
+ fd = accept(sock, a, l);
+
+ if (fd >= 0)
+ close(fd);
+
+ reserve_reject_fd();
+
+ return (fd >= 0 ? 0 : -1);
+}
diff --git a/src/common/socket-utils.h b/src/common/socket-utils.h
new file mode 100644
index 0000000..ad1b998
--- /dev/null
+++ b/src/common/socket-utils.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SOCKET_UTILS_H__
+#define __MURPHY_SOCKET_UTILS_H__
+
+#include <sys/socket.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+int mrp_reject_connection(int sock, struct sockaddr *addr, socklen_t alen);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_SOCKET_UTILS_H__ */
diff --git a/src/common/stream-transport.c b/src/common/stream-transport.c
new file mode 100644
index 0000000..d8ee1e6
--- /dev/null
+++ b/src/common/stream-transport.c
@@ -0,0 +1,853 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/fragbuf.h>
+#include <murphy/common/socket-utils.h>
+#include <murphy/common/transport.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)NULL)->sun_path)
+#endif
+
+#define TCP4 "tcp4"
+#define TCP4L 4
+#define TCP6 "tcp6"
+#define TCP6L 4
+#define UNXS "unxs"
+#define UNXSL 4
+
+#define DEFAULT_SIZE 128 /* default input buffer size */
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ int sock; /* TCP socket */
+ mrp_io_watch_t *iow; /* socket I/O watch */
+ mrp_fragbuf_t *buf; /* fragment buffer */
+} strm_t;
+
+
+static void strm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data);
+static int strm_disconnect(mrp_transport_t *mt);
+static int open_socket(strm_t *t, int family);
+
+
+
+static int parse_address(const char *str, int *familyp, char *nodep,
+ size_t nsize, char **servicep, const char **typep)
+{
+ char *node, *service;
+ const char *type;
+ int family;
+ size_t l, nl;
+
+ node = (char *)str;
+
+ if (!strncmp(node, TCP4":", l=TCP4L+1)) {
+ family = AF_INET;
+ type = TCP4;
+ node += l;
+ }
+ else if (!strncmp(node, TCP6":", l=TCP6L+1)) {
+ family = AF_INET6;
+ type = TCP6;
+ node += l;
+ }
+ else if (!strncmp(node, UNXS":", l=UNXSL+1)) {
+ family = AF_UNIX;
+ type = UNXS;
+ node += l;
+ }
+ else {
+ if (node[0] == '[') family = AF_INET6;
+ else if (node[0] == '/') family = AF_UNIX;
+ else if (node[0] == '@') family = AF_UNIX;
+ else family = AF_UNSPEC;
+
+ type = NULL;
+ }
+
+ switch (family) {
+ case AF_INET:
+ service = strrchr(node, ':');
+ if (service == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node;
+ service++;
+
+ case AF_INET6:
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else
+ nl = service - node;
+
+ service++;
+ break;
+
+ case AF_UNSPEC:
+ if (!strncmp(node, "tcp:", l=4))
+ node += l;
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+ family = AF_INET6;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else {
+ family = AF_INET;
+ nl = service - node;
+ }
+ service++;
+ break;
+
+ case AF_UNIX:
+ service = NULL;
+ nl = strlen(node);
+ }
+
+ if (nl >= nsize) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ strncpy(nodep, node, nl);
+ nodep[nl] = '\0';
+ *servicep = service;
+ *familyp = family;
+ if (typep != NULL)
+ *typep = type;
+
+ return 0;
+}
+
+
+static socklen_t strm_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ struct addrinfo *ai, hints;
+ struct sockaddr_un *un;
+ char node[UNIX_PATH_MAX], *port;
+ socklen_t len;
+
+ mrp_clear(&hints);
+
+ if (parse_address(str, &hints.ai_family, node, sizeof(node),
+ &port, typep) < 0)
+ return 0;
+
+ switch (hints.ai_family) {
+ case AF_UNIX:
+ un = &addr->unx;
+ len = MRP_OFFSET(typeof(*un), sun_path) + strlen(node) + 1;
+
+ if (size < len)
+ errno = ENOMEM;
+ else {
+ un->sun_family = AF_UNIX;
+ strncpy(un->sun_path, node, UNIX_PATH_MAX-1);
+ if (un->sun_path[0] == '@')
+ un->sun_path[0] = '\0';
+ }
+
+ /* When binding the socket, we don't need the null at the end */
+ len--;
+
+ break;
+
+ case AF_INET:
+ case AF_INET6:
+ default:
+ if (getaddrinfo(node, port, &hints, &ai) == 0) {
+ if (ai->ai_addrlen <= size) {
+ memcpy(addr, ai->ai_addr, ai->ai_addrlen);
+ len = ai->ai_addrlen;
+ }
+ else
+ len = 0;
+
+ freeaddrinfo(ai);
+ }
+ else
+ len = 0;
+ }
+
+ return len;
+}
+
+
+static int strm_open(mrp_transport_t *mt)
+{
+ strm_t *t = (strm_t *)mt;
+
+ t->sock = -1;
+
+ return TRUE;
+}
+
+
+static int set_nonblocking(int sock, int nonblocking)
+{
+ long nb = (nonblocking ? 1 : 0);
+
+ return fcntl(sock, F_SETFL, O_NONBLOCK, nb);
+}
+
+
+static int set_cloexec(int fd, int cloexec)
+{
+ int on = cloexec ? 1 : 0;
+
+ return fcntl(fd, F_SETFL, O_CLOEXEC, on);
+}
+
+
+static int set_reuseaddr(int sock, int reuseaddr)
+{
+ int on;
+
+ if (reuseaddr) {
+ on = 1;
+ return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ }
+ else
+ return 0;
+}
+
+
+static int strm_createfrom(mrp_transport_t *mt, void *conn)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_io_event_t events;
+
+ t->sock = *(int *)conn;
+
+ if (t->sock >= 0) {
+ if (mt->flags & MRP_TRANSPORT_REUSEADDR)
+ if (set_reuseaddr(t->sock, true) < 0)
+ return FALSE;
+
+ if (mt->flags & MRP_TRANSPORT_NONBLOCK || t->listened)
+ if (set_nonblocking(t->sock, true) < 0)
+ return FALSE;
+
+ if (t->connected || t->listened) {
+ if (!t->connected ||
+ (t->buf = mrp_fragbuf_create(TRUE, 0)) != NULL) {
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events,
+ strm_recv_cb, t);
+
+ if (t->iow != NULL)
+ return TRUE;
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void strm_close(mrp_transport_t *mt)
+{
+ strm_t *t = (strm_t *)mt;
+
+ mrp_debug("closing transport %p", mt);
+
+ mrp_del_io_watch(t->iow);
+ t->iow = NULL;
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+
+ if (t->sock >= 0){
+ close(t->sock);
+ t->sock = -1;
+ }
+}
+
+
+static int strm_bind(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ strm_t *t = (strm_t *)mt;
+
+ if (t->sock != -1 || open_socket(t, addr->any.sa_family)) {
+ if (bind(t->sock, &addr->any, addrlen) == 0) {
+ mrp_debug("transport %p bound", mt);
+ return TRUE;
+ }
+ }
+
+ mrp_debug("failed to bind transport %p", mt);
+ return FALSE;
+}
+
+
+static int strm_listen(mrp_transport_t *mt, int backlog)
+{
+ strm_t *t = (strm_t *)mt;
+
+ if (t->sock != -1 && t->iow != NULL && t->evt.connection != NULL) {
+ if (set_nonblocking(t->sock, true) < 0)
+ return FALSE;
+
+ if (listen(t->sock, backlog) == 0) {
+ mrp_debug("transport %p listening", mt);
+ t->listened = TRUE;
+ return TRUE;
+ }
+ }
+
+ mrp_debug("transport %p failed to listen", mt);
+ return FALSE;
+}
+
+
+static int strm_accept(mrp_transport_t *mt, mrp_transport_t *mlt)
+{
+ strm_t *t, *lt;
+ mrp_sockaddr_t addr;
+ socklen_t addrlen;
+ mrp_io_event_t events;
+
+ t = (strm_t *)mt;
+ lt = (strm_t *)mlt;
+
+ if (lt->sock < 0) {
+ errno = EBADF;
+
+ return FALSE;
+ }
+
+ addrlen = sizeof(addr);
+ t->sock = accept(lt->sock, &addr.any, &addrlen);
+
+ if (t->sock >= 0) {
+ if (mt->flags & MRP_TRANSPORT_REUSEADDR)
+ if (set_reuseaddr(t->sock, true) < 0)
+ goto reject;
+
+ if (mt->flags & MRP_TRANSPORT_NONBLOCK)
+ if (set_nonblocking(t->sock, true) < 0)
+ goto reject;
+
+ if (mt->flags & MRP_TRANSPORT_CLOEXEC)
+ if (set_cloexec(t->sock, true) < 0)
+ goto reject;
+
+ t->buf = mrp_fragbuf_create(TRUE, 0);
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events, strm_recv_cb, t);
+
+ if (t->iow != NULL && t->buf != NULL) {
+ mrp_debug("accepted connection on transport %p/%p", mlt, mt);
+ return TRUE;
+ }
+ else {
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+ close(t->sock);
+ t->sock = -1;
+ }
+ }
+ else {
+ reject:
+ if (mrp_reject_connection(lt->sock, NULL, 0) < 0) {
+ mrp_log_error("%s(): accept failed, closing transport %p (%d: %s).",
+ __FUNCTION__, mlt, errno, strerror(errno));
+ strm_close(mlt);
+
+ /* Notes:
+ * Unfortunately we cannot safely emit a closed event here.
+ * The closed event is semantically attached to an accepted
+ * tranport being closed and there is no equivalent for a
+ * listening transport (we should have had a generic error
+ * event). There for the transport owner expects and treats
+ * (IOW casts) the associated user_data accordingly. That
+ * would end up in a disaster... Once we cleanup/rework the
+ * transport infra, this needs to be done better.
+ */
+ }
+ else
+ mrp_log_error("%s(): rejected connection for transport %p (%d: %s).",
+ __FUNCTION__, mlt, errno, strerror(errno));
+ }
+
+ return FALSE;
+}
+
+
+static void strm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ strm_t *t = (strm_t *)user_data;
+ mrp_transport_t *mt = (mrp_transport_t *)t;
+ void *data, *buf;
+ uint32_t pending;
+ size_t size;
+ ssize_t n;
+ int error;
+
+ MRP_UNUSED(w);
+
+ mrp_debug("event 0x%x for transport %p", events, t);
+
+ if (events & MRP_IO_EVENT_IN) {
+ if (MRP_UNLIKELY(mt->listened != 0)) {
+ MRP_TRANSPORT_BUSY(mt, {
+ mrp_debug("connection event on transport %p", mt);
+ mt->evt.connection(mt, mt->user_data);
+ });
+
+ t->check_destroy(mt);
+ return;
+ }
+
+ while (ioctl(fd, FIONREAD, &pending) == 0 && pending > 0) {
+ buf = mrp_fragbuf_alloc(t->buf, pending);
+
+ if (buf == NULL) {
+ error = ENOMEM;
+ fatal_error:
+ mrp_debug("transport %p closed with error %d", mt, error);
+ closed:
+ strm_disconnect(mt);
+
+ if (t->evt.closed != NULL)
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.closed(mt, error, mt->user_data);
+ });
+
+ t->check_destroy(mt);
+ return;
+ }
+
+ n = read(fd, buf, pending);
+
+ if (n >= 0) {
+ if (n < (ssize_t)pending)
+ mrp_fragbuf_trim(t->buf, buf, pending, n);
+ }
+
+ if (n < 0 && errno != EAGAIN) {
+ error = EIO;
+ goto fatal_error;
+ }
+ }
+
+ data = NULL;
+ size = 0;
+ while (mrp_fragbuf_pull(t->buf, &data, &size)) {
+ if (t->mode != MRP_TRANSPORT_MODE_JSON)
+ error = t->recv_data(mt, data, size, NULL, 0);
+ else {
+ mrp_json_t *msg = mrp_json_string_to_object(data, size);
+
+ if (msg != NULL) {
+ error = t->recv_data((mrp_transport_t *)t, msg, 0, NULL, 0);
+ mrp_json_unref(msg);
+ }
+ else
+ error = EILSEQ;
+ }
+
+ if (error)
+ goto fatal_error;
+
+ if (t->check_destroy(mt))
+ return;
+ }
+ }
+
+ if (events & MRP_IO_EVENT_HUP) {
+ mrp_debug("transport %p closed by peer", mt);
+ error = 0;
+ goto closed;
+ }
+}
+
+
+static int open_socket(strm_t *t, int family)
+{
+ mrp_io_event_t events;
+
+ t->sock = socket(family, SOCK_STREAM, 0);
+
+ if (t->sock != -1) {
+ if (t->flags & MRP_TRANSPORT_REUSEADDR)
+ if (set_reuseaddr(t->sock, true) < 0)
+ goto fail;
+
+ if (t->flags & MRP_TRANSPORT_NONBLOCK)
+ if (set_nonblocking(t->sock, true) < 0)
+ goto fail;
+
+ if (t->flags & MRP_TRANSPORT_CLOEXEC)
+ if (set_cloexec(t->sock, true) < 0)
+ goto fail;
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events, strm_recv_cb, t);
+
+ if (t->iow != NULL)
+ return TRUE;
+ else {
+ fail:
+ close(t->sock);
+ t->sock = -1;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_connect(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_io_event_t events;
+
+ t->sock = socket(addr->any.sa_family, SOCK_STREAM, 0);
+
+ if (t->sock < 0)
+ goto fail;
+
+ if (connect(t->sock, &addr->any, addrlen) == 0) {
+ if (set_reuseaddr(t->sock, true) < 0 ||
+ set_nonblocking(t->sock, true) < 0)
+ goto close_and_fail;
+
+ t->buf = mrp_fragbuf_create(TRUE, 0);
+
+ if (t->buf != NULL) {
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events, strm_recv_cb, t);
+
+ if (t->iow != NULL) {
+ mrp_debug("connected transport %p", mt);
+
+ return TRUE;
+ }
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+ }
+ }
+
+ if (t->sock != -1) {
+ close_and_fail:
+ close(t->sock);
+ t->sock = -1;
+ }
+
+ fail:
+ mrp_debug("failed to connect transport %p", mt);
+
+ return FALSE;
+}
+
+
+static int strm_disconnect(mrp_transport_t *mt)
+{
+ strm_t *t = (strm_t *)mt;
+
+ if (t->connected/* || t->iow != NULL*/) {
+ mrp_del_io_watch(t->iow);
+ t->iow = NULL;
+
+ shutdown(t->sock, SHUT_RDWR);
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+
+ mrp_debug("disconnected transport %p", mt);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int strm_send(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ strm_t *t = (strm_t *)mt;
+ struct iovec iov[2];
+ void *buf;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (t->connected) {
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (size >= 0) {
+ len = htobe32(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = size;
+
+ n = writev(t->sock, iov, 2);
+ mrp_free(buf);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ strm_t *t = (strm_t *)mt;
+ ssize_t n;
+
+ if (t->connected) {
+ n = write(t->sock, data, size);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_data_descr_t *type;
+ ssize_t n;
+ void *buf;
+ size_t size, reserve, len;
+ uint32_t *lenp;
+ uint16_t *tagp;
+
+ if (t->connected) {
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ reserve = sizeof(*lenp) + sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size > 0) {
+ lenp = buf;
+ len = size - sizeof(*lenp);
+ tagp = buf + sizeof(*lenp);
+ *lenp = htobe32(len);
+ *tagp = htobe16(tag);
+
+ n = write(t->sock, buf, len + sizeof(*lenp));
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)(len + sizeof(*lenp)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add"
+ " output queueing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_sendnative(mrp_transport_t *mt, void *data, uint32_t type_id)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_typemap_t *map = t->map;
+ void *buf;
+ size_t size, reserve;
+ uint32_t *lenp;
+ ssize_t n;
+
+ if (t->connected) {
+ reserve = sizeof(*lenp);
+
+ if (mrp_encode_native(data, type_id, reserve, &buf, &size, map) == 0) {
+ lenp = buf;
+ *lenp = htobe32(size - sizeof(*lenp));
+
+ n = write(t->sock, buf, size);
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add"
+ " output queueing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_sendjson(mrp_transport_t *mt, mrp_json_t *msg)
+{
+ strm_t *t = (strm_t *)mt;
+ struct iovec iov[2];
+ const char *s;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (t->connected && (s = mrp_json_object_to_string(msg)) != NULL) {
+ size = strlen(s);
+ len = htobe32(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = (void *)s;
+ iov[1].iov_len = size;
+
+ n = writev(t->sock, iov, 2);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+MRP_REGISTER_TRANSPORT(tcp4, TCP4, strm_t, strm_resolve,
+ strm_open, strm_createfrom, strm_close, NULL,
+ strm_bind, strm_listen, strm_accept,
+ strm_connect, strm_disconnect,
+ strm_send, NULL,
+ strm_sendraw, NULL,
+ strm_senddata, NULL,
+ NULL, NULL,
+ strm_sendnative, NULL,
+ strm_sendjson, NULL);
+
+MRP_REGISTER_TRANSPORT(tcp6, TCP6, strm_t, strm_resolve,
+ strm_open, strm_createfrom, strm_close, NULL,
+ strm_bind, strm_listen, strm_accept,
+ strm_connect, strm_disconnect,
+ strm_send, NULL,
+ strm_sendraw, NULL,
+ strm_senddata, NULL,
+ NULL, NULL,
+ strm_sendnative, NULL,
+ strm_sendjson, NULL);
+
+MRP_REGISTER_TRANSPORT(unxstrm, UNXS, strm_t, strm_resolve,
+ strm_open, strm_createfrom, strm_close, NULL,
+ strm_bind, strm_listen, strm_accept,
+ strm_connect, strm_disconnect,
+ strm_send, NULL,
+ strm_sendraw, NULL,
+ strm_senddata, NULL,
+ NULL, NULL,
+ strm_sendnative, NULL,
+ strm_sendjson, NULL);
diff --git a/src/common/tests/Makefile.am b/src/common/tests/Makefile.am
new file mode 100644
index 0000000..0162fad
--- /dev/null
+++ b/src/common/tests/Makefile.am
@@ -0,0 +1,144 @@
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
+
+noinst_PROGRAMS = mm-test hash-test hash12-test msg-test transport-test \
+ internal-transport-test process-watch-test native-test \
+ mkdir-test path-test mask-test
+
+if LIBDBUS_ENABLED
+noinst_PROGRAMS += mainloop-test dbus-test
+endif
+
+noinst_PROGRAMS += fragbuf-test
+
+# memory management test
+mm_test_SOURCES = mm-test.c
+mm_test_CFLAGS = $(AM_CFLAGS)
+mm_test_LDADD = ../../libmurphy-common.la
+
+# hash table test
+hash_test_SOURCES = hash-test.c
+hash_test_CFLAGS = $(AM_CFLAGS)
+hash_test_LDADD = ../../libmurphy-common.la
+
+# hash12-test
+hash12_test_SOURCES = hash12-test.c
+hash12_test_CFLAGS = $(AM_CFLAGS)
+hash12_test_LDADD = ../../libmurphy-common.la
+
+# mainloop test
+mainloop_test_SOURCES = mainloop-test.c
+mainloop_test_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) $(LIBDBUS_CFLAGS)
+mainloop_test_LDADD = ../../libmurphy-common.la $(GLIB_LIBS) $(LIBDBUS_LIBS)
+if PULSE_ENABLED
+mainloop_test_CFLAGS += $(PULSE_CFLAGS)
+mainloop_test_LDADD += ../../libmurphy-pulse.la $(PULSE_LIBS)
+endif
+if ECORE_ENABLED
+mainloop_test_CFLAGS += $(ECORE_CFLAGS)
+mainloop_test_LDADD += ../../libmurphy-ecore.la $(ECORE_LIBS)
+endif
+if GLIB_ENABLED
+mainloop_test_CFLAGS += $(GLIB_CFLAGS)
+mainloop_test_LDADD += ../../libmurphy-glib.la $(GLIB_LIBS)
+endif
+
+if QT_ENABLED
+noinst_LTLIBRARIES = libmainloop-qt-test.la
+libmainloop_qt_test_la_SOURCES = mainloop-qt-test.cpp
+libmainloop_qt_test_la_CPPFLAGS = $(AM_CFLAGS) $(QTCORE_CFLAGS)
+libmainloop_qt_test_la_LIBADD = ../../libmurphy-common.la \
+ ../../libmurphy-qt.la $(QTCORE_LIBS)
+mainloop_test_LDADD += libmainloop-qt-test.la $(QTCORE_LIBS) -lstdc++
+endif
+
+# msg test
+msg_test_SOURCES = msg-test.c
+msg_test_CFLAGS = $(AM_CFLAGS)
+msg_test_LDADD = ../../libmurphy-common.la
+
+# native type test
+native_test_SOURCES = native-test.c
+native_test_CFLAGS = $(AM_CFLAGS)
+native_test_LDADD = ../../libmurphy-common.la
+
+# transport test
+transport_test_SOURCES = transport-test.c
+transport_test_CFLAGS = $(AM_CFLAGS)
+transport_test_LDADD = ../../libmurphy-common.la
+
+# internal transport test
+internal_transport_test_SOURCES = internal-transport-test.c
+internal_transport_test_CFLAGS = $(AM_CFLAGS)
+internal_transport_test_LDADD = ../../libmurphy-common.la
+
+# process watch test
+process_watch_test_SOURCES = process-test.c
+process_watch_test_CFLAGS = $(AM_CFLAGS)
+process_watch_test_LDADD = ../../libmurphy-common.la
+
+if LIBDBUS_ENABLED
+transport_test_LDADD += ../../libmurphy-libdbus.la
+
+noinst_PROGRAMS += mainloop-test
+
+# DBUS tests
+noinst_PROGRAMS += dbus-test
+dbus_test_SOURCES = dbus-test.c
+dbus_test_CFLAGS = $(AM_CFLAGS) $(LIBDBUS_CFLAGS)
+dbus_test_LDADD = ../../libmurphy-libdbus.la ../../libmurphy-common.la $(LIBDBUS_LIBS)
+
+noinst_PROGRAMS += libdbus-test libdbus-transport-test
+libdbus_test_SOURCES = libdbus-test.c
+libdbus_test_CFLAGS = $(AM_CFLAGS) $(LIBDBUS_CFLAGS)
+libdbus_test_LDADD = ../../libmurphy-dbus-libdbus.la ../../libmurphy-common.la $(LIBDBUS_LIBS)
+
+libdbus_transport_test_SOURCES = libdbus-transport-test.c
+libdbus_transport_test_CFLAGS = $(AM_CFLAGS)
+libdbus_transport_test_LDADD = ../../libmurphy-common.la \
+ ../../libmurphy-dbus-libdbus.la
+endif
+
+if SDBUS_ENABLED
+noinst_PROGRAMS += sdbus-test dbus-sdbus-test sdbus-transport-test sdbus-error-message
+
+sdbus_test_SOURCES = sdbus-test.c
+sdbus_test_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS)
+sdbus_test_LDADD = ../../libmurphy-common.la $(SDBUS_LIBS)
+
+dbus_sdbus_test_SOURCES = dbus-sdbus-test.c
+dbus_sdbus_test_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS)
+dbus_sdbus_test_LDADD = \
+ ../../libmurphy-common.la \
+ ../../libmurphy-dbus-sdbus.la
+
+sdbus_transport_test_SOURCES = libdbus-transport-test.c
+sdbus_transport_test_CFLAGS = $(AM_CFLAGS)
+sdbus_transport_test_LDADD = \
+ ../../libmurphy-common.la \
+ ../../libmurphy-dbus-sdbus.la
+
+sdbus_error_message_SOURCES = sdbus-error-message.c
+sdbus_error_message_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS)
+sdbus_error_message_LDADD = \
+ ../../libmurphy-common.la \
+ ../../libmurphy-dbus-sdbus.la
+endif
+
+# fragbuf test
+fragbuf_test_SOURCES = fragbuf-test.c
+fragbuf_test_CFLAGS = $(AM_CFLAGS)
+fragbuf_test_LDADD = ../../libmurphy-common.la
+
+# mkdir-test
+mkdir_test_SOURCES = mkdir-test.c
+mkdir_test_CFLAGS = $(AM_CFLAGS) -I.
+mkdir_test_LDADD = ../../libmurphy-common.la
+
+# path-test
+path_test_SOURCES = path-test.c
+path_test_CFLAGS = $(AM_CFLAGS) -I.
+path_test_LDADD = ../../libmurphy-common.la
+
+mask_test_SOURCES = mask-test.c
+mask_test_CFLAGS = $(AM_CFLAGS) -I.
+mask_test_LDADD = ../../libmurphy-common.la
diff --git a/src/common/tests/dbus-pump.c b/src/common/tests/dbus-pump.c
new file mode 100644
index 0000000..4dc323d
--- /dev/null
+++ b/src/common/tests/dbus-pump.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dbus/dbus.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+
+typedef struct dbus_glue_s dbus_glue_t;
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_io_watch_t *mw;
+ DBusWatch *dw;
+ mrp_list_hook_t hook;
+} watch_t;
+
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_timer_t *mt;
+ DBusTimeout *dt;
+ mrp_list_hook_t hook;
+} timeout_t;
+
+
+struct dbus_glue_s {
+ DBusConnection *conn;
+ mrp_mainloop_t *ml;
+ mrp_list_hook_t watches;
+ mrp_list_hook_t timers;
+ mrp_deferred_t *pump;
+};
+
+
+static dbus_int32_t data_slot = -1;
+
+static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *watch = (watch_t *)user_data;
+ DBusConnection *conn = watch->glue->conn;
+ unsigned int mask = 0;
+
+ MRP_UNUSED(mw);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ mask |= DBUS_WATCH_READABLE;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= DBUS_WATCH_WRITABLE;
+ if (events & MRP_IO_EVENT_HUP)
+ mask |= DBUS_WATCH_HANGUP;
+ if (events & MRP_IO_EVENT_ERR)
+ mask |= DBUS_WATCH_ERROR;
+
+ dbus_connection_ref(conn);
+ dbus_watch_handle(watch->dw, mask);
+ dbus_connection_unref(conn);
+}
+
+
+static void watch_freed_cb(void *data)
+{
+ watch_t *watch = (watch_t *)data;
+
+ if (watch != NULL) {
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+ mrp_free(watch);
+ }
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *dw, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ watch_t *watch;
+ mrp_io_watch_t *mw;
+ mrp_io_event_t mask;
+ int fd;
+ unsigned int flags;
+
+ if (!dbus_watch_get_enabled(dw))
+ return TRUE;
+
+ fd = dbus_watch_get_unix_fd(dw);
+ flags = dbus_watch_get_flags(dw);
+ mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= MRP_IO_EVENT_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= MRP_IO_EVENT_OUT;
+
+ if ((watch = mrp_allocz(sizeof(*watch))) != NULL) {
+ mrp_list_init(&watch->hook);
+ mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch);
+
+ if (mw != NULL) {
+ watch->glue = glue;
+ watch->mw = mw;
+ watch->dw = dw;
+ dbus_watch_set_data(dw, watch, watch_freed_cb);
+ mrp_list_append(&glue->watches, &watch->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(watch);
+ }
+
+ return FALSE;
+}
+
+
+static void del_watch(DBusWatch *dw, void *data)
+{
+ watch_t *watch = (watch_t *)dbus_watch_get_data(dw);
+
+ MRP_UNUSED(data);
+
+ if (watch != NULL) {
+ mrp_del_io_watch(watch->mw);
+ watch->mw = NULL;
+ }
+}
+
+
+static void toggle_watch(DBusWatch *dw, void *data)
+{
+ if (dbus_watch_get_enabled(dw))
+ add_watch(dw, data);
+ else
+ del_watch(dw, data);
+}
+
+
+static void dispatch_timeout(mrp_timer_t *mt, void *user_data)
+{
+ timeout_t *timer = (timeout_t *)user_data;
+
+ MRP_UNUSED(mt);
+
+ dbus_timeout_handle(timer->dt);
+}
+
+
+static void timeout_freed_cb(void *data)
+{
+ timeout_t *timer = (timeout_t *)data;
+
+ if (timer != NULL) {
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *dt, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ timeout_t *timer;
+ mrp_timer_t *mt;
+ unsigned int msecs;
+
+ if ((timer = mrp_allocz(sizeof(*timer))) != NULL) {
+ mrp_list_init(&timer->hook);
+ msecs = dbus_timeout_get_interval(dt);
+ mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer);
+
+ if (mt != NULL) {
+ timer->glue = glue;
+ timer->mt = mt;
+ timer->dt = dt;
+ dbus_timeout_set_data(dt, timer, timeout_freed_cb);
+ mrp_list_append(&glue->timers, &timer->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(timer);
+ }
+
+ return FALSE;
+}
+
+
+static void del_timeout(DBusTimeout *dt, void *data)
+{
+ timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt);
+
+ MRP_UNUSED(data);
+
+ if (timer != NULL) {
+ mrp_del_timer(timer->mt);
+ timer->mt = NULL;
+ }
+}
+
+
+static void toggle_timeout(DBusTimeout *dt, void *data)
+{
+ if (dbus_timeout_get_enabled(dt))
+ add_timeout(dt, data);
+ else
+ del_timeout(dt, data);
+}
+
+
+static void wakeup_mainloop(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+
+ mrp_enable_deferred(glue->pump);
+}
+
+
+static void glue_free_cb(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ mrp_list_hook_t *p, *n;
+ watch_t *watch;
+ timeout_t *timer;
+
+ mrp_list_foreach(&glue->watches, p, n) {
+ watch = mrp_list_entry(p, typeof(*watch), hook);
+
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+
+ mrp_free(watch);
+ }
+
+ mrp_list_foreach(&glue->timers, p, n) {
+ timer = mrp_list_entry(p, typeof(*timer), hook);
+
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+
+ mrp_free(glue);
+}
+
+
+static void pump_cb(mrp_deferred_t *d, void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE)
+ mrp_disable_deferred(d);
+}
+
+
+static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status,
+ void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ MRP_UNUSED(conn);
+
+ switch (status) {
+ case DBUS_DISPATCH_COMPLETE:
+ mrp_disable_deferred(glue->pump);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ mrp_enable_deferred(glue->pump);
+ break;
+ }
+}
+
+
+int mrp_setup_dbus_connection(mrp_mainloop_t *ml, DBusConnection *conn)
+{
+ dbus_glue_t *glue;
+
+ if (!dbus_connection_allocate_data_slot(&data_slot))
+ return FALSE;
+
+ if (dbus_connection_get_data(conn, data_slot) != NULL)
+ return FALSE;
+
+ if ((glue = mrp_allocz(sizeof(*glue))) != NULL) {
+ mrp_list_init(&glue->watches);
+ mrp_list_init(&glue->timers);
+ glue->pump = mrp_add_deferred(ml, pump_cb, glue);
+
+ if (glue->pump == NULL) {
+ mrp_free(glue);
+ return FALSE;
+ }
+
+ glue->ml = ml;
+ glue->conn = conn;
+ }
+ else
+ return FALSE;
+
+ if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb))
+ return FALSE;
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+ glue, NULL);
+
+ dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop,
+ glue, NULL);
+
+ return
+ dbus_connection_set_watch_functions(conn, add_watch, del_watch,
+ toggle_watch, glue, NULL) &&
+ dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout,
+ toggle_timeout, glue, NULL);
+}
+
diff --git a/src/common/tests/dbus-sdbus-test.c b/src/common/tests/dbus-sdbus-test.c
new file mode 100644
index 0000000..a7fcb86
--- /dev/null
+++ b/src/common/tests/dbus-sdbus-test.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <murphy/common/dbus-sdbus.h>
+
+#define SERVER_NAME "org.test.murphy-server"
+#define SERVER_PATH "/server"
+#define SERVER_INTERFACE "Murphy.Server"
+#define PING "ping"
+#define CLIENT_NAME "org.test.murphy-client"
+#define CLIENT_PATH "/client"
+#define CLIENT_INTERFACE "Murphy.Client"
+#define PONG "pong"
+
+
+typedef struct {
+ char *busaddr;
+ char *srvname;
+ int server;
+ int log_mask;
+ const char *log_target;
+ mrp_mainloop_t *ml;
+ mrp_timer_t *timer;
+ uint32_t seqno;
+ mrp_dbus_t *dbus;
+ const char *name;
+ int32_t cid;
+ int server_up;
+ int all_pongs;
+} context_t;
+
+
+static mrp_dbus_msg_t *create_pong_signal(mrp_dbus_t *dbus, const char *dest,
+ uint32_t seq)
+{
+ const char *sig = "u";
+ mrp_dbus_msg_t *msg;
+
+ msg = mrp_dbus_msg_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG);
+
+ if (msg != NULL) {
+ if (mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, sig) &&
+ mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT32, &seq) &&
+ mrp_dbus_msg_close_container(msg))
+ return msg;
+ else
+ mrp_dbus_msg_unref(msg);
+ }
+
+ return NULL;
+}
+
+
+static uint32_t parse_pong_signal(mrp_dbus_msg_t *msg)
+{
+ const char *sig = "u";
+ uint32_t seq;
+
+ if (mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, sig) &&
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq) &&
+ mrp_dbus_msg_exit_container(msg))
+ return seq;
+ else
+ return (uint32_t)-1;
+}
+
+
+static int ping_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_dbus_msg_t *pong;
+ uint32_t seq;
+ const char *dest;
+
+ MRP_UNUSED(c);
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping request #%u", seq);
+ else
+ mrp_log_error("-> malformed ping request");
+
+ if (mrp_dbus_reply(dbus, msg, MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID))
+ mrp_log_info("<- ping reply #%u", seq);
+ else
+ mrp_log_error("Failed to send ping reply #%u.", seq);
+
+ if (seq & 0x1)
+ dest = mrp_dbus_msg_sender(msg);
+ else
+ dest = NULL;
+
+ if ((pong = create_pong_signal(dbus, dest, seq)) != NULL) {
+ if (mrp_dbus_send_msg(dbus, pong))
+ mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq);
+ else
+ mrp_log_error("Failed to send pong signal #%u.", seq);
+
+ mrp_dbus_msg_unref(pong);
+ }
+ else
+ mrp_log_error("Failed to create pong signal #%u.", seq);
+
+ return TRUE;
+}
+
+
+static int name_owner_changed(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
+ void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ const char *name, *prev, *next;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &name) &&
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &prev) &&
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &next))
+ mrp_log_info("Name %s was reassigned from %s to %s...", name,
+ prev, next);
+ else
+ mrp_log_error("Failed to parse NameOwnerChanged signal.");
+
+ return TRUE;
+}
+
+
+static void server_setup(context_t *c)
+{
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ if (c->srvname && *c->srvname) {
+ if (!mrp_dbus_acquire_name(c->dbus, c->srvname, NULL)) {
+ mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.",
+ c->srvname, c->busaddr);
+ exit(1);
+ }
+ }
+
+ if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c)) {
+ mrp_log_error("Failed to export D-BUS method '%s'.", PING);
+ exit(1);
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, name_owner_changed, c,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "NameOwnerChanged",
+ NULL)) {
+ mrp_log_error("Failed to subscribe to NameOwnerChanged signals.");
+ exit(1);
+ }
+}
+
+
+void server_cleanup(context_t *c)
+{
+ if (c->srvname && *c->srvname)
+ mrp_dbus_release_name(c->dbus, c->srvname, NULL);
+
+ mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void ping_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(user_data);
+
+ if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_ERROR) {
+ mrp_log_error("Received errorping reply.");
+
+ return;
+ }
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping reply #%u", seq);
+ else
+ mrp_log_error("Received malformedping reply.");
+
+ c->cid = 0;
+}
+
+
+static void ping_request(context_t *c)
+{
+ uint32_t seq;
+
+ if (c->cid != 0) {
+ mrp_log_warning("Previous ping request still unanswered...");
+ return;
+ }
+
+ seq = c->seqno++;
+ c->cid = mrp_dbus_call(c->dbus,
+ c->srvname, SERVER_PATH, SERVER_INTERFACE,
+ PING, 500, ping_reply, c,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID);
+
+ if (c->cid > 0)
+ mrp_log_info("<- ping request #%u", seq);
+ else
+ mrp_log_warning("Failed to send ping request #%u.", seq);
+}
+
+
+static void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ ping_request(c);
+}
+
+
+static int pong_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if ((seq = parse_pong_signal(msg)) != (uint32_t)-1)
+ mrp_log_info("-> pong signal #%u", seq);
+ else
+ mrp_log_error("-> malformed pong signal");
+
+ return TRUE;
+}
+
+
+static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ mrp_log_info("%s came up (as %s)", name, owner);
+
+ if (c->timer == NULL) {
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+ }
+ }
+ else {
+ mrp_log_info("%s went down", name);
+
+ if (c->timer != NULL) {
+ mrp_del_timer(c->timer);
+ c->timer = NULL;
+ }
+ }
+}
+
+
+static void client_setup(context_t *c)
+{
+ const char *dest;
+
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c);
+
+ if (c->all_pongs) {
+ mrp_log_info("Subscribing for all pong signals...");
+ dest = NULL;
+ }
+ else {
+ mrp_log_info("Subscribing only for pong signals to us...");
+ dest = c->name;
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ dest, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL)) {
+ mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH,
+ SERVER_INTERFACE, PONG);
+ exit(1);
+ }
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+}
+
+
+static void client_cleanup(context_t *c)
+{
+ mrp_dbus_forget_name(c->dbus, c->srvname, server_status_cb, c);
+ mrp_del_timer(c->timer);
+ mrp_dbus_unsubscribe_signal(c->dbus, pong_handler, c,
+ c->name, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -b, --bus connect the given D-BUS\n"
+ " If omitted, defaults to the session bus.\n"
+ " -a, --all-pongs subscribe for all pong signals\n"
+ " If omitted, only pong with the client address are handled.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug site enable debug message for <site>\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->busaddr = "session";
+ ctx->srvname = SERVER_NAME;
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "sab:n:l:t:vd:h"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "bus" , required_argument, NULL, 'b' },
+ { "name" , required_argument, NULL, 'n' },
+ { "all-pongs" , no_argument , NULL, 'a' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'b':
+ ctx->busaddr = optarg;
+ break;
+
+ case 'n':
+ ctx->srvname = optarg;
+ break;
+
+ case 'a':
+ ctx->all_pongs = TRUE;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(c);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+
+ case SIGTERM:
+ mrp_log_info("Got SIGTERM, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr);
+ else
+ mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr);
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c);
+
+ if (c.server)
+ server_setup(&c);
+ else
+ client_setup(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ if (c.server)
+ server_cleanup(&c);
+ else
+ client_cleanup(&c);
+
+ return 0;
+}
diff --git a/src/common/tests/dbus-test.c b/src/common/tests/dbus-test.c
new file mode 100644
index 0000000..e0ab062
--- /dev/null
+++ b/src/common/tests/dbus-test.c
@@ -0,0 +1,513 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <murphy/common/libdbus.h>
+
+#define SERVER_NAME "org.test.murphy-server"
+#define SERVER_PATH "/server"
+#define SERVER_INTERFACE "Murphy.Server"
+#define PING "ping"
+#define CLIENT_NAME "org.test.murphy-client"
+#define CLIENT_PATH "/client"
+#define CLIENT_INTERFACE "Murphy.Client"
+#define PONG "pong"
+
+
+typedef struct {
+ char *busaddr;
+ char *srvname;
+ int server;
+ int log_mask;
+ const char *log_target;
+ mrp_mainloop_t *ml;
+ mrp_timer_t *timer;
+ uint32_t seqno;
+ mrp_dbus_t *dbus;
+ const char *name;
+ int32_t cid;
+ int server_up;
+ int all_pongs;
+} context_t;
+
+
+static int ping_handler(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+ const char *dest;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
+ dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_info("-> ping request #%u", seq);
+ else
+ mrp_log_error("-> malformed ping request");
+
+ if (!mrp_dbus_reply(dbus, msg,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send ping reply #%u.", seq);
+ else
+ mrp_log_info("<- ping reply #%u", seq);
+
+ if (seq & 0x1)
+ dest = dbus_message_get_sender(msg);
+ else
+ dest = NULL;
+
+ if (!mrp_dbus_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send pong signal #%u.", seq);
+ else
+ mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq);
+
+ return TRUE;
+}
+
+
+static void server_setup(context_t *c)
+{
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ if (c->srvname && *c->srvname) {
+ if (!mrp_dbus_acquire_name(c->dbus, c->srvname, NULL)) {
+ mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.",
+ c->srvname, c->busaddr);
+ exit(1);
+ }
+ }
+
+ if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c)) {
+ mrp_log_error("Failed to export D-BUS method '%s'.", PING);
+ exit(1);
+ }
+}
+
+
+void server_cleanup(context_t *c)
+{
+ if (c->srvname && *c->srvname)
+ mrp_dbus_release_name(c->dbus, c->srvname, NULL);
+ mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void ping_reply(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(user_data);
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *ename, *emsg;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &ename,
+ DBUS_TYPE_STRING, &emsg,
+ DBUS_TYPE_INVALID)) {
+ ename = "<unknown>";
+ emsg = "<unknown>";
+ }
+
+ mrp_log_error("Received error reply (%s, %s) to ping.", ename, emsg);
+
+ c->cid = 0;
+ return;
+ }
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_info("-> ping reply #%u", seq);
+ else
+ mrp_log_error("Received malformed ping reply.");
+
+ c->cid = 0;
+}
+
+
+static void ping_request(context_t *c)
+{
+ uint32_t seq;
+
+ if (c->cid != 0) {
+ mrp_log_warning("Previous ping request still unanswered...");
+ return;
+ }
+
+ seq = c->seqno++;
+ c->cid = mrp_dbus_call(c->dbus,
+ c->srvname, SERVER_PATH, SERVER_INTERFACE,
+ PING, 500, ping_reply, c,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID);
+
+ if (c->cid > 0)
+ mrp_log_info("<- ping request #%u", seq);
+ else
+ mrp_log_warning("Failed to send ping request #%u.", seq);
+}
+
+
+static void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ ping_request(c);
+}
+
+
+static int pong_handler(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL &&
+ dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_info("-> pong signal #%u", seq);
+ else
+ mrp_log_error("-> malformed pong signal");
+
+ return TRUE;
+}
+
+
+static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ mrp_log_info("%s came up (as %s)", name, owner);
+
+ if (c->timer == NULL) {
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+ }
+ }
+ else {
+ mrp_log_info("%s went down", name);
+
+ if (c->timer != NULL) {
+ mrp_del_timer(c->timer);
+ c->timer = NULL;
+ }
+ }
+}
+
+
+static void client_setup(context_t *c)
+{
+ const char *dest;
+
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c);
+
+ if (c->all_pongs) {
+ mrp_log_info("Subscribing for all pong signals...");
+ dest = NULL;
+ }
+ else {
+ mrp_log_info("Subscribing only for pong signals to us...");
+ dest = c->name;
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ dest, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL)) {
+ mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH,
+ SERVER_INTERFACE, PONG);
+ exit(1);
+ }
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+}
+
+
+static void client_cleanup(context_t *c)
+{
+ mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c);
+ mrp_del_timer(c->timer);
+ mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ c->name, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -b, --bus connect the given D-BUS\n"
+ " If omitted, defaults to the session bus.\n"
+ " -a, --all-pongs subscribe for all pong signals\n"
+ " If omitted, only pong with the client address are handled.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->busaddr = "session";
+ ctx->srvname = SERVER_NAME;
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "sab:n:l:t:vdh"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "bus" , required_argument, NULL, 'b' },
+ { "name" , required_argument, NULL, 'n' },
+ { "all-pongs" , no_argument , NULL, 'a' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , no_argument , NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt, debug;
+
+ debug = FALSE;
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'b':
+ ctx->busaddr = optarg;
+ break;
+
+ case 'n':
+ ctx->srvname = optarg;
+ break;
+
+ case 'a':
+ ctx->all_pongs = TRUE;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (debug)
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+
+ return TRUE;
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(c);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+
+ case SIGTERM:
+ mrp_log_info("Got SIGTERM, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr);
+ else
+ mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr);
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c);
+
+ if (c.server)
+ server_setup(&c);
+ else
+ client_setup(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ if (c.server)
+ server_cleanup(&c);
+ else
+ client_cleanup(&c);
+
+ return 0;
+}
diff --git a/src/common/tests/fragbuf-test.c b/src/common/tests/fragbuf-test.c
new file mode 100644
index 0000000..4da9a34
--- /dev/null
+++ b/src/common/tests/fragbuf-test.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/fragbuf.h>
+
+
+#define fatal(fmt, args...) do { \
+ mrp_log_error(fmt, ## args); \
+ exit(1); \
+ } while (0)
+
+
+typedef struct {
+ int log_mask;
+ const char *log_target;
+ int framed;
+} context_t;
+
+context_t ctx;
+
+void check_message(void *data, size_t size, char **messages,
+ int *chk, int *offs)
+{
+ char *p, *d;
+ int l;
+
+ if (ctx.framed) {
+ if (!strncmp(messages[*chk], data, size) && !messages[*chk][size])
+ mrp_debug("message check: OK");
+ else
+ fatal("message check: failed");
+
+ *chk += 1;
+ }
+ else {
+ d = data;
+ while (size > 0) {
+ p = messages[*chk] + *offs;
+ l = strlen(p);
+
+ if (l > (int)size)
+ l = (int)size;
+
+ if (strncmp(p, d, l))
+ fatal("message check: failed");
+
+ *offs += l;
+ size -= l;
+ d += l;
+
+ if (messages[*chk][*offs] == '\0') {
+ *chk += 1;
+ *offs = 0;
+ }
+ }
+ mrp_debug("message check: OK");
+ }
+}
+
+
+void dump_buffer(mrp_fragbuf_t *buf, char **messages, int *chk, int *offs)
+{
+ void *data;
+ size_t size;
+ int cnt;
+
+ data = NULL;
+ size = 0;
+ cnt = 0;
+
+ while (mrp_fragbuf_pull(buf, &data, &size)) {
+ mrp_log_info("got message: (%zd bytes) [%*.*s]", size,
+ (int)size, (int)size, (char *)data);
+
+ check_message(data, size, messages, chk, offs);
+
+ cnt++;
+ }
+
+ if (!cnt)
+ mrp_debug("no full messages in buffer");
+ else
+ mrp_debug("pulled %d messages from buffer...", cnt);
+}
+
+
+int test(mrp_fragbuf_t *buf, size_t *chunks, int dump_interval)
+{
+ char *messages[] = {
+ "Ticking away the moments",
+ "That make up a dull day",
+ "Fritter and waste the hours",
+ "In an off-hand way",
+ "Kicking around on a piece of ground",
+ "In your home town",
+ "Waiting for someone or something",
+ "To show you the way",
+ "Tired of lying in the sunshine",
+ "Staying home to watch the rain",
+ "You are young and life is long",
+ "And there is time to kill today",
+ "And then the one day you find",
+ "Ten years have got behind you",
+ "No one told you when to run",
+ "You missed the starting gun",
+ "And you run and you run",
+ "To catch up with the sun",
+ "But it's sinking",
+ "Racing around",
+ "To come up behind you again",
+ "The sun is the same",
+ "In a relative way",
+ "But you're older",
+ "Shorter of breath",
+ "And one day closer to death",
+ "Every year is getting shorter",
+ "Never seem to find the time",
+ "Plans that either come to naught",
+ "Or half a page of scribbled lines",
+ "Hanging on in quiet desperation",
+ "Is the English way",
+ "The time is gone",
+ "The song is over",
+ "Thought I'd something more to say",
+ "Home",
+ "Home again",
+ "I like to be here",
+ "When I can",
+ "When I come home",
+ "Cold and tired",
+ "It's good to warm my bones",
+ "Beside the fire",
+ "Far away",
+ "Across the field",
+ "Tolling on the iron bell",
+ "Calls the faithful to their knees",
+ "To hear the softly spoken magic spell...",
+ "test #1",
+ "test #2",
+ "this is a test #3",
+ "message #4",
+ "message #5",
+ "test message #6",
+ "a test #7",
+ "the quick brown (#8)",
+ "fox (#9)",
+ "jumps over the (#10)",
+ "lazy dog (#11)",
+ "this is another test message (#12)",
+ "and here is one more for you (#13)",
+ "foo (#14)",
+ "bar (#15)",
+ "foobar (#16)",
+ "barfoo (#17)",
+ "xyzzykukkuluuruu (#18)"
+ };
+
+ char *msg, *p;
+ uint32_t size, nbo_size;
+ size_t n, total;
+ int dump, chk, offs, i, j;
+
+ dump = chk = offs = 0;
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(messages); i++) {
+ msg = messages[i];
+ size = strlen(msg);
+
+ total = 0;
+ p = msg;
+
+ if (ctx.framed) {
+ nbo_size = htobe32(size);
+ if (!mrp_fragbuf_push(buf, &nbo_size, sizeof(nbo_size)))
+ fatal("failed to push message size to buffer");
+ }
+
+ for (j = 0; *p != '\0'; j++) {
+ if (!chunks[j])
+ j = 0;
+ n = chunks[j];
+ if (n > strlen(p))
+ n = strlen(p);
+
+ mrp_debug("pushing %zd bytes (%*.*s)...", n, (int)n, (int)n, p);
+
+ if (!mrp_fragbuf_push(buf, p, n))
+ fatal("failed to push %*.*s to buffer", (int)n, (int)n, p);
+
+ p += n;
+ total += n;
+
+ dump++;
+
+ if (!dump_interval ||
+ (dump_interval > 0 && !(dump % dump_interval)))
+ dump_buffer(buf, messages, &chk, &offs);
+ }
+
+ if (dump_interval < -1) {
+ if (i && !(i % -dump_interval))
+ dump_buffer(buf, messages, &chk, &offs);
+ }
+ }
+
+ dump_buffer(buf, messages, &chk, &offs);
+
+ return TRUE;
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -n, --non-framed set buffer to non-framed mode\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(void)
+{
+ mrp_clear(&ctx);
+ ctx.log_mask = MRP_LOG_UPTO(MRP_LOG_INFO);
+ ctx.log_target = MRP_LOG_TO_STDOUT;
+ ctx.framed = TRUE;
+}
+
+
+void parse_cmdline(int argc, char **argv)
+{
+# define OPTIONS "l:t:vd:nh"
+ struct option options[] = {
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "non-framed", no_argument , NULL, 'n' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults();
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'v':
+ ctx.log_mask <<= 1;
+ ctx.log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx.log_mask = mrp_log_parse_levels(optarg);
+ if (ctx.log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx.log_target = mrp_log_parse_target(optarg);
+ if (!ctx.log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx.log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case'n':
+ ctx.framed = FALSE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ case '?':
+ if (opterr)
+ print_usage(argv[0], EINVAL, "");
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ mrp_fragbuf_t *buf;
+ size_t chunkstbl[][8] = {
+ { 3, 1, 2, 3, 5, 0, 0, 0 },
+ { 1, 2, 3, 4, 3, 2, 1, 0 },
+ { 1, 5, 3, 4, 2, 1, 1, 0 },
+ { 4, 3, 2, 1, 2, 3, 4, 0 },
+ };
+ size_t *chunks;
+ size_t single[] = { 1, 0 };
+ int intervals[] = { 1, 2, 3, 4, 5, 0, -1 };
+ int i, j, interval;
+
+ parse_cmdline(argc, argv);
+
+ mrp_log_set_mask(ctx.log_mask);
+ mrp_log_set_target(ctx.log_target);
+
+ buf = mrp_fragbuf_create(ctx.framed, 0);
+
+ if (buf == NULL)
+ fatal("failed to create data collecting buffer");
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(intervals); i++) {
+ interval = intervals[i];
+ for (j = 0; j < (int)MRP_ARRAY_SIZE(chunkstbl); j++) {
+ chunks = &chunkstbl[j][0];
+ mrp_log_info("testing with interval %d, chunks #%d", interval, j);
+ test(buf, chunks, interval);
+ test(buf, single, interval);
+ mrp_log_info("testing with interval %d, chunks #%d", -i -2, j);
+ test(buf, chunks, -i - 2);
+ test(buf, single, -i - 2);
+ }
+ }
+
+ mrp_fragbuf_destroy(buf);
+
+ return 0;
+}
diff --git a/src/common/tests/glib-pump.c b/src/common/tests/glib-pump.c
new file mode 100644
index 0000000..ece07ff
--- /dev/null
+++ b/src/common/tests/glib-pump.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+
+
+/*
+ * A simple glue layer to pump GMainLoop from mrp_mainloop_t. This
+ * will pretty much be turned into a murphy plugin as such...
+ */
+
+
+typedef struct {
+ GMainLoop *ml;
+ GMainContext *mc;
+ gint maxprio;
+ mrp_subloop_t *sl;
+} glib_glue_t;
+
+static glib_glue_t *glib_glue;
+
+
+static int glib_prepare(void *user_data)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_prepare(glue->mc, &glue->maxprio);
+}
+
+
+static int glib_query(void *user_data, struct pollfd *fds, int nfd,
+ int *timeout)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_query(glue->mc, glue->maxprio, timeout,
+ (GPollFD *)fds, nfd);
+}
+
+
+static int glib_check(void *user_data, struct pollfd *fds, int nfd)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_check(glue->mc, glue->maxprio, (GPollFD *)fds, nfd);
+
+}
+
+
+static void glib_dispatch(void *user_data)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ g_main_context_dispatch(glue->mc);
+
+}
+
+
+static int glib_pump_setup(mrp_mainloop_t *ml)
+{
+ static mrp_subloop_ops_t glib_ops = {
+ .prepare = glib_prepare,
+ .query = glib_query,
+ .check = glib_check,
+ .dispatch = glib_dispatch
+ };
+
+ GMainContext *main_context;
+ GMainLoop *main_loop;
+
+ if (sizeof(GPollFD) != sizeof(struct pollfd)) {
+ mrp_log_error("sizeof(GPollFD:%zd) != sizeof(struct pollfd:%zd)\n",
+ sizeof(GPollFD), sizeof(struct pollfd));
+ return FALSE;
+ }
+
+ main_context = NULL;
+ main_loop = NULL;
+ glib_glue = NULL;
+
+ if ((main_context = g_main_context_default()) != NULL &&
+ (main_loop = g_main_loop_new(main_context, FALSE)) != NULL &&
+ (glib_glue = mrp_allocz(sizeof(*glib_glue))) != NULL) {
+
+ glib_glue->mc = main_context;
+ glib_glue->ml = main_loop;
+ glib_glue->sl = mrp_add_subloop(ml, &glib_ops, glib_glue);
+
+ if (glib_glue->sl != NULL)
+ return TRUE;
+ else
+ mrp_log_error("glib-pump failed to register subloop.");
+ }
+
+ /* all of these handle a NULL argument gracefully... */
+ g_main_loop_unref(main_loop);
+ g_main_context_unref(main_context);
+
+ mrp_free(glib_glue);
+ glib_glue = NULL;
+
+ return FALSE;
+}
+
+
+static void glib_pump_cleanup(void)
+{
+ if (glib_glue != NULL) {
+ mrp_del_subloop(glib_glue->sl);
+
+ g_main_loop_unref(glib_glue->ml);
+ g_main_context_unref(glib_glue->mc);
+
+ mrp_free(glib_glue);
+ glib_glue = NULL;
+ }
+}
+
diff --git a/src/common/tests/hash-test.c b/src/common/tests/hash-test.c
new file mode 100644
index 0000000..6195775
--- /dev/null
+++ b/src/common/tests/hash-test.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/macros.h>
+#include <murphy/common/hashtbl.h>
+
+#define MEMBER_OFFSET MRP_OFFSET
+#define ALLOC_ARR(type, n) mrp_allocz(sizeof(type) * (n))
+#define FREE mrp_free
+#define STRDUP mrp_strdup
+
+#define hash_tbl_t mrp_htbl_t
+#define hash_tbl_cfg_t mrp_htbl_config_t
+#define hash_tbl_create mrp_htbl_create
+#define hash_tbl_delete mrp_htbl_destroy
+#define hash_tbl_add mrp_htbl_insert
+#define hash_tbl_del mrp_htbl_remove
+#define hash_tbl_lookup mrp_htbl_lookup
+
+#define list_hook_t mrp_list_hook_t
+#define list_init mrp_list_init
+#define list_append mrp_list_append
+#define list_delete mrp_list_delete
+
+#define NKEY 4
+#define NPHASE 0xff
+
+#define INFO(fmt, args...) do { \
+ printf("[%s] "fmt"\n" , __FUNCTION__ , ## args); \
+ fflush(stdout); \
+ } while (0)
+
+#define ERROR(fmt, args...) do { \
+ printf("[%s] error: "fmt"\n" , __FUNCTION__, ## args); \
+ fflush(stdout); \
+ } while (0)
+
+#define FATAL(fmt, args...) do { \
+ printf("[%s] fatal error: "fmt"\n" , __FUNCTION__, ## args); \
+ fflush(stdout); \
+ exit(1); \
+ } while (0)
+
+#define MKSTR(fmt, args...) ({ \
+ char *_ptr, _buf[64] = ""; \
+ snprintf(_buf, sizeof(_buf), fmt , ## args); \
+ _ptr = STRDUP(_buf); \
+ _ptr; })
+
+#define ENTRY_KEY(entry, idx) ({ \
+ char *_key; \
+ switch ((idx)) { \
+ case 0: _key = (entry)->str1; break; \
+ case 1: _key = (entry)->str2; break; \
+ case 2: _key = (entry)->str3; break; \
+ case 3: _key = (entry)->str4; break; \
+ default: FATAL("invalid key idx %d", (idx)); \
+ } \
+ _key; })
+
+#define PATTERN_BIT(pattern, idx) \
+ (pattern & (1 << ((idx) & ((sizeof(pattern) * 8) - 1))))
+
+typedef struct {
+ char *str1;
+ int int1;
+ char *str2;
+ list_hook_t hook;
+ char *str3;
+ int int2;
+ char *str4;
+} entry_t;
+
+
+typedef struct {
+ hash_tbl_t *ht;
+ size_t size;
+
+ entry_t *entries;
+ int nentry;
+
+ int keyidx;
+ uint32_t pattern;
+} test_t;
+
+
+test_t test;
+
+void
+populate(void)
+{
+ entry_t *entry;
+ char *key;
+ int i;
+
+ INFO("populating...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ key = ENTRY_KEY(entry, test.keyidx);
+
+ if (hash_tbl_add(test.ht, key, entry))
+ INFO("hashed in entry '%s'", key);
+ else
+ FATAL("failed to hash in entry '%s'", key);
+ }
+
+ INFO("done.");
+}
+
+
+void
+evict(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("evicting...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ if (PATTERN_BIT(test.pattern, i)) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_del(test.ht, key, FALSE);
+
+ if (found != entry)
+ FATAL("expected entry to delete '%s' not found (%p != %p)",
+ key, found, entry);
+
+ INFO("removed entry '%s' (%p)", key, found);
+ }
+ }
+
+ INFO("done.");
+}
+
+
+void
+readd(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("re-adding...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ if (PATTERN_BIT(test.pattern, i)) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_lookup(test.ht, key);
+
+ if (found != NULL)
+ FATAL("unexpected entry to re-add '%s' found (%p)", key, found);
+
+ if (!hash_tbl_add(test.ht, key, entry))
+ FATAL("failed to re-add entry '%s'", key);
+
+ INFO("re-added entry '%s'", key);
+ }
+ }
+
+ INFO("done.");
+}
+
+
+void
+check(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("checking...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_lookup(test.ht, key);
+
+ if (!PATTERN_BIT(test.pattern, i)) {
+ if (found != entry)
+ FATAL("expected entry '%s' not found (%p != %p)",
+ key, found, entry);
+ }
+ else {
+ if (found != NULL)
+ FATAL("unexpected entry '%s' found", key);
+ }
+ }
+
+ INFO("done.");
+}
+
+
+void
+empty_cb(char *key, entry_t *entry, void *data)
+{
+ (void)data;
+
+ FATAL("unexpected entry %p (%s) in hash table", entry, key);
+}
+
+
+void
+reset(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("resetting...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_del(test.ht, key, FALSE);
+
+ if (found != entry)
+ FATAL("expected entry %s not found (%p != %p)",
+ key, found, entry);
+
+ INFO("removed entry '%s' (%p)", key, found);
+ }
+
+ INFO("done.");
+}
+
+
+unsigned int hash_func(const void *key)
+{
+ unsigned int h;
+ const char *p;
+
+ for (h = 0, p = key; *p; p++) {
+ h <<= 1;
+ h ^= *p;
+ }
+
+ return h;
+}
+
+
+int cmp_func(const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+
+void
+test_init(void)
+{
+ int i;
+ entry_t *entry;
+
+ INFO("setting up tests...");
+
+ if ((test.entries = ALLOC_ARR(entry_t, test.nentry)) == NULL)
+ FATAL("failed to allocate test set");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ list_init(&entry->hook);
+
+ entry->str1 = MKSTR("entry-string-%d:1", i);
+ entry->int1 = i;
+ entry->str2 = MKSTR("entry-string-%d:2", i);
+ entry->str3 = MKSTR("entry-string-%d:3", i);
+ entry->int2 = i * 2;
+ entry->str4 = MKSTR("entry-string-%d:4", i);
+
+ if (!entry->str1 || !entry->str2 || !entry->str3 || !entry->str4)
+ FATAL("failed to initialize test set");
+ }
+
+ INFO("test setup done.");
+}
+
+
+void
+test_exit(void)
+{
+ entry_t *entry;
+ int i;
+
+ INFO("cleaning up tests...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ FREE(entry->str1);
+ FREE(entry->str2);
+ FREE(entry->str3);
+ FREE(entry->str4);
+ }
+
+ FREE(test.entries);
+
+ test.entries = NULL;
+ test.nentry = 0;
+
+ INFO("test cleanup done.");
+}
+
+
+void
+test_run(void)
+{
+ hash_tbl_cfg_t cfg;
+ entry_t *entry;
+ int i, j;
+
+
+ /*
+ * Create a hash table, run a test loop consisting of
+ *
+ * 1) populate table
+ * 2) selectively remove entries
+ * 3) check the table
+ * 4) check the entries (for corruption)
+ * 5) reset the table
+ *
+ * then delete the hash table
+ */
+
+ cfg.nbucket = test.size / 4;
+ cfg.hash = hash_func;
+ cfg.comp = cmp_func;
+ cfg.free = NULL;
+ test.ht = hash_tbl_create(&cfg);
+
+ if (test.ht == NULL)
+ FATAL("failed to create hash table (#%d, size %zd)",
+ test.keyidx, test.size);
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ populate();
+
+ test.pattern = 0;
+ for (j = 0; j < NPHASE; j++) {
+ INFO("Running test phase #%d...", j);
+
+ evict();
+ check();
+ readd();
+
+ test.pattern++;
+
+ INFO("done.");
+ }
+
+ reset();
+ }
+
+ hash_tbl_delete(test.ht, FALSE);
+ test.ht = NULL;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ memset(&test, 0, sizeof(test));
+
+ if (argc < 2 || (test.nentry = (int)strtoul(argv[1], NULL, 10)) <= 16)
+ test.nentry = 16;
+
+ test_init();
+
+ for (i = 0; i < NKEY; i++) {
+ test.keyidx = i;
+ test.size = test.nentry; test_run();
+ test.size = test.nentry / 2; test_run();
+ test.size = test.nentry / 4; test_run();
+ }
+
+ test_exit();
+
+ return 0;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim:set expandtab shiftwidth=4:
+ */
+
diff --git a/src/common/tests/hash12-test.c b/src/common/tests/hash12-test.c
new file mode 100644
index 0000000..667c212
--- /dev/null
+++ b/src/common/tests/hash12-test.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <murphy/common.h>
+
+#define LE_STRING "/org/murphy/resource/0/%d"
+
+// Placeholder structure
+typedef struct {
+ void *pointer;
+} test_object;
+
+static void htbl_free_test_object(void *key, void *object) {
+ test_object *obj = object;
+
+ if (key)
+ mrp_free(key);
+
+ if (obj)
+ mrp_free(obj);
+}
+
+int main(int argc, char *argv[]) {
+ mrp_htbl_config_t cfg;
+
+ mrp_htbl_t *table = NULL;
+ test_object *object = NULL;
+
+ char *string = NULL;
+ size_t string_size = 0;
+ int written_count = 0;
+
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ cfg.comp = mrp_string_comp;
+ cfg.hash = mrp_string_hash;
+ cfg.free = htbl_free_test_object;
+ cfg.nbucket = 0; // nentry/4 -> smaller than min -> 8 by def
+ cfg.nentry = 10;
+
+ table = mrp_htbl_create(&cfg);
+ if (!table) {
+ printf("blergh @ creating initial hash table\n");
+ return 1;
+ }
+
+ // broken range: 12 - 66
+ for (int i = 0; i < 12; i++) {
+ object = mrp_allocz(sizeof(test_object));
+ if (!object) {
+ printf("blergh @ allocating object %d\n", i);
+ return 1;
+ }
+ // allocz should handle this, but let's just have a test value written there
+ object->pointer = NULL;
+
+ string_size = snprintf(NULL, 0, LE_STRING, i);
+ if (!string_size) {
+ printf("blergh @ calculating string %d size\n", i);
+ return 1;
+ }
+ // we need the null character as well
+ string_size++;
+
+ string = mrp_allocz(string_size);
+ if (!string) {
+ printf("blergh @ allocating string %d\n", i);
+ return 1;
+ }
+
+ written_count = snprintf(string, string_size, LE_STRING, i);
+ if (written_count <= 0 || written_count + 1 < (int)string_size) {
+ printf("blergh @ writing string %d\n", i);
+ return 1;
+ }
+
+ mrp_htbl_insert(table, string, object);
+ mrp_htbl_remove(table, string, TRUE);
+ }
+
+ mrp_htbl_destroy(table, TRUE);
+ printf("Successfully finished the test\n");
+ return 0;
+}
diff --git a/src/common/tests/internal-transport-test.c b/src/common/tests/internal-transport-test.c
new file mode 100644
index 0000000..e4fa131
--- /dev/null
+++ b/src/common/tests/internal-transport-test.c
@@ -0,0 +1,784 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+
+
+/*
+ * tags for generic message fields
+ */
+
+#define TAG_SEQ ((uint16_t)0x1)
+#define TAG_MSG ((uint16_t)0x2)
+#define TAG_U8 ((uint16_t)0x3)
+#define TAG_S8 ((uint16_t)0x4)
+#define TAG_U16 ((uint16_t)0x5)
+#define TAG_S16 ((uint16_t)0x6)
+#define TAG_DBL ((uint16_t)0x7)
+#define TAG_BLN ((uint16_t)0x8)
+#define TAG_ASTR ((uint16_t)0x9)
+#define TAG_AU32 ((uint16_t)0xa)
+#define TAG_RPL ((uint16_t)0xb)
+#define TAG_END MRP_MSG_FIELD_END
+
+#define U32_GUARD (uint32_t)-1
+
+/*
+ * our test custom data type
+ */
+
+#define TAG_CUSTOM 0x1
+
+typedef struct {
+ uint32_t seq;
+ char *msg;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ double dbl;
+ bool bln;
+ char **astr;
+ uint32_t nstr;
+ uint32_t fsck;
+ uint32_t *au32;
+ char *rpl;
+} custom_t;
+
+
+MRP_DATA_DESCRIPTOR(custom_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, nstr,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+MRP_DATA_DESCRIPTOR(buggy_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, fsck,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+mrp_data_descr_t *data_descr;
+
+typedef struct {
+ mrp_mainloop_t *ml;
+ mrp_transport_t *lt, *st;
+ char *addrstr;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *atype;
+ int server;
+ int sock;
+ mrp_io_watch_t *iow;
+ mrp_timer_t *timer;
+ int custom;
+ int buggy;
+ int connect;
+ int stream;
+ int log_mask;
+ const char *log_target;
+ uint32_t seqno;
+ mrp_list_hook_t clients;
+} context_t;
+
+typedef struct {
+ int id;
+ mrp_transport_t *t;
+ context_t *c;
+ mrp_list_hook_t hook;
+} client_t;
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data);
+
+void recv_custom(mrp_transport_t *t, void *data, uint16_t tag, void *user_data);
+void recvfrom_custom(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+
+
+void dump_msg(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_dump(msg, fp);
+}
+
+
+void srv_recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_msg_field_t *f;
+ uint32_t seq;
+ char buf[256];
+ int status;
+
+ mrp_log_info("received a message");
+ dump_msg(msg, stdout);
+
+ seq = 0;
+ if ((f = mrp_msg_find(msg, TAG_SEQ)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_UINT32)
+ seq = f->u32;
+ }
+
+ snprintf(buf, sizeof(buf), "reply to message #%u", seq);
+
+ if (!mrp_msg_append(msg, TAG_RPL, MRP_MSG_FIELD_STRING, buf,
+ TAG_END)) {
+ mrp_log_info("failed to append to received message");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(t, msg);
+ else
+ status = mrp_transport_sendto(t, msg, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ /* message unreffed by transport layer */
+}
+
+
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ MRP_UNUSED(t);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+ MRP_UNUSED(user_data);
+
+ mrp_log_info("client received a message");
+ dump_msg(msg, stdout);
+}
+
+
+void srv_recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return srv_recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void dump_custom(custom_t *msg, FILE *fp)
+{
+ uint32_t i;
+
+ mrp_data_dump(msg, data_descr, fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, " seq = %u\n" , msg->seq);
+ fprintf(fp, " msg = '%s'\n", msg->msg);
+ fprintf(fp, " u8 = %u\n" , msg->u8);
+ fprintf(fp, " s8 = %d\n" , msg->s8);
+ fprintf(fp, " u16 = %u\n" , msg->u16);
+ fprintf(fp, " s16 = %d\n" , msg->s16);
+ fprintf(fp, " dbl = %f\n" , msg->dbl);
+ fprintf(fp, " bln = %s\n" , msg->bln ? "true" : "false");
+ fprintf(fp, " astr = (%u)\n", msg->nstr);
+ for (i = 0; i < msg->nstr; i++)
+ fprintf(fp, " %s\n", msg->astr[i]);
+ fprintf(fp, " au32 =\n");
+ for (i = 0; msg->au32[i] != U32_GUARD; i++)
+ fprintf(fp, " %u\n", msg->au32[i]);
+ fprintf(fp, " rpl = '%s'\n", msg->rpl);
+ fprintf(fp, "}\n");
+}
+
+
+void free_custom(custom_t *msg)
+{
+ mrp_data_free(msg, data_descr->tag);
+}
+
+
+
+void srv_recvfrom_custom(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ custom_t *msg = (custom_t *)data;
+ custom_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("server received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_senddata(t, &rpl, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(t, &rpl, data_descr->tag,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ free_custom(msg);
+}
+
+void recvfrom_custom(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ custom_t *msg = (custom_t *)data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(data);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+ MRP_UNUSED(user_data);
+
+ mrp_log_info("received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ free_custom(msg);
+}
+
+
+void recv_custom(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ recvfrom_custom(t, data, tag, NULL, 0, user_data);
+}
+
+void srv_recv_custom(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ srv_recvfrom_custom(t, data, tag, NULL, 0, user_data);
+}
+
+void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(c);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s).", error,
+ strerror(error));
+ exit(1);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection.");
+ exit(0);
+ }
+}
+
+
+void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ int flags;
+
+ mrp_log_info("connection event!");
+
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ c->st = mrp_transport_accept(lt, c, flags);
+
+ if (c->st == NULL) {
+ mrp_log_error("Failed to accept new connection.");
+ exit(1);
+ }
+}
+
+
+void type_init(context_t *c)
+{
+ if (c->buggy && c->server) {
+ data_descr = &buggy_descr;
+ mrp_log_info("Deliberately using buggy data descriptor...");
+ }
+ else
+ data_descr = &custom_descr;
+
+ if (!mrp_msg_register_type(data_descr)) {
+ mrp_log_error("Failed to register custom data type.");
+ exit(1);
+ }
+}
+
+
+void server_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = connection_evt
+ };
+
+ int flags;
+
+ if (c->custom) {
+ evt.recvdata = srv_recv_custom;
+ evt.recvdatafrom = srv_recvfrom_custom;
+ }
+ else {
+ evt.recvmsg = srv_recv_msg;
+ evt.recvmsgfrom = srv_recvfrom_msg;
+ }
+
+
+ flags = MRP_TRANSPORT_REUSEADDR |
+ (c->custom ? MRP_TRANSPORT_MODE_DATA : 0);
+ c->lt = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->lt == NULL) {
+ mrp_log_error("Failed to create listening server transport.");
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->lt, &c->addr, c->alen)) {
+ mrp_log_error("Failed to bind transport to address %s.", c->addrstr);
+ exit(1);
+ }
+
+ if (c->stream) {
+ if (!mrp_transport_listen(c->lt, 0)) {
+ mrp_log_error("Failed to listen on server transport.");
+ exit(1);
+ }
+ }
+}
+
+
+void send_msg(client_t *client)
+{
+ mrp_msg_t *msg;
+ uint32_t seq;
+ char buf[256];
+ char *astr[] = { "this", "is", "an", "array", "of", "strings" };
+ uint32_t au32[] = { 1, 2, 3,
+ 1 << 16, 2 << 16, 3 << 16,
+ 1 << 24, 2 << 24, 3 << 24 };
+ uint32_t nstr = MRP_ARRAY_SIZE(astr);
+ uint32_t nu32 = MRP_ARRAY_SIZE(au32);
+ int status;
+ context_t *c = client->c;
+
+ seq = c->seqno++;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+
+ msg = mrp_msg_create(TAG_SEQ , MRP_MSG_FIELD_UINT32, seq,
+ TAG_MSG , MRP_MSG_FIELD_STRING, buf,
+ TAG_U8 , MRP_MSG_FIELD_UINT8 , seq & 0xf,
+ TAG_S8 , MRP_MSG_FIELD_SINT8 , -(seq & 0xf),
+ TAG_U16 , MRP_MSG_FIELD_UINT16, seq,
+ TAG_S16 , MRP_MSG_FIELD_SINT16, - seq,
+ TAG_DBL , MRP_MSG_FIELD_DOUBLE, seq / 3.0,
+ TAG_BLN , MRP_MSG_FIELD_BOOL , seq & 0x1,
+ TAG_ASTR, MRP_MSG_FIELD_ARRAY_OF(STRING), nstr, astr,
+ TAG_AU32, MRP_MSG_FIELD_ARRAY_OF(UINT32), nu32, au32,
+ TAG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(client->t, msg);
+ else
+ status = mrp_transport_sendto(client->t, msg, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", seq);
+
+ mrp_msg_unref(msg);
+}
+
+
+void send_custom(client_t *client)
+{
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+ context_t *c = client->c;
+ uint32_t seq = c->seqno++;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_senddata(client->t, &msg, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(client->t, &msg, data_descr->tag,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+
+void send_cb(mrp_timer_t *t, void *user_data)
+{
+ client_t *client = (client_t *)user_data;
+ context_t *c = client->c;
+
+ MRP_UNUSED(t);
+
+ if (c->custom)
+ send_custom(client);
+ else
+ send_msg(client);
+}
+
+
+void client_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ int flags;
+ client_t *client;
+
+ if (c->custom) {
+ evt.recvdata = recv_custom;
+ evt.recvdatafrom = recvfrom_custom;
+ }
+ else {
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ }
+
+ client = mrp_allocz(sizeof(client_t));
+ mrp_list_init(&client->hook);
+ client->c = c;
+
+ mrp_list_append(&c->clients, &client->hook);
+
+ flags = c->custom ? MRP_TRANSPORT_MODE_DATA : 0;
+ client->t = mrp_transport_create(c->ml, c->atype, &evt, client, flags);
+
+ if (client->t == NULL) {
+ mrp_log_error("Failed to create new transport.");
+ exit(1);
+ }
+
+ if (!strcmp(c->atype, "unxd")) {
+ char addrstr[] = "unxd:@stream-test-client";
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ alen = mrp_transport_resolve(NULL, addrstr, &addr, sizeof(addr), NULL);
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", addrstr);
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(client->t, &addr, alen)) {
+ mrp_log_error("Failed to bind to transport address '%s'.", addrstr);
+ exit(1);
+ }
+ }
+
+ if (c->connect) {
+ if (!mrp_transport_connect(client->t, &c->addr, c->alen)) {
+ mrp_log_error("Failed to connect to %s.", c->addrstr);
+ exit(1);
+ }
+ }
+
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, client);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create send timer.");
+ exit(1);
+ }
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [transport-address]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -C, --connect connect transport\n"
+ " For connection-oriented transports, this is automatic.\n"
+ " -a, --address address to use\n"
+ " -c, --custom use custom messages\n"
+ " -m, --message use generic messages (default)\n"
+ " -b, --buggy use buggy data descriptors\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->addrstr = "tcp4:127.0.0.1:3000";
+ ctx->server = FALSE;
+ ctx->custom = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "scmbCa:l:t:vdh"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "address" , required_argument, NULL, 'a' },
+ { "custom" , no_argument , NULL, 'c' },
+ { "connect" , no_argument , NULL, 'C' },
+ { "message" , no_argument , NULL, 'm' },
+ { "buggy" , no_argument , NULL, 'b' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , no_argument , NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt, debug;
+
+ debug = FALSE;
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'c':
+ ctx->custom = TRUE;
+ break;
+
+ case 'm':
+ ctx->custom = FALSE;
+ break;
+
+ case 'b':
+ ctx->buggy = TRUE;
+ break;
+
+ case 'C':
+ ctx->connect = TRUE;
+ break;
+
+ case 'a':
+ ctx->addrstr = optarg;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (debug)
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ mrp_log_info("Using address '%s'...", c.addrstr);
+
+ mrp_list_init(&c.clients);
+
+ if (c.custom)
+ mrp_log_info("Using custom messages...");
+ else
+ mrp_log_info("Using generic messages...");
+
+ if (!strncmp(c.addrstr, "tcp", 3) ||
+ !strncmp(c.addrstr, "unxs", 4)) {
+ c.stream = TRUE;
+ c.connect = TRUE;
+ }
+
+ c.alen = mrp_transport_resolve(NULL, c.addrstr,
+ &c.addr, sizeof(c.addr), &c.atype);
+ if (c.alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", c.addrstr);
+ exit(1);
+ }
+
+ c.ml = mrp_mainloop_create();
+
+ type_init(&c);
+
+ server_init(&c);
+
+ client_init(&c);
+ client_init(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ return 0;
+}
diff --git a/src/common/tests/libdbus-test.c b/src/common/tests/libdbus-test.c
new file mode 100644
index 0000000..1f68bc4
--- /dev/null
+++ b/src/common/tests/libdbus-test.c
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <murphy/common/dbus-libdbus.h>
+
+#define SERVER_NAME "org.test.murphy-server"
+#define SERVER_PATH "/server"
+#define SERVER_INTERFACE "Murphy.Server"
+#define PING "ping"
+#define CLIENT_NAME "org.test.murphy-client"
+#define CLIENT_PATH "/client"
+#define CLIENT_INTERFACE "Murphy.Client"
+#define PONG "pong"
+
+
+typedef struct {
+ char *busaddr;
+ int server;
+ int log_mask;
+ const char *log_target;
+ mrp_mainloop_t *ml;
+ mrp_timer_t *timer;
+ uint32_t seqno;
+ mrp_dbus_t *dbus;
+ const char *name;
+ int32_t cid;
+ int server_up;
+ int all_pongs;
+} context_t;
+
+
+static int ping_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+ const char *dest;
+
+ MRP_UNUSED(c);
+
+ if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_METHOD_CALL) {
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping request #%u", seq);
+ else
+ mrp_log_error("-> malformed ping request");
+
+ if (!mrp_dbus_reply(dbus, msg,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send ping reply #%u.", seq);
+ else
+ mrp_log_info("<- ping reply #%u", seq);
+
+ if (seq & 0x1)
+ dest = mrp_dbus_msg_sender(msg);
+ else
+ dest = NULL;
+
+ if (!mrp_dbus_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send pong signal #%u.", seq);
+ else
+ mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq);
+ }
+
+ return TRUE;
+}
+
+
+static void server_setup(context_t *c)
+{
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ if (!mrp_dbus_acquire_name(c->dbus, SERVER_NAME, NULL)) {
+ mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.",
+ SERVER_NAME, c->busaddr);
+ exit(1);
+ }
+
+ if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c)) {
+ mrp_log_error("Failed to export D-BUS method '%s'.", PING);
+ exit(1);
+ }
+}
+
+
+void server_cleanup(context_t *c)
+{
+ mrp_dbus_release_name(c->dbus, SERVER_NAME, NULL);
+ mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void ping_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(user_data);
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping reply #%u", seq);
+ else
+ mrp_log_error("Received malformed ping reply.");
+
+ c->cid = 0;
+}
+
+
+static void ping_request(context_t *c)
+{
+ uint32_t seq;
+
+ if (c->cid != 0) {
+ mrp_log_warning("Previous ping request still unanswered...");
+ return;
+ }
+
+ seq = c->seqno++;
+ c->cid = mrp_dbus_call(c->dbus,
+ SERVER_NAME, SERVER_PATH, SERVER_INTERFACE,
+ PING, 500, ping_reply, c,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID);
+
+ if (c->cid > 0)
+ mrp_log_info("<- ping request #%u", seq);
+ else
+ mrp_log_warning("Failed to send ping request #%u.", seq);
+}
+
+
+static void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ ping_request(c);
+}
+
+
+static int pong_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_SIGNAL) {
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> pong signal #%u", seq);
+ else
+ mrp_log_error("-> malformed pong signal");
+ }
+
+ return TRUE;
+}
+
+
+static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ mrp_log_info("%s came up (as %s)", name, owner);
+
+ if (c->timer == NULL) {
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+ }
+ }
+ else {
+ mrp_log_info("%s went down", name);
+
+ if (c->timer != NULL) {
+ mrp_del_timer(c->timer);
+ c->timer = NULL;
+ }
+ }
+}
+
+
+static void client_setup(context_t *c)
+{
+ const char *dest;
+
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c);
+
+ if (c->all_pongs) {
+ mrp_log_info("Subscribing for all pong signals...");
+ dest = NULL;
+ }
+ else {
+ mrp_log_info("Subscribing only for pong signals to us...");
+ dest = c->name;
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ dest, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL)) {
+ mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH,
+ SERVER_INTERFACE, PONG);
+ exit(1);
+ }
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+}
+
+
+static void client_cleanup(context_t *c)
+{
+ mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c);
+ mrp_del_timer(c->timer);
+ mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ c->name, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -b, --bus connect the given D-BUS\n"
+ " If omitted, defaults to the session bus.\n"
+ " -a, --all-pongs subscribe for all pong signals\n"
+ " If omitted, only pong with the client address are handled.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->busaddr = "session";
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "sab:l:t:vdh"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "bus" , required_argument, NULL, 'b' },
+ { "all-pongs" , no_argument , NULL, 'a' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , no_argument , NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt, debug;
+
+ debug = FALSE;
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'b':
+ ctx->busaddr = optarg;
+ break;
+
+ case 'a':
+ ctx->all_pongs = TRUE;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (debug)
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+
+ return TRUE;
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(c);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+
+ case SIGTERM:
+ mrp_log_info("Got SIGTERM, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr);
+ else
+ mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr);
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c);
+
+ if (c.server)
+ server_setup(&c);
+ else
+ client_setup(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ if (c.server)
+ server_cleanup(&c);
+ else
+ client_cleanup(&c);
+
+ return 0;
+}
diff --git a/src/common/tests/libdbus-transport-test.c b/src/common/tests/libdbus-transport-test.c
new file mode 100644
index 0000000..06c2320
--- /dev/null
+++ b/src/common/tests/libdbus-transport-test.c
@@ -0,0 +1,847 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+
+/*
+ * tags for generic message fields
+ */
+
+#define TAG_SEQ ((uint16_t)0x1)
+#define TAG_MSG ((uint16_t)0x2)
+#define TAG_U8 ((uint16_t)0x3)
+#define TAG_S8 ((uint16_t)0x4)
+#define TAG_U16 ((uint16_t)0x5)
+#define TAG_S16 ((uint16_t)0x6)
+#define TAG_DBL ((uint16_t)0x7)
+#define TAG_BLN ((uint16_t)0x8)
+#define TAG_ASTR ((uint16_t)0x9)
+#define TAG_AU32 ((uint16_t)0xa)
+#define TAG_RPL ((uint16_t)0xb)
+#define TAG_END MRP_MSG_FIELD_END
+
+#define U32_GUARD (uint32_t)-1
+
+/*
+ * our test custom data type
+ */
+
+#define TAG_CUSTOM 0x1
+
+typedef struct {
+ uint32_t seq;
+ char *msg;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ double dbl;
+ bool bln;
+ char **astr;
+ uint32_t nstr;
+ uint32_t fsck;
+ uint32_t *au32;
+ char *rpl;
+} custom_t;
+
+
+MRP_DATA_DESCRIPTOR(custom_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, nstr,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+MRP_DATA_DESCRIPTOR(buggy_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, fsck,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+mrp_data_descr_t *data_descr;
+
+
+typedef enum {
+ MODE_DEFAULT = 0,
+ MODE_MESSAGE = 1,
+ MODE_DATA = 2,
+ MODE_RAW = 3,
+} msg_mode_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml;
+ mrp_transport_t *lt, *t;
+ char *addrstr;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *atype;
+ int server;
+ int sock;
+ mrp_io_watch_t *iow;
+ mrp_timer_t *timer;
+ int mode;
+ int buggy;
+ int connect;
+ int stream;
+ int log_mask;
+ const char *log_target;
+ uint32_t seqno;
+} context_t;
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data);
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data);
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+void recvraw(mrp_transport_t *t, void *data, size_t size, void *user_data);
+void recvrawfrom(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+
+void dump_msg(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_dump(msg, fp);
+}
+
+
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_msg_field_t *f;
+ uint32_t seq;
+ char buf[256];
+ int status;
+
+ mrp_log_info("received a message");
+ dump_msg(msg, stdout);
+
+ if (c->server) {
+ seq = 0;
+ if ((f = mrp_msg_find(msg, TAG_SEQ)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_UINT32)
+ seq = f->u32;
+ }
+
+ snprintf(buf, sizeof(buf), "reply to message #%u", seq);
+
+ if (!mrp_msg_append(msg, TAG_RPL, MRP_MSG_FIELD_STRING, buf,
+ TAG_END)) {
+ mrp_log_info("failed to append to received message");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(t, msg);
+ else
+ status = mrp_transport_sendto(t, msg, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ /* message unreffed by transport layer */
+ }
+}
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void dump_custom(custom_t *msg, FILE *fp)
+{
+ uint32_t i;
+
+ mrp_data_dump(msg, data_descr, fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, " seq = %u\n" , msg->seq);
+ fprintf(fp, " msg = '%s'\n", msg->msg);
+ fprintf(fp, " u8 = %u\n" , msg->u8);
+ fprintf(fp, " s8 = %d\n" , msg->s8);
+ fprintf(fp, " u16 = %u\n" , msg->u16);
+ fprintf(fp, " s16 = %d\n" , msg->s16);
+ fprintf(fp, " dbl = %f\n" , msg->dbl);
+ fprintf(fp, " bln = %s\n" , msg->bln ? "true" : "false");
+ fprintf(fp, " astr = (%u)\n", msg->nstr);
+ for (i = 0; i < msg->nstr; i++)
+ fprintf(fp, " %s\n", msg->astr[i]);
+ fprintf(fp, " au32 =\n");
+ for (i = 0; msg->au32[i] != U32_GUARD; i++)
+ fprintf(fp, " %u\n", msg->au32[i]);
+ fprintf(fp, " rpl = '%s'\n", msg->rpl);
+ fprintf(fp, "}\n");
+}
+
+
+void free_custom(custom_t *msg)
+{
+ mrp_data_free(msg, data_descr->tag);
+}
+
+
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ custom_t *msg = (custom_t *)data;
+ custom_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ if (c->server) {
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_senddata(t, &rpl, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(t, &rpl, data_descr->tag,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+
+ free_custom(msg);
+}
+
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ recvfrom_data(t, data, tag, NULL, 0, user_data);
+}
+
+
+void dump_raw(void *data, size_t size, FILE *fp)
+{
+ int len = (int)size;
+
+ fprintf(fp, "[%*.*s]\n", len, len, (char *)data);
+}
+
+
+void recvfrom_raw(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ char rpl[256];
+ size_t rpl_size;
+ int status;
+
+ rpl_size = snprintf(rpl, sizeof(rpl), "reply to message [%*.*s]",
+ (int)size, (int)size, (char *)data);
+
+ mrp_log_info("received raw message");
+ dump_raw(data, size, stdout);
+
+ if (strncmp((char *)data, "reply to ", 9) != 0) {
+ if (c->connect)
+ status = mrp_transport_sendraw(t, rpl, rpl_size);
+ else
+ status = mrp_transport_sendrawto(t, rpl, rpl_size, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+}
+
+
+void recv_raw(mrp_transport_t *t, void *data, size_t size, void *user_data)
+{
+ recvfrom_raw(t, data, size, NULL, 0, user_data);
+}
+
+
+
+void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(c);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s).", error,
+ strerror(error));
+ exit(1);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection.");
+ exit(0);
+ }
+}
+
+
+void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ int flags;
+
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ c->t = mrp_transport_accept(lt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to accept new connection.");
+ exit(1);
+ }
+}
+
+
+void type_init(context_t *c)
+{
+ if (c->buggy && c->server) {
+ data_descr = &buggy_descr;
+ mrp_log_info("Deliberately using buggy data descriptor...");
+ }
+ else
+ data_descr = &custom_descr;
+
+ if (!mrp_msg_register_type(data_descr)) {
+ mrp_log_error("Failed to register custom data type.");
+ exit(1);
+ }
+}
+
+
+void server_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = NULL,
+ .connection = NULL,
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ break;
+ case MODE_MESSAGE:
+ default:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ }
+
+ if (c->stream) {
+ evt.connection = connection_evt;
+ evt.closed = closed_evt;
+ }
+
+ flags = MRP_TRANSPORT_REUSEADDR;
+
+ switch (c->mode) {
+ case MODE_DATA: flags |= MRP_TRANSPORT_MODE_DATA; break;
+ case MODE_RAW: flags |= MRP_TRANSPORT_MODE_RAW; break;
+ default:
+ case MODE_MESSAGE: flags |= MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->lt = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->lt == NULL) {
+ mrp_log_error("Failed to create listening server transport.");
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->lt, &c->addr, c->alen)) {
+ mrp_log_error("Failed to bind transport to address %s.", c->addrstr);
+ exit(1);
+ }
+
+ if (c->stream) {
+ if (!mrp_transport_listen(c->lt, 0)) {
+ mrp_log_error("Failed to listen on server transport.");
+ exit(1);
+ }
+ }
+}
+
+
+void send_msg(context_t *c)
+{
+ mrp_msg_t *msg;
+ uint32_t seq;
+ char buf[256];
+ char *astr[] = { "this", "is", "an", "array", "of", "strings" };
+ uint32_t au32[] = { 1, 2, 3,
+ 1 << 16, 2 << 16, 3 << 16,
+ 1 << 24, 2 << 24, 3 << 24 };
+ uint32_t nstr = MRP_ARRAY_SIZE(astr);
+ uint32_t nu32 = MRP_ARRAY_SIZE(au32);
+ int status;
+
+ seq = c->seqno++;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+
+ msg = mrp_msg_create(TAG_SEQ , MRP_MSG_FIELD_UINT32, seq,
+ TAG_MSG , MRP_MSG_FIELD_STRING, buf,
+ TAG_U8 , MRP_MSG_FIELD_UINT8 , seq & 0xf,
+ TAG_S8 , MRP_MSG_FIELD_SINT8 , -(seq & 0xf),
+ TAG_U16 , MRP_MSG_FIELD_UINT16, seq,
+ TAG_S16 , MRP_MSG_FIELD_SINT16, - seq,
+ TAG_DBL , MRP_MSG_FIELD_DOUBLE, seq / 3.0,
+ TAG_BLN , MRP_MSG_FIELD_BOOL , seq & 0x1,
+ TAG_ASTR, MRP_MSG_FIELD_ARRAY_OF(STRING), nstr, astr,
+ TAG_AU32, MRP_MSG_FIELD_ARRAY_OF(UINT32), nu32, au32,
+ TAG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(c->t, msg);
+ else
+ status = mrp_transport_sendto(c->t, msg, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", seq);
+
+ mrp_msg_unref(msg);
+}
+
+
+void send_data(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_senddata(c->t, &msg, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(c->t, &msg, data_descr->tag,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+void send_raw(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ char msg[256];
+ size_t size;
+ int status;
+
+ size = snprintf(msg, sizeof(msg), "this is message #%u", seq);
+
+ if (c->connect)
+ status = mrp_transport_sendraw(c->t, msg, size);
+ else
+ status = mrp_transport_sendrawto(c->t, msg, size, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send raw message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%u succesfully sent.", seq);
+}
+
+
+
+void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ switch (c->mode) {
+ case MODE_DATA: send_data(c); break;
+ case MODE_RAW: send_raw(c); break;
+ default:
+ case MODE_MESSAGE: send_msg(c);
+ }
+}
+
+
+void client_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ flags = MRP_TRANSPORT_MODE_DATA;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ flags = MRP_TRANSPORT_MODE_RAW;
+ break;
+ default:
+ case MODE_MESSAGE:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ flags = MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->t = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to create new transport.");
+ exit(1);
+ }
+
+ if (!strcmp(c->atype, "unxd")) {
+ char addrstr[] = "unxd:@stream-test-client";
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ alen = mrp_transport_resolve(NULL, addrstr, &addr, sizeof(addr), NULL);
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", addrstr);
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->t, &addr, alen)) {
+ mrp_log_error("Failed to bind to transport address '%s'.", addrstr);
+ exit(1);
+ }
+ }
+
+ if (c->connect) {
+ if (!mrp_transport_connect(c->t, &c->addr, c->alen)) {
+ mrp_log_error("Failed to connect to %s.", c->addrstr);
+ exit(1);
+ }
+ }
+
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create send timer.");
+ exit(1);
+ }
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [transport-address]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -C, --connect connect transport\n"
+ " For connection-oriented transports, this is automatic.\n"
+ " -a, --address address to use\n"
+ " -c, --custom use custom messages\n"
+ " -m, --message use generic messages (default)\n"
+ " -r, --raw use raw messages\n"
+ " -b, --buggy use buggy data descriptors\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->addrstr = "tcp4:127.0.0.1:3000";
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "scmrbCa:l:t:v:d:h"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "address" , required_argument, NULL, 'a' },
+ { "custom" , no_argument , NULL, 'c' },
+ { "message" , no_argument , NULL, 'm' },
+ { "raw" , no_argument , NULL, 'r' },
+ { "connect" , no_argument , NULL, 'C' },
+
+ { "buggy" , no_argument , NULL, 'b' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'c':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_DATA;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'm':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_MESSAGE;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_RAW;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'b':
+ ctx->buggy = TRUE;
+ break;
+
+ case 'C':
+ ctx->connect = TRUE;
+ break;
+
+ case 'a':
+ ctx->addrstr = optarg;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using address '%s'...", c.addrstr);
+ else
+ mrp_log_info("Running as client, using address '%s'...", c.addrstr);
+
+ switch (c.mode) {
+ case MODE_DATA: mrp_log_info("Using custom data messages..."); break;
+ case MODE_RAW: mrp_log_info("Using raw messages..."); break;
+ default:
+ case MODE_MESSAGE: mrp_log_info("Using generic messages...");
+ }
+
+ if (!strncmp(c.addrstr, "tcp", 3) || !strncmp(c.addrstr, "unxs", 4) ||
+ !strncmp(c.addrstr, "wsck", 4)) {
+ c.stream = TRUE;
+ c.connect = TRUE;
+ }
+
+ c.alen = mrp_transport_resolve(NULL, c.addrstr,
+ &c.addr, sizeof(c.addr), &c.atype);
+ if (c.alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", c.addrstr);
+ exit(1);
+ }
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.server)
+ server_init(&c);
+ else
+ client_init(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ return 0;
+}
diff --git a/src/common/tests/mainloop-ecore-test.c b/src/common/tests/mainloop-ecore-test.c
new file mode 100644
index 0000000..d094fac
--- /dev/null
+++ b/src/common/tests/mainloop-ecore-test.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef ECORE_ENABLED
+
+struct ecore_config_s {
+ int dummy;
+};
+
+
+mrp_mainloop_t *ecore_mainloop_create(test_config_t *cfg)
+{
+ cfg->ml = mrp_mainloop_ecore_get();
+
+ return cfg->ml;
+}
+
+
+int ecore_mainloop_run(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ ecore_main_loop_begin();
+
+ return TRUE;
+}
+
+
+int ecore_mainloop_quit(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ ecore_main_loop_quit();
+
+ return TRUE;
+}
+
+
+int ecore_mainloop_cleanup(test_config_t *cfg)
+{
+ mrp_mainloop_unregister(cfg->ml);
+ mrp_mainloop_destroy(cfg->ml);
+
+ cfg->ml = NULL;
+
+ return TRUE;
+}
+
+
+#else
+
+
+mrp_mainloop_t *ecore_mainloop_create(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+int ecore_mainloop_run(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+int ecore_mainloop_quit(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+int ecore_mainloop_cleanup(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+#endif
diff --git a/src/common/tests/mainloop-glib-test.c b/src/common/tests/mainloop-glib-test.c
new file mode 100644
index 0000000..a13741e
--- /dev/null
+++ b/src/common/tests/mainloop-glib-test.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef GLIB_ENABLED
+
+#include <murphy/common/glib-glue.h>
+
+struct glib_config_s {
+ GMainLoop *gml;
+};
+
+
+mrp_mainloop_t *glib_mainloop_create(test_config_t *cfg)
+{
+ glib_config_t *glib;
+ mrp_mainloop_t *ml;
+
+ glib = mrp_allocz(sizeof(*glib));
+
+ if (glib != NULL) {
+ glib->gml = g_main_loop_new(NULL, FALSE);
+ ml = mrp_mainloop_glib_get(glib->gml);
+
+ if (ml != NULL) {
+ cfg->glib = glib;
+ cfg->ml = ml;
+
+ return ml;
+ }
+ else {
+ g_main_loop_unref(glib->gml);
+ mrp_free(glib);
+ }
+ }
+
+ return NULL;
+}
+
+
+int glib_mainloop_run(test_config_t *cfg)
+{
+ if (cfg->glib != NULL) {
+ g_main_loop_run(cfg->glib->gml);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int glib_mainloop_quit(test_config_t *cfg)
+{
+ if (cfg->glib != NULL) {
+ g_main_loop_quit(cfg->glib->gml);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int glib_mainloop_cleanup(test_config_t *cfg)
+{
+ if (cfg->glib != NULL) {
+ mrp_mainloop_unregister(cfg->ml);
+ mrp_mainloop_destroy(cfg->ml);
+ cfg->ml = NULL;
+
+ g_main_loop_unref(cfg->glib->gml);
+ mrp_free(cfg->glib);
+ cfg->glib = NULL;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+#else
+
+
+mrp_mainloop_t *glib_mainloop_create(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+int glib_mainloop_run(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+int glib_mainloop_quit(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+int glib_mainloop_cleanup(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+#endif
diff --git a/src/common/tests/mainloop-pulse-test.c b/src/common/tests/mainloop-pulse-test.c
new file mode 100644
index 0000000..58945dc
--- /dev/null
+++ b/src/common/tests/mainloop-pulse-test.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef PULSE_ENABLED
+
+#include <murphy/common/pulse-glue.h>
+
+struct pulse_config_s {
+ pa_mainloop *pa_main;
+ pa_mainloop_api *pa;
+};
+
+
+mrp_mainloop_t *pulse_mainloop_create(test_config_t *cfg)
+{
+ pulse_config_t *pulse;
+ mrp_mainloop_t *ml;
+
+ pulse = mrp_allocz(sizeof(*pulse));
+
+ if (pulse != NULL) {
+ pulse->pa_main = pa_mainloop_new();
+ pulse->pa = pa_mainloop_get_api(pulse->pa_main);
+ ml = mrp_mainloop_pulse_get(pulse->pa);
+
+ if (ml != NULL) {
+ cfg->pulse = pulse;
+ cfg->ml = ml;
+
+ return ml;
+ }
+ else {
+ pa_mainloop_free(pulse->pa_main);
+ mrp_free(pulse);
+ }
+ }
+
+ return NULL;
+}
+
+
+int pulse_mainloop_run(test_config_t *cfg)
+{
+ int retval;
+
+ if (cfg->pulse && cfg->pulse->pa != NULL) {
+ pa_mainloop_run(cfg->pulse->pa_main, &retval);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int pulse_mainloop_quit(test_config_t *cfg)
+{
+ if (cfg->pulse && cfg->pulse->pa) {
+ pa_mainloop_quit(cfg->pulse->pa_main, 0);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int pulse_mainloop_cleanup(test_config_t *cfg)
+{
+ if (cfg->pulse != NULL) {
+ mrp_mainloop_unregister(cfg->ml);
+ mrp_mainloop_destroy(cfg->ml);
+ cfg->ml = NULL;
+
+ pa_mainloop_free(cfg->pulse->pa_main);
+ mrp_free(cfg->pulse);
+ cfg->pulse = NULL;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+#else
+
+
+mrp_mainloop_t *pulse_mainloop_create(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+int pulse_mainloop_run(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+int pulse_mainloop_quit(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+int pulse_mainloop_cleanup(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+#endif
diff --git a/src/common/tests/mainloop-qt-test.cpp b/src/common/tests/mainloop-qt-test.cpp
new file mode 100644
index 0000000..4c047a6
--- /dev/null
+++ b/src/common/tests/mainloop-qt-test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/config.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+
+#include "mainloop-qt-test.h"
+
+#include <QCoreApplication>
+#include <murphy/common/qt-glue.h>
+
+
+typedef struct qt_config_s {
+ QCoreApplication *app;
+ mrp_mainloop_t *ml;
+} qt_config_t ;
+
+static qt_config_t *qt;
+
+mrp_mainloop_t *qt_mainloop_create()
+{
+ mrp_mainloop_t *ml = NULL;
+
+ if (qt == NULL) {
+ int argc = 0;
+ char **argv = 0;
+
+ qt = (qt_config_t *)mrp_allocz(sizeof(*qt));
+ if (!qt) return NULL;
+
+ qt->app = new QCoreApplication(argc, argv);
+ ml = mrp_mainloop_qt_get();
+
+ if (ml == NULL) {
+ delete qt->app;
+ mrp_free(qt);
+ qt = NULL;
+ }
+ }
+ else {
+ return qt->ml;
+ }
+
+ return ml;
+}
+
+int qt_mainloop_run()
+{
+ if (qt != NULL) {
+ QCoreApplication::exec();
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int qt_mainloop_quit()
+{
+ if (qt != NULL) {
+ QCoreApplication::quit();
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int qt_mainloop_cleanup(mrp_mainloop_t *ml)
+{
+ if (qt != NULL) {
+ mrp_mainloop_unregister(ml);
+ mrp_mainloop_destroy(ml);
+
+ delete qt->app;
+ mrp_free(qt);
+ qt = NULL;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
diff --git a/src/common/tests/mainloop-qt-test.h b/src/common/tests/mainloop-qt-test.h
new file mode 100644
index 0000000..cfa3f3e
--- /dev/null
+++ b/src/common/tests/mainloop-qt-test.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_QT_TEST_H_
+#define __MURPHY_QT_TEST_H_
+
+#include <murphy/config.h>
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+mrp_mainloop_t *qt_mainloop_create(void);
+int qt_mainloop_run(void);
+int qt_mainloop_quit(void);
+int qt_mainloop_cleanup(mrp_mainloop_t *ml);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_QT_TEST_H_ */
diff --git a/src/common/tests/mainloop-test.c b/src/common/tests/mainloop-test.c
new file mode 100644
index 0000000..77dea36
--- /dev/null
+++ b/src/common/tests/mainloop-test.c
@@ -0,0 +1,1795 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <dbus/dbus.h>
+
+#include <murphy/config.h>
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+#ifdef PULSE_ENABLED
+# include <pulse/mainloop.h>
+# include <murphy/common/pulse-glue.h>
+#endif
+
+#ifdef ECORE_ENABLED
+# include <Ecore.h>
+# include <murphy/common/ecore-glue.h>
+#endif
+
+#ifdef GLIB_ENABLED
+# include <glib.h>
+# include "glib-pump.c"
+# include <murphy/common/glib-glue.h>
+#endif
+
+#ifdef QT_ENABLED
+#include <murphy/common/qt-glue.h>
+#endif
+
+#define info(fmt, args...) do { \
+ fprintf(stdout, "I: "fmt"\n" , ## args); \
+ fflush(stdout); \
+ } while (0)
+
+#define warning(fmt, args...) do { \
+ fprintf(stderr, "W: "fmt"\n" , ## args); \
+ fflush(stderr); \
+ } while (0)
+
+#define error(fmt, args...) do { \
+ fprintf(stderr, "E: "fmt"\n" , ## args); \
+ fflush(stderr); \
+ } while (0)
+
+#define fatal(fmt, args...) do { \
+ fprintf(stderr, "C: "fmt"\n" , ## args); \
+ fflush(stderr); \
+ exit(1); \
+ } while (0)
+
+#define USECS_PER_SEC (1000 * 1000)
+
+#define DEFAULT_RUNTIME 30 /* run for 30 seconds */
+
+
+enum {
+ MAINLOOP_NATIVE,
+ MAINLOOP_PULSE,
+ MAINLOOP_ECORE,
+ MAINLOOP_GLIB,
+ MAINLOOP_QT
+};
+
+
+struct pulse_config_s;
+typedef struct pulse_config_s pulse_config_t;
+
+struct ecore_config_s;
+typedef struct ecore_config_s ecore_config_t;
+
+struct glib_config_s;
+typedef struct glib_config_s glib_config_t;
+
+
+
+typedef struct {
+ int nio;
+ int ntimer;
+ int deferred;
+ int nsignal;
+
+ int ngio;
+ int ngtimer;
+
+ int ndbus_method;
+ int ndbus_signal;
+
+ int log_mask;
+ const char *log_target;
+
+ int mainloop_type;
+
+ mrp_mainloop_t *ml;
+ pulse_config_t *pulse;
+ ecore_config_t *ecore;
+ glib_config_t *glib;
+
+ int nrunning;
+ int runtime;
+
+ pid_t child;
+ unsigned int wlpf;
+ unsigned int wfrc;
+} test_config_t;
+
+
+#include "mainloop-pulse-test.c"
+#include "mainloop-ecore-test.c"
+#include "mainloop-glib-test.c"
+#include "mainloop-qt-test.h"
+
+static test_config_t cfg;
+
+
+static mrp_mainloop_t *mainloop_create(test_config_t *cfg);
+static void mainloop_run(test_config_t *cfg);
+static void mainloop_quit(test_config_t *cfg);
+static void mainloop_cleanup(test_config_t *cfg);
+
+
+/*
+ * native timers
+ */
+
+#define TIMER_INTERVALS 1, 2, 3, 4, 6, 8, 1, 3, 12, 15, 18, 21, 24
+
+
+typedef struct {
+ int id;
+ mrp_timer_t *timer;
+ int interval;
+ int count;
+ int target;
+ struct timeval prev;
+} test_timer_t;
+
+
+static test_timer_t *timers;
+
+static mrp_wakeup_t *wakeup;
+static mrp_wakeup_t *wuplim;
+
+
+
+static int timeval_diff(struct timeval *tv1, struct timeval *tv2)
+{
+ int64_t u1, u2;
+
+ u1 = tv1->tv_sec * USECS_PER_SEC + tv1->tv_usec;
+ u2 = tv2->tv_sec * USECS_PER_SEC + tv2->tv_usec;
+
+ return (int)(u1 - u2);
+}
+
+
+static void timeval_now(struct timeval *tv)
+{
+ gettimeofday(tv, NULL);
+}
+
+
+void timer_cb(mrp_timer_t *timer, void *user_data)
+{
+ test_timer_t *t = (test_timer_t *)user_data;
+ struct timeval now;
+ double diff, error;
+
+ MRP_UNUSED(timer);
+
+ timeval_now(&now);
+ diff = timeval_diff(&now, &t->prev) / 1000.0;
+ error = diff - t->interval;
+ if (error < 0.0)
+ error = -error;
+
+ info("MRPH timer #%d: %d/%d, diff %.2f (lag %.2f, %.3f %%)",
+ t->id, t->count, t->target, diff, error, 100 * error / diff);
+
+ t->count++;
+ t->prev = now;
+
+ if (t->count >= t->target) {
+ info("MRPH timer #%d has finished.", t->id);
+
+ mrp_del_timer(t->timer);
+ t->timer = NULL;
+ cfg.nrunning--;
+ }
+}
+
+
+static void setup_timers(mrp_mainloop_t *ml)
+{
+ test_timer_t *t;
+ int intervals[] = { TIMER_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+ if ((timers = mrp_allocz_array(test_timer_t, cfg.ntimer)) != NULL) {
+ for (i = 0, t = timers; i < cfg.ntimer; i++, t++) {
+ t->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->interval = msecs;
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ timeval_now(&t->prev);
+ t->timer = mrp_add_timer(ml, t->interval, timer_cb, t);
+
+ if (t->timer != NULL)
+ info("MRPH timer #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+ else
+ fatal("MRPH timer #%d: failed to create", t->id);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.ntimer > 0)
+ fatal("could not allocate %d timers", cfg.ntimer);
+}
+
+
+static void check_timers(void)
+{
+ test_timer_t *t;
+ int i;
+
+ for (i = 0, t = timers; i < cfg.ntimer; i++, t++) {
+ if (t->target != 0 && t->count != t->target)
+ warning("MRPH timer #%d: FAIL (only %d/%d)", t->id, t->count,
+ t->target);
+ else
+ info("MRPH timer #%d: OK (%d/%d)", t->id, t->count, t->target);
+ }
+}
+
+
+/*
+ * native I/O
+ */
+
+#define IO_INTERVALS 1, 3, 5, 9, 12, 15, 18, 21
+
+typedef struct {
+ int id;
+ int pipe[2];
+ mrp_io_watch_t *watch;
+ mrp_timer_t *timer;
+ int target;
+ int sent;
+ int received;
+} test_io_t;
+
+
+static test_io_t *ios;
+
+
+static void send_io(mrp_timer_t *timer, void *user_data)
+{
+ test_io_t *w = (test_io_t *)user_data;
+ char buf[1024];
+ int plural, size;
+
+ MRP_UNUSED(timer);
+
+ plural = (w->target - w->sent) != 1;
+ size = snprintf(buf, sizeof(buf),
+ "I/O #%d: %d message%s remain%s.", w->id,
+ w->target - w->sent,
+ plural ? "s" : "", plural ? "" : "s");
+
+ if (write(w->pipe[1], buf, size) < 0) {
+ /* just ignore it... */
+ }
+ w->sent++;
+
+ info("MRPH I/O #%d: sent message %d/%d.", w->id, w->sent, w->target);
+
+ if (w->sent >= w->target) {
+ info("MRPH I/O #%d: sending done.", w->id);
+
+ close(w->pipe[1]);
+ mrp_del_timer(timer);
+ w->timer = NULL;
+
+ cfg.nrunning--;
+ }
+}
+
+
+static void recv_io(mrp_io_watch_t *watch, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ test_io_t *w = (test_io_t *)user_data;
+ char buf[1024];
+ int size;
+
+ MRP_UNUSED(watch);
+
+ if (watch != w->watch)
+ fatal("MRPH I/O #%d called with incorrect data.", w->id);
+
+ if (events & MRP_IO_EVENT_IN) {
+ size = read(fd, buf, sizeof(buf) - 1);
+
+ if (size > 0) {
+ w->received++;
+ buf[size] = '\0';
+ info("MRPH I/O #%d: received message [%s]", w->id, buf);
+ }
+ else
+ warning("MRPH I/O #%d: got empty message", w->id);
+ }
+
+ if (events & MRP_IO_EVENT_HUP) {
+ info("MRPH I/O #%d: receiver done (got %d/%d)", w->id, w->received,
+ w->sent);
+ close(w->pipe[0]);
+ mrp_del_io_watch(watch);
+ }
+}
+
+
+void setup_io(mrp_mainloop_t *ml)
+{
+ test_io_t *w;
+ mrp_io_event_t mask;
+ int intervals[] = { IO_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+
+ if ((ios = mrp_allocz_array(test_io_t, cfg.nio)) != NULL) {
+ mask = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+
+ for (i = 0, w = ios; i < cfg.nio; i++, w++) {
+ w->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ w->target = 1000 * cfg.runtime / msecs;
+ if (!w->target)
+ continue;
+
+ if (pipe(w->pipe) != 0)
+ fatal("MRPH I/O #%d: could not create pipe", w->id);
+
+ w->watch = mrp_add_io_watch(ml, w->pipe[0], mask, recv_io, w);
+ w->timer = mrp_add_timer(ml, msecs, send_io, w);
+
+ if (w->timer == NULL)
+ fatal("MRPH I/O #%d: could not create I/O timer", w->id);
+
+ if (w->watch == NULL)
+ fatal("MRPH I/O #%d: could not create I/O watch", w->id);
+ else
+ info("MRPH I/O #%d: interval=%d, target=%d", w->id, *iv,
+ w->target);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.nio > 0)
+ fatal("could not allocate %d I/O watches", cfg.nio);
+}
+
+
+static void check_io(void)
+{
+ test_io_t *w;
+ int i;
+
+ for (i = 0, w = ios; i < cfg.nio; i++, w++) {
+ if (w->target != 0 && w->sent != w->received)
+ warning("MRPH I/O #%d: FAIL (only %d/%d)", w->id, w->received,
+ w->sent);
+ else
+ info("MRPH I/O #%d: OK (%d/%d)", w->id, w->received, w->sent);
+ }
+}
+
+
+/*
+ * native deferred/idle callbacks
+ */
+
+
+static void setup_deferred(void)
+{
+ return;
+}
+
+
+
+/*
+ * native signals
+ */
+
+#define SIG_INTERVALS 1, 5, 9, 3, 6, 12
+#define SIGNUMS { SIGUSR1, SIGUSR2, SIGTERM, SIGCONT, SIGQUIT, 0 }
+
+static const char *signames[] = {
+ [SIGINT] = "SIGINT", [SIGTERM] = "SIGTERM", [SIGQUIT] = "SIGQUIT",
+ [SIGCONT] = "SIGCONT", [SIGUSR1] = "SIGUSR1", [SIGUSR2] = "SIGUSR2",
+ [SIGCHLD] = "SIGCHLD"
+};
+
+
+typedef struct {
+ int id;
+ int signum;
+ mrp_sighandler_t *watch;
+ mrp_timer_t *timer;
+ int target;
+ int sent;
+ int received;
+} test_signal_t;
+
+test_signal_t *signals;
+
+
+static void send_signal(mrp_timer_t *timer, void *user_data)
+{
+ test_signal_t *t = (test_signal_t *)user_data;
+
+ MRP_UNUSED(timer);
+
+ if (t->sent >= t->target)
+ return;
+
+ kill(getpid(), t->signum);
+ t->sent++;
+ info("MRPH signal #%d: sent signal %d/%d of %s", t->id,
+ t->sent, t->target, strsignal(t->signum));
+
+ if (t->sent >= t->target) {
+ info("MRPH signal #%d: sending done", t->id);
+ mrp_del_timer(t->timer);
+ t->timer = NULL;
+ }
+}
+
+
+static void recv_signal(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ test_signal_t *t = (test_signal_t *)user_data;
+
+ MRP_UNUSED(h);
+
+ if (h != t->watch)
+ fatal("MRPH signal #%d called with incorrect data", t->id);
+
+ t->received++;
+ info("MRPH signal #%d: received signal %d/%d of %s", t->id,
+ t->received, t->target, signames[signum]);
+
+ if (t->sent >= t->target) {
+ info("MRPH signal #%d: receiving done", t->id);
+ cfg.nrunning--;
+ }
+}
+
+
+static void setup_signals(mrp_mainloop_t *ml)
+{
+ test_signal_t *t;
+ int intervals[] = { SIG_INTERVALS, 0 }, *iv = intervals;
+ int signums[] = SIGNUMS, *s = signums;
+ int msecs, i;
+
+ if ((signals = mrp_allocz_array(test_signal_t, cfg.nsignal)) != NULL) {
+ for (i = 0, t = signals; i < cfg.nsignal; i++, t++) {
+ t->id = i;
+ t->signum = *s;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ t->watch = mrp_add_sighandler(ml, *s, recv_signal, t);
+ t->timer = mrp_add_timer(ml, msecs, send_signal, t);
+
+ if (t->timer == NULL)
+ fatal("MRPH signal #%d: could not create timer", t->id);
+
+ if (t->watch == NULL)
+ fatal("MRPH signal #%d: could not create watch", t->id);
+ else
+ info("MRPH signal #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+
+ s++;
+ if (!*s)
+ s = signums;
+ }
+ }
+ else
+ if (cfg.nsignal > 0)
+ fatal("could not allocate %d signal watches", cfg.nsignal);
+}
+
+
+static void check_signals(void)
+{
+ test_signal_t *t;
+ int i;
+
+ for (i = 0, t = signals; i < cfg.nsignal; i++, t++) {
+ if (t->sent < t->received)
+ warning("MRPH signal #%d: FAIL (only %d/%d", t->id,
+ t->received, t->sent);
+ else
+ info("MRPH signal #%d: OK (%d/%d)", t->id, t->received, t->sent);
+ }
+}
+
+
+static void wakeup_cb(mrp_wakeup_t *w, mrp_wakeup_event_t event,
+ void *user_data)
+{
+ static struct timeval prev[2] = { {0, 0}, {0, 0} };
+ const char *evt;
+ struct timeval now;
+ double diff;
+ int id;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(user_data);
+
+ timeval_now(&now);
+
+ switch (event) {
+ case MRP_WAKEUP_EVENT_TIMER: evt = "timer"; break;
+ case MRP_WAKEUP_EVENT_IO: evt = "I/O (or signal)"; break;
+ case MRP_WAKEUP_EVENT_LIMIT: evt = "limit"; break;
+ default: evt = "???";
+ }
+
+ id = user_data ? 1 : 0;
+
+ if (MRP_LIKELY(prev[id].tv_usec != 0)) {
+ diff = timeval_diff(&now, &prev[id]) / 1000.0;
+ info("woken up #%d by %s, %.2f msecs since previous", id, evt, diff);
+ }
+
+ prev[id] = now;
+}
+
+
+static void setup_wakeup(mrp_mainloop_t *ml)
+{
+ unsigned int nolim = MRP_WAKEUP_NOLIMIT;
+
+ if (cfg.child == 0)
+ return;
+
+ wakeup = mrp_add_wakeup(ml, MRP_WAKEUP_EVENT_ANY, nolim, nolim,
+ wakeup_cb, (void *)0);
+ wuplim = mrp_add_wakeup(ml, MRP_WAKEUP_EVENT_ANY, cfg.wlpf, cfg.wfrc,
+ wakeup_cb, (void *)1);
+}
+
+
+static void cleanup_wakeup(void)
+{
+ mrp_del_wakeup(wakeup);
+ wakeup = NULL;
+ mrp_del_wakeup(wuplim);
+ wuplim = NULL;
+}
+
+
+static void check_quit(mrp_timer_t *timer, void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ if (cfg.nrunning <= 0) {
+ mrp_del_timer(timer);
+ mainloop_quit(&cfg);
+ }
+}
+
+
+
+#ifdef GLIB_ENABLED
+/*
+ * glib timers
+ */
+
+#define GTIMER_INTERVALS 1, 2, 3, 4, 6, 8, 1, 3, 12, 15, 18, 21, 24
+
+typedef struct {
+ int id;
+ guint gsrc;
+ int interval;
+ int count;
+ int target;
+ struct timeval prev;
+} glib_timer_t;
+
+
+static glib_timer_t *gtimers;
+
+
+static gboolean glib_timer_cb(gpointer user_data)
+{
+ glib_timer_t *t = (glib_timer_t *)user_data;
+ struct timeval now;
+ double diff, error;
+
+ timeval_now(&now);
+ diff = timeval_diff(&now, &t->prev) / 1000.0;
+ error = diff - t->interval;
+ if (error < 0.0)
+ error = -error;
+
+ info("GLIB timer #%d: %d/%d, diff %.2f (lag %.2f, %.3f %%)",
+ t->id, t->count, t->target, diff, error, 100 * error / diff);
+
+ t->count++;
+ t->prev = now;
+
+ if (t->count >= t->target) {
+ info("GLIB timer #%d has finished.", t->id);
+
+ t->gsrc = 0;
+ cfg.nrunning--;
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+static void setup_glib_timers(void)
+{
+ glib_timer_t *t;
+ int intervals[] = { GTIMER_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+ if ((gtimers = mrp_allocz_array(glib_timer_t, cfg.ngtimer)) != NULL) {
+ for (i = 0, t = gtimers; i < cfg.ngtimer; i++, t++) {
+ t->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->interval = msecs;
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ timeval_now(&t->prev);
+ t->gsrc = g_timeout_add(msecs, glib_timer_cb, t);
+
+ if (t->gsrc != 0)
+ info("GLIB timer #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+ else
+ fatal("GLIB timer #%d: failed to create", t->id);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.ntimer > 0)
+ fatal("could not allocate %d GLIB timers", cfg.ngtimer);
+}
+
+
+static void check_glib_timers(void)
+{
+ glib_timer_t *t;
+ int i;
+
+ for (i = 0, t = gtimers; i < cfg.ngtimer; i++, t++) {
+ if (t->target != 0 && t->count != t->target)
+ warning("GLIB timer #%d: FAIL (only %d/%d)", t->id, t->count,
+ t->target);
+ else
+ info("GLIB timer #%d: OK (%d/%d)", t->id, t->count, t->target);
+ }
+}
+
+
+/*
+ * glib I/O
+ */
+
+#define GIO_INTERVALS 1, 3, 4, 5, 6, 7, 9, 12, 15, 18, 21
+
+typedef struct {
+ int id;
+ int pipe[2];
+ GIOChannel *gioc;
+ guint gsrc;
+ guint timer;
+ int target;
+ int sent;
+ int received;
+} glib_io_t;
+
+
+static glib_io_t *gios;
+
+
+static gboolean glib_send_io(gpointer user_data)
+{
+ glib_io_t *t = (glib_io_t *)user_data;
+ char buf[1024];
+ int plural, size;
+
+ plural = (t->target - t->sent) != 1;
+ size = snprintf(buf, sizeof(buf),
+ "I/O #%d: %d message%s remain%s.", t->id,
+ t->target - t->sent,
+ plural ? "s" : "", plural ? "" : "s");
+
+ if (write(t->pipe[1], buf, size) < 0) {
+ /* just ignore it... */
+ }
+ t->sent++;
+
+ info("GLIB I/O #%d: sent message %d/%d.", t->id, t->sent, t->target);
+
+ if (t->sent >= t->target) {
+ info("GLIB I/O #%d: sending done.", t->id);
+
+ close(t->pipe[1]);
+ t->timer = 0;
+
+ cfg.nrunning--;
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+static gboolean glib_recv_io(GIOChannel *ioc, GIOCondition cond,
+ gpointer user_data)
+{
+ glib_io_t *t = (glib_io_t *)user_data;
+ int fd = g_io_channel_unix_get_fd(ioc);
+ char buf[1024];
+ int size;
+
+ if (cond & G_IO_IN) {
+ size = read(fd, buf, sizeof(buf) - 1);
+
+ if (size > 0) {
+ t->received++;
+ buf[size] = '\0';
+ info("GLIB I/O #%d: received message [%s]", t->id, buf);
+ }
+ else
+ warning("GLIB I/O #%d: got empty message", t->id);
+ }
+
+ if (cond & G_IO_HUP) {
+ info("GLIB I/O #%d: receiver done (got %d/%d)", t->id, t->received,
+ t->sent);
+ close(fd);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+void setup_glib_io(void)
+{
+ glib_io_t *t;
+ GIOCondition cond;
+ int intervals[] = { GIO_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+ if ((gios = mrp_allocz_array(glib_io_t, cfg.ngio)) != NULL) {
+ cond = G_IO_IN | G_IO_HUP;
+
+ for (i = 0, t = gios; i < cfg.ngio; i++, t++) {
+ t->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ if (pipe(t->pipe) != 0)
+ fatal("GLIB I/O #%d: could not create pipe", t->id);
+
+ t->gioc = g_io_channel_unix_new(t->pipe[0]);
+ if (t->gioc == NULL)
+ fatal("GLIB I/O #%d: failed to create I/O channel", t->id);
+
+ t->gsrc = g_io_add_watch(t->gioc, cond, glib_recv_io, t);
+ if (t->gsrc == 0)
+ fatal("GLIB I/O #%d: failed to add I/O watch", t->id);
+
+ t->timer = g_timeout_add(msecs, glib_send_io, t);
+ if (t->timer == 0)
+ fatal("GLIB I/O #%d: could not create I/O timer", t->id);
+
+ info("GLIB I/O #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.ngio > 0)
+ fatal("could not allocate %d glib I/O watches", cfg.ngio);
+}
+
+
+static void check_glib_io(void)
+{
+ glib_io_t *t;
+ int i;
+
+ for (i = 0, t = gios; i < cfg.ngio; i++, t++) {
+ if (t->target != 0 && t->sent != t->received) {
+ warning("GLIB I/O #%d (fd %d): FAIL (only %d/%d)",
+ t->id, t->pipe[0], t->received, t->sent);
+ }
+
+ else
+ info("GLIB I/O #%d (fd %d): OK (%d/%d)", t->id, t->pipe[0],
+ t->received, t->sent);
+ }
+}
+
+static void glib_pump_cleanup(void);
+#endif
+
+
+/*
+ * DBUS tests (quite a mess the whole shebang...)
+ */
+
+#define DBUS_PATH "/"
+#define DBUS_IFACE "org.murphy.test"
+#define DBUS_METHOD "message"
+#define DBUS_SIGNAL "signal"
+
+typedef struct {
+ int pipe[2];
+ pid_t client;
+ char address[256];
+
+ mrp_mainloop_t *ml;
+ DBusConnection *conn;
+ mrp_timer_t *sigtimer;
+
+ int nmethod;
+ int nack;
+ int nsignal;
+} dbus_test_t;
+
+
+static dbus_test_t dbus_test = { pipe: { -1, -1 } };
+
+
+
+
+int mrp_setup_dbus_connection(mrp_mainloop_t *ml, DBusConnection *conn);
+
+
+static void open_dbus_pipe(void)
+{
+ if (pipe(dbus_test.pipe) < 0)
+ fatal("failed to opend pipe for DBUS tests");
+}
+
+
+static void close_dbus_pipe(char *dir)
+{
+ while (*dir) {
+ switch (*dir++) {
+ case 'r':
+ if (dbus_test.pipe[0] != -1) {
+ close(dbus_test.pipe[0]);
+ dbus_test.pipe[0] = -1;
+ }
+ break;
+
+ case 'w':
+ if (dbus_test.pipe[1] != -1) {
+ close(dbus_test.pipe[1]);
+ dbus_test.pipe[1] = -1;
+ }
+ break;
+ }
+ }
+}
+
+
+static void recv_dbus_reply(DBusPendingCall *pending, void *user_data)
+{
+ DBusMessage *msg;
+ char *reply;
+
+ MRP_UNUSED(user_data);
+
+ if ((msg = dbus_pending_call_steal_reply(pending)) != NULL) {
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,
+ &reply, DBUS_TYPE_INVALID)) {
+ info("DBUS test: got reply #%d '%s'", dbus_test.nack, reply);
+ dbus_test.nack++;
+ }
+
+ dbus_message_unref(msg);
+ }
+
+ dbus_pending_call_unref(pending);
+
+ if (dbus_test.nack >= cfg.ndbus_method) {
+ char dummy[256];
+
+ cfg.nrunning--;
+
+ /* block until the client is done */
+ if (read(dbus_test.pipe[0], dummy, sizeof(dummy)) < 0) {
+ /* just ignore it... */
+ }
+ }
+}
+
+
+static int send_dbus_message(DBusConnection *conn, char *addr, char *buf)
+{
+ DBusMessage *msg;
+ DBusPendingCall *pending;
+
+ msg = dbus_message_new_method_call(addr, DBUS_PATH,
+ DBUS_IFACE, DBUS_METHOD);
+
+ if (msg == NULL)
+ fatal("failed to create DBUS message");
+
+ if (!dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &buf, DBUS_TYPE_INVALID))
+ fatal("failed to add arguments to DBUS method call");
+
+ if (!dbus_connection_send_with_reply(conn, msg, &pending, 5000))
+ fatal("failed to send DBUS message");
+
+ if (!dbus_pending_call_set_notify(pending, recv_dbus_reply, NULL, NULL))
+ fatal("failed to set pending call notification callback");
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+
+static int send_dbus_reply(DBusConnection *conn, DBusMessage *msg, char *buf)
+{
+ DBusMessage *reply;
+
+ if ((reply = dbus_message_new_method_return(msg)) != NULL) {
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &buf,
+ DBUS_TYPE_INVALID))
+ fatal("failed to add arguments to DBUS method reply");
+
+ if (!dbus_connection_send(conn, reply, NULL))
+ fatal("failed to send DBUS reply");
+
+ dbus_message_unref(reply);
+ }
+
+ dbus_test.nmethod++;
+ if (dbus_test.nmethod >= cfg.ndbus_method)
+ cfg.nrunning--;
+
+ return TRUE;
+}
+
+
+static DBusConnection *connect_to_dbus(char *name)
+{
+ DBusConnection *conn;
+ DBusError error;
+ unsigned int flags;
+ int status;
+
+ dbus_error_init(&error);
+
+ if ((conn = dbus_bus_get(DBUS_BUS_SESSION, NULL)) != NULL) {
+ if (!name || !*name)
+ return conn;
+
+ flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+ status = dbus_bus_request_name(conn, name, flags, &error);
+
+ if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ return conn;
+ else
+ error("failed to get name '%s' on DBUS (error: %s)", name,
+ error.message ? error.message : "unknown");
+ }
+
+ return NULL;
+}
+
+
+static void client_send_msg(mrp_timer_t *t, void *user_data)
+{
+ char buf[1024];
+
+ MRP_UNUSED(user_data);
+
+ if (dbus_test.nmethod < cfg.ndbus_method) {
+ snprintf(buf, sizeof(buf), "DBUS message #%d", dbus_test.nmethod);
+ send_dbus_message(dbus_test.conn, dbus_test.address, buf);
+
+ info("DBUS client: sent #%d message", dbus_test.nmethod);
+
+ dbus_test.nmethod++;
+ }
+
+ if (dbus_test.nmethod >= cfg.ndbus_method) {
+ mrp_del_timer(t);
+ if (cfg.ndbus_method == 0)
+ cfg.nrunning--;
+ else {
+ /* cfg.nrunning updated only once we've received the last reply */
+ }
+ }
+}
+
+
+static void setup_dbus_client(mrp_mainloop_t *ml)
+{
+ DBusConnection *conn;
+ int i, nmethod, nsignal;
+ size_t size;
+ ssize_t amount_read;
+
+ nmethod = cfg.ndbus_method;
+ nsignal = cfg.ndbus_signal;
+ mrp_clear(&cfg);
+ cfg.ndbus_method = nmethod;
+ cfg.ndbus_signal = nsignal;
+
+ mrp_mainloop_quit(ml, 0);
+#ifdef GLIB_ENABLED
+ glib_pump_cleanup();
+#endif
+ mrp_mainloop_destroy(ml);
+
+ for (i = 3; i < 1024; i++)
+ if (i != dbus_test.pipe[0])
+ close(i);
+
+ size = sizeof(dbus_test.address) - 1;
+ amount_read = read(dbus_test.pipe[0], dbus_test.address, size);
+ if (amount_read > 0) {
+ dbus_test.address[amount_read] = '\0';
+ info("DBUS test: got address '%s'", dbus_test.address);
+ }
+
+ /*sleep(5);*/
+
+ if ((ml = dbus_test.ml = mrp_mainloop_create()) == NULL)
+ fatal("failed to create mainloop");
+
+ cfg.ml = ml;
+
+ if ((conn = dbus_test.conn = connect_to_dbus(NULL)) == NULL)
+ fatal("failed to connect to DBUS");
+
+ if (!mrp_setup_dbus_connection(ml, conn))
+ fatal("failed to setup DBUS connection with mainloop");
+
+ if (mrp_add_timer(ml, 1000, client_send_msg, NULL) == NULL)
+ fatal("failed to create DBUS message sending timer");
+
+ if (mrp_add_timer(ml, 1000, check_quit, NULL) == NULL)
+ fatal("failed to create quit-check timer");
+
+ cfg.nrunning++;
+}
+
+
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+ const char *message;
+ char reply[1024];
+
+ MRP_UNUSED(data);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (strcmp(path, DBUS_PATH) ||
+ strcmp(interface, DBUS_IFACE) ||
+ strcmp(member, DBUS_METHOD))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /*info("DBUS server: got call: path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));*/
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &message, DBUS_TYPE_INVALID)) {
+ snprintf(reply, sizeof(reply), "ACK: got '%s'", message);
+ if (!send_dbus_reply(c, msg, reply))
+ fatal("failed to sent reply to DBUS message");
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+
+
+static void setup_dbus_server(mrp_mainloop_t *ml)
+{
+ static struct DBusObjectPathVTable vtable = {
+ .message_function = dispatch_method
+ };
+
+ char *addr = "org.murphy.test";
+
+ MRP_UNUSED(ml);
+
+ if ((dbus_test.conn = connect_to_dbus(addr)) == NULL)
+ fatal("failed to connect to DBUS");
+
+ if (!mrp_setup_dbus_connection(ml, dbus_test.conn))
+ fatal("failed to setup DBUS connection with mainloop");
+
+ if (!dbus_connection_register_fallback(dbus_test.conn, "/", &vtable, NULL))
+ fatal("failed to set up method dispatching");
+
+ if (write(dbus_test.pipe[1], addr, strlen(addr) + 1) < 0) {
+ /* just ignore it... */
+ }
+
+ cfg.nrunning++;
+}
+
+
+
+static void fork_dbus_client(mrp_mainloop_t *ml)
+{
+ dbus_test.client = cfg.child = fork();
+
+ switch (dbus_test.client) {
+ case -1:
+ fatal("failed to fork DBUS test client");
+ break;
+
+ case 0:
+ setup_dbus_client(ml);
+ break;
+
+ default:
+ info("DBUS test: child pid %u", dbus_test.client);
+ close(0);
+ /*sleep(10);*/
+ setup_dbus_server(ml);
+ }
+}
+
+
+static void sigchild_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ int status;
+
+ MRP_UNUSED(user_data);
+
+ info("DBUS test: received signal %d (%s)", signum, signames[signum]);
+
+ if (dbus_test.client != 0) {
+ if (waitpid(dbus_test.client, &status, WNOHANG) == dbus_test.client) {
+ info("DBUS test: client exited with status %d.", status);
+ dbus_test.client = 0;
+ close_dbus_pipe("w");
+ mrp_del_sighandler(h);
+ cfg.nrunning--;
+ }
+ else
+ error("waitpid failed for pid %u", dbus_test.client);
+ }
+}
+
+
+static void setup_dbus_tests(mrp_mainloop_t *ml)
+{
+ mrp_sighandler_t *h;
+
+ if (cfg.ndbus_method == 0 && cfg.ndbus_signal == 0)
+ return;
+
+ if ((h = mrp_add_sighandler(ml, SIGCHLD, sigchild_handler, NULL)) != NULL) {
+ open_dbus_pipe();
+ fork_dbus_client(ml);
+ }
+ else
+ fatal("failed create SIGCHLD handler");
+}
+
+
+static void check_dbus(void)
+{
+ if (cfg.ndbus_method == 0 && cfg.ndbus_signal == 0)
+ return;
+
+ if (dbus_test.client != 0) {
+ if (dbus_test.nmethod == cfg.ndbus_method)
+ info("DBUS test: method calls: OK (%d/%d)",
+ dbus_test.nmethod, cfg.ndbus_method);
+ else
+ error("DBUS test: method calls: FAILED (%d/%d)",
+ dbus_test.nmethod, cfg.ndbus_method);
+ }
+ else {
+ if (dbus_test.nack == cfg.ndbus_method)
+ info("DBUS test: method replies: OK (%d/%d)",
+ dbus_test.nack, cfg.ndbus_method);
+ else
+ error("DBUS test: method replies: FAILED (%d/%d)",
+ dbus_test.nack, cfg.ndbus_method);
+ }
+}
+
+
+
+#include "dbus-pump.c"
+
+
+
+static void config_set_defaults(test_config_t *cfg)
+{
+ mrp_clear(cfg);
+
+ cfg->nio = 5;
+ cfg->ntimer = 10;
+ cfg->nsignal = 5;
+ cfg->ngio = 5;
+ cfg->ngtimer = 10;
+
+ cfg->ndbus_method = 10;
+ cfg->ndbus_signal = 10;
+
+ cfg->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ cfg->log_target = MRP_LOG_TO_STDERR;
+
+ cfg->wlpf = 1750;
+ cfg->wfrc = 5000;
+
+ cfg->runtime = DEFAULT_RUNTIME;
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -r, --runtime how many seconds to run tests\n"
+ " -i, --ios number of I/O watches\n"
+ " -t, --timers number of timers\n"
+ " -s, --signals number of POSIX signals\n"
+ " -I, --glib-ios number of glib I/O watches\n"
+ " -T, --glib-timers number of glib timers\n"
+ " -S, --dbus-signals number of D-Bus signals\n"
+ " -M, --dbus-methods number of D-Bus methods\n"
+ " -o, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug site enable debug messages for <site>\n"
+#ifdef PULSE_ENABLED
+ " -p, --pulse use pulse mainloop\n"
+#endif
+#ifdef ECORE_ENABLED
+ " -e, --ecore use ecore mainloop\n"
+#endif
+#ifdef GLIB_ENABLED
+ " -g, --glib use glib mainloop\n"
+#endif
+#ifdef QT_ENABLED
+ " -q, --qt use qt mainloop\n"
+#endif
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+int parse_cmdline(test_config_t *cfg, int argc, char **argv)
+{
+#ifdef PULSE_ENABLED
+# define PULSE_OPTION "p"
+#else
+# define PULSE_OPTION ""
+#endif
+#ifdef ECORE_ENABLED
+# define ECORE_OPTION "e"
+#else
+# define ECORE_OPTION ""
+#endif
+#ifdef GLIB_ENABLED
+# define GLIB_OPTION "g"
+#else
+# define GLIB_OPTION ""
+#endif
+#ifdef QT_ENABLED
+# define QT_OPTION "q"
+#else
+# define QT_OPTION ""
+#endif
+
+
+# define OPTIONS "r:i:t:s:I:T:S:M:l:w:W:o:vd:h" \
+ PULSE_OPTION""ECORE_OPTION""GLIB_OPTION""QT_OPTION
+ struct option options[] = {
+ { "runtime" , required_argument, NULL, 'r' },
+ { "ios" , required_argument, NULL, 'i' },
+ { "timers" , required_argument, NULL, 't' },
+ { "signals" , required_argument, NULL, 's' },
+ { "glib-ios" , required_argument, NULL, 'I' },
+ { "glib-timers" , required_argument, NULL, 'T' },
+ { "dbus-signals", required_argument, NULL, 'S' },
+ { "dbus-methods", required_argument, NULL, 'M' },
+#ifdef PULSE_ENABLED
+ { "pulse" , no_argument , NULL, 'p' },
+#endif
+#ifdef ECORE_ENABLED
+ { "ecore" , no_argument , NULL, 'e' },
+#endif
+#ifdef GLIB_ENABLED
+ { "glib" , no_argument , NULL, 'g' },
+#endif
+#ifdef QT_ENABLED
+ { "qt" , no_argument , NULL, 'q' },
+#endif
+ { "wakeup-lpf" , required_argument, NULL, 'w' },
+ { "wakeup-force", required_argument, NULL, 'W' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target" , required_argument, NULL, 'o' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ char *end;
+ int opt;
+
+ config_set_defaults(cfg);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'r':
+ cfg->runtime = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid runtime length '%s'.", optarg);
+ break;
+
+ case 'i':
+ cfg->nio = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of I/O watches '%s'.", optarg);
+ break;
+
+ case 't':
+ cfg->ntimer = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of timers '%s'.", optarg);
+ break;
+
+ case 's':
+ cfg->nsignal = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of signals '%s'.", optarg);
+ break;
+
+ case 'I':
+ cfg->ngio = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of glib I/O watches '%s'.", optarg);
+ break;
+
+ case 'T':
+ cfg->ngtimer = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of glib timers '%s'.", optarg);
+ break;
+
+ case 'S':
+ cfg->ndbus_signal = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of DBUS signals '%s'.", optarg);
+ break;
+
+ case 'M':
+ cfg->ndbus_method = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of DBUS methods '%s'.", optarg);
+ break;
+
+#ifdef PULSE_ENABLED
+ case 'p':
+ cfg->mainloop_type = MAINLOOP_PULSE;
+ break;
+#endif
+
+#ifdef ECORE_ENABLED
+ case 'e':
+ cfg->mainloop_type = MAINLOOP_ECORE;
+ break;
+#endif
+
+#ifdef GLIB_ENABLED
+ case 'g':
+ cfg->mainloop_type = MAINLOOP_GLIB;
+ break;
+#endif
+
+#ifdef QT_ENABLED
+ case 'q':
+ cfg->mainloop_type = MAINLOOP_QT;
+ break;
+#endif
+
+ case 'w':
+ cfg->wlpf = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid wakeup low-pass filter limit '%s'.",
+ optarg);
+ break;
+
+ case 'W':
+ cfg->wfrc = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid wakeup force trigger limit '%s'.",
+ optarg);
+ break;
+
+ case 'v':
+ cfg->log_mask <<= 1;
+ cfg->log_mask |= 1;
+ break;
+
+ case 'l':
+ cfg->log_mask = mrp_log_parse_levels(optarg);
+ if (cfg->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 'o':
+ cfg->log_target = mrp_log_parse_target(optarg);
+ if (!cfg->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ cfg->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+static mrp_mainloop_t *mainloop_create(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ cfg->ml = mrp_mainloop_create();
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_create(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_create(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_create(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ cfg->ml = qt_mainloop_create();
+ break;
+#endif
+
+ default:
+ mrp_log_error("Invalid mainloop type 0x%x.", cfg->mainloop_type);
+ exit(1);
+ }
+
+ if (cfg->ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ return cfg->ml;
+}
+
+
+static void mainloop_run(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ mrp_mainloop_run(cfg->ml);
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_run(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_run(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_run(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ qt_mainloop_run();
+ break;
+#endif
+
+ default:
+ mrp_log_error("Invalid mainloop type 0x%x.", cfg->mainloop_type);
+ exit(1);
+ }
+}
+
+
+static void mainloop_quit(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ mrp_mainloop_quit(cfg->ml, 0);
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_quit(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_quit(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_quit(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ qt_mainloop_quit();
+ break;
+#endif
+
+ default:
+ mrp_log_error("Invalid mainloop type 0x%x.", cfg->mainloop_type);
+ exit(1);
+ }
+}
+
+
+void mainloop_cleanup(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_cleanup(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_cleanup(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_cleanup(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ qt_mainloop_cleanup(cfg->ml);
+ cfg->ml = NULL;
+ break;
+#endif
+
+ default:
+ mrp_log_error("Unknown mainloop type (0x%x).", cfg->mainloop_type);
+ exit(1);
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ mrp_mainloop_t *ml;
+
+ mrp_clear(&cfg);
+ parse_cmdline(&cfg, argc, argv);
+
+ mrp_log_set_mask(cfg.log_mask);
+ mrp_log_set_target(cfg.log_target);
+
+ ml = mainloop_create(&cfg);
+
+ if (ml == NULL)
+ fatal("failed to create main loop.");
+
+ dbus_test.ml = ml;
+ setup_dbus_tests(ml);
+ ml = dbus_test.ml;
+
+ setup_timers(ml);
+ setup_io(ml);
+ setup_signals(ml);
+ MRP_UNUSED(setup_deferred); /* XXX TODO: add deferred tests... */
+
+#ifdef GLIB_ENABLED
+ if (cfg.mainloop_type != MAINLOOP_GLIB && cfg.mainloop_type != MAINLOOP_QT) {
+ if (cfg.ngio > 0 || cfg.ngtimer > 0)
+ glib_pump_setup(ml);
+ }
+
+ setup_glib_io();
+ setup_glib_timers();
+#endif
+
+ if (mrp_add_timer(ml, 1000, check_quit, NULL) == NULL)
+ fatal("failed to create quit-check timer");
+
+ setup_wakeup(ml);
+
+ mainloop_run(&cfg);
+
+ check_io();
+ check_timers();
+ check_signals();
+
+#ifdef GLIB_ENABLED
+ check_glib_io();
+ check_glib_timers();
+#endif
+
+ if (dbus_test.client != 0)
+ close(dbus_test.pipe[1]); /* let the client continue */
+
+ check_dbus();
+
+#ifdef GLIB_ENABLED
+ if (cfg.mainloop_type != MAINLOOP_GLIB) {
+ if (cfg.ngio > 0 || cfg.ngtimer > 0)
+ glib_pump_cleanup();
+ }
+#endif
+
+ cleanup_wakeup();
+
+ mainloop_cleanup(&cfg);
+}
diff --git a/src/common/tests/mask-test.c b/src/common/tests/mask-test.c
new file mode 100644
index 0000000..e4db6d0
--- /dev/null
+++ b/src/common/tests/mask-test.c
@@ -0,0 +1,130 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <murphy/common/mask.h>
+
+int main(int argc, char *argv[])
+{
+ uint64_t bits;
+ int i, j, prev, set, n, cnt, clr, bit;
+ mrp_mask_t m = MRP_MASK_EMPTY, m1;
+ int b[] = { 0, 1, 5, 16, 32, 48, 97, 112, 113, 114, 295, 313, -1 };
+
+ cnt = argc > 1 ? strtoul(argv[1], NULL, 10) : 100;
+
+ srand((unsigned int)time(NULL) ^ (unsigned int)getpid());
+
+ bits = 0x17;
+ bits <<= 35;
+ n = mrp_ffsll(bits);
+ printf("ffsl(0x%lx) = %d\n", bits, n);
+
+ for (i = 0; i < cnt; i++) {
+ bits = (unsigned long)rand();
+ n = mrp_ffsll(bits);
+ clr = ~((((unsigned long)-1) >> (n - 1)) << (n - 1));
+
+ if (n > 1) {
+ if ((bits & clr) != 0) {
+ fail:
+ printf("ffs(0x%lx) = %d: FAIL\n", bits, n);
+ exit(1);
+ }
+ else
+ printf("ffs(0x%lx) = %d: OK\n", bits, n);
+ }
+
+ if (n != __builtin_ffsl(bits))
+ goto fail;
+
+ }
+
+ for (i = 0; b[i] != -1; i++) {
+ printf("setting bit %d...\n", b[i]);
+ mrp_mask_set(&m, b[i]);
+ if (!mrp_mask_test(&m, b[i])) {
+ printf("testing bit %d: FAILED\n", b[i]);
+ exit(1);
+ }
+ }
+
+
+ prev = 0;
+ for (i = 0; b[i] != -1; i++) {
+ for (j = prev + 1; j < b[i]; j++) {
+ set = mrp_mask_test(&m, j);
+ if (set) {
+ printf("negative mask_test(%d): FAILED\n", j);
+ exit(1);
+ }
+ }
+
+ set = mrp_mask_test(&m, b[i]);
+
+ if (!set) {
+ printf("mask_test(%d): FAILED\n", b[i]);
+ exit(1);
+ }
+
+ prev = b[i];
+ }
+
+ printf("mask tests: OK\n");
+
+ MRP_MASK_FOREACH_SET(&m, bit, 0) {
+ printf("next bit set: %d\n", bit);
+ }
+
+ MRP_MASK_FOREACH_CLEAR(&m, bit, 150) {
+ printf("next bit clear: %d\n", bit);
+ }
+
+ mrp_mask_neg(&m);
+ MRP_MASK_FOREACH_CLEAR(&m, bit, 150) {
+ printf("next bit clear: %d\n", bit);
+ }
+
+ MRP_MASK_FOREACH_CLEAR(&m, bit, 0) {
+ printf("next bit clear (negated): %d\n", bit);
+ }
+
+ mrp_mask_neg(&m);
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_neg(&m1);
+ mrp_mask_or(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (or'd): %d\n", bit);
+ }
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_neg(&m1);
+ mrp_mask_xor(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (neg'd+xor'd): %d\n", bit);
+ }
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_neg(&m1);
+ mrp_mask_and(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (neg'd+and'd): %d\n", bit);
+ }
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_and(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (and'd): %d\n", bit);
+ }
+
+ mrp_mask_reset(&m);
+
+ return 0;
+}
diff --git a/src/common/tests/mkdir-test.c b/src/common/tests/mkdir-test.c
new file mode 100644
index 0000000..611b82a
--- /dev/null
+++ b/src/common/tests/mkdir-test.c
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <murphy/common/file-utils.h>
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ printf("Trying to create directory '%s'..\n", argv[i]);
+ if (mrp_mkdir(argv[i], 0755) < 0)
+ printf("failed (%d: %s)\n", errno, strerror(errno));
+ else
+ printf("ok\n");
+ }
+
+ return 0;
+}
diff --git a/src/common/tests/mm-test.c b/src/common/tests/mm-test.c
new file mode 100644
index 0000000..c22b866
--- /dev/null
+++ b/src/common/tests/mm-test.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <murphy/common/mm.h>
+
+#define fatal(fmt, args...) do { \
+ fprintf(stderr, "fatal error: "fmt"\n" , ## args); \
+ exit(1); \
+ } while (0)
+
+#define error(fmt, args...) do { \
+ fprintf(stdout, "error: "fmt"\n" , ## args); \
+ } while (0)
+
+#define info(fmt, args...) do { \
+ fprintf(stdout, fmt"\n" , ## args); \
+ } while (0)
+
+
+
+static int basic_tests(int n)
+{
+ void **ptrs;
+ char buf[1024], *p;
+ int i;
+
+ mrp_mm_config(MRP_MM_DEBUG);
+
+ ptrs = mrp_allocz(n * sizeof(*ptrs));
+
+ if (ptrs == NULL)
+ fatal("Failed to allocate pointer table.");
+
+ for (i = 0; i < n; i++) {
+ snprintf(buf, sizeof(buf), "#%d: message number %d (0x%x)", i, i, i);
+
+ p = ptrs[i] = mrp_strdup(buf);
+
+ if (p != NULL) {
+ if (!strcmp(buf, p)) {
+ printf("'%s' was duplicated as '%s'\n", buf, p);
+ }
+ else {
+ printf("'%s' was incorrectly duplicated as '%s'\n", buf, p);
+ return FALSE;
+ }
+ }
+ else {
+ printf("failed to duplicate '%s'\n", buf);
+ return FALSE;
+ }
+ }
+
+ mrp_mm_check(stdout);
+
+ for (i = 0; i < n; i += 2) {
+ mrp_free(ptrs[i]);
+ ptrs[i] = NULL;
+ }
+
+ mrp_mm_check(stdout);
+
+ for (i = 0; i < n; i++) {
+ mrp_free(ptrs[i]);
+ ptrs[i] = NULL;
+ }
+
+ mrp_mm_check(stdout);
+
+ mrp_free(ptrs);
+
+ mrp_mm_check(stdout);
+
+ return TRUE;
+}
+
+
+typedef struct {
+ char name[32];
+ int i;
+ double d;
+ char *s;
+ void *p;
+} obj_t;
+
+
+#define NAME_FORMAT "#%d test object"
+#define POISON 0xf3
+
+static int obj_setup(void *ptr)
+{
+ static int idx = 0;
+ obj_t *obj = ptr;
+
+ snprintf(obj->name, sizeof(obj->name), NAME_FORMAT, idx);
+ obj->i = idx;
+ obj->d = 2.0 * idx;
+ obj->s = mrp_strdup(obj->name);
+ obj->p = ptr;
+
+ return TRUE;
+}
+
+
+static void obj_cleanup(void *ptr)
+{
+ obj_t *obj = ptr;
+
+ mrp_free(obj->s);
+}
+
+
+static int obj_check(obj_t *obj, int alloced)
+{
+ char name[32];
+
+ if (alloced) {
+ snprintf(name, sizeof(name), NAME_FORMAT, obj->i);
+
+ return (!strcmp(name, obj->name) && !strcmp(name, obj->s) &&
+ obj->d == 2 * obj->i && obj->p == obj);
+ }
+ else {
+ char check[sizeof(obj_t)];
+
+ memset(check, POISON, sizeof(check));
+ if (memcmp(check, obj, sizeof(*obj)))
+ error("Object %p not properly poisoned.", obj);
+ }
+
+ return TRUE;
+}
+
+
+static int pool_tests(void)
+{
+ mrp_objpool_config_t cfg;
+ mrp_objpool_t *pool;
+ obj_t **ptrs;
+ int limit, prealloc, i, max;
+ int success;
+
+ limit = 0;
+ prealloc = 512;
+ max = 8382;
+ ptrs = mrp_allocz(max * sizeof(obj_t));
+
+ if (ptrs == NULL) {
+ error("Failed to allocate check pointer table.");
+ return FALSE;
+ }
+
+ cfg.name = "test pool";
+ cfg.limit = limit;
+ cfg.objsize = sizeof(obj_t);
+ cfg.prealloc = prealloc;
+ cfg.setup = obj_setup;
+ cfg.cleanup = obj_cleanup;
+ cfg.poison = POISON;
+ cfg.flags = MRP_OBJPOOL_FLAG_POISON;
+
+ info("Creating object pool...");
+ pool = mrp_objpool_create(&cfg);
+
+ if (pool == NULL) {
+ error("Failed to create test object pool.");
+ return FALSE;
+ }
+
+ info("Allocating objects...");
+ for (i = 0; i < max; i++) {
+ ptrs[i] = mrp_objpool_alloc(pool);
+
+ if (ptrs[i] == NULL) {
+ error("Failed to allocate test object #%d.", i);
+ success = FALSE;
+ goto out;
+ }
+
+ if (!obj_check(ptrs[i], TRUE)) {
+ error("Object check failed for %p.", ptrs[i]);
+ success = FALSE;
+ }
+ }
+
+ info("Freeing objects...");
+ for (i = 0; i < max; i += 2) {
+ mrp_objpool_free(ptrs[i]);
+ obj_check(ptrs[i], FALSE);
+ ptrs[i] = NULL;
+ }
+
+ info("Reallocating objects...");
+ for (i = 0; i < max; i += 2) {
+ ptrs[i] = mrp_objpool_alloc(pool);
+
+ if (ptrs[i] == NULL) {
+ error("Failed to re-allocate test object #%d.", i);
+ success = FALSE;
+ goto out;
+ }
+
+ if (!obj_check(ptrs[i], TRUE)) {
+ error("Object check failed for %p.", ptrs[i]);
+ success = FALSE;
+ }
+
+ }
+
+ info("Freeing objects...");
+ for (i = 0; i < max; i++) {
+ mrp_objpool_free(ptrs[i]);
+ ptrs[i] = NULL;
+ }
+
+ info("Reallocating again objects...");
+ for (i = 0; i < max; i++) {
+ ptrs[i] = mrp_objpool_alloc(pool);
+
+ if (ptrs[i] == NULL) {
+ error("Failed to re-allocate test object #%d.", i);
+ success = FALSE;
+ goto out;
+ }
+
+ if (!obj_check(ptrs[i], TRUE)) {
+ error("Object check failed for %p.", ptrs[i]);
+ success = FALSE;
+ }
+ }
+
+ out:
+ mrp_free(ptrs);
+ info("Destroying object pool...");
+ mrp_objpool_destroy(pool);
+
+ return success;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int max;
+
+ if (argc > 1)
+ max = (int)strtol(argv[1], NULL, 10);
+ else
+ max = 256;
+
+ info("Running basic tests...");
+ basic_tests(max);
+
+ info("Running object pool tests...");
+ pool_tests();
+
+ return 0;
+}
diff --git a/src/common/tests/msg-test.c b/src/common/tests/msg-test.c
new file mode 100644
index 0000000..44c5cd2
--- /dev/null
+++ b/src/common/tests/msg-test.c
@@ -0,0 +1,823 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common.h>
+
+#include <murphy/common/msg.h>
+#include <murphy/common/msg.c>
+
+#define TYPE(type, name) [MRP_MSG_FIELD_##type] = name
+const char *types[] = {
+ TYPE(INVALID, "invalid"),
+ TYPE(STRING , "string" ),
+ TYPE(BOOL , "bool" ),
+ TYPE(SINT8 , "sint8" ),
+ TYPE(UINT8 , "uint8" ),
+ TYPE(SINT16 , "sint16" ),
+ TYPE(UINT16 , "uint16" ),
+ TYPE(SINT32 , "sint32" ),
+ TYPE(UINT32 , "uint32" ),
+ TYPE(SINT64 , "sint64" ),
+ TYPE(UINT64 , "uint64" ),
+ TYPE(DOUBLE , "double" ),
+ TYPE(BLOB , "blob" ),
+ NULL,
+};
+#undef TYPE
+
+
+uint16_t get_type(const char **types, const char *name)
+{
+ const char **t;
+
+ for (t = types; *t != NULL; t++) {
+ if (!strcmp(*t, name))
+ return (uint16_t)(t - types);
+ }
+
+ return MRP_MSG_FIELD_INVALID;
+}
+
+
+void test_default_encode_decode(int argc, char **argv)
+{
+ mrp_msg_t *msg, *decoded;
+ void *encoded;
+ ssize_t size;
+ uint16_t tag, type, prev_tag;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ uint64_t u64;
+ int64_t s64;
+ double dbl;
+ bool bln;
+ char *val, *end;
+ int i, ok;
+
+ if ((msg = mrp_msg_create_empty()) == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ prev_tag = 0;
+ i = 1;
+ while (i < argc) {
+
+ if ('0' <= *argv[i] && *argv[i] <= '9') {
+ if (argc <= i + 2) {
+ mrp_log_error("Missing field type or value.");
+ exit(1);
+ }
+
+ tag = prev_tag = (uint16_t)strtoul(argv[i++], &end, 0);
+ if (end && *end) {
+ mrp_log_error("Invalid field tag '%s'.", argv[i]);
+ exit(1);
+ }
+ }
+ else {
+ if (argc <= i + 1) {
+ mrp_log_error("Missing field type or value.");
+ exit(1);
+ }
+
+ tag = ++prev_tag;
+ }
+
+ type = get_type(types, argv[i++]);
+ val = argv[i++];
+
+ if (type == MRP_MSG_FIELD_INVALID) {
+ mrp_log_error("Invalid field type '%s'.", argv[i + 1]);
+ exit(1);
+ }
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ ok = mrp_msg_append(msg, tag, type, val);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ if (!strcasecmp(val, "true"))
+ bln = TRUE;
+ else if (!strcasecmp(val, "false"))
+ bln = FALSE;
+ else {
+ mrp_log_error("Invalid boolean value '%s'.", val);
+ exit(1);
+ }
+ ok = mrp_msg_append(msg, tag, type, bln);
+ break;
+
+#define HANDLE_INT(_bits, _uget, _sget) \
+ case MRP_MSG_FIELD_UINT##_bits: \
+ u##_bits = (uint##_bits##_t)strtoul(val, &end, 0); \
+ if (end && *end) { \
+ mrp_log_error("Invalid uint%d value '%s'.", _bits, val); \
+ exit(1); \
+ } \
+ ok = mrp_msg_append(msg, tag, type, u##_bits); \
+ break; \
+ case MRP_MSG_FIELD_SINT##_bits: \
+ s##_bits = (int##_bits##_t)strtol(val, &end, 0); \
+ if (end && *end) { \
+ mrp_log_error("Invalid sint%d value '%s'.", _bits, val); \
+ exit(1); \
+ } \
+ ok = mrp_msg_append(msg, tag, type, s##_bits); \
+ break
+
+ HANDLE_INT(8 , strtol , strtoul);
+ HANDLE_INT(16, strtol , strtoul);
+ HANDLE_INT(32, strtol , strtoul);
+ HANDLE_INT(64, strtoll, strtoull);
+
+ case MRP_MSG_FIELD_DOUBLE:
+ dbl = strtod(val, &end);
+ if (end && *end) {
+ mrp_log_error("Invalid double value '%s'.", val);
+ exit(1);
+ }
+ ok = mrp_msg_append(msg, tag, type, dbl);
+ break;
+
+ default:
+ mrp_log_error("Invalid (or unimplemented) type 0x%x (%s).",
+ type, argv[i + 1]);
+ ok = FALSE;
+ }
+
+ if (!ok) {
+ mrp_log_error("Failed to add field to message.");
+ exit(1);
+ }
+ }
+
+ mrp_msg_dump(msg, stdout);
+
+ size = mrp_msg_default_encode(msg, &encoded);
+ if (size <= 0) {
+ mrp_log_error("Failed to encode message with default encoder.");
+ exit(1);
+ }
+
+ mrp_log_info("encoded message size: %d", (int)size);
+
+ decoded = mrp_msg_default_decode(encoded, size);
+ if (decoded == NULL) {
+ mrp_log_error("Failed to decode message with default decoder.");
+ exit(1);
+ }
+
+ mrp_msg_dump(decoded, stdout);
+
+ mrp_msg_unref(msg);
+ mrp_msg_unref(decoded);
+}
+
+
+typedef struct {
+ char *str1;
+ uint16_t u16;
+ int32_t s32;
+ char *str2;
+ double dbl1;
+ bool bln1;
+ double dbl2;
+ char *str3;
+ bool bln2;
+} data1_t;
+
+typedef struct {
+ char *str;
+ uint8_t u8;
+ bool bln;
+} data2_t;
+
+typedef struct {
+ char *str;
+ uint16_t u16;
+ int32_t s32;
+ double dbl;
+} data3_t;
+
+#if 0
+typedef struct {
+ uint16_t offs; /* member offset within structure */
+ uint16_t tag; /* tag for member */
+ uint16_t type; /* type of this member */
+} mrp_msg_member_t;
+
+typedef struct {
+ uint16_t tag; /* structure tag */
+ size_t size; /* structure size */
+ int nfield; /* number of members */
+ mrp_msg_member_t *fields; /* member descriptor */
+} mrp_msg_descr_t;
+#endif
+
+#define DUMP_FIELD(memb, fmt) printf(" %s: "fmt"\n", #memb, d->memb)
+
+int cmp_data1(data1_t *d1, data1_t *d2)
+{
+ return
+ !strcmp(d1->str1, d2->str1) &&
+ !strcmp(d1->str2, d2->str2) &&
+ !strcmp(d1->str3, d2->str3) &&
+ d1->u16 == d2->u16 &&
+ d1->s32 == d2->s32 &&
+ d1->dbl1 == d2->dbl1 &&
+ d1->bln1 == d2->bln1 &&
+ d1->dbl2 == d2->dbl2 &&
+ d1->bln2 == d2->bln2;
+}
+
+int cmp_data2(data2_t *d1, data2_t *d2)
+{
+ return
+ !strcmp(d1->str, d2->str) &&
+ d1->u8 == d2->u8 &&
+ d1->bln == d2->bln;
+}
+
+int cmp_data3(data3_t *d1, data3_t *d2)
+{
+ return
+ !strcmp(d1->str, d2->str) &&
+ d1->u16 == d2->u16 &&
+ d1->s32 == d2->s32 &&
+ d1->dbl == d2->dbl;
+}
+
+void dump_data1(char *prefix, data1_t *d)
+{
+ printf("%s{\n", prefix);
+ DUMP_FIELD(str1, "%s");
+ DUMP_FIELD(u16 , "%u");
+ DUMP_FIELD(s32 , "%d");
+ DUMP_FIELD(str2, "%s");
+ DUMP_FIELD(dbl1, "%f");
+ DUMP_FIELD(bln1, "%d");
+ DUMP_FIELD(dbl2, "%f");
+ DUMP_FIELD(str2, "%s");
+ DUMP_FIELD(bln2, "%d");
+ printf("}\n");
+
+}
+
+void dump_data2(char *prefix, data2_t *d)
+{
+ printf("%s{\n", prefix);
+ DUMP_FIELD(str, "%s");
+ DUMP_FIELD(u8 , "%u");
+ DUMP_FIELD(bln, "%d");
+ printf("}\n");
+}
+
+void dump_data3(char *prefix, data3_t *d)
+{
+ printf("%s{\n", prefix);
+ DUMP_FIELD(str, "%s");
+ DUMP_FIELD(u16, "%u");
+ DUMP_FIELD(s32, "%d");
+ DUMP_FIELD(dbl, "%f");
+ printf("}\n");
+}
+
+#undef DUMP_FIELD
+
+static size_t mrp_msg_encode(void **bufp, void *data,
+ mrp_data_member_t *fields, int nfield);
+
+static void *mrp_msg_decode(void **bufp, size_t *sizep, size_t data_size,
+ mrp_data_member_t *fields, int nfield);
+
+void test_custom_encode_decode(void)
+{
+#define DESCRIBE(_type, _memb, _tag, _ftype) { \
+ .offs = MRP_OFFSET(_type, _memb), \
+ .tag = _tag, \
+ .type = MRP_MSG_FIELD_##_ftype, \
+ .guard = FALSE, \
+ { NULL }, \
+ .hook = { NULL, NULL } \
+ }
+
+ mrp_data_member_t data1_descr[] = {
+ DESCRIBE(data1_t, str1, 0x1, STRING),
+ DESCRIBE(data1_t, u16, 0x2, UINT16),
+ DESCRIBE(data1_t, str1, 0x1, STRING),
+ DESCRIBE(data1_t, u16 , 0x2, UINT16),
+ DESCRIBE(data1_t, s32 , 0x3, SINT32),
+ DESCRIBE(data1_t, str2, 0x4, STRING),
+ DESCRIBE(data1_t, dbl1, 0x5, DOUBLE),
+ DESCRIBE(data1_t, bln1, 0x6, BOOL ),
+ DESCRIBE(data1_t, dbl2, 0x7, DOUBLE),
+ DESCRIBE(data1_t, str3, 0x8, STRING),
+ DESCRIBE(data1_t, bln2, 0x9, BOOL ),
+ };
+ int data1_nfield = MRP_ARRAY_SIZE(data1_descr);
+
+ mrp_data_member_t data2_descr[] = {
+ DESCRIBE(data2_t, str, 0x1, STRING),
+ DESCRIBE(data2_t, u8 , 0x2, UINT8 ),
+ DESCRIBE(data2_t, bln, 0x3, BOOL ),
+ };
+ int data2_nfield = MRP_ARRAY_SIZE(data2_descr);
+
+ mrp_data_member_t data3_descr[] = {
+ DESCRIBE(data3_t, str, 0x1, STRING),
+ DESCRIBE(data3_t, u16, 0x2, UINT16),
+ DESCRIBE(data3_t, s32, 0x3, SINT32),
+ DESCRIBE(data3_t, dbl, 0x4, DOUBLE),
+ };
+ int data3_nfield = MRP_ARRAY_SIZE(data3_descr);
+
+#define TAG_DATA1 0x1
+#define TAG_DATA2 0x2
+#define TAG_DATA3 0x3
+
+
+ data1_t data1 = {
+ .str1 = "data1, str1",
+ .u16 = 32768U,
+ .s32 = -12345678,
+ .str2 = "data1, str2",
+ .dbl1 = 9.81,
+ .bln1 = TRUE,
+ .dbl2 = -3.141,
+ .str3 = "data1, str3",
+ .bln2 = FALSE
+ };
+ data2_t data2 = {
+ .str = "data2, str",
+ .u8 = 128,
+ .bln = TRUE
+ };
+ data3_t data3 = {
+ .str = "data3, str",
+ .u16 = 32768U,
+ .s32 = -12345678,
+ .dbl = 1.2345
+ };
+
+ data1_t *d1;
+ data2_t *d2;
+ data3_t *d3;
+ void *buf;
+ size_t size;
+
+ size = mrp_msg_encode(&buf, &data1, data1_descr, data1_nfield);
+
+ if (size <= 0) {
+ mrp_log_error("failed to encode data1_t");
+ exit(1);
+ }
+
+ d1 = mrp_msg_decode(&buf, &size, sizeof(data1_t), data1_descr,data1_nfield);
+
+ if (d1 == NULL) {
+ mrp_log_error("failed to decode encoded data1_t");
+ exit(1);
+ }
+
+ dump_data1("original data1: ", &data1);
+ dump_data1("decoded data1: ", d1);
+ if (!cmp_data1(&data1, d1)) {
+ mrp_log_error("Original and decoded data1_t do not match!");
+ exit(1);
+ }
+ else
+ mrp_log_info("ok, original and decoded match...");
+
+
+ size = mrp_msg_encode(&buf, &data2, data2_descr, data2_nfield);
+
+ if (size <= 0) {
+ mrp_log_error("failed to encode data2_t");
+ exit(1);
+ }
+
+ d2 = mrp_msg_decode(&buf, &size, sizeof(data2_t), data2_descr,data2_nfield);
+
+ if (d2 == NULL) {
+ mrp_log_error("failed to decode encoded data2_t");
+ exit(1);
+ }
+
+ dump_data2("original data2: ", &data2);
+ dump_data2("decoded data2: ", d2);
+ if (!cmp_data2(&data2, d2)) {
+ mrp_log_error("Original and decoded data2_t do not match!");
+ exit(1);
+ }
+ else
+ mrp_log_info("ok, original and decoded match...");
+
+
+ size = mrp_msg_encode(&buf, &data3, data3_descr, data3_nfield);
+
+ if (size <= 0) {
+ mrp_log_error("failed to encode data3_t");
+ exit(1);
+ }
+
+ d3 = mrp_msg_decode(&buf, &size, sizeof(data3_t), data3_descr,data3_nfield);
+
+ if (d3 == NULL) {
+ mrp_log_error("failed to decode encoded data3_t");
+ exit(1);
+ }
+
+ dump_data3("original data3: ", &data3);
+ dump_data3("decoded data3: ", d3);
+ if (!cmp_data3(&data3, d3)) {
+ mrp_log_error("Original and decoded data3_t do not match!");
+ exit(1);
+ }
+ else
+ mrp_log_info("ok, original and decoded match...");
+}
+
+
+static void test_basic(void)
+{
+ mrp_msg_t *msg;
+ char *str1, *str2;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ double dbl1, dbl2;
+ int i;
+
+ struct field_t {
+ uint16_t tag;
+ uint16_t type;
+ void *ptr;
+ } f[] = {
+ { 0x1, MRP_MSG_FIELD_STRING, &str1 },
+ { 0x2, MRP_MSG_FIELD_STRING, &str2 },
+ { 0x3, MRP_MSG_FIELD_UINT16, &u16 },
+ { 0x4, MRP_MSG_FIELD_SINT16, &s16 },
+ { 0x5, MRP_MSG_FIELD_UINT32, &u32 },
+ { 0x6, MRP_MSG_FIELD_SINT32, &s32 },
+ { 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1 },
+ { 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2 }
+ };
+
+ msg = mrp_msg_create(MRP_MSG_TAG_STRING(0x1, "string 0x1"),
+ MRP_MSG_TAG_STRING(0x2, "string 0x2"),
+ MRP_MSG_TAG_UINT16(0x3, 3),
+ MRP_MSG_TAG_SINT16(0x4, -4),
+ MRP_MSG_TAG_UINT32(0x5, 5),
+ MRP_MSG_TAG_SINT32(0x6, -6),
+ MRP_MSG_TAG_DOUBLE(0x7, 3.14),
+ MRP_MSG_TAG_DOUBLE(0x8, -9.81),
+ MRP_MSG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create message.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Message created OK.");
+
+
+ if (!mrp_msg_get(msg,
+ 0x1, MRP_MSG_FIELD_STRING, &str1,
+ 0x2, MRP_MSG_FIELD_STRING, &str2,
+ 0x3, MRP_MSG_FIELD_UINT16, &u16,
+ 0x4, MRP_MSG_FIELD_SINT16, &s16,
+ 0x5, MRP_MSG_FIELD_UINT32, &u32,
+ 0x6, MRP_MSG_FIELD_SINT32, &s32,
+ 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1,
+ 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2,
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields.");
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields:");
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+
+ if (!mrp_msg_get(msg,
+ 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2,
+ 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1,
+ 0x6, MRP_MSG_FIELD_SINT32, &s32,
+ 0x5, MRP_MSG_FIELD_UINT32, &u32,
+ 0x4, MRP_MSG_FIELD_SINT16, &s16,
+ 0x3, MRP_MSG_FIELD_UINT16, &u16,
+ 0x2, MRP_MSG_FIELD_STRING, &str2,
+ 0x1, MRP_MSG_FIELD_STRING, &str1,
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields.");
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields:");
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+
+
+#define TAG(idx) f[(idx) & 0x7].tag
+#define TYPE(idx) f[(idx) & 0x7].type
+#define PTR(idx) f[(idx) & 0x7].ptr
+#define FIELD(idx) TAG((idx)), TYPE((idx)), PTR((idx))
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(f); i++) {
+ if (!mrp_msg_get(msg,
+ FIELD(i+0), FIELD(i+1), FIELD(i+2), FIELD(i+3),
+ FIELD(i+4), FIELD(i+5), FIELD(i+6), FIELD(i+7),
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields for offset %d.", i);
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields for offset %d:", i);
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+ }
+
+ if (mrp_msg_get(msg,
+ 0x9, MRP_MSG_FIELD_STRING, &str1, MRP_MSG_END)) {
+ mrp_log_error("Hmm... non-existent field found.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Ok, non-existent field not found...");
+}
+
+
+int main(int argc, char *argv[])
+{
+ mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_DEBUG));
+ mrp_log_set_target(MRP_LOG_TO_STDOUT);
+
+ test_basic();
+
+ test_default_encode_decode(argc, argv);
+ test_custom_encode_decode();
+
+ return 0;
+}
+
+
+static size_t mrp_msg_encode(void **bufp, void *data,
+ mrp_data_member_t *fields, int nfield)
+{
+ mrp_data_member_t *f;
+ mrp_msgbuf_t mb;
+ mrp_msg_value_t *v;
+ uint32_t len;
+ int i;
+ size_t size;
+
+ size = nfield * (2 * sizeof(uint16_t) + sizeof(uint64_t));
+
+ if (mrp_msgbuf_write(&mb, size)) {
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->tag) , 1, nomem);
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(v->str) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->str, len, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->bln ? TRUE : FALSE), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, v->u8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, v->s8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->u16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->s16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->u32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->s32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->u64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->s64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, v->dbl, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ errno = EOPNOTSUPP;
+ /* intentional fall through */
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ errno = EOPNOTSUPP;
+ mrp_log_error("XXX TODO: MRP_MSG_FIELD_ARRAY "
+ "not implemented");
+ }
+ else
+ errno = EINVAL;
+
+ mrp_msgbuf_cancel(&mb);
+ nomem:
+ *bufp = NULL;
+ return 0;
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ return (size_t)(mb.p - mb.buf);
+}
+
+
+#if 0
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+#endif
+
+static void *mrp_msg_decode(void **bufp, size_t *sizep, size_t data_size,
+ mrp_data_member_t *fields, int nfield)
+{
+ void *data;
+ mrp_data_member_t *f;
+ mrp_msgbuf_t mb;
+ uint16_t tag;
+ mrp_msg_value_t *v;
+ void *value;
+ uint32_t len;
+ int i;
+
+ if (MRP_UNLIKELY((data = mrp_allocz(data_size)) == NULL))
+ return NULL;
+
+ mrp_msgbuf_read(&mb, *bufp, *sizep);
+
+ for (i = 0; i < nfield; i++) {
+ tag = be16toh(MRP_MSGBUF_PULL(&mb, typeof(tag) , 1, nodata));
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto unknown_field;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ v->str = mrp_strdup((char *)value);
+ if (v->str == NULL)
+ goto nomem;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v->bln = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v->u8 = MRP_MSGBUF_PULL(&mb, typeof(v->u8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v->s8 = MRP_MSGBUF_PULL(&mb, typeof(v->s8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v->u16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->u16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v->s16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->s16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v->u32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->u32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v->s32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->s32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v->u64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->u64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v->s64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->s64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v->dbl = MRP_MSGBUF_PULL(&mb, typeof(v->dbl), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ errno = EOPNOTSUPP;
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ errno = EOPNOTSUPP;
+ mrp_log_error("XXX TODO: MRP_MSG_FIELD_ARRAY "
+ "not implemented");
+ }
+ else {
+ unknown_field:
+ errno = EINVAL;
+ }
+ goto fail;
+ }
+ }
+
+ *bufp = mb.buf;
+ *sizep -= mb.p - mb.buf;
+ return data;
+
+ nodata:
+ nomem:
+ fail:
+ if (data != NULL) {
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ case MRP_MSG_FIELD_BLOB:
+ mrp_free(data + f->offs);
+ }
+ }
+
+ mrp_free(data);
+ }
+
+ return NULL;
+}
diff --git a/src/common/tests/native-test.c b/src/common/tests/native-test.c
new file mode 100644
index 0000000..171f5a4
--- /dev/null
+++ b/src/common/tests/native-test.c
@@ -0,0 +1,307 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/native-types.h>
+
+
+typedef enum {
+ MUSIC,
+ MOVIE,
+ BOOK,
+ PAINTING,
+} art_type_t;
+
+
+typedef struct {
+ art_type_t type;
+ char *artist;
+ char *title;
+ uint16_t year;
+ char *location;
+ double price;
+} art_t;
+
+
+typedef enum {
+ LEFT = 0,
+ RIGHT,
+ BOTH
+} hand_t;
+
+
+typedef enum {
+ MALE = 0,
+ FEMALE = 1,
+} gender_t;
+
+
+typedef struct {
+ char *name;
+ gender_t gender;
+ int age;
+ char **languages;
+ unsigned int height;
+ float weight;
+ char nationality[32];
+ hand_t hand;
+ bool glasses;
+ art_t *favourites;
+ size_t nfavourite;
+} person_t;
+
+
+typedef struct {
+ person_t *father;
+ person_t *mother;
+ person_t *children;
+} family_t;
+
+
+art_t paps_favourites[] = {
+ {
+ BOOK ,
+ "Douglas Adams", "Dirk Gently's Holistic Detective Agency",
+ 1987, "bookshelf", 9.5
+ },
+ {
+ MUSIC,
+ "Megadeth", "Sweating Bullets",
+ 1992, "pocket", 12.5
+ },
+ {
+ MUSIC,
+ "Sentenced", "Noose",
+ 1996, "phone", 12
+ },
+ {
+ MOVIE,
+ "Bananas", "Woody Allen",
+ 1971, "PVR", 20.5
+ }
+};
+
+
+char *paps_languages[] = {
+ "english", "swedish", "finnish", NULL
+};
+
+person_t pap = {
+ .name = "Pap",
+ .gender = MALE,
+ .age = 30,
+ .languages = paps_languages,
+ .height = 180,
+ .weight = 84.5,
+ .nationality = "martian",
+ .hand = RIGHT,
+ .glasses = false,
+ .favourites = paps_favourites,
+ .nfavourite = MRP_ARRAY_SIZE(paps_favourites),
+};
+
+
+art_t moms_favourites[] = {
+ {
+ BOOK ,
+ "Douglas Adams", "THHGTTG",
+ 1982, "bookshelf", 11.8
+ },
+ {
+ MUSIC,
+ "Megadeth", "Sweating Bullets",
+ 1992, "pocket", 12.5
+ },
+ {
+ MOVIE,
+ "Hottie Chick", "GGW-II",
+ 1996, "PVR", 0.5
+ },
+ {
+ BOOK ,
+ "Douglas Adams", "The Long Dark Tea-Time of the Soul",
+ 1988, "Kindle Touch", 8.50
+ }
+};
+
+
+char *moms_languages[] = {
+ "finnish", "english", "swedish", "french", NULL
+};
+
+person_t mom = {
+ .name = "Mom",
+ .gender = FEMALE,
+ .age = 28,
+ .languages = moms_languages,
+ .height = 165,
+ .weight = 57.8,
+ .nationality = "venusian",
+ .hand = LEFT,
+ .glasses = true,
+ .favourites = moms_favourites,
+ .nfavourite = MRP_ARRAY_SIZE(moms_favourites),
+};
+
+
+char *kids_languages[] = {
+ "english", "finnish", "swedish", NULL
+};
+
+person_t tom_dick_and_harry[] = {
+ {
+ .name = "Tom",
+ .gender = MALE,
+ .age = 10,
+ .languages = kids_languages + 1,
+ .height = 135,
+ .weight = 40.5,
+ .nationality = "UFO",
+ .hand = BOTH,
+ .glasses = false,
+ .favourites = NULL,
+ .nfavourite = 0,
+ },
+ {
+ .name = "Dick",
+ .gender = MALE,
+ .age = 12,
+ .languages = kids_languages,
+ .height = 145,
+ .weight = 45.5,
+ .nationality = "UFO",
+ .hand = RIGHT,
+ .glasses = true,
+ .favourites = paps_favourites + 1,
+ .nfavourite = MRP_ARRAY_SIZE(paps_favourites) - 2,
+ },
+ {
+ .name = "Harry",
+ .gender = MALE,
+ .age = 14,
+ .languages = kids_languages + 2,
+ .height = 165,
+ .weight = 60.5,
+ .nationality = "UFO",
+ .hand = LEFT,
+ .glasses = false,
+ .favourites = moms_favourites + 1,
+ .nfavourite = MRP_ARRAY_SIZE(moms_favourites) - 2,
+ },
+ {
+ .name = NULL,
+ },
+};
+
+
+family_t family = { &pap, &mom, &tom_dick_and_harry[0] };
+
+
+int main(int argc, char *argv[])
+{
+ MRP_NATIVE_TYPE(art_type, art_t,
+ MRP_UINT32(art_t, type , DEFAULT),
+ MRP_STRING(art_t, artist , DEFAULT),
+ MRP_STRING(art_t, title , DEFAULT),
+ MRP_UINT16(art_t, year , DEFAULT),
+ MRP_STRING(art_t, location, DEFAULT),
+ MRP_DOUBLE(art_t, price , DEFAULT));
+ MRP_NATIVE_TYPE(person_type, person_t,
+ MRP_STRING(person_t, name , DEFAULT),
+ MRP_UINT32(person_t, gender , DEFAULT),
+ MRP_INT (person_t, age , DEFAULT),
+ MRP_ARRAY (person_t, languages , DEFAULT, GUARDED,
+ char *, "", .strp = NULL),
+ MRP_UINT (person_t, height , DEFAULT),
+ MRP_FLOAT (person_t, weight , DEFAULT),
+ MRP_STRING(person_t, nationality, INLINED),
+ MRP_UINT32(person_t, hand , DEFAULT),
+ MRP_BOOL (person_t, glasses , DEFAULT),
+ MRP_ARRAY (person_t, favourites , DEFAULT, SIZED,
+ art_t, nfavourite),
+ MRP_SIZET (person_t, nfavourite , DEFAULT));
+ MRP_NATIVE_TYPE(family_type, family_t,
+ MRP_STRUCT(family_t, father , DEFAULT, person_t),
+ MRP_STRUCT(family_t, mother , DEFAULT, person_t),
+ MRP_ARRAY (family_t, children, DEFAULT, GUARDED,
+ person_t, name, .strp = NULL));
+ mrp_typemap_t map[4];
+
+ uint32_t art_type_id, person_type_id, family_type_id;
+ void *ebuf;
+ size_t esize;
+ int fd;
+ void *dbuf;
+ family_t *decoded;
+ char dump[16 * 1024];
+
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_INFO));
+
+ art_type_id = mrp_register_native(&art_type);
+
+ if (art_type_id == MRP_INVALID_TYPE)
+ mrp_log_error("Failed to register art_t type.");
+ else
+ mrp_log_info("Type art_t sucessfully registered.");
+
+ person_type_id = mrp_register_native(&person_type);
+
+ if (person_type_id == MRP_INVALID_TYPE)
+ mrp_log_error("Failed to register person_t type.");
+ else
+ mrp_log_info("Type person_t sucessfully registered.");
+
+ family_type_id = mrp_register_native(&family_type);
+
+ if (family_type_id == MRP_INVALID_TYPE)
+ mrp_log_error("Failed to register family_t type.");
+ else
+ mrp_log_info("Type family_t sucessfully registered.");
+
+ ebuf = NULL;
+
+ map[0] = (mrp_typemap_t)MRP_TYPEMAP(1, art_type_id );
+ map[1] = (mrp_typemap_t)MRP_TYPEMAP(2, person_type_id);
+ map[2] = (mrp_typemap_t)MRP_TYPEMAP(3, family_type_id);
+ map[3] = (mrp_typemap_t)MRP_TYPEMAP_END;
+
+ if (mrp_encode_native(&family, family_type_id, 0, &ebuf, &esize, map) < 0) {
+ mrp_log_error("Failed to encode test data.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Test data successfully encoded (%zd bytes).", esize);
+
+ if ((fd = open("type-test.encoded",
+ O_CREAT | O_TRUNC | O_WRONLY, 0644)) >= 0) {
+ if (write(fd, ebuf, esize) != (ssize_t)esize)
+ mrp_log_error("Failed to write encoded data.");
+ close(fd);
+ }
+
+ if (mrp_decode_native(&ebuf, &esize, &dbuf, &family_type_id, map) < 0) {
+ mrp_log_error("Failed to decode test data.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Test data sucessfully decoded.");
+
+ decoded = dbuf;
+
+ if (mrp_print_native(dump, sizeof(dump), decoded, family_type_id) >= 0)
+ mrp_log_info("dump of decoded data: %s", dump);
+ else
+ mrp_log_error("Failed to dump decoded data.");
+
+ mrp_free_native(dbuf, family_type_id);
+
+ return 0;
+}
diff --git a/src/common/tests/path-test.c b/src/common/tests/path-test.c
new file mode 100644
index 0000000..c2b0918
--- /dev/null
+++ b/src/common/tests/path-test.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/file-utils.h>
+
+int main(int argc, char *argv[])
+{
+ int i;
+ size_t size;
+ char *p, buf[PATH_MAX];
+ struct stat ost, nst;
+
+ if (argc > 1) {
+ size = strtoul(argv[1], &p, 10);
+ if (*p || size > sizeof(buf))
+ size = sizeof(buf);
+ }
+ else
+ size = sizeof(buf);
+
+ for (i = 1; i < argc; i++) {
+ printf("'%s':\n", argv[i]);
+ if ((p = mrp_normalize_path(buf, size, argv[i])) != NULL) {
+ printf(" -> '%s'\n", p);
+
+ if (stat(argv[i], &ost) < 0)
+ printf(" Non-existing path, can't test in practice...\n");
+ else{
+ if (stat(buf, &nst) == 0 &&
+ ost.st_dev == nst.st_dev && ost.st_ino == nst.st_ino)
+ printf(" Filesystem-equality check: OK.\n");
+ else {
+ printf(" Filesystem-equality check: FAILED\n");
+ exit(1);
+ }
+ }
+ }
+ else {
+ printf(" failed (%d: %s)\n", errno, strerror(errno));
+ exit(1);
+ }
+ }
+
+ return 0;
+}
diff --git a/src/common/tests/process-test.c b/src/common/tests/process-test.c
new file mode 100644
index 0000000..41a55e6
--- /dev/null
+++ b/src/common/tests/process-test.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common.h>
+#include <murphy/common/process.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+
+static void process_watch(const char *id, mrp_process_state_t s,
+ void *userdata)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *) userdata;
+
+ printf("process watch received event for %s: %s (%p)\n",
+ id, s == MRP_PROCESS_STATE_READY ? "ready" : "not ready", userdata);
+
+ mrp_mainloop_quit(ml, 0);
+}
+
+
+static void test_process_watch(mrp_mainloop_t *ml)
+{
+ mrp_process_state_t s = mrp_process_query_state("foobar");
+
+ printf("initial state %s\n",
+ s == MRP_PROCESS_STATE_READY ? "ready" : "not ready");
+
+ if (mrp_process_set_state("foobar", MRP_PROCESS_STATE_READY) < 0) {
+ printf("error setting the state 1\n");
+ }
+
+ s = mrp_process_query_state("foobar");
+
+ printf("second state %s\n",
+ s == MRP_PROCESS_STATE_READY ? "ready" : "not ready");
+
+ if (mrp_process_set_state("foobar", MRP_PROCESS_STATE_NOT_READY) < 0) {
+ printf("error setting the state 2\n");
+ }
+
+ s = mrp_process_query_state("foobar");
+
+ printf("third state %s\n",
+ s == MRP_PROCESS_STATE_READY ? "ready" : "not ready");
+
+ if (mrp_process_set_watch("foobar", ml, process_watch, ml) < 0) {
+ printf("failed to register watch\n");
+ }
+
+ printf("setting state to ready\n");
+
+ if (mrp_process_set_state("foobar", MRP_PROCESS_STATE_READY) < 0) {
+ printf("error setting the state 3\n");
+ }
+
+ mrp_mainloop_run(ml);
+
+ printf("removing the watch\n");
+
+ if(mrp_process_remove_watch("foobar") < 0) {
+ printf("failed to remove watch\n");
+ }
+}
+
+static void pid_watch(pid_t pid, mrp_process_state_t s, void *userdata)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *) userdata;
+
+ printf("pid watch received event for %d: %s (%p)\n",
+ pid, s == MRP_PROCESS_STATE_READY ? "ready" : "not ready", userdata);
+
+ mrp_mainloop_quit(ml, 0);
+}
+
+static void test_pid_watch(mrp_mainloop_t *ml)
+{
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ printf("error forking\n");
+ }
+ else if (pid > 0) {
+ mrp_pid_watch_t *w;
+
+ if (mrp_pid_query_state(pid) != MRP_PROCESS_STATE_READY) {
+ printf("failed to query the process READY state\n");
+ }
+
+ printf("setting pid watch\n");
+ w = mrp_pid_set_watch(pid, ml, pid_watch, ml);
+
+ printf("killing the process '%d'\n", pid);
+ kill(pid, 15);
+ waitpid(pid, NULL, 0);
+
+ printf("running main loop\n");
+ mrp_mainloop_run(ml);
+
+ if (mrp_pid_query_state(pid) != MRP_PROCESS_STATE_NOT_READY) {
+ printf("failed to query the process NOT READY state\n");
+ }
+ printf("removing the watch\n");
+ mrp_pid_remove_watch(w);
+ }
+}
+
+int main(int argc, char **argv) {
+ mrp_mainloop_t *ml = mrp_mainloop_create();
+
+ if (argc == 2 && strcmp(argv[1], "pid") == 0) {
+ test_pid_watch(ml);
+ }
+ else if (argc == 2 && strcmp(argv[1], "process") == 0) {
+ test_process_watch(ml);
+ }
+ else {
+ printf("Usage: process-watch-test <process|pid>\n");
+ }
+
+ mrp_mainloop_destroy(ml);
+}
+
diff --git a/src/common/tests/sdbus-error-message.c b/src/common/tests/sdbus-error-message.c
new file mode 100644
index 0000000..1e4b9f2
--- /dev/null
+++ b/src/common/tests/sdbus-error-message.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <murphy/common.h>
+#include <murphy/core.h>
+#include <murphy/common/dbus-sdbus.h>
+
+static int msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
+{
+ mrp_dbus_err_t err;
+ mrp_dbus_msg_t *reply;
+ const char *member = mrp_dbus_msg_member(msg);
+ const char *iface = mrp_dbus_msg_interface(msg);
+ const char *path = mrp_dbus_msg_path(msg);
+
+ MRP_UNUSED(data);
+
+ printf("Message callback called -- member: '%s', path: '%s',"
+ " interface: '%s'\n", member, path, iface);
+
+ mrp_dbus_error_init(&err);
+ mrp_dbus_error_set(&err, "org.freedesktop.DBus.Error.Failed", "Error message");
+
+ reply = mrp_dbus_msg_error(dbus, msg, &err);
+
+ if (reply) {
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ return TRUE;
+}
+
+int main()
+{
+ mrp_dbus_t *dbus;
+ mrp_mainloop_t *ml;
+
+ ml = mrp_mainloop_create();
+
+ if (!(dbus = mrp_dbus_connect(ml, "session", NULL))) {
+ printf("Failed to connect to D-Bus\n");
+ }
+
+ if (!mrp_dbus_acquire_name(dbus, "org.example", NULL)) {
+ printf("Failed to acquire name on D-Bus\n");
+ goto error;
+ }
+
+ if (!mrp_dbus_export_method(dbus, "/example", "org.example", "member",
+ msg_cb, NULL)) {
+ printf("Failed to register method\n");
+ goto error;
+ }
+
+ printf("waiting for 'dbus-send --session --print-reply --type=method_call"
+ "--dest=org.example /example org.example.member'\n");
+
+ mrp_mainloop_run(ml);
+
+ return 0;
+
+error:
+ return 1;
+}
diff --git a/src/common/tests/sdbus-test.c b/src/common/tests/sdbus-test.c
new file mode 100644
index 0000000..f785139
--- /dev/null
+++ b/src/common/tests/sdbus-test.c
@@ -0,0 +1,226 @@
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+#include "sd-bus.h"
+#include "bus-message.h"
+
+#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000))
+
+typedef struct {
+ sd_bus *bus;
+ mrp_mainloop_t *ml;
+ mrp_subloop_t *sl;
+} bus_t;
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ mrp_log_info("Received signal %d (%s), exiting...", signum,
+ strsignal(signum));
+ mrp_mainloop_quit(mrp_get_sighandler_mainloop(h), 0);
+ }
+}
+
+
+static int bus_prepare(void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ return FALSE;
+}
+
+
+static int bus_query(void *user_data, struct pollfd *fds, int nfd,
+ int *timeout)
+{
+ bus_t *b = (bus_t *)user_data;
+ uint64_t usec;
+
+ mrp_log_info("nfd: %d", nfd);
+
+ if (nfd > 0) {
+ fds[0].fd = sd_bus_get_fd(b->bus);
+ fds[0].events = sd_bus_get_events(b->bus) | POLLIN;
+ fds[0].revents = 0;
+
+ if (sd_bus_get_timeout(b->bus, &usec) < 0)
+ *timeout = -1;
+ else
+ *timeout = USEC_TO_MSEC(usec);
+
+ mrp_log_info("fd: %d, events: 0x%x, timeout: %u", fds[0].fd,
+ fds[0].events, *timeout);
+ }
+
+ return 1;
+}
+
+
+static int bus_check(void *user_data, struct pollfd *fds, int nfd)
+{
+ MRP_UNUSED(user_data);
+
+ if (nfd > 0 && fds[0].revents != 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static void bus_dispatch(void *user_data)
+{
+ bus_t *b = (bus_t *)user_data;
+
+ if (sd_bus_process(b->bus, NULL) > 0)
+ sd_bus_flush(b->bus);
+}
+
+
+static int bus_signal_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data)
+{
+ mrp_log_info("%s(): got bus signal...", __FUNCTION__);
+
+ bus_message_dump(m);
+
+ return 0;
+}
+
+
+static int bus_method_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data)
+{
+ mrp_log_info("%s(): got bus method call message %p...", __FUNCTION__, m);
+
+ bus_message_dump(m);
+
+ if (!strcmp(sd_bus_message_get_member(m), "unhandled"))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static int bus_return_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data)
+{
+ mrp_log_info("%s(): got bus method reply...", __FUNCTION__);
+
+ bus_message_dump(m);
+
+ return 0;
+}
+
+
+static void emit_signal(mrp_timer_t *t, void *user_data)
+{
+ sd_bus *bus = (sd_bus *)user_data;
+
+ sd_bus_emit_signal(bus, "/foo/bar", "foo.bar", "foobar", NULL);
+}
+
+
+static void call_method(mrp_timer_t *t, void *user_data)
+{
+ sd_bus *bus = (sd_bus *)user_data;
+ sd_bus_message *msg = NULL;
+ int r;
+ uint64_t serial;
+
+ r = sd_bus_message_new_method_call(bus, "org.freedesktop.DBus",
+ "/", "org.freedesktop.DBus", "GetId",
+ &msg);
+
+ if (r != 0) {
+ mrp_log_error("Failed to create new method call message.");
+ return;
+ }
+
+ r = sd_bus_send_with_reply(bus, msg, bus_return_cb, NULL, 100000 * 1000, &serial);
+
+ if (r != 0)
+ mrp_log_error("Failed to call method... (r = %d)", r);
+}
+
+
+int main(int argc, char *argv[])
+{
+ static mrp_subloop_ops_t bus_ops = {
+ .prepare = bus_prepare,
+ .query = bus_query,
+ .check = bus_check,
+ .dispatch = bus_dispatch
+ };
+
+ mrp_mainloop_t *ml = NULL;
+ mrp_timer_t *ts = NULL;
+ mrp_timer_t *tm = NULL;
+ sd_bus *bus = NULL;
+ int r;
+ bus_t *b;
+
+ mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_INFO));
+
+ ml = mrp_mainloop_create();
+ r = sd_bus_open_user(&bus);
+
+ if (ml == NULL || r != 0)
+ goto fail;
+
+ mrp_add_sighandler(ml, SIGINT , signal_handler, NULL);
+ mrp_add_sighandler(ml, SIGTERM, signal_handler, NULL);
+ mrp_add_sighandler(ml, SIGQUIT, signal_handler, NULL);
+
+ b = mrp_allocz(sizeof(*b));
+
+ if (b == NULL)
+ goto fail;
+
+ sd_bus_add_match(bus, "type='signal'" , bus_signal_cb, bus);
+#if 0
+ sd_bus_add_match(bus, "type='method_call'" , bus_method_cb, bus);
+ sd_bus_add_match(bus, "type='method_return'", bus_return_cb, bus);
+#else
+ sd_bus_add_fallback(bus, "/", bus_method_cb, bus);
+#endif
+
+ while (sd_bus_process(bus, NULL) > 0)
+ sd_bus_flush(bus);
+
+ b->bus = bus;
+ b->ml = ml;
+ b->sl = mrp_add_subloop(ml, &bus_ops, b);
+
+ if (b->sl == NULL) {
+ mrp_log_error("Failed to register D-Bus subloop.");
+ exit(1);
+ }
+
+#if 0
+ if ((ts = mrp_add_timer(ml, 1000, emit_signal, bus)) == NULL) {
+ mrp_log_error("Failed to create signal emission timer.");
+ exit(1);
+ }
+#endif
+
+ if ((ts = mrp_add_timer(ml, 1000, call_method, bus)) == NULL) {
+ mrp_log_error("Failed to create method call timer.");
+ exit(1);
+ }
+
+ mrp_mainloop_run(ml);
+
+ fail:
+ mrp_del_timer(ts);
+ mrp_del_timer(tm);
+
+ sd_bus_unref(bus);
+ mrp_mainloop_destroy(ml);
+
+ return 0;
+}
diff --git a/src/common/tests/transport-test.c b/src/common/tests/transport-test.c
new file mode 100644
index 0000000..e5797a0
--- /dev/null
+++ b/src/common/tests/transport-test.c
@@ -0,0 +1,998 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+
+
+/*
+ * tags for generic message fields
+ */
+
+#define TAG_SEQ ((uint16_t)0x1)
+#define TAG_MSG ((uint16_t)0x2)
+#define TAG_U8 ((uint16_t)0x3)
+#define TAG_S8 ((uint16_t)0x4)
+#define TAG_U16 ((uint16_t)0x5)
+#define TAG_S16 ((uint16_t)0x6)
+#define TAG_DBL ((uint16_t)0x7)
+#define TAG_BLN ((uint16_t)0x8)
+#define TAG_ASTR ((uint16_t)0x9)
+#define TAG_AU32 ((uint16_t)0xa)
+#define TAG_RPL ((uint16_t)0xb)
+#define TAG_END MRP_MSG_FIELD_END
+
+#define U32_GUARD (uint32_t)-1
+
+/*
+ * our test custom data type
+ */
+
+#define TAG_CUSTOM 0x1
+
+typedef struct {
+ uint32_t seq;
+ char *msg;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ double dbl;
+ bool bln;
+ char **astr;
+ uint32_t nstr;
+ uint32_t fsck;
+ uint32_t *au32;
+ char *rpl;
+} custom_t;
+
+
+typedef custom_t native_t;
+
+static uint32_t native_id;
+
+MRP_DATA_DESCRIPTOR(custom_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, nstr,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+MRP_DATA_DESCRIPTOR(buggy_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, fsck,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+
+
+mrp_data_descr_t *data_descr;
+
+
+typedef enum {
+ MODE_DEFAULT = 0,
+ MODE_MESSAGE = 1,
+ MODE_DATA = 2,
+ MODE_RAW = 3,
+ MODE_NATIVE = 4,
+} msg_mode_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml;
+ mrp_transport_t *lt, *t;
+ char *addrstr;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *atype;
+ int server;
+ int sock;
+ mrp_io_watch_t *iow;
+ mrp_timer_t *timer;
+ int mode;
+ int buggy;
+ int connect;
+ int stream;
+ int log_mask;
+ const char *log_target;
+ uint32_t seqno;
+} context_t;
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data);
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data);
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+void recvraw(mrp_transport_t *t, void *data, size_t size, void *user_data);
+void recvrawfrom(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+
+void dump_msg(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_dump(msg, fp);
+}
+
+
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_msg_field_t *f;
+ uint32_t seq;
+ char buf[256];
+ int status;
+
+ mrp_log_info("received a message");
+ dump_msg(msg, stdout);
+
+ if (c->server) {
+ seq = 0;
+ if ((f = mrp_msg_find(msg, TAG_SEQ)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_UINT32)
+ seq = f->u32;
+ }
+
+ snprintf(buf, sizeof(buf), "reply to message #%u", seq);
+
+ if (!mrp_msg_append(msg, TAG_RPL, MRP_MSG_FIELD_STRING, buf,
+ TAG_END)) {
+ mrp_log_info("failed to append to received message");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(t, msg);
+ else
+ status = mrp_transport_sendto(t, msg, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ /* message unreffed by transport layer */
+ }
+}
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void dump_custom(custom_t *msg, FILE *fp)
+{
+ uint32_t i;
+
+ mrp_data_dump(msg, data_descr, fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, " seq = %u\n" , msg->seq);
+ fprintf(fp, " msg = '%s'\n", msg->msg);
+ fprintf(fp, " u8 = %u\n" , msg->u8);
+ fprintf(fp, " s8 = %d\n" , msg->s8);
+ fprintf(fp, " u16 = %u\n" , msg->u16);
+ fprintf(fp, " s16 = %d\n" , msg->s16);
+ fprintf(fp, " dbl = %f\n" , msg->dbl);
+ fprintf(fp, " bln = %s\n" , msg->bln ? "true" : "false");
+ fprintf(fp, " astr = (%u)\n", msg->nstr);
+ for (i = 0; i < msg->nstr; i++)
+ fprintf(fp, " %s\n", msg->astr[i]);
+ fprintf(fp, " au32 =\n");
+ for (i = 0; msg->au32[i] != U32_GUARD; i++)
+ fprintf(fp, " %u\n", msg->au32[i]);
+ fprintf(fp, " rpl = '%s'\n", msg->rpl);
+ fprintf(fp, "}\n");
+}
+
+
+void free_custom(custom_t *msg)
+{
+ mrp_data_free(msg, data_descr->tag);
+}
+
+
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ custom_t *msg = (custom_t *)data;
+ custom_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ if (c->server) {
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_senddata(t, &rpl, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(t, &rpl, data_descr->tag,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+
+ free_custom(msg);
+}
+
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ recvfrom_data(t, data, tag, NULL, 0, user_data);
+}
+
+
+void dump_raw(void *data, size_t size, FILE *fp)
+{
+ int len = (int)size;
+
+ fprintf(fp, "[%*.*s]\n", len, len, (char *)data);
+}
+
+
+void recvfrom_raw(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ char rpl[256];
+ size_t rpl_size;
+ int status;
+
+ rpl_size = snprintf(rpl, sizeof(rpl), "reply to message [%*.*s]",
+ (int)size, (int)size, (char *)data);
+
+ mrp_log_info("received raw message");
+ dump_raw(data, size, stdout);
+
+ if (strncmp((char *)data, "reply to ", 9) != 0) {
+ if (c->connect)
+ status = mrp_transport_sendraw(t, rpl, rpl_size);
+ else
+ status = mrp_transport_sendrawto(t, rpl, rpl_size, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+}
+
+
+void recv_raw(mrp_transport_t *t, void *data, size_t size, void *user_data)
+{
+ recvfrom_raw(t, data, size, NULL, 0, user_data);
+}
+
+
+void free_native(native_t *msg)
+{
+ mrp_free_native(msg, native_id);
+}
+
+
+void recvfrom_native(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ native_t *msg = (native_t *)data;
+ native_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("received native message of type 0x%x", type_id);
+ dump_custom(data, stdout);
+
+ if (type_id != native_id) {
+ mrp_log_error("Received type 0x%x, expected 0x%x.", type_id, native_id);
+ exit(1);
+ }
+
+ if (c->server) {
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_sendnative(t, &rpl, native_id);
+ else
+ status = mrp_transport_sendnativeto(t, &rpl, native_id,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+
+ free_native(msg);
+}
+
+
+void recv_native(mrp_transport_t *t, void *data, uint32_t type_id,
+ void *user_data)
+{
+ recvfrom_native(t, data, type_id, NULL, 0, user_data);
+}
+
+
+void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(c);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s).", error,
+ strerror(error));
+ exit(1);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection.");
+ exit(0);
+ }
+}
+
+
+void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ int flags;
+
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ c->t = mrp_transport_accept(lt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to accept new connection.");
+ exit(1);
+ }
+}
+
+
+void type_init(context_t *c)
+{
+ if (c->buggy && c->server) {
+ data_descr = &buggy_descr;
+ mrp_log_info("Deliberately using buggy data descriptor...");
+ }
+ else
+ data_descr = &custom_descr;
+
+ if (!mrp_msg_register_type(data_descr)) {
+ mrp_log_error("Failed to register custom data type.");
+ exit(1);
+ }
+}
+
+
+void register_native(void)
+{
+ MRP_NATIVE_TYPE(native_type, native_t,
+ MRP_UINT32(native_t, seq , DEFAULT),
+ MRP_STRING(native_t, msg , DEFAULT),
+ MRP_UINT8 (native_t, u8 , DEFAULT),
+ MRP_INT8 (native_t, s8 , DEFAULT),
+ MRP_UINT16(native_t, u16 , DEFAULT),
+ MRP_INT16 (native_t, s16 , DEFAULT),
+ MRP_DOUBLE(native_t, dbl , DEFAULT),
+ MRP_BOOL (native_t, bln , DEFAULT),
+ MRP_ARRAY (native_t, astr , DEFAULT, SIZED,
+ char *, nstr),
+ MRP_UINT32(native_t, nstr , DEFAULT),
+ MRP_ARRAY (native_t, au32 , DEFAULT, GUARDED,
+ uint32_t, "", .u32 = -1),
+ MRP_STRING(native_t, rpl , DEFAULT));
+
+
+ if ((native_id = mrp_register_native(&native_type)) != MRP_INVALID_TYPE)
+ mrp_log_info("Successfully registered native type 'native_t'.");
+ else {
+ mrp_log_error("Failed to register native type 'native_t'.");
+ exit(1);
+ }
+}
+
+
+void server_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = NULL,
+ .connection = NULL,
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ break;
+ case MODE_NATIVE:
+ evt.recvnative = recv_native;
+ evt.recvnativefrom = recvfrom_native;
+ break;
+ case MODE_MESSAGE:
+ default:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ }
+
+ if (c->stream) {
+ evt.connection = connection_evt;
+ evt.closed = closed_evt;
+ }
+
+ flags = MRP_TRANSPORT_REUSEADDR;
+
+ switch (c->mode) {
+ case MODE_DATA: flags |= MRP_TRANSPORT_MODE_DATA; break;
+ case MODE_RAW: flags |= MRP_TRANSPORT_MODE_RAW; break;
+ case MODE_NATIVE: flags |= MRP_TRANSPORT_MODE_NATIVE; break;
+ default:
+ case MODE_MESSAGE: flags |= MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->lt = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->lt == NULL) {
+ mrp_log_error("Failed to create listening server transport.");
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->lt, &c->addr, c->alen)) {
+ mrp_log_error("Failed to bind transport to address %s.", c->addrstr);
+ exit(1);
+ }
+
+ if (c->stream) {
+ if (!mrp_transport_listen(c->lt, 0)) {
+ mrp_log_error("Failed to listen on server transport.");
+ exit(1);
+ }
+ }
+}
+
+
+void send_msg(context_t *c)
+{
+ mrp_msg_t *msg;
+ uint32_t seq;
+ char buf[256];
+ char *astr[] = { "this", "is", "an", "array", "of", "strings" };
+ uint32_t au32[] = { 1, 2, 3,
+ 1 << 16, 2 << 16, 3 << 16,
+ 1 << 24, 2 << 24, 3 << 24 };
+ uint32_t nstr = MRP_ARRAY_SIZE(astr);
+ uint32_t nu32 = MRP_ARRAY_SIZE(au32);
+ int status;
+
+ seq = c->seqno++;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+
+ msg = mrp_msg_create(TAG_SEQ , MRP_MSG_FIELD_UINT32, seq,
+ TAG_MSG , MRP_MSG_FIELD_STRING, buf,
+ TAG_U8 , MRP_MSG_FIELD_UINT8 , seq & 0xf,
+ TAG_S8 , MRP_MSG_FIELD_SINT8 , -(seq & 0xf),
+ TAG_U16 , MRP_MSG_FIELD_UINT16, seq,
+ TAG_S16 , MRP_MSG_FIELD_SINT16, - seq,
+ TAG_DBL , MRP_MSG_FIELD_DOUBLE, seq / 3.0,
+ TAG_BLN , MRP_MSG_FIELD_BOOL , seq & 0x1,
+ TAG_ASTR, MRP_MSG_FIELD_ARRAY_OF(STRING), nstr, astr,
+ TAG_AU32, MRP_MSG_FIELD_ARRAY_OF(UINT32), nu32, au32,
+ TAG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(c->t, msg);
+ else
+ status = mrp_transport_sendto(c->t, msg, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", seq);
+
+ mrp_msg_unref(msg);
+}
+
+
+void send_data(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_senddata(c->t, &msg, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(c->t, &msg, data_descr->tag,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+void send_raw(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ char msg[256];
+ size_t size;
+ int status;
+
+ size = snprintf(msg, sizeof(msg), "this is message #%u", seq);
+
+ if (c->connect)
+ status = mrp_transport_sendraw(c->t, msg, size);
+ else
+ status = mrp_transport_sendrawto(c->t, msg, size, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send raw message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%u succesfully sent.", seq);
+}
+
+
+void send_native(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_sendnative(c->t, &msg, native_id);
+ else
+ status = mrp_transport_sendnativeto(c->t, &msg, native_id,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ switch (c->mode) {
+ case MODE_DATA: send_data(c); break;
+ case MODE_RAW: send_raw(c); break;
+ case MODE_NATIVE: send_native(c); break;
+ default:
+ case MODE_MESSAGE: send_msg(c);
+ }
+}
+
+
+void client_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ flags = MRP_TRANSPORT_MODE_DATA;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ flags = MRP_TRANSPORT_MODE_RAW;
+ break;
+ case MODE_NATIVE:
+ evt.recvnative = recv_native;
+ evt.recvnativefrom = recvfrom_native;
+ flags = MRP_TRANSPORT_MODE_NATIVE;
+ break;
+ default:
+ case MODE_MESSAGE:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ flags = MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->t = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to create new transport.");
+ exit(1);
+ }
+
+ if (!strcmp(c->atype, "unxd")) {
+ char addrstr[] = "unxd:@stream-test-client";
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ alen = mrp_transport_resolve(NULL, addrstr, &addr, sizeof(addr), NULL);
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", addrstr);
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->t, &addr, alen)) {
+ mrp_log_error("Failed to bind to transport address '%s'.", addrstr);
+ exit(1);
+ }
+ }
+
+ if (c->connect) {
+ if (!mrp_transport_connect(c->t, &c->addr, c->alen)) {
+ mrp_log_error("Failed to connect to %s.", c->addrstr);
+ exit(1);
+ }
+ }
+
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create send timer.");
+ exit(1);
+ }
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [transport-address]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -C, --connect connect transport\n"
+ " For connection-oriented transports, this is automatic.\n"
+ " -a, --address address to use\n"
+ " -c, --custom use custom messages\n"
+ " -m, --message use generic messages (default)\n"
+ " -r, --raw use raw messages\n"
+ " -n, --native use native messages\n"
+ " -b, --buggy use buggy data descriptors\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->addrstr = "tcp4:127.0.0.1:3000";
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "scmrnbCa:l:t:v:d:h"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "address" , required_argument, NULL, 'a' },
+ { "custom" , no_argument , NULL, 'c' },
+ { "message" , no_argument , NULL, 'm' },
+ { "raw" , no_argument , NULL, 'r' },
+ { "native" , no_argument , NULL, 'n' },
+ { "connect" , no_argument , NULL, 'C' },
+
+ { "buggy" , no_argument , NULL, 'b' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'c':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_DATA;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'm':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_MESSAGE;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_RAW;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'n':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_NATIVE;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'b':
+ ctx->buggy = TRUE;
+ break;
+
+ case 'C':
+ ctx->connect = TRUE;
+ break;
+
+ case 'a':
+ ctx->addrstr = optarg;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using address '%s'...", c.addrstr);
+ else
+ mrp_log_info("Running as client, using address '%s'...", c.addrstr);
+
+ switch (c.mode) {
+ case MODE_DATA: mrp_log_info("Using custom data messages..."); break;
+ case MODE_RAW: mrp_log_info("Using raw messages..."); break;
+ case MODE_NATIVE:
+ register_native();
+ mrp_log_info("Using native messages...");
+ break;
+ default:
+ case MODE_MESSAGE: mrp_log_info("Using generic messages...");
+ }
+
+ if (!strncmp(c.addrstr, "tcp", 3) || !strncmp(c.addrstr, "unxs", 4) ||
+ !strncmp(c.addrstr, "wsck", 4)) {
+ c.stream = TRUE;
+ c.connect = TRUE;
+ }
+
+ c.alen = mrp_transport_resolve(NULL, c.addrstr,
+ &c.addr, sizeof(c.addr), &c.atype);
+ if (c.alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", c.addrstr);
+ exit(1);
+ }
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.server)
+ server_init(&c);
+ else
+ client_init(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ return 0;
+}
diff --git a/src/common/tlv.c b/src/common/tlv.c
new file mode 100644
index 0000000..fd216a3
--- /dev/null
+++ b/src/common/tlv.c
@@ -0,0 +1,662 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/tlv.h>
+
+#define TLV_MIN_PREALLOC 4096
+#define TLV_MIN_CHUNK 64
+
+int mrp_tlv_setup_write(mrp_tlv_t *tlv, size_t prealloc)
+{
+ if (prealloc < TLV_MIN_PREALLOC)
+ prealloc = TLV_MIN_PREALLOC;
+
+ if ((tlv->buf = mrp_allocz(prealloc)) == NULL)
+ return -1;
+
+ tlv->size = prealloc;
+ tlv->p = tlv->buf;
+ tlv->write = 1;
+
+ return 0;
+}
+
+
+static inline size_t tlv_space(mrp_tlv_t *tlv)
+{
+ if (tlv->size > 0 && tlv->write)
+ return tlv->size - (tlv->p - tlv->buf);
+ else
+ return 0;
+}
+
+
+static inline size_t tlv_data(mrp_tlv_t *tlv)
+{
+ if (!tlv->write)
+ return tlv->size - (tlv->p - tlv->buf);
+ else
+ return tlv->p - tlv->buf;
+}
+
+
+int mrp_tlv_ensure(mrp_tlv_t *tlv, size_t size)
+{
+ size_t left, diff;
+
+ if (!tlv->write)
+ return -1;
+
+ if ((left = tlv_space(tlv)) < size) {
+ diff = size - left;
+
+ if (diff < TLV_MIN_CHUNK)
+ diff = TLV_MIN_CHUNK;
+
+ tlv->p -= (ptrdiff_t)tlv->buf;
+
+ if (mrp_realloc(tlv->buf, tlv->size + diff) == NULL) {
+ tlv->p += (ptrdiff_t)tlv->buf;
+
+ return -1;
+ }
+
+ memset(tlv->buf + tlv->size, 0, diff);
+
+ tlv->size += diff;
+ tlv->p += (ptrdiff_t)tlv->buf;
+ }
+
+ return 0;
+}
+
+
+void *mrp_tlv_reserve(mrp_tlv_t *tlv, size_t size, int align)
+{
+ void *reserved;
+ ptrdiff_t offs, pad;
+ size_t len;
+
+ offs = tlv->p - tlv->buf;
+
+ if (align > 1)
+ pad = align - (offs & (align - 1));
+ else
+ pad = 0;
+
+ len = size + pad;
+
+ if (mrp_tlv_ensure(tlv, len) < 0)
+ return NULL;
+
+ if (pad)
+ memset(tlv->p, 0, pad);
+
+ reserved = tlv->p + pad;
+ tlv->p += len;
+
+ return reserved;
+}
+
+
+int mrp_tlv_setup_read(mrp_tlv_t *tlv, void *buf, size_t size)
+{
+ tlv->buf = tlv->p = buf;
+ tlv->size = size;
+ tlv->write = 0;
+
+ return 0;
+}
+
+
+static void *tlv_consume(mrp_tlv_t *tlv, size_t size)
+{
+ char *p;
+
+ if (tlv_data(tlv) < size)
+ return NULL;
+
+ p = tlv->p;
+ tlv->p += size;
+
+ return p;
+}
+
+
+void mrp_tlv_trim(mrp_tlv_t *tlv)
+{
+ size_t left;
+
+ if (!tlv->write)
+ return;
+
+ if ((left = tlv_space(tlv)) == 0)
+ return;
+
+ tlv->p -= (ptrdiff_t)tlv->buf;
+
+ if (mrp_realloc(tlv->buf, tlv->size - left) != NULL) {
+ tlv->size -= left;
+ tlv->p += (ptrdiff_t)tlv->buf;
+ }
+}
+
+
+size_t mrp_tlv_offset(mrp_tlv_t *tlv)
+{
+ return (size_t)(tlv->p - tlv->buf);
+}
+
+
+void mrp_tlv_cleanup(mrp_tlv_t *tlv)
+{
+ if (tlv->write)
+ mrp_free(tlv->buf);
+
+ tlv->buf = tlv->p = NULL;
+ tlv->size = 0;
+}
+
+
+void mrp_tlv_steal(mrp_tlv_t *tlv, void **bufp, size_t *sizep)
+{
+ if (tlv->write) {
+ *bufp = tlv->buf;
+ *sizep = tlv->p - tlv->buf;
+
+ tlv->buf = tlv->p = NULL;
+ tlv->size = 0;
+ }
+ else {
+ *bufp = NULL;
+ *sizep = 0;
+ }
+}
+
+
+static inline int push_tag(mrp_tlv_t *tlv, uint32_t tag)
+{
+ uint32_t *tagp;
+
+ if (tag) {
+ if ((tagp = mrp_tlv_reserve(tlv, sizeof(*tagp), 1)) == NULL)
+ return -1;
+ else
+ *tagp = htobe32(tag);
+ }
+
+ return 0;
+}
+
+
+int mrp_tlv_push_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t v)
+{
+ int8_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t v)
+{
+ uint8_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t v)
+{
+ int16_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe16(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t v)
+{
+ uint16_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe16(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t v)
+{
+ int32_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe32(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t v)
+{
+ uint32_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe32(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t v)
+{
+ int64_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe64(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t v)
+{
+ uint64_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe64(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_float(mrp_tlv_t *tlv, uint32_t tag, float v)
+{
+ float *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_double(mrp_tlv_t *tlv, uint32_t tag, double v)
+{
+ double *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_bool(mrp_tlv_t *tlv, uint32_t tag, bool v)
+{
+ bool *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_string(mrp_tlv_t *tlv, uint32_t tag, const char *str)
+{
+ uint32_t *sizep;
+ char *strp;
+ size_t len = str ? strlen(str) + 1 : 0;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((sizep = mrp_tlv_reserve(tlv, sizeof(*sizep), 1)) == NULL)
+ return -1;
+
+ *sizep = htobe32((uint32_t)len);
+
+ if (len > 0) {
+ if ((strp = mrp_tlv_reserve(tlv, len, 1)) == NULL)
+ return -1;
+
+ strcpy(strp, str);
+ }
+
+ return 0;
+}
+
+
+int pull_tag(mrp_tlv_t *tlv, uint32_t tag)
+{
+ uint32_t *tagp;
+
+ if (tag) {
+ if ((tagp = tlv_consume(tlv, sizeof(*tagp))) == NULL)
+ return -1;
+
+ if (be32toh(*tagp) != tag)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t *v)
+{
+ int8_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t *v)
+{
+ uint8_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t *v)
+{
+ int16_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be16toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t *v)
+{
+ uint16_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be16toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t *v)
+{
+ int32_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be32toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t *v)
+{
+ uint32_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be32toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t *v)
+{
+ int64_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be64toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t *v)
+{
+ uint64_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be64toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_float(mrp_tlv_t *tlv, uint32_t tag, float *v)
+{
+ float *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_double(mrp_tlv_t *tlv, uint32_t tag, double *v)
+{
+ double *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_bool(mrp_tlv_t *tlv, uint32_t tag, bool *v)
+{
+ bool *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_string(mrp_tlv_t *tlv, uint32_t tag, char **v, size_t max,
+ void *(alloc)(size_t, void *), void *alloc_data)
+{
+ uint32_t *sizep, size;
+ char *str;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((sizep = tlv_consume(tlv, sizeof(*sizep))) == NULL)
+ return -1;
+
+ size = be32toh(*sizep);
+
+ if (max != (size_t)-1 && max < size) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ if (size > 0) {
+ if ((str = tlv_consume(tlv, size)) == NULL)
+ return -1;
+
+ if (*v == NULL)
+ if ((*v = alloc(size, alloc_data)) == NULL)
+ return -1;
+
+ strncpy(*v, str, size - 1);
+ (*v)[size - 1] = '\0';
+ }
+ else
+ *v = NULL;
+
+ return 0;
+}
diff --git a/src/common/tlv.h b/src/common/tlv.h
new file mode 100644
index 0000000..b746546
--- /dev/null
+++ b/src/common/tlv.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MRP_COMMON_TLV_H__
+#define __MRP_COMMON_TLV_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_TLV_UNTAGGED 0
+
+/**
+ * a tagged-value-list encoding/decoding buffer
+ */
+
+typedef struct {
+ void *buf; /* actual data buffer */
+ size_t size; /* allocated buffer size */
+ void *p; /* encoding/decoding pointer */
+ int write : 1; /* whether set up for writing */
+} mrp_tlv_t;
+
+/** Set up the given TLV buffer for encoding. */
+int mrp_tlv_setup_write(mrp_tlv_t *tlv, size_t prealloc);
+
+/** Set up the given TLV buffer for decoding. */
+int mrp_tlv_setup_read(mrp_tlv_t *tlv, void *buf, size_t size);
+
+/** Clean up the given TLV buffer. */
+void mrp_tlv_cleanup(mrp_tlv_t *tlv);
+
+/** Ensure the given amount of space is available in the TLV buffer. */
+int mrp_tlv_ensure(mrp_tlv_t *tlv, size_t size);
+
+/** Reserve the given amount of buffer space from the TLV buffer. */
+void *mrp_tlv_reserve(mrp_tlv_t *tlv, size_t size, int align);
+
+/** Take ownership of the data buffer from the TLV buffer. */
+void mrp_tlv_steal(mrp_tlv_t *tlv, void **bufp, size_t *sizep);
+
+/** Trim the data buffer of the TLV buffer to current amount of data. */
+void mrp_tlv_trim(mrp_tlv_t *tlv);
+
+/** Get the current read/write offset from the TLV buffer. */
+size_t mrp_tlv_offset(mrp_tlv_t *tlv);
+
+/** Add an int8_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t v);
+
+/** Add an uint8_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t v);
+
+/** Add an int16_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t v);
+
+/** Add an uint16_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t v);
+
+/** Add an int32_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t v);
+
+/** Add an uint32_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t v);
+
+/** Add an int64_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t v);
+
+/** Add an uint64_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t v);
+
+/** Add an float with an optional tag to the TLV buffer. */
+int mrp_tlv_push_float(mrp_tlv_t *tlv, uint32_t tag, float v);
+
+/** Add an double with an optional tag to the TLV buffer. */
+int mrp_tlv_push_double(mrp_tlv_t *tlv, uint32_t tag, double v);
+
+/** Add a boolean with an optional tag to the TLV buffer. */
+int mrp_tlv_push_bool(mrp_tlv_t *tlv, uint32_t tag, bool v);
+
+/** Add a string with an optional tag to the TLV buffer. */
+int mrp_tlv_push_string(mrp_tlv_t *tlv, uint32_t tag, const char *str);
+
+
+int mrp_tlv_pull_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t *v);
+int mrp_tlv_pull_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t *v);
+int mrp_tlv_pull_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t *v);
+int mrp_tlv_pull_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t *v);
+int mrp_tlv_pull_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t *v);
+int mrp_tlv_pull_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t *v);
+int mrp_tlv_pull_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t *v);
+int mrp_tlv_pull_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t *v);
+int mrp_tlv_pull_float(mrp_tlv_t *tlv, uint32_t tag, float *v);
+int mrp_tlv_pull_double(mrp_tlv_t *tlv, uint32_t tag, double *v);
+int mrp_tlv_pull_bool(mrp_tlv_t *tlv, uint32_t tag, bool *v);
+int mrp_tlv_pull_string(mrp_tlv_t *tlv, uint32_t tag, char **v, size_t max,
+ void *(alloc)(size_t, void *), void *alloc_data);
+
+MRP_CDECL_END
+
+#endif /* __MRP_COMMON_TLV_H__ */
diff --git a/src/common/transport.c b/src/common/transport.c
new file mode 100644
index 0000000..d00c588
--- /dev/null
+++ b/src/common/transport.c
@@ -0,0 +1,829 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/native-types.h>
+#include <murphy/common/transport.h>
+
+static int check_destroy(mrp_transport_t *t);
+static int recv_data(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+static inline int purge_destroyed(mrp_transport_t *t);
+
+
+static MRP_LIST_HOOK(transports);
+static mrp_sighandler_t *pipe_handler;
+
+
+static int check_request_callbacks(mrp_transport_req_t *req)
+{
+ /* XXX TODO: hmm... this probably needs more thought/work */
+
+ if (!req->open || !req->close)
+ return FALSE;
+
+ if (req->accept) {
+ if (!req->sendmsg || !req->sendraw || !req->senddata)
+ return FALSE;
+ }
+ else {
+ if (!req->sendmsgto || !req->sendrawto || !req->senddatato)
+ return FALSE;
+ }
+
+ if (( req->connect && !req->disconnect) ||
+ (!req->connect && req->disconnect))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+int mrp_transport_register(mrp_transport_descr_t *d)
+{
+ if (!check_request_callbacks(&d->req))
+ return FALSE;
+
+ if (d->size >= sizeof(mrp_transport_t)) {
+ mrp_list_init(&d->hook);
+ mrp_list_append(&transports, &d->hook);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void mrp_transport_unregister(mrp_transport_descr_t *d)
+{
+ mrp_list_delete(&d->hook);
+}
+
+
+static mrp_transport_descr_t *find_transport(const char *type)
+{
+ mrp_transport_descr_t *d;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&transports, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ if (!strcmp(d->type, type))
+ return d;
+ }
+
+ return NULL;
+}
+
+
+static int check_event_callbacks(mrp_transport_evt_t *evt)
+{
+ /*
+ * For connection-oriented transports we require a recv* callback
+ * and a closed callback.
+ *
+ * For connectionless transports we only require a recvfrom* callback.
+ * A recv* callback is optional, however the transport cannot be put
+ * to connected mode (usually for doing sender-based filtering) if
+ * recv* is omitted.
+ */
+
+ if (evt->connection != NULL) {
+ if (evt->recvmsg == NULL || evt->closed == NULL)
+ return FALSE;
+ }
+ else {
+ if (evt->recvmsgfrom == NULL)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void sigpipe_handler(mrp_sighandler_t *h, int sig, void *user_data)
+{
+ MRP_UNUSED(h);
+ MRP_UNUSED(user_data);
+
+ mrp_debug("caught signal %d (%s)...", sig, strsignal(sig));
+}
+
+
+mrp_transport_t *mrp_transport_create(mrp_mainloop_t *ml, const char *type,
+ mrp_transport_evt_t *evt, void *user_data,
+ int flags)
+{
+ mrp_transport_descr_t *d;
+ mrp_transport_t *t;
+
+ if (!pipe_handler)
+ pipe_handler = mrp_add_sighandler(ml, SIGPIPE, sigpipe_handler, NULL);
+
+ if (!check_event_callbacks(evt)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((d = find_transport(type)) != NULL) {
+ if ((t = mrp_allocz(d->size)) != NULL) {
+ t->descr = d;
+ t->ml = ml;
+ t->evt = *evt;
+ t->user_data = user_data;
+
+ t->check_destroy = check_destroy;
+ t->recv_data = recv_data;
+ t->flags = flags & ~MRP_TRANSPORT_MODE_MASK;
+ t->mode = flags & MRP_TRANSPORT_MODE_MASK;
+
+ if (!t->descr->req.open(t)) {
+ mrp_free(t);
+ t = NULL;
+ }
+ }
+ }
+ else
+ t = NULL;
+
+ return t;
+}
+
+
+mrp_transport_t *mrp_transport_create_from(mrp_mainloop_t *ml, const char *type,
+ void *conn, mrp_transport_evt_t *evt,
+ void *user_data, int flags,
+ int state)
+{
+ mrp_transport_descr_t *d;
+ mrp_transport_t *t;
+
+ if (!pipe_handler)
+ pipe_handler = mrp_add_sighandler(ml, SIGPIPE, sigpipe_handler, NULL);
+
+ if (!check_event_callbacks(evt)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((d = find_transport(type)) != NULL) {
+ if ((t = mrp_allocz(d->size)) != NULL) {
+ t->descr = d;
+ t->ml = ml;
+ t->evt = *evt;
+ t->user_data = user_data;
+
+ t->check_destroy = check_destroy;
+ t->recv_data = recv_data;
+ t->flags = flags & ~MRP_TRANSPORT_MODE_MASK;
+ t->mode = flags & MRP_TRANSPORT_MODE_MASK;
+
+ t->connected = !!(state & MRP_TRANSPORT_CONNECTED);
+ t->listened = !!(state & MRP_TRANSPORT_LISTENED);
+
+ if (t->connected && t->listened) {
+ mrp_free(t);
+ return NULL;
+ }
+
+ if (!t->descr->req.createfrom(t, conn)) {
+ mrp_free(t);
+ t = NULL;
+ }
+ }
+ }
+ else
+ t = NULL;
+
+ return t;
+}
+
+
+int mrp_transport_setopt(mrp_transport_t *t, const char *opt, const void *val)
+{
+ if (t != NULL) {
+ if (t->descr->req.setopt != NULL)
+ return t->descr->req.setopt(t, opt, val);
+ else {
+ if (t->mode == MRP_TRANSPORT_MODE_NATIVE) {
+ if (!strcmp(opt, MRP_TRANSPORT_OPT_TYPEMAP)) {
+ t->map = (void *)val;
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static inline int type_matches(const char *type, const char *addr)
+{
+ while (*type == *addr)
+ type++, addr++;
+
+ return (*type == '\0' && *addr == ':');
+}
+
+
+socklen_t mrp_transport_resolve(mrp_transport_t *t, const char *str,
+ mrp_sockaddr_t *addr, socklen_t size,
+ const char **typep)
+{
+ mrp_transport_descr_t *d;
+ mrp_list_hook_t *p, *n;
+ socklen_t l;
+
+ if (t != NULL)
+ return t->descr->resolve(str, addr, size, typep);
+ else {
+ mrp_list_foreach(&transports, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ l = d->resolve(str, addr, size, typep);
+
+ if (l > 0)
+ return l;
+ }
+ }
+
+ return 0;
+}
+
+
+int mrp_transport_bind(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ if (t != NULL) {
+ if (t->descr->req.bind != NULL)
+ return t->descr->req.bind(t, addr, addrlen);
+ else
+ return TRUE; /* assume no binding is needed */
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_transport_listen(mrp_transport_t *t, int backlog)
+{
+ int result;
+
+ if (t != NULL) {
+ if (t->descr->req.listen != NULL) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.listen(t, backlog);
+ });
+
+ purge_destroyed(t);
+
+ return result;
+ }
+ }
+
+ return FALSE;
+}
+
+
+mrp_transport_t *mrp_transport_accept(mrp_transport_t *lt,
+ void *user_data, int flags)
+{
+ mrp_transport_t *t;
+
+ if ((t = mrp_allocz(lt->descr->size)) != NULL) {
+ bool failed = FALSE;
+ t->descr = lt->descr;
+ t->ml = lt->ml;
+ t->evt = lt->evt;
+ t->user_data = user_data;
+
+ t->check_destroy = check_destroy;
+ t->recv_data = recv_data;
+ t->flags = (lt->flags & MRP_TRANSPORT_INHERIT) | flags;
+ t->flags = t->flags & ~MRP_TRANSPORT_MODE_MASK;
+ t->mode = lt->mode;
+ t->map = lt->map;
+
+ MRP_TRANSPORT_BUSY(t, {
+ if (!t->descr->req.accept(t, lt)) {
+ failed = TRUE;
+ }
+ else {
+ t->connected = TRUE;
+ }
+ });
+
+ if (failed) {
+ mrp_free(t);
+ t = NULL;
+ }
+ }
+
+ return t;
+}
+
+
+static inline int purge_destroyed(mrp_transport_t *t)
+{
+ if (t->destroyed && !t->busy) {
+ mrp_debug("destroying transport %p...", t);
+ mrp_free(t);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void mrp_transport_destroy(mrp_transport_t *t)
+{
+ if (t != NULL) {
+ t->destroyed = TRUE;
+
+ MRP_TRANSPORT_BUSY(t, {
+ t->descr->req.disconnect(t);
+ t->descr->req.close(t);
+ });
+
+ purge_destroyed(t);
+ }
+}
+
+
+static int check_destroy(mrp_transport_t *t)
+{
+ return purge_destroyed(t);
+}
+
+
+int mrp_transport_connect(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ int result;
+
+ if (!t->connected) {
+
+ /* make sure we can deliver reception noifications */
+ if (t->evt.recvmsg == NULL) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ MRP_TRANSPORT_BUSY(t, {
+ if (t->descr->req.connect(t, addr, addrlen)) {
+ t->connected = TRUE;
+ result = TRUE;
+ }
+ else
+ result = FALSE;
+ });
+
+ purge_destroyed(t);
+ }
+ else {
+ errno = EISCONN;
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
+int mrp_transport_disconnect(mrp_transport_t *t)
+{
+ int result;
+
+ if (t != NULL && t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ if (t->descr->req.disconnect(t)) {
+ t->connected = FALSE;
+ result = TRUE;
+ }
+ else
+ result = TRUE;
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_send(mrp_transport_t *t, mrp_msg_t *msg)
+{
+ int result;
+
+ if (t->connected && t->descr->req.sendmsg) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendmsg(t, msg);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendto(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->descr->req.sendmsgto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendmsgto(t, msg, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendraw(mrp_transport_t *t, void *data, size_t size)
+{
+ int result;
+
+ if (t->connected &&
+ t->mode == MRP_TRANSPORT_MODE_RAW && t->descr->req.sendraw) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendraw(t, data, size);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendrawto(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_RAW && t->descr->req.sendrawto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendrawto(t, data, size, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_senddata(mrp_transport_t *t, void *data, uint16_t tag)
+{
+ int result;
+
+ if (t->connected &&
+ t->mode == MRP_TRANSPORT_MODE_DATA && t->descr->req.senddata) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.senddata(t, data, tag);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_senddatato(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_DATA && t->descr->req.senddatato) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.senddatato(t, data, tag, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendcustom(mrp_transport_t *t, void *data)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_CUSTOM && t->descr->req.sendcustom) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendcustom(t, data);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendcustomto(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_CUSTOM && t->descr->req.sendcustomto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendcustomto(t, data, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendnative(mrp_transport_t *t, void *data, uint32_t type_id)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_NATIVE && t->descr->req.sendnative) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendnative(t, data, type_id);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendnativeto(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_NATIVE && t->descr->req.sendnativeto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendnativeto(t, data, type_id,
+ addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendjson(mrp_transport_t *t, mrp_json_t *msg)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_JSON && t->descr->req.sendjson) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendjson(t, msg);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendjsonto(mrp_transport_t *t, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_JSON && t->descr->req.sendjsonto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendjsonto(t, msg, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+static int recv_data(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_data_descr_t *type;
+ uint16_t tag;
+ mrp_msg_t *msg;
+ uint32_t type_id;
+ void *decoded;
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ tag = be16toh(*(uint16_t *)data);
+ data += sizeof(tag);
+ size -= sizeof(tag);
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ decoded = mrp_data_decode(&data, &size, type);
+
+ if (decoded != NULL && size == 0) {
+ if (t->connected && t->evt.recvdata) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvdata(t, decoded, tag, t->user_data);
+ });
+ }
+ else if (t->evt.recvdatafrom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvdatafrom(t, decoded, tag, addr, addrlen,
+ t->user_data);
+ });
+ }
+ else
+ mrp_free(decoded); /* no callback, discard */
+
+ return 0;
+ }
+ else {
+ if (decoded != NULL) {
+ mrp_free(decoded);
+ return -EMSGSIZE;
+ }
+ else
+ return -errno;
+ }
+ }
+ else
+ return -ENOPROTOOPT;
+ break;
+
+ case MRP_TRANSPORT_MODE_RAW:
+ if (t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvraw(t, data, size, t->user_data);
+ });
+ }
+ else {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvrawfrom(t, data, size, addr, addrlen,
+ t->user_data);
+ });
+ }
+ return 0;
+
+ case MRP_TRANSPORT_MODE_MSG:
+ tag = be16toh(*(uint16_t *)data);
+ data += sizeof(tag);
+ size -= sizeof(tag);
+
+ if (tag != MRP_MSG_TAG_DEFAULT ||
+ (msg = mrp_msg_default_decode(data, size)) == NULL) {
+ return -EPROTO;
+ }
+ else {
+ if (t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvmsg(t, msg, t->user_data);
+ });
+ }
+ else {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvmsgfrom(t, msg, addr, addrlen,
+ t->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ return 0;
+ }
+ break;
+
+ case MRP_TRANSPORT_MODE_CUSTOM:
+ if (t->connected) {
+ if (t->evt.recvcustom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvcustom(t, data, t->user_data);
+ });
+
+ return 0;
+ }
+ }
+ else {
+ if (t->evt.recvcustomfrom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvcustomfrom(t, data, addr, addrlen,
+ t->user_data);
+ });
+
+ return 0;
+ }
+ }
+ return -EPROTOTYPE;
+
+ case MRP_TRANSPORT_MODE_NATIVE:
+ type_id = 0;
+ if (mrp_decode_native(&data, &size, &decoded, &type_id, t->map) < 0)
+ return -EPROTO;
+
+ if (decoded == NULL || size != 0) {
+ mrp_free_native(decoded, type_id);
+ return -EPROTO;
+ }
+
+ if (t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvnative(t, decoded, type_id, t->user_data);
+ });
+ }
+ else {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvnativefrom(t, decoded, type_id, addr, addrlen,
+ t->user_data);
+ });
+ }
+ return 0;
+
+ case MRP_TRANSPORT_MODE_JSON:
+ if (t->connected) {
+ if (t->evt.recvjson) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvjson(t, data, t->user_data);
+ });
+ }
+ }
+ else {
+ if (t->evt.recvjsonfrom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvjsonfrom(t, data, addr, addrlen,
+ t->user_data);
+ });
+ }
+ }
+ return 0;
+
+ default:
+ return -EPROTOTYPE;
+ }
+}
+
diff --git a/src/common/transport.h b/src/common/transport.h
new file mode 100644
index 0000000..7b57a2c
--- /dev/null
+++ b/src/common/transport.h
@@ -0,0 +1,559 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_TRANSPORT_H__
+#define __MURPHY_TRANSPORT_H__
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/native-types.h>
+
+/*
+ * json-c and JSON-Glib have a symbol clash on json_object_get_type.
+ * Unfortunately we'd really need to include our own json.h here to
+ * get mrp_json_t defined. That however pulls in json-c's headers as
+ * our implementation uses json-c and the type itself is just a
+ * typedef'd alias to json_object.
+ *
+ * Now if some unfortunate sould ends up directly or indirectly
+ * including both our transport.h, and consequently json.h, and
+ * JSON-Glib, we'll trigger the symbol clash.
+ *
+ * As a workaround if we detect that JSON-Glib has already been
+ * included we'll compile with alternative signatures (void *,
+ * instead of mrp_json_t *) and omit including json.h. Also we
+ * let people give us a warning by defining __JSON_GLIB_DANGER__
+ * that they will or might include JSON-Glib, in which case
+ * we also compile with the alternative signatures. Oh boy...
+ */
+
+#if !defined(__JSON_TYPES_H__) && !defined(__JSON_GLIB_DANGER__)
+# include <murphy/common/json.h>
+#else
+# define mrp_json_t void
+#endif
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_transport_s mrp_transport_t;
+
+
+
+/*
+ * transport socket address
+ */
+
+#define MRP_SOCKADDR_SIZE 256
+
+typedef union {
+ struct sockaddr any;
+ struct sockaddr_in ipv4;
+ struct sockaddr_in6 ipv6;
+ struct sockaddr_un unx;
+ char data[MRP_SOCKADDR_SIZE];
+} mrp_sockaddr_t;
+
+
+static inline mrp_sockaddr_t *mrp_sockaddr_cpy(mrp_sockaddr_t *d,
+ mrp_sockaddr_t *s, socklen_t n)
+{
+ memcpy(d, s, n);
+ return d;
+}
+
+
+/*
+ * various transport flags
+ */
+
+typedef enum {
+ MRP_TRANSPORT_MODE_MSG = 0x00, /* generic message encoding */
+ MRP_TRANSPORT_MODE_RAW = 0x01, /* uses bitpipe mode */
+ MRP_TRANSPORT_MODE_DATA = 0x02, /* uses registered data types */
+ MRP_TRANSPORT_MODE_CUSTOM = 0x03, /* custom message encoding */
+ MRP_TRANSPORT_MODE_NATIVE = 0x04, /* uses registered native-types */
+ MRP_TRANSPORT_MODE_JSON = 0x05, /* uses JSON messages */
+} mrp_transport_mode_t;
+
+typedef enum {
+ MRP_TRANSPORT_MODE_MASK = 0x0f, /* mask of mode bits */
+ MRP_TRANSPORT_INHERIT = 0x0f, /* mask of all inherited flags */
+
+ MRP_TRANSPORT_REUSEADDR = 0x010,
+ MRP_TRANSPORT_NONBLOCK = 0x020,
+ MRP_TRANSPORT_CLOEXEC = 0x040,
+ MRP_TRANSPORT_CONNECTED = 0x080,
+ MRP_TRANSPORT_LISTENED = 0x001,
+} mrp_transport_flag_t;
+
+#define MRP_TRANSPORT_MODE(t) ((t)->flags & MRP_TRANSPORT_MODE_MASK)
+
+
+#define MRP_TRANSPORT_OPT_TYPEMAP "type-map"
+
+/*
+ * transport requests
+ *
+ * Transport requests correspond to top-down event propagation in the
+ * communication stack. These requests are made by the core tansport
+ * abstraction layer to the underlying actual transport implementation
+ * to carry out the implementation-specific details of some transport
+ * operation.
+ */
+
+typedef struct {
+ /** Open a new transport. */
+ int (*open)(mrp_transport_t *t);
+ /** Create a new transport from an existing backend object. */
+ int (*createfrom)(mrp_transport_t *t, void *obj);
+ /** Bind a transport to a given transport-specific address. */
+ int (*bind)(mrp_transport_t *t, mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Listen on a transport for incoming connections. */
+ int (*listen)(mrp_transport_t *t, int backlog);
+ /** Accept a new transport connection over an existing transport. */
+ int (*accept)(mrp_transport_t *t, mrp_transport_t *lt);
+ /** Connect a transport to an endpoint. */
+ int (*connect)(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+ /** Disconnect a transport, if it is connection-oriented. */
+ int (*disconnect)(mrp_transport_t *t);
+ /** Close a transport, free all resources from open/accept/connect. */
+ void (*close)(mrp_transport_t *t);
+ /** Set a (possibly type specific) transport option. */
+ int (*setopt)(mrp_transport_t *t, const char *opt, const void *value);
+ /** Send a message over a (connected) transport. */
+ int (*sendmsg)(mrp_transport_t *t, mrp_msg_t *msg);
+ /** Send raw data over a (connected) transport. */
+ int (*sendraw)(mrp_transport_t *t, void *buf, size_t size);
+ /** Send registered data over a (connected) transport. */
+ int (*senddata)(mrp_transport_t *t, void *data, uint16_t tag);
+ /** Send data with a custom encoder over a transport. */
+ int (*sendcustom)(mrp_transport_t *t, void *data);
+ /** Send a native type over a (connected) transport. */
+ int (*sendnative)(mrp_transport_t *t, void *data, uint32_t type_id);
+ /** Send a JSON message over a (connected) transport. */
+ int (*sendjson)(mrp_transport_t *t, mrp_json_t *msg);
+
+ /** Send a message over a(n unconnected) transport. */
+ int (*sendmsgto)(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+ /** Send raw data over a(n unconnected) transport. */
+ int (*sendrawto)(mrp_transport_t *t, void *buf, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send registered data over a(n unconnected) transport. */
+ int (*senddatato)(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send data with a custom encoder over a transport. */
+ int (*sendcustomto)(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send a native type over a transport. */
+ int (*sendnativeto)(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send a JSON messgae over a(n unconnected) transport. */
+ int (*sendjsonto)(mrp_transport_t *t, mrp_json_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+} mrp_transport_req_t;
+
+
+/*
+ * transport events
+ *
+ * Transport events correspond to bottom-up event propagation in the
+ * communication stack. These callbacks are made by the actual transport
+ * implementation to the generic transport abstraction to inform it
+ * about relevant transport events, such as the reception of data, or
+ * transport disconnection by the peer.
+ */
+
+typedef struct {
+ /** Message received on a connected transport. */
+ union {
+ /** Generic message callback for connected transports. */
+ void (*recvmsg)(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+ /** Raw data callback for connected transports. */
+ void (*recvraw)(mrp_transport_t *t, void *data, size_t size,
+ void *user_data);
+ /** Registered data callback for connected transports. */
+ void (*recvdata)(mrp_transport_t *t, void *data, uint16_t tag,
+ void *user_data);
+ /** Custom encoded data callback for connected transports. */
+ void (*recvcustom)(mrp_transport_t *t, void *data,
+ void *user_data);
+ /** Native type callback for connected transports. */
+ void (*recvnative)(mrp_transport_t *t, void *data, uint32_t type_id,
+ void *user_data);
+ /** JSON type callback for connected transports. */
+ void (*recvjson)(mrp_transport_t *t, mrp_json_t *msg, void *user_data);
+ };
+
+ /** Message received on an unconnected transport. */
+ union {
+ /** Generic message callback for unconnected transports. */
+ void (*recvmsgfrom)(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Raw data callback for unconnected transports. */
+ void (*recvrawfrom)(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Registered data callback for unconnected transports. */
+ void (*recvdatafrom)(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Custom encoded data callback for unconnected transports. */
+ void (*recvcustomfrom)(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Native type callback for unconnected transports. */
+ void (*recvnativefrom)(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** JSON type callback for unconnected transports. */
+ void (*recvjsonfrom)(mrp_transport_t *t, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ };
+ /** Connection closed by peer. */
+ void (*closed)(mrp_transport_t *t, int error, void *user_data);
+ /** Connection attempt on a socket being listened on. */
+ void (*connection)(mrp_transport_t *t, void *user_data);
+} mrp_transport_evt_t;
+
+
+/*
+ * transport descriptor
+ */
+
+typedef struct {
+ const char *type; /* transport type name */
+ size_t size; /* full transport struct size */
+ mrp_transport_req_t req; /* transport requests */
+ socklen_t (*resolve)(const char *str, mrp_sockaddr_t *addr,
+ socklen_t addrlen, const char **typep);
+ mrp_list_hook_t hook; /* to list of registered transports */
+} mrp_transport_descr_t;
+
+
+/*
+ * transport
+ */
+
+#define MRP_TRANSPORT_PUBLIC_FIELDS \
+ mrp_mainloop_t *ml; \
+ mrp_transport_descr_t *descr; \
+ mrp_transport_evt_t evt; \
+ int (*check_destroy)(mrp_transport_t *t); \
+ int (*recv_data)(mrp_transport_t *t, void *data, \
+ size_t size, \
+ mrp_sockaddr_t *addr, \
+ socklen_t addrlen); \
+ void *user_data; \
+ mrp_typemap_t *map; \
+ int flags; \
+ int mode; \
+ int busy; \
+ int connected : 1; \
+ int listened : 1; \
+ int destroyed : 1 \
+
+
+struct mrp_transport_s {
+ MRP_TRANSPORT_PUBLIC_FIELDS;
+};
+
+
+/*
+ * Notes:
+ *
+ * Transports can get destructed in two slightly different ways.
+ *
+ * 1)
+ * Someone calls mrp_transport_destroy while the transport is
+ * idle, ie. with no callbacks or operations being active. This
+ * is simple and straightforward:
+ * - mrp_transport_destroy calls req.disconnect
+ * - mrp_transport_destroy calls req.close
+ * - mrp_transport_destroy check and sees the transport is idle
+ * so it frees the transport
+ *
+ * 2)
+ * Someone calls mrp_tansport_destroy while the transport is
+ * busy, ie. it has an unfinished callback or operation running.
+ * This typically happens when an operation or callback function,
+ * or a user function called from either of those calls
+ * mrp_transport_destroy as a result of a received message, or a
+ * (communication) error. In this case destroying the transport
+ * is less straightforward and needs to get delayed to avoid
+ * shooting out the transport underneath the active operation or
+ * callback.
+ *
+ * To handle the latter case, the generic (ie. top-level) transport
+ * layer has a member function check_destroy. This function checks
+ * for pending destroy requests and destroys the transport if it
+ * is not busy. All transport backends MUST CALL this function and
+ * CHECK ITS RETURN VALUE, whenever a user callback or a transport
+ * callback (ie. bottom-up event propagation) function invoked by
+ * the backend returns.
+ *
+ * If the transport has been left intact, check_destroy returns
+ * FALSE and processing can continue normally, taking into account
+ * that any transport state stored locally in the stack frame of the
+ * backend function might have changed during the callback. However,
+ * if check_destroy returns TRUE, it has nuked the transport and the
+ * backend MUST NOT touch or try to dereference the transport any more
+ * as its resources have already been released.
+ */
+
+
+/*
+ * convenience macros
+ */
+
+/**
+ * Macro to mark a transport busy while running a block of code.
+ *
+ * The backend needs to make sure the transport is not freed while a
+ * transport request or event callback function is active. Similarly,
+ * the backend needs to check if the transport has been marked for
+ * destruction whenever an event callback returns and trigger the
+ * destruction if it is necessary and possible (ie. the above criterium
+ * of not being active is fullfilled).
+ *
+ * These are the easiest to accomplish using the provided MRP_TRANSPORT_BUSY
+ * macro and the check_destroy callback member provided by mrp_transport_t.
+ *
+ * 1) Use the provided MRP_TRANSPORT_BUSY macro to enclose al blocks of
+ * code that invoke event callbacks. Do not do a return directly
+ * from within the enclosed call blocks, rather just set a flag
+ * within the block, check it after the block and do the return
+ * from there if necessary.
+ *
+ * 2) Call mrp_transport_t->check_destroy after any call to an event
+ * callback. check_destroy will check for any pending destroy
+ * request and perform the actual destruction if it is necessary
+ * and possible. If the transport has been left intact, check_destroy
+ * returns FALSE. However, if the transport has been destroyed and
+ * freed it returns TRUE, in which case the caller must not attempt
+ * to use or dereference the transport data structures any more.
+ */
+
+
+#ifndef __MRP_TRANSPORT_DISABLE_CODE_CHECK__
+# define W mrp_log_error
+# define __TRANSPORT_CHK_BLOCK(...) do { \
+ static int __checked = FALSE, __warned = FALSE; \
+ \
+ if (MRP_UNLIKELY(!__checked)) { \
+ __checked = TRUE; \
+ if (MRP_UNLIKELY(!__warned && \
+ strstr(#__VA_ARGS__, "return") != NULL)) { \
+ W("*********************** WARNING ********************"); \
+ W("* You seem to directly do a return from a block of *"); \
+ W("* code protected by MRP_TRANSPORT_BUSY. Are you *"); \
+ W("* absolutely sure you know what you are doing and *"); \
+ W("* that you are also doing it correctly ? *"); \
+ W("****************************************************"); \
+ W("The suspicious code block is located at: "); \
+ W(" %s@%s:%d", __FUNCTION__, __FILE__, __LINE__); \
+ W("and it looks like this:"); \
+ W("---------------------------------------------"); \
+ W("%s", #__VA_ARGS__); \
+ W("---------------------------------------------"); \
+ W("If you understand what MRP_TRANSPORT_BUSY does and"); \
+ W("how, and you are sure about the corretness of your"); \
+ W("code you can disable this error message by"); \
+ W("#defining __MRP_TRANSPORT_DISABLE_CODE_CHECK__"); \
+ W("when compiling %s.", __FILE__); \
+ __warned = TRUE; \
+ } \
+ } \
+ } while (0)
+#else
+# define __TRANSPORT_CHK_BLOCK(...) do { } while (0)
+#endif
+
+#define MRP_TRANSPORT_BUSY(t, ...) do { \
+ __TRANSPORT_CHK_BLOCK(__VA_ARGS__); \
+ (t)->busy++; \
+ __VA_ARGS__ \
+ (t)->busy--; \
+ } while (0)
+
+
+
+/** Automatically register a transport on startup. */
+#define MRP_REGISTER_TRANSPORT(_prfx, _typename, _structtype, _resolve, \
+ _open, _createfrom, _close, _setopt, \
+ _bind, _listen, _accept, \
+ _connect, _disconnect, \
+ _sendmsg, _sendmsgto, \
+ _sendraw, _sendrawto, \
+ _senddata, _senddatato, \
+ _sendcustom, _sendcustomto, \
+ _sendnative, _sendnativeto, \
+ _sendjson, _sendjsonto) \
+ static void _prfx##_register_transport(void) \
+ __attribute__((constructor)); \
+ \
+ static void _prfx##_register_transport(void) { \
+ static mrp_transport_descr_t descriptor = { \
+ .type = _typename, \
+ .size = sizeof(_structtype), \
+ .resolve = _resolve, \
+ .req = { \
+ .open = _open, \
+ .createfrom = _createfrom, \
+ .bind = _bind, \
+ .listen = _listen, \
+ .accept = _accept, \
+ .close = _close, \
+ .setopt = _setopt, \
+ .connect = _connect, \
+ .disconnect = _disconnect, \
+ .sendmsg = _sendmsg, \
+ .sendmsgto = _sendmsgto, \
+ .sendraw = _sendraw, \
+ .sendrawto = _sendrawto, \
+ .senddata = _senddata, \
+ .senddatato = _senddatato, \
+ .sendcustom = _sendcustom, \
+ .sendcustomto = _sendcustomto, \
+ .sendnative = _sendnative, \
+ .sendnativeto = _sendnativeto, \
+ .sendjson = _sendjson, \
+ .sendjsonto = _sendjsonto, \
+ }, \
+ }; \
+ \
+ if (!mrp_transport_register(&descriptor)) \
+ mrp_log_error("Failed to register transport '%s'.", \
+ _typename); \
+ else \
+ mrp_log_info("Registered transport '%s'.", _typename); \
+ } \
+ struct mrp_allow_trailing_semicolon
+
+
+
+/** Register a new transport type. */
+int mrp_transport_register(mrp_transport_descr_t *d);
+
+/** Unregister a transport. */
+void mrp_transport_unregister(mrp_transport_descr_t *d);
+
+/** Create a new transport. */
+mrp_transport_t *mrp_transport_create(mrp_mainloop_t *ml, const char *type,
+ mrp_transport_evt_t *evt,
+ void *user_data, int flags);
+
+/** Create a new transport from a backend object. */
+mrp_transport_t *mrp_transport_create_from(mrp_mainloop_t *ml, const char *type,
+ void *conn, mrp_transport_evt_t *evt,
+ void *user_data, int flags,
+ int state);
+
+/** Set a (possibly type-specific) transport option. */
+int mrp_transport_setopt(mrp_transport_t *t, const char *opt, const void *val);
+
+/** Resolve an address string to a transport-specific address. */
+socklen_t mrp_transport_resolve(mrp_transport_t *t, const char *str,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ const char **type);
+
+/** Bind a given transport to a transport-specific address. */
+int mrp_transport_bind(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+
+/** Listen for incoming connection on the given transport. */
+int mrp_transport_listen(mrp_transport_t *t, int backlog);
+
+/** Accept and create a new transport connection. */
+mrp_transport_t *mrp_transport_accept(mrp_transport_t *t,
+ void *user_data, int flags);
+
+/** Destroy a transport. */
+void mrp_transport_destroy(mrp_transport_t *t);
+
+/** Connect a transport to the given address. */
+int mrp_transport_connect(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+
+/** Disconnect a transport. */
+int mrp_transport_disconnect(mrp_transport_t *t);
+
+/** Send a message through the given (connected) transport. */
+int mrp_transport_send(mrp_transport_t *t, mrp_msg_t *msg);
+
+/** Send a message through the given transport to the remote address. */
+int mrp_transport_sendto(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send raw data through the given (connected) transport. */
+int mrp_transport_sendraw(mrp_transport_t *t, void *data, size_t size);
+
+/** Send raw data through the given transport to the remote address. */
+int mrp_transport_sendrawto(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send registered data through the given (connected) transport. */
+int mrp_transport_senddata(mrp_transport_t *t, void *data, uint16_t tag);
+
+/** Send registered data through the given transport to the remote address. */
+int mrp_transport_senddatato(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send custom data through the given (connected) transport. */
+int mrp_transport_sendcustom(mrp_transport_t *t, void *data);
+
+/** Send registered data through the given transport to the remote address. */
+int mrp_transport_sendcustomto(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send a native type through the given (connected) transport. */
+int mrp_transport_sendnative(mrp_transport_t *t, void *data, uint32_t type_id);
+
+/** Send a native type through the given transport to the remote address. */
+int mrp_transport_sendnativeto(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send a JSON message through the given (connected) transport. */
+int mrp_transport_sendjson(mrp_transport_t *t, mrp_json_t *msg);
+
+/** Send a JSON message through the given transport to the remote address. */
+int mrp_transport_sendjsonto(mrp_transport_t *t, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+MRP_CDECL_END
+
+#endif /* __MURPHY_TRANSPORT_H__ */
diff --git a/src/common/utils.c b/src/common/utils.c
new file mode 100644
index 0000000..8fd1f76
--- /dev/null
+++ b/src/common/utils.c
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/log.h>
+#include <murphy/common/utils.h>
+
+#define MSG_OK "OK"
+
+static int notify_parent(int fd, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = vdprintf(fd, fmt, ap);
+ va_end(ap);
+
+ return (len > 0);
+}
+
+
+int mrp_daemonize(const char *dir, const char *new_out, const char *new_err)
+{
+ pid_t pid;
+ int in, out, err;
+ char msg[1024];
+ int chnl[2], len;
+
+ /*
+ * create a pipe for communicating back the child status
+ */
+
+ if (pipe(chnl) == -1) {
+ mrp_log_error("Failed to create pipe to get child status (%d: %s).",
+ errno, strerror(errno));
+ return FALSE;
+ }
+
+
+ /*
+ * fork, change to our new working directory and create a new session
+ */
+
+ switch ((pid = fork())) {
+ case -1: /* failed */
+ mrp_log_error("Could not daemonize, fork failed (%d: %s).",
+ errno, strerror(errno));
+ return FALSE;
+
+ case 0: /* child */
+ close(chnl[0]);
+ break;
+
+ default: /* parent */
+ close(chnl[1]);
+
+ /*
+ * wait for and check the status report from the child
+ */
+
+ len = read(chnl[0], msg, sizeof(msg) - 1);
+
+ if (len > 0) {
+ msg[len] = '\0';
+
+ if (!strcmp(msg, MSG_OK)) {
+ mrp_log_info("Successfully daemonized.");
+ exit(0);
+ }
+ else
+ mrp_log_error("Daemonizing failed after fork: %s.", msg);
+ }
+ else
+ mrp_log_error("Daemonizing failed in forked child.");
+
+ return FALSE;
+ }
+
+
+ if (chdir(dir) != 0) {
+ mrp_log_error("Could not daemonize, failed to chdir to %s (%d: %s).",
+ dir, errno, strerror(errno));
+ return FALSE;
+ }
+
+ if (setsid() < 0) {
+ notify_parent(chnl[1], "Failed to create new session (%d: %s).",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+
+ /*
+ * fork again and redirect our stdin, stdout, and stderr
+ */
+
+ switch ((pid = fork())) {
+ case -1: /* failed */
+ notify_parent(chnl[1], "Could not daemonize, fork failed (%d: %s).",
+ errno, strerror(errno));
+ exit(1);
+
+ case 0: /* child */
+ break;
+
+ default: /* parent */
+ close(chnl[1]);
+ exit(0);
+ }
+
+
+ if ((in = open("/dev/null", O_RDONLY)) < 0) {
+ notify_parent(chnl[1], "Failed to open /dev/null (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if ((out = open(new_out, O_WRONLY)) < 0) {
+ notify_parent(chnl[1], "Failed to open %s (%d: %s).", new_out, errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if ((err = open(new_err, O_WRONLY)) < 0) {
+ notify_parent(chnl[1], "Failed to open %s (%d: %s).", new_err, errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (dup2(in, fileno(stdin)) < 0) {
+ notify_parent(chnl[1], "Failed to redirect stdin (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (dup2(out, fileno(stdout)) < 0) {
+ notify_parent(chnl[1], "Failed to redirect stdout (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (dup2(err, fileno(stderr)) < 0) {
+ notify_parent(chnl[1], "Failed to redirect stderr (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ close(in);
+ close(out);
+ close(err);
+
+ notify_parent(chnl[1], "%s", MSG_OK);
+
+ return TRUE;
+}
+
+
+int mrp_string_comp(const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+
+uint32_t mrp_string_hash(const void *key)
+{
+ uint32_t h;
+ const char *p;
+
+ for (h = 0, p = key; *p; p++) {
+ h <<= 1;
+ h ^= *p;
+ }
+
+ return h;
+}
diff --git a/src/common/utils.h b/src/common/utils.h
new file mode 100644
index 0000000..fef28a8
--- /dev/null
+++ b/src/common/utils.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_UTILS_H__
+#define __MURPHY_UTILS_H__
+
+#include <stdint.h>
+
+int mrp_daemonize(const char *dir, const char *new_out, const char *new_err);
+
+int mrp_string_comp(const void *key1, const void *key2);
+uint32_t mrp_string_hash(const void *key);
+
+#endif /* __MURPHY_UTILS_H__ */
diff --git a/src/common/websocket.c b/src/common/websocket.c
new file mode 100644
index 0000000..25d3dc8
--- /dev/null
+++ b/src/common/websocket.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/websocket.h>
+
+
+void mrp_websock_set_loglevel(mrp_websock_loglevel_t mask)
+{
+ wsl_set_loglevel(mask);
+}
+
+
+mrp_websock_context_t *mrp_websock_create_context(mrp_mainloop_t *ml,
+ mrp_websock_config_t *cfg)
+{
+ return wsl_create_context(ml, cfg);
+}
+
+
+mrp_websock_context_t *mrp_websock_ref_context(mrp_websock_context_t *ctx)
+{
+ return wsl_ref_context(ctx);
+}
+
+
+int mrp_websock_unref_context(mrp_websock_context_t *ctx)
+{
+ return wsl_unref_context(ctx);
+}
+
+
+mrp_websock_t *mrp_websock_connect(mrp_websock_context_t *ctx,
+ struct sockaddr *sa, const char *protocol,
+ mrp_wsl_ssl_t ssl, void *user_data)
+{
+ return wsl_connect(ctx, sa, protocol, ssl, user_data);
+}
+
+
+mrp_websock_t *mrp_websock_accept_pending(mrp_websock_context_t *ctx,
+ void *user_data)
+{
+ return wsl_accept_pending(ctx, user_data);
+}
+
+
+void mrp_websock_reject_pending(mrp_websock_context_t *ctx)
+{
+ wsl_reject_pending(ctx);
+}
+
+
+void *mrp_websock_close(mrp_websock_t *sck)
+{
+ return wsl_close(sck);
+}
+
+
+int mrp_websock_send(mrp_websock_t *sck, void *payload, size_t size)
+{
+ return wsl_send(sck, payload, size);
+}
+
+
+int mrp_websock_server_http_file(mrp_websock_t *sck, const char *path,
+ const char *mime)
+{
+ return wsl_serve_http_file(sck, path, mime);
+}
diff --git a/src/common/websocket.h b/src/common/websocket.h
new file mode 100644
index 0000000..cd380f2
--- /dev/null
+++ b/src/common/websocket.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_WEBSOCKET_H__
+#define __MURPHY_WEBSOCKET_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/websocklib.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * websocket types (mapped)
+ */
+
+typedef wsl_ctx_cfg_t mrp_websock_config_t;
+typedef wsl_ctx_t mrp_websock_context_t;
+typedef wsl_sck_t mrp_websock_t;
+typedef wsl_callbacks_t mrp_websock_evt_t;
+typedef wsl_proto_t mrp_websock_proto_t;
+typedef wsl_ssl_t mrp_wsl_ssl_t;
+
+/*
+ * websocket log levels (mapped)
+ */
+
+typedef enum {
+#define MAP(mrp, wsl) MRP_WEBSOCK_LOG_##mrp = WSL_LOG_##wsl
+ MAP(NONE , NONE),
+ MAP(ERROR , ERROR),
+ MAP(WARNING, WARNING),
+ MAP(INFO , INFO),
+ MAP(DEBUG , DEBUG),
+ MAP(ALL , ALL),
+ MAP(PARSER , PARSER),
+ MAP(EXT , EXT),
+ MAP(CLIENT , CLIENT),
+ MAP(EXTRA , EXTRA),
+ MAP(VERBOSE, VERBOSE)
+#undef MAP
+} mrp_websock_loglevel_t;
+
+
+
+/*
+ * websocket function prototypes
+ */
+
+/** Set websocket logging level. */
+void mrp_websock_set_loglevel(mrp_websock_loglevel_t mask);
+
+/** Create a websocket context. */
+mrp_websock_context_t *mrp_websock_create_context(mrp_mainloop_t *ml,
+ mrp_websock_config_t *cfg);
+
+/** Add a reference to a websocket context. */
+mrp_websock_context_t *mrp_websock_ref_context(mrp_websock_context_t *ctx);
+
+/** Remove a context reference. */
+int mrp_websock_unref_context(mrp_websock_context_t *ctx);
+
+/** Create and connect a websocket to a given address. */
+mrp_websock_t *mrp_websock_connect(mrp_websock_context_t *ctx,
+ struct sockaddr *sa, const char *protocol,
+ mrp_wsl_ssl_t ssl, void *user_data);
+
+/** Accept a pending connection of a context. */
+mrp_websock_t *mrp_websock_accept_pending(mrp_websock_context_t *ctx,
+ void *user_data);
+
+/** Reject a pending connection of a context. */
+void mrp_websock_reject_pending(mrp_websock_context_t *ctx);
+
+/** Close a websocket. Return the user_data of it's associated context. */
+void *mrp_websock_close(mrp_websock_t *sck);
+
+/** Send data over a connected websocket. */
+int mrp_websock_send(mrp_websock_t *sck, void *payload, size_t size);
+
+/** Serve the given file, with MIME type, over the given websocket. */
+int mrp_websock_server_http_file(mrp_websock_t *sck, const char *path,
+ const char *mime);
+
+MRP_CDECL_END
+
+
+#endif /* __MURPHY_WEBSOCKET_H__ */
diff --git a/src/common/websocklib.c b/src/common/websocklib.c
new file mode 100644
index 0000000..0595c46
--- /dev/null
+++ b/src/common/websocklib.c
@@ -0,0 +1,2105 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/fragbuf.h>
+#include <murphy/common/hashtbl.h>
+
+#include "websocklib.h"
+
+#define LWS_EVENT_OK 0 /* event handler result: ok */
+#define LWS_EVENT_DENY 1 /* event handler result: deny */
+#define LWS_EVENT_ERROR 1 /* event handler result: error */
+#define LWS_EVENT_CLOSE -1 /* event handler result: close */
+
+/* libwebsocket status used to close sockets upon error */
+#ifndef WEBSOCKETS_OLD
+# define LWS_INTERNAL_ERROR LWS_CLOSE_STATUS_UNEXPECTED_CONDITION
+#else
+# define LWS_INTERNAL_ERROR LWS_CLOSE_STATUS_PROTOCOL_ERR /* arrghmm... */
+#endif
+
+/* SSL modes */
+#define LWS_NO_SSL 0 /* no SSL at all */
+#define LWS_SSL 1 /* SSL, deny self-signed certs */
+#define LWS_SSL_SELFSIGNED 2 /* SSL, allow self-signed certs */
+
+/*
+ * define shorter aliasen for libwebsocket types
+ */
+
+typedef struct libwebsocket lws_t;
+typedef struct libwebsocket_context lws_ctx_t;
+typedef struct libwebsocket_extension lws_ext_t;
+typedef struct libwebsocket_protocols lws_proto_t;
+typedef enum libwebsocket_callback_reasons lws_event_t;
+#ifdef WEBSOCKETS_CONTEXT_INFO
+ typedef struct lws_context_creation_info lws_cci_t;
+#else /* !WEBSOCKETS_CONTEXT_INFO */
+typedef struct {
+ int port;
+ const char *iface;
+ struct libwebsocket_protocols *protocols;
+ struct libwebsocket_extension *extensions;
+ const char *ssl_cert_filepath;
+ const char *ssl_private_key_filepath;
+ const char *ssl_ca_filepath;
+ const char *ssl_cipher_list;
+ int gid;
+ int uid;
+ unsigned int options;
+ void *user;
+ int ka_time;
+ int ka_probes;
+ int ka_interval;
+} lws_cci_t;
+#endif /* !WEBSOCKETS_CONTEXT_INFO */
+
+static lws_ext_t *lws_get_internal_extensions(void);
+
+/*
+ * a libwebsocket fd we (e)poll
+ *
+ * Unfortunately the mechanism offered by libwebsockets for external
+ * mainloop integration uses event mask diffs when asking the mainloop
+ * to modify what an fd is polled for. This forces us to do double
+ * bookkeeping: we need to to keep track of the current event mask for
+ * all descriptors just to figure out the new mask when libwebsockets
+ * hands us a diff.
+ */
+
+typedef struct {
+ int fd; /* libwebsocket file descriptor */
+ uint32_t events; /* monitored (epoll) events */
+} pollfd_t;
+
+
+typedef enum {
+ POLLFD_SET = 0,
+ POLLFD_CLEAR = TRUE,
+ POLLFD_CHANGE,
+} pollfd_op_t;
+
+/*
+ * a websocket context
+ */
+
+struct wsl_ctx_s {
+ lws_ctx_t *ctx; /* libwebsocket context */
+ wsl_proto_t *http; /* has HTTP as upper layer protocol */
+ wsl_proto_t *protos; /* protocols */
+ int nproto; /* number of protocols */
+ lws_proto_t *lws_protos; /* libwebsocket protocols */
+ mrp_refcnt_t refcnt; /* reference count */
+ int epollfd; /* epoll descriptor */
+ mrp_io_watch_t *w; /* I/O watch for epollfd */
+ mrp_mainloop_t *ml; /* pumping mainloop */
+ pollfd_t *fds; /* polled descriptors */
+ int nfd; /* number descriptors */
+ void *user_data; /* opaque user data */
+ lws_t *pending; /* pending connection */
+ void *pending_user; /* user_data of pending */
+ wsl_proto_t *pending_proto; /* protocol of pending */
+ mrp_list_hook_t pure_http; /* pure HTTP sockets */
+};
+
+/*
+ * a websocket instance
+ */
+
+struct wsl_sck_s {
+ wsl_ctx_t *ctx; /* associated context */
+ lws_t *sck; /* libwebsocket instance */
+ wsl_proto_t *proto; /* protocol data */
+ wsl_sendmode_t send_mode; /* libwebsocket write mode */
+ mrp_fragbuf_t *buf; /* fragment collection buffer */
+ void *user_data; /* opaque user data */
+ wsl_sck_t **sckptr; /* back pointer from sck to us */
+ int closing : 1; /* close in progress */
+ int pure_http : 1; /* pure HTTP socket */
+ int busy; /* upper-layer callback(s) active */
+ mrp_list_hook_t hook; /* to pure HTTP list, if such */
+};
+
+
+/*
+ * mark a socket busy while executing a piece of code
+ */
+
+#define SOCKET_BUSY_REGION(sck, ...) do { \
+ (sck)->busy++; \
+ __VA_ARGS__; \
+ (sck)->busy--; \
+ } while (0)
+
+
+
+static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len);
+static int wsl_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len);
+static void destroy_context(wsl_ctx_t *ctx);
+
+static void MRP_EXIT destroy_context_table(void);
+
+
+
+static inline uint32_t map_poll_to_event(int in)
+{
+ uint32_t mask = 0;
+
+ if (in & POLLIN) mask |= MRP_IO_EVENT_IN;
+ if (in & POLLOUT) mask |= MRP_IO_EVENT_OUT;
+ if (in & POLLHUP) mask |= MRP_IO_EVENT_HUP;
+ if (in & POLLERR) mask |= MRP_IO_EVENT_ERR;
+
+ return mask;
+
+}
+
+
+static inline short map_event_to_poll(uint32_t in)
+{
+ short mask = 0;
+
+ if (in & MRP_IO_EVENT_IN) mask |= POLLIN;
+ if (in & MRP_IO_EVENT_OUT) mask |= POLLOUT;
+ if (in & MRP_IO_EVENT_HUP) mask |= POLLHUP;
+ if (in & MRP_IO_EVENT_ERR) mask |= POLLERR;
+
+ return mask;
+}
+
+
+static int add_fd(wsl_ctx_t *wsc, int fd, int events)
+{
+ struct epoll_event e;
+
+ if (wsc != NULL) {
+ e.data.u64 = 0;
+ e.data.fd = fd;
+ e.events = map_poll_to_event(events);
+
+ if (epoll_ctl(wsc->epollfd, EPOLL_CTL_ADD, fd, &e) == 0) {
+ if (mrp_reallocz(wsc->fds, wsc->nfd, wsc->nfd + 1) != NULL) {
+ wsc->fds[wsc->nfd].fd = fd;
+ wsc->fds[wsc->nfd].events = e.events;
+ wsc->nfd++;
+
+ return TRUE;
+ }
+ else
+ epoll_ctl(wsc->epollfd, EPOLL_CTL_DEL, fd, &e);
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int del_fd(wsl_ctx_t *wsc, int fd)
+{
+ struct epoll_event e;
+ int i;
+
+ if (wsc != NULL) {
+ e.data.u64 = 0;
+ e.data.fd = fd;
+ e.events = 0;
+ epoll_ctl(wsc->epollfd, EPOLL_CTL_DEL, fd, &e);
+
+ for (i = 0; i < wsc->nfd; i++) {
+ if (wsc->fds[i].fd == fd) {
+ if (i < wsc->nfd - 1)
+ memmove(wsc->fds + i, wsc->fds + i + 1,
+ (wsc->nfd - i - 1) * sizeof(*wsc->fds));
+
+ mrp_reallocz(wsc->fds, wsc->nfd, wsc->nfd - 1);
+ wsc->nfd--;
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static pollfd_t *find_fd(wsl_ctx_t *wsc, int fd)
+{
+ int i;
+
+ if (wsc != NULL) {
+ for (i = 0; i < wsc->nfd; i++)
+ if (wsc->fds[i].fd == fd)
+ return wsc->fds + i;
+ }
+
+ return NULL;
+}
+
+
+static int mod_fd(wsl_ctx_t *wsc, int fd, int events, int op)
+{
+ struct epoll_event e;
+ pollfd_t *wfd;
+
+ if (wsc != NULL) {
+ wfd = find_fd(wsc, fd);
+
+ if (wfd != NULL) {
+ e.data.u64 = 0;
+ e.data.fd = fd;
+
+ switch (op) {
+ case POLLFD_CLEAR:
+ e.events = wfd->events & ~map_poll_to_event(events);
+ break;
+ case POLLFD_SET:
+ e.events = wfd->events | map_poll_to_event(events);
+ break;
+ case POLLFD_CHANGE:
+ e.events = wfd->events = map_poll_to_event(events);
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (epoll_ctl(wsc->epollfd, EPOLL_CTL_MOD, fd, &e) == 0)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_fds(wsl_ctx_t *wsc)
+{
+ if (wsc != NULL) {
+ mrp_free(wsc->fds);
+ wsc->fds = NULL;
+ wsc->nfd = 0;
+ }
+}
+
+
+static void epoll_event(mrp_io_watch_t *w, int fd, mrp_io_event_t mask,
+ void *user_data)
+{
+ wsl_ctx_t *wsc = (wsl_ctx_t *)user_data;
+ pollfd_t *wfd;
+ struct epoll_event *events, *e;
+ int nevent, n, i;
+ struct pollfd pollfd;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(fd);
+
+ if (wsc->nfd <= 0 || !(mask & MRP_IO_EVENT_IN))
+ return;
+
+ nevent = wsc->nfd;
+ events = alloca(nevent * sizeof(*events));
+
+ while ((n = epoll_wait(wsc->epollfd, events, nevent, 0)) > 0) {
+ mrp_debug("got %d epoll events for websocket context %p", n, wsc);
+
+ for (i = 0, e = events; i < n; i++, e++) {
+ wfd = find_fd(wsc, e->data.fd);
+
+ if (wfd != NULL) {
+ pollfd.fd = wfd->fd;
+ pollfd.events = map_event_to_poll(wfd->events);
+ pollfd.revents = map_event_to_poll(e->events);
+
+ mrp_debug("delivering events 0x%x to websocket fd %d",
+ pollfd.revents, pollfd.fd);
+
+ libwebsocket_service_fd(wsc->ctx, &pollfd);
+ }
+ }
+ }
+}
+
+
+/*
+ * context handling
+ */
+
+#ifdef WEBSOCKETS_OLD
+
+/*
+ * Notes:
+ * In some environments we might be forced to run with really old
+ * versions of libwebsockets. This causes some amount of pain as
+ * some of the recent features in libwebsockets are essential for
+ * building a reasonable abstraction on top of it.
+ *
+ * Most notably, versions prior to 0291eb3..d764e84 (Oct 19 2012)
+ * do not support per-context user data. Since we need to associate
+ * our context with that of libwebsockets we have to build an extra
+ * mechanism for mapping between the two when user data support is
+ * not available. We use an extra hash table to store our context
+ * and use directly the (low 32-bits of the) libwebsocket context
+ * pointer as the key to store and fetch it.
+ *
+ * To minimize unreadibility and other code uglification factors,
+ * most of the code that deals with this version-dependent extra
+ * mechanism is put into the contamination chamber formed by the
+ * triplet of {set,get,clear}_context_userdata routines below.
+ *
+ * Note that since we decided (maybe unecessarily) to do mainloop
+ * integration also on a per-context basis, with old versions we
+ * also have a phase error: the first pollfd manipulation events
+ * for a context being created come __before__ the libwebsockets
+ * context creation routine returns, IOW before we get a chance to
+ * administer the reverse mapping between the contexts. This is
+ * unfortunate because if we cannot find our context for a pollfd
+ * request/event we just cannot handle it. This in turn would result
+ * in a practically useless context as its socket would not be
+ * (e)polled at all.
+ *
+ * The ugly pending_userdata kludge below is to overcome this problem.
+ * While creating a context, we register its intended userdata as the
+ * pending userdata before calling libwebsockets context creation
+ * routine and clear it afterwards. get_context_userdata knows to
+ * return the pending_userdata if it cannot do the reverse mapping
+ * using the libwebsocket context pointer. While this is quite ugly,
+ * I really don't see any other way. A limitation of this is that we
+ * cannot have two unfinished contexts being created in parallel, but
+ * that really should not happen under any circumstances anyway.
+ */
+
+
+static mrp_htbl_t *ctxtbl;
+static void *pending_userdata;
+
+static int ctx_cmp(const void *ctx1, const void *ctx2)
+{
+ return ctx2 - ctx1;
+}
+
+static uint32_t ctx_hash(const void *ctx)
+{
+ uint64_t h;
+
+ h = (ptrdiff_t)ctx;
+
+ return (uint32_t)(h & 0xffffffff);
+}
+
+static int create_context_table(void)
+{
+ mrp_htbl_config_t hcfg;
+
+ if (ctxtbl == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = ctx_cmp;
+ hcfg.hash = ctx_hash;
+ hcfg.free = NULL;
+
+ ctxtbl = mrp_htbl_create(&hcfg);
+
+ return (ctxtbl != NULL);
+ }
+ else
+ return TRUE;
+}
+
+static void destroy_context_table(void)
+{
+ if (ctxtbl != NULL)
+ mrp_htbl_destroy(ctxtbl, FALSE);
+}
+
+
+static int set_pending_userdata(void *ptr)
+{
+ if (pending_userdata == NULL) {
+ pending_userdata = ptr;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void clear_pending_userdata(void *ptr)
+{
+ if (pending_userdata == ptr)
+ pending_userdata = NULL;
+}
+
+
+static void *get_pending_userdata(void)
+{
+ return pending_userdata;
+}
+
+
+static int set_context_userdata(lws_ctx_t *ws_ctx, wsl_ctx_t *ctx)
+{
+ if (ctxtbl == NULL)
+ create_context_table();
+
+ if (ctxtbl != NULL)
+ return mrp_htbl_insert(ctxtbl, ws_ctx, ctx);
+ else
+ return FALSE;
+}
+
+
+static wsl_ctx_t *get_context_userdata(lws_ctx_t *ws_ctx)
+{
+ wsl_ctx_t *ctx;
+
+ if (ctxtbl != NULL)
+ ctx = (wsl_ctx_t *)mrp_htbl_lookup(ctxtbl, (void *)ws_ctx);
+ else
+ ctx = NULL;
+
+ if (ctx != NULL)
+ return ctx;
+ else
+ return get_pending_userdata();
+}
+
+
+static void clear_context_userdata(lws_ctx_t *ws_ctx)
+{
+ if (ctxtbl != NULL)
+ mrp_htbl_remove(ctxtbl, ws_ctx, FALSE);
+}
+
+
+static lws_ctx_t *lws_create_ctx(lws_cci_t *cci)
+{
+ lws_ctx_t *ws_ctx;
+
+ set_pending_userdata(cci->user);
+
+ ws_ctx = libwebsocket_create_context(cci->port, cci->iface,
+ cci->protocols, cci->extensions,
+ cci->ssl_cert_filepath,
+ cci->ssl_private_key_filepath,
+ /* no ssl_ca */
+ cci->gid, cci->uid, cci->options
+ /*no user_data*/);
+ if (ws_ctx != NULL)
+ set_context_userdata(ws_ctx, cci->user);
+
+ clear_pending_userdata(cci->user);
+
+ return ws_ctx;
+}
+
+#else /* !WEBSOCKETS_OLD */
+
+static void destroy_context_table(void)
+{
+ return;
+}
+
+
+static wsl_ctx_t *get_context_userdata(lws_ctx_t *ws_ctx)
+{
+ return libwebsocket_context_user(ws_ctx);
+}
+
+
+static void clear_context_userdata(lws_ctx_t *ws_ctx)
+{
+ MRP_UNUSED(ws_ctx);
+}
+
+
+static lws_ctx_t *lws_create_ctx(lws_cci_t *cci)
+{
+#ifdef WEBSOCKETS_CONTEXT_INFO
+ return libwebsocket_create_context(cci);
+#else
+ return libwebsocket_create_context(cci->port, cci->iface,
+ cci->protocols, cci->extensions,
+ cci->ssl_cert_filepath,
+ cci->ssl_private_key_filepath,
+ cci->ssl_ca_filepath,
+ cci->gid, cci->uid, cci->options,
+ cci->user);
+#endif
+}
+
+#endif /* !WEBSOCKETS_OLD */
+
+static lws_ext_t *lws_get_internal_extensions(void)
+{
+#ifdef WEBSOCKETS_QUERY_EXTENSIONS
+ return libwebsocket_get_internal_extensions();
+#else
+ return libwebsocket_internal_extensions;
+#endif
+}
+
+static int find_device(struct sockaddr *sa, char *buf, size_t size)
+{
+ struct sockaddr *ia;
+ struct ifreq ifreq[64];
+ struct ifconf ifconf;
+ int status, sck, n, i;
+
+ /*
+ * XXX FIXME: we only handle primary addresses at the moment...
+ */
+
+ if (size < IFNAMSIZ) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ if (sa->sa_family != AF_INET) { /* libwebsockets can't handle IPv6 */
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ if (((struct sockaddr_in *)sa)->sin_addr.s_addr == 0x0) {
+ *buf = '\0';
+ return 0;
+ }
+
+ sck = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (sck < 0)
+ return -1;
+
+ ifconf.ifc_len = sizeof(ifreq);
+ ifconf.ifc_buf = (char *)&ifreq[0];
+
+ status = ioctl(sck, SIOCGIFCONF, &ifconf);
+ close(sck);
+
+ if (status < 0)
+ return -1;
+
+ n = ifconf.ifc_len / sizeof(ifreq[0]);
+
+ for (i = 0; i < n; i++) {
+ ia = &ifreq[i].ifr_addr;
+
+ if (ia->sa_family == sa->sa_family) {
+ if (((struct sockaddr_in *)sa)->sin_addr.s_addr ==
+ ((struct sockaddr_in *)ia)->sin_addr.s_addr) {
+ strncpy(buf, ifreq[i].ifr_name, IFNAMSIZ - 1);
+ buf[IFNAMSIZ - 1] = '\0';
+ return 0;
+ }
+ }
+ }
+
+ errno = EADDRNOTAVAIL;
+ return -1;
+}
+
+
+wsl_ctx_t *wsl_create_context(mrp_mainloop_t *ml, wsl_ctx_cfg_t *cfg)
+{
+ lws_ext_t *builtin = lws_get_internal_extensions();
+ lws_cci_t cci;
+ wsl_ctx_t *ctx;
+ wsl_proto_t *up, *http;
+ lws_proto_t *lws_protos, *lp;
+ int lws_nproto;
+ mrp_io_event_t events;
+ char ifname[IFNAMSIZ + 1];
+ int i;
+
+ ctx = NULL;
+ mrp_clear(&cci);
+
+ if (cfg->addr != NULL) {
+ if (find_device(cfg->addr, ifname, sizeof(ifname)) < 0)
+ return NULL;
+ else {
+ mrp_debug("address mapped to device '%s'",
+ *ifname ? ifname : "<any>");
+
+ cci.iface = ifname;
+
+ switch (cfg->addr->sa_family) {
+ case AF_INET:
+ cci.port = ntohs(((struct sockaddr_in *)cfg->addr)->sin_port);
+ break;
+ case AF_INET6:
+ cci.port = ntohs(((struct sockaddr_in6 *)cfg->addr)->sin6_port);
+ break;
+ default:
+ goto fail;
+ }
+ }
+ }
+
+ ctx = mrp_allocz(sizeof(*ctx));
+
+ if (ctx == NULL)
+ goto fail;
+
+ mrp_refcnt_init(&ctx->refcnt);
+ mrp_list_init(&ctx->pure_http);
+
+ ctx->protos = cfg->protos;
+ ctx->nproto = cfg->nproto;
+
+ if (!strcmp(cfg->protos[0].name, "http") ||
+ !strcmp(cfg->protos[0].name, "http-only"))
+ http = &cfg->protos[0];
+ else
+ http = NULL;
+
+ lws_nproto = (http ? cfg->nproto : cfg->nproto + 1) + 1;
+ lws_protos = mrp_allocz_array(lws_proto_t, lws_nproto);
+
+ if (lws_protos == NULL)
+ goto fail;
+
+ lws_protos[0].name = "http";
+ lws_protos[0].callback = http_event;
+ if (!http)
+ lws_protos[0].per_session_data_size = sizeof(void *);
+ else
+ lws_protos[0].per_session_data_size = sizeof(void *);
+
+ lp = lws_protos + 1;
+ up = cfg->protos + (http ? 1 : 0);
+
+ for (i = (http ? 1 : 0); i < cfg->nproto; i++) {
+ lp->name = up->name;
+ lp->callback = wsl_event;
+ lp->per_session_data_size = sizeof(void *);
+
+ lp++;
+ up++;
+ }
+
+ ctx->lws_protos = lws_protos;
+ ctx->http = http;
+
+ ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
+
+ if (ctx->epollfd < 0)
+ goto fail;
+
+ events = MRP_IO_EVENT_IN;
+ ctx->ml = ml;
+ ctx->w = mrp_add_io_watch(ml, ctx->epollfd, events, epoll_event, ctx);
+
+ if (ctx->w == NULL)
+ goto fail;
+
+ cci.protocols = lws_protos;
+ cci.extensions = builtin;
+ cci.user = ctx;
+ cci.gid = cfg->gid;
+ cci.uid = cfg->uid;
+
+ cci.ssl_cert_filepath = cfg->ssl_cert;
+ cci.ssl_private_key_filepath = cfg->ssl_pkey;
+ cci.ssl_ca_filepath = cfg->ssl_ca;
+ cci.ssl_cipher_list = cfg->ssl_ciphers;
+
+ cci.options = 0;
+ cci.ka_time = cfg->timeout;
+ cci.ka_probes = cfg->nprobe;
+ cci.ka_interval = cfg->interval;
+
+ ctx->ctx = lws_create_ctx(&cci);
+
+ if (ctx->ctx != NULL) {
+ ctx->user_data = cfg->user_data;
+
+ return ctx;
+ }
+
+ fail:
+ if (ctx != NULL) {
+ if (ctx->epollfd >= 0) {
+ mrp_del_io_watch(ctx->w);
+ close(ctx->epollfd);
+ }
+
+ mrp_free(ctx);
+ }
+
+ return NULL;
+}
+
+
+wsl_ctx_t *wsl_ref_context(wsl_ctx_t *ctx)
+{
+ return mrp_ref_obj(ctx, refcnt);
+}
+
+
+int wsl_unref_context(wsl_ctx_t *ctx)
+{
+ if (mrp_unref_obj(ctx, refcnt)) {
+ mrp_debug("refcount of context %p dropped to zero", ctx);
+ destroy_context(ctx);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void destroy_context(wsl_ctx_t *ctx)
+{
+ if (ctx != NULL) {
+ mrp_debug("destroying context %p", ctx);
+
+ mrp_del_io_watch(ctx->w);
+ ctx->w = NULL;
+
+ close(ctx->epollfd);
+ ctx->epollfd = -1;
+
+ purge_fds(ctx);
+
+ if (ctx->ctx != NULL) {
+ clear_context_userdata(ctx->ctx);
+ libwebsocket_context_destroy(ctx->ctx);
+ }
+
+ mrp_free(ctx->lws_protos);
+ mrp_free(ctx);
+ }
+}
+
+
+static wsl_proto_t *find_context_protocol(wsl_ctx_t *ctx, const char *protocol)
+{
+ wsl_proto_t *up;
+ int i;
+
+ if (protocol != NULL) {
+ for (i = 0, up = ctx->protos; i < ctx->nproto; i++, up++)
+ if (!strcmp(up->name, protocol))
+ return up;
+ }
+
+ return NULL;
+}
+
+
+static wsl_sck_t *find_pure_http(wsl_ctx_t *ctx, lws_t *ws)
+{
+ mrp_list_hook_t *p, *n;
+ wsl_sck_t *sck;
+
+ /*
+ * Notes:
+ * We expect an extremely low number of concurrent pure
+ * HTTP connections so we do asimple linear search here.
+ * We can change this if this turns out to be a false
+ * assumption.
+ */
+
+ mrp_list_foreach(&ctx->pure_http, p, n) {
+ sck = mrp_list_entry(p, typeof(*sck), hook);
+
+ if (sck->sck == ws)
+ return sck;
+ }
+
+ return NULL;
+}
+
+
+wsl_sck_t *wsl_connect(wsl_ctx_t *ctx, struct sockaddr *sa,
+ const char *protocol, wsl_ssl_t ssl, void *user_data)
+{
+ wsl_sck_t *sck, **ptr;
+ wsl_proto_t *up;
+ int port;
+ void *aptr;
+ char abuf[256];
+ const char *astr;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ aptr = &((struct sockaddr_in *)sa)->sin_addr;
+ port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+ break;
+ case AF_INET6:
+ aptr = &((struct sockaddr_in6 *)sa)->sin6_addr;
+ port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+
+ astr = inet_ntop(sa->sa_family, aptr, abuf, sizeof(abuf));
+
+ if (astr == NULL)
+ return NULL;
+
+ up = find_context_protocol(ctx, protocol);
+
+ if (up == NULL) {
+ errno = ENOPROTOOPT;
+ return NULL;
+ }
+
+ sck = mrp_allocz(sizeof(*sck));
+ ptr = mrp_allocz(sizeof(*ptr));
+
+ if (sck != NULL && ptr != NULL) {
+ /*
+ * Now we need to create and connect a new libwebsocket instance
+ * within the given context. We also need to set up a one-to-one
+ * mapping between the underlying libwebsocket and our wsl_sck_t
+ * so that we can handle both top-down (sending) and bottom-up
+ * (receiving) event propagation in the stack.
+ *
+ * We use the user data associated with the libwebsocket instance
+ * to store a back pointer to us. Whenever the socket instance
+ * is deleted locally (as opposed to our peer closing the session)
+ * we need to prevent the propagation of any potentially pending
+ * events to our deleted wsl_sck_t (which might have been freed).
+ * This we do by clearing the back pointer from the instance to us.
+ *
+ * However, since libwebsockets does not provide an API for this,
+ * as a trick we use an indirect back pointer and store a pointer
+ * to the actual back pointer also in wsl_sck_t here. This way we
+ * can always clear the back pointer when we need to.
+ *
+ * Also note, that memory management for the associated user data
+ * is asymmetric in many sense. For client connections, we allocate
+ * the data buffer and pass it on to libwebsockets. For incoming
+ * connections the user data buffer is allocated by libwebsockets
+ * and we only get a chance to fill it in the event handler for
+ * connection establishment. However, for both incoming and outgoing
+ * connections libwebsockets will free the buffer on behalf of us.
+ *
+ * The exact same notes apply to wsl_accept_pending below...
+ */
+
+ mrp_list_init(&sck->hook);
+ sck->ctx = wsl_ref_context(ctx);
+ sck->proto = up;
+ sck->buf = mrp_fragbuf_create(/*up->framed*/TRUE, 0);
+
+ if (sck->buf != NULL) {
+ sck->user_data = user_data;
+
+ if (strncmp(protocol, "http", 4)) { /* Think harder, Homer ! */
+ *ptr = sck;
+ sck->sckptr = ptr;
+ }
+ else
+ mrp_list_append(&ctx->pure_http, &sck->hook);
+
+ sck->sck = libwebsocket_client_connect_extended(ctx->ctx,
+ astr, port,
+ ssl,
+ "/", astr, astr,
+ protocol, -1,
+ ptr);
+
+ if (sck->sck != NULL)
+ return sck;
+
+ mrp_fragbuf_destroy(sck->buf);
+ mrp_list_delete(&sck->hook);
+ }
+
+ wsl_unref_context(ctx);
+ mrp_free(ptr);
+ mrp_free(sck);
+ }
+
+ return NULL;
+}
+
+
+wsl_sck_t *wsl_accept_pending(wsl_ctx_t *ctx, void *user_data)
+{
+ wsl_sck_t *sck, **ptr;
+
+ if (ctx->pending == NULL || ctx->pending_proto == NULL)
+ return NULL;
+
+ mrp_debug("accepting pending websocket connection %p/%p", ctx->pending,
+ ctx->pending_user);
+
+ sck = mrp_allocz(sizeof(*sck));
+
+ if (sck != NULL) {
+ mrp_list_init(&sck->hook);
+
+ /*
+ * Notes:
+ * The same notes apply here for context creation as for
+ * wsl_connect above...
+ */
+ sck->ctx = wsl_ref_context(ctx);
+ sck->buf = mrp_fragbuf_create(/*ctx->pending_proto->framed*/TRUE, 0);
+
+ if (sck->buf != NULL) {
+ sck->proto = ctx->pending_proto;
+ sck->user_data = user_data;
+ sck->sck = ctx->pending;
+ ptr = (wsl_sck_t **)ctx->pending_user;
+ sck->sckptr = ptr;
+
+ mrp_debug("pending connection was a %s websocket",
+ ptr != NULL ? "real" : "HTTP");
+
+ if (ptr != NULL) /* genuine websocket */
+ *ptr = sck;
+ else /* pure http socket */
+ mrp_list_append(&ctx->pure_http, &sck->hook);
+
+ /* let the event handler know we accepted the client */
+ ctx->pending = NULL;
+ /* for pure http communicate sck back in pending_user */
+ ctx->pending_user = (ptr == NULL ? sck : NULL);
+ ctx->pending_proto = NULL;
+
+ return sck;
+ }
+
+ wsl_unref_context(ctx);
+ mrp_free(sck);
+ }
+
+ return NULL;
+}
+
+
+void wsl_reject_pending(wsl_ctx_t *ctx)
+{
+ mrp_debug("reject pending websocket (%s) connection %p/%p",
+ ctx->pending_proto->name, ctx->pending, ctx->pending_user);
+
+ /*
+ * Nothing to do here really... just don't clear ctx->pending so the
+ * event handler will know to reject once it regains control.
+ */
+}
+
+
+#ifdef WEBSOCKETS_CLOSE_SESSION
+
+/*
+ * WTF ? The prototype for this has been moved from libwebsockets.h to
+ * the uninstalled private-libwebsockets.h. If this is really going to
+ * be made private eventually, how is one supposed to close a websocket
+ * without closing its context and a side effect all other websockets
+ * associated with the same context ?
+ */
+extern void libwebsocket_close_and_free_session(struct libwebsocket_context *,
+ struct libwebsocket *,
+ enum lws_close_status);
+
+
+void *wsl_close(wsl_sck_t *sck)
+{
+ wsl_ctx_t *ctx;
+ void *user_data;
+ int status;
+
+ user_data = NULL;
+
+ if (sck != NULL) {
+ if (sck->sck != NULL && sck->busy <= 0) {
+ mrp_debug("closing websocket %p/%p", sck, sck->sck);
+
+ status = LWS_CLOSE_STATUS_NORMAL;
+ ctx = sck->ctx;
+
+ sck->closing = TRUE;
+ libwebsocket_close_and_free_session(ctx->ctx, sck->sck, status);
+ sck->sck = NULL;
+
+ if (sck->sckptr != NULL) /* genuine websocket */
+ *sck->sckptr = NULL;
+ else /* pure http socket */
+ mrp_list_delete(&sck->hook);
+
+ if (ctx != NULL) {
+ user_data = ctx->user_data;
+ wsl_unref_context(ctx);
+ sck->ctx = NULL;
+ }
+
+ mrp_fragbuf_destroy(sck->buf);
+ sck->buf = NULL;
+
+ mrp_debug("freeing websocket %p", sck);
+ mrp_free(sck);
+ }
+ else {
+ mrp_debug("marking websocket %p/%p for closing", sck, sck->sck);
+ sck->closing = TRUE;
+ }
+ }
+
+ return user_data;
+}
+
+
+#else /* !WEBSOCKET_CLOSE_SESSION */
+
+void *wsl_close(wsl_sck_t *sck)
+{
+ lws_ctx_t *ws_ctx;
+ lws_t *ws;
+ void *user_data;
+
+ /*
+ * With recent libwebsockets libwebsocket_close_and_free_session has
+ * been fully turned into a private library symbol. According to the
+ * docs the official way to trigger closing a websocket from the
+ * 'upper layers' (ie. outside of libwebsocket event callbacks) is to
+ * 1) administer the fact that the websocket should be closed
+ * 2) enable pollouts for the websocket (callback_on_writable)
+ * 3) hope that libwebsockets will not decide to omit delivering a
+ * LWS_CALLBACK_{CLIENT,SERVER}_WRITEABLE event, and
+ * 4) in the event callback check if the websocket is marked for
+ * deletion, and if it is reutrn -1 to indicate libwebsockets that
+ * it should close the socket
+ * Hmm... I guess simple elegance was not one of the design principles.
+ *
+ * Anyway, here's our second attempt to implement this indirect socket
+ * closing scheme without too much memory corruption and leaks... Argh.
+ *
+ * Notes: XXX TODO
+ * Currently we only check and handle pending deletion when
+ * dealing with *_WRITEABLE events. Probably we should also do
+ * it for a few other events as well, for instance for *_RECEIVE
+ * and *_CALLBACK_HTTP).
+ */
+
+ user_data = NULL;
+
+ if (sck != NULL) {
+ if (sck->sck != NULL && sck->busy <= 0) {
+ mrp_debug("closing %s websocket %p/%p",
+ sck->sckptr ? "real" : "HTTP", sck->sck, sck);
+
+ ws = sck->sck;
+ sck->sck = NULL;
+ sck->closing = TRUE;
+
+ /* clear the back pointer to us */
+ if (sck->sckptr != NULL)
+ *sck->sckptr = NULL;
+ else
+ mrp_list_delete(&sck->hook);
+
+ if (sck->ctx != NULL) {
+ ws_ctx = sck->ctx->ctx;
+ user_data = sck->ctx->user_data;
+ wsl_unref_context(sck->ctx);
+ sck->ctx = NULL;
+ }
+ else
+ ws_ctx = NULL;
+
+ mrp_fragbuf_destroy(sck->buf);
+ sck->buf = NULL;
+
+ mrp_debug("freeing websocket %p", sck);
+ mrp_free(sck);
+
+ if (ws_ctx != NULL)
+ libwebsocket_callback_on_writable(ws_ctx, ws);
+ }
+ else
+ sck->closing = TRUE;
+ }
+
+ return user_data;
+}
+
+
+#endif /* !WEBSOCKET_CLOSE_SESSION */
+
+
+static int check_closed(wsl_sck_t *sck)
+{
+ if (sck != NULL) {
+ if (sck->closing && sck->busy <= 0) {
+ wsl_close(sck);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int wsl_set_sendmode(wsl_sck_t *sck, wsl_sendmode_t mode)
+{
+ const char *name;
+
+ switch (mode) {
+ case WSL_SEND_TEXT: name = "text"; break;
+ case WSL_SEND_BINARY: name = "binary"; break;
+ default: return FALSE;
+ }
+
+ mrp_debug("websocket %p/%p mode changed to %s", sck, sck->sck, name);
+ sck->send_mode = mode;
+
+ return TRUE;
+}
+
+
+int wsl_send(wsl_sck_t *sck, void *payload, size_t size)
+{
+ unsigned char *buf;
+ size_t pre, post, total;
+ uint32_t *len;
+
+ if (sck != NULL && sck->sck != NULL) {
+ if (sck->proto->framed) {
+ pre = LWS_SEND_BUFFER_PRE_PADDING;
+ post = LWS_SEND_BUFFER_POST_PADDING;
+ buf = alloca(pre + sizeof(*len) + size + post);
+ len = (uint32_t *)(buf + pre);
+ *len = htobe32(size);
+
+ memcpy(buf + pre + sizeof(*len), payload, size);
+ total = sizeof(*len) + size;
+ }
+ else {
+ pre = LWS_SEND_BUFFER_PRE_PADDING;
+ post = LWS_SEND_BUFFER_POST_PADDING;
+ buf = alloca(pre + size + post);
+
+ memcpy(buf + pre, payload, size);
+ total = size;
+ }
+
+#if (WSL_SEND_TEXT != 0)
+ if (!sck->send_mode)
+ sck->send_mode = WSL_SEND_TEXT;
+#endif
+
+ if (libwebsocket_write(sck->sck, buf + pre, total, sck->send_mode) >= 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int wsl_serve_http_file(wsl_sck_t *sck, const char *path, const char *type)
+{
+ mrp_debug("serving file '%s' (%s) over websocket %p", path, type, sck->sck);
+
+#ifndef WEBSOCKETS_OLD
+# ifdef WEBSOCKETS_SERVE_FILE_EXTRAARG
+ if (libwebsockets_serve_http_file(sck->ctx->ctx, sck->sck, path,
+ type, NULL) == 0)
+ return TRUE;
+ else
+ return FALSE;
+# else
+ if (libwebsockets_serve_http_file(sck->ctx->ctx, sck->sck, path, type) == 0)
+ return TRUE;
+ else
+ return FALSE;
+# endif
+#else
+ if (libwebsockets_serve_http_file(sck->sck, path, type) == 0)
+ return TRUE;
+ else
+ return FALSE;
+#endif
+}
+
+
+#ifdef LWS_OPENSSL_SUPPORT
+
+static void load_extra_certs(wsl_ctx_t *ctx, void *user, lws_event_t event)
+{
+ int is_server;
+
+ if (ctx != NULL && ctx->load_certs != NULL) {
+ if (event == LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS)
+ is_server = TRUE;
+ else
+ is_server = FALSE;
+
+ ctx->load_certs(ctx, (SSL_CTX *)user, is_server);
+ }
+}
+
+
+static int verify_client_cert(void *user, void *in, size_t len)
+{
+ X509_STORE_CTX *x509_ctx;
+ SSL *ssl;
+ int pre_ok;
+
+ if (verify_client_cert_cb != NULL) {
+ x509_ctx = (X509_STORE_CTX *)user;
+ ssl = (SSL *)in;
+ pre_ok = (int)len;
+
+ if (verify_client_cert_cb(x509_ctx, ssl, pre_ok))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+#else /* !LWS_OPENSSL_SUPPORT */
+
+static void load_extra_certs(wsl_ctx_t *ctx, void *user, lws_event_t event)
+{
+ MRP_UNUSED(ctx);
+ MRP_UNUSED(user);
+ MRP_UNUSED(event);
+
+ return;
+}
+
+
+static int verify_client_cert(void *user, void *in, size_t len)
+{
+ MRP_UNUSED(user);
+ MRP_UNUSED(in);
+ MRP_UNUSED(len);
+
+ return TRUE;
+}
+
+#endif
+
+
+
+static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len)
+{
+ wsl_ctx_t *ctx = get_context_userdata(ws_ctx);
+ wsl_sck_t *sck;
+ wsl_proto_t *up;
+ const char *ext, *uri;
+ int fd, mask, status, accepted;
+
+ switch (event) {
+ case LWS_CALLBACK_ESTABLISHED:
+ mrp_debug("client-handshake completed on websocket %p/%p", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLOSED:
+ mrp_debug("websocket %p/%p closed", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ mrp_debug("server-handshake completed on websocket %p/%p", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ mrp_debug("client connection failed");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_RECEIVE:
+ mrp_debug("received HTTP data from client");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ mrp_debug("recived HTTP data from server");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
+ mrp_debug("client received pong");
+ return LWS_EVENT_OK;
+
+ /*
+ * mainloop integration
+ */
+#ifdef WEBSOCKETS_CHANGE_MODE_POLL_FD
+
+ case LWS_CALLBACK_ADD_POLL_FD: {
+ struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+ fd = pa->fd;
+ mask = pa->events;
+
+ mrp_debug("start polling fd %d for events 0x%x", fd, mask);
+ if (add_fd(ctx, fd, mask))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+ }
+
+ case LWS_CALLBACK_DEL_POLL_FD: {
+ struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+ fd = pa->fd;
+
+ mrp_debug("stop polling fd %d", fd);
+ if (del_fd(ctx, fd))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+ }
+
+ case LWS_CALLBACK_CHANGE_MODE_POLL_FD: {
+ struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+ fd = pa->fd;
+ mask = pa->events;
+
+ mrp_debug("setting poll events to 0x%x for fd %d", mask, fd);
+ if (mod_fd(ctx, fd, mask, FALSE))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+ }
+
+#else /* WEBSOCKETS_CHANGE_MODE_POLL_FD */
+
+ case LWS_CALLBACK_ADD_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mask = (int)len;
+ mrp_debug("start polling fd %d for events 0x%x", fd, mask);
+ if (add_fd(ctx, fd, mask))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+ case LWS_CALLBACK_DEL_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mrp_debug("stop polling fd %d", fd);
+ if (del_fd(ctx, fd))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+ case LWS_CALLBACK_SET_MODE_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mask = (int)len;
+ mrp_debug("enable poll events 0x%x for fd %d", mask, fd);
+ if (mod_fd(ctx, fd, mask, FALSE))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+ case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mask = (int)len;
+ mrp_debug("disable poll events 0x%x for fd %d", mask, fd);
+ if (mod_fd(ctx, fd, mask, TRUE))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+#endif /* WEBSOCKETS_CHANGE_MODE_POLL_FD */
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = find_pure_http(ctx, ws);
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket server side writeable again");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = find_pure_http(ctx, ws);
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket client side writeable again");
+ return LWS_EVENT_OK;
+
+ /*
+ * clients wanting to stay pure HTTP clients
+ *
+ * Notes:
+ * Clients that stay pure HTTP clients (ie. do not negotiate a
+ * websocket connection) never get an LWS_CALLBACK_ESTABLISHED
+ * event emitted for. This is a bit unfortunate, since that is
+ * the event we map to the incoming connection event of our
+ * transport layer.
+ *
+ * However, we'd really like to keep pure HTTP and websocket
+ * connections as much equal as possible. First and foremost
+ * this means that we'd like to associate our own websocklib
+ * wsl_sck_t socket context to lws_t and vice versa. Also
+ * similarly to websocket connections we want to give the upper
+ * layer a chance to accept or reject the connection.
+ *
+ * Since there is no ESTABLISHED event for pure HTTP clients,
+ * we have to emulate one such here. We need to check if test
+ * ws belongs to a known connection by checking if it has an
+ * associated wsl_sck_t. If not we need to call the upper layer
+ * to let it accept or reject the connection. If it has already
+ * we need to call the reception handler of the upper layer.
+ *
+ * However, unfortunately libwebsockets never allocates user
+ * data for the HTTP websockets even we specify a non-zero size
+ * for protocol 0. Hence, we cannot use our normal mechanism of
+ * associating the upper layer wsl_sck_t context using the ws
+ * user data. Instead we need to separately keep track of HTTP
+ * websockets and look up the associated wsl_sck_t using this
+ * secondary bookkeeping.
+ */
+
+
+#ifdef WEBSOCKETS_FILTER_HTTP_CONNECTION
+ case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
+ return 0;
+#endif
+
+ case LWS_CALLBACK_HTTP:
+ uri = (const char *)in;
+
+ if (ctx->http == NULL) {
+ mrp_debug("denying HTTP request of '%s' for httpless context", uri);
+ return LWS_EVENT_DENY;
+ }
+
+ sck = find_pure_http(ctx, ws);
+
+ if (sck != NULL) { /* known socket, deliver event */
+ deliver_event:
+ up = sck->proto;
+
+ if (up != NULL) {
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.recv(sck, in, strlen(uri), sck->user_data,
+ up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ sck = find_pure_http(ctx, ws);
+
+ if (check_closed(sck))
+ return 0;
+ }
+
+ status = LWS_EVENT_OK;
+ }
+ else { /* unknown socket, needs to accept */
+ if (ctx->pending != NULL) {
+ mrp_log_error("Multiple pending connections, rejecting.");
+ return LWS_EVENT_DENY;
+ }
+
+ up = ctx->http;
+
+ ctx->pending = ws;
+ ctx->pending_user = NULL;
+ ctx->pending_proto = up;
+
+ wsl_ref_context(ctx);
+ up->cbs.connection(ctx, "XXX TODO dig out peer address", up->name,
+ ctx->user_data, up->proto_data);
+ sck = ctx->pending_user;
+ ctx->pending_user = NULL;
+
+ /* XXX TODO
+ * check if sockets gets properly closed and freed if
+ * cb->connection calls close on the 'listening' websocket in
+ * the transport layer...
+ */
+
+ accepted = (ctx->pending == NULL);
+ wsl_unref_context(ctx);
+
+ if (accepted)
+ goto deliver_event;
+ else
+ status = LWS_EVENT_DENY;
+ }
+
+ return status;
+
+#ifndef WEBSOCKETS_OLD
+ case LWS_CALLBACK_HTTP_FILE_COMPLETION:
+ uri = (const char *)in;
+ if (uri != NULL)
+ mrp_debug("serving '%s' over HTTP completed", uri);
+ else
+ mrp_debug("serving HTTP content completed");
+
+ sck = find_pure_http(ctx, ws);
+
+ if (sck != NULL) { /* known socket, deliver event */
+ up = sck->proto;
+
+ if (up != NULL) {
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.http_done(sck, in, sck->user_data,
+ up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ sck = find_pure_http(ctx, ws);
+
+ if (check_closed(sck))
+ return 0;
+ }
+
+ status = LWS_EVENT_OK;
+ }
+
+ return LWS_EVENT_OK;
+#endif
+
+ /*
+ * events always routed to protocols[0]
+ *
+ * XXX TODO: we need to open up for the upper layers using
+ * optionally settable wsl_ctx_t-level callbacks at least
+ *
+ * FILTER_NETWORK_CONNECTION
+ * FILTER_PROTOCOL_CONNECTION
+ * OPENSSL_*
+ *
+ * Probably for the sake of completeness we should open up
+ * all of these...
+ */
+
+ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+ fd = (ptrdiff_t)user;
+ /* we don't filter based on the socket/address */
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+ /* we don't filter based on headers */
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
+ load_extra_certs(ctx, user, event);
+ return LWS_EVENT_OK;
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (ctx != NULL && ctx->load_certs != NULL)
+ ctx->load_certs(ctx, user, FALSE);
+#endif
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
+ load_extra_certs(ctx, user, TRUE);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
+ if (verify_client_cert(user, in, len))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_DENY;
+
+ case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
+ /* no extra headers we'd like to add */
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
+ ext = (const char *)in;
+ /* deny all extensions on the server side */
+ mrp_debug("denying server extension '%s'", ext);
+ return LWS_EVENT_DENY;
+
+ case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
+ ext = (const char *)in;
+ /* deny all extensions on the client side */
+ mrp_debug("denying client extension '%s'", ext);
+ return LWS_EVENT_DENY;
+
+ default:
+ break;
+ }
+
+ return LWS_EVENT_DENY;
+}
+
+
+static int wsl_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len)
+{
+ wsl_ctx_t *ctx = get_context_userdata(ws_ctx);
+ wsl_sck_t *sck;
+ wsl_proto_t *up;
+ void *data;
+ size_t size;
+ uint32_t total;
+ const char *ext;
+ lws_proto_t *proto;
+ int status;
+
+ MRP_UNUSED(ext);
+ MRP_UNUSED(ws_ctx);
+
+ switch (event) {
+ case LWS_CALLBACK_ESTABLISHED:
+ mrp_debug("client-handshake completed on websocket %p/%p", ws, user);
+
+ /*
+ * Connection acceptance is a bit tricky. Once libwebsockets
+ * has completed its handshaking phase with the client it lets
+ * us know about a new established connection. This is what we
+ * want to map to an incoming connection attempt. Since we don't
+ * want to know about the internals of the upper layer, neither
+ * want the upper layer to know about our internals, the only
+ * way to pass information about the connection around in the
+ * context at this point.
+ *
+ * To keep things simple we only prepare and handle once
+ * outstanding connection attemp at a time. This is equivalent
+ * to listening on a stream-socket with a backlog of 1. Since we
+ * run single-threaded it shouldn't ever be possible to have more
+ * than one pending connection if the upper layer does things
+ * right but we do check for this and reject multiple pending
+ * connections here...
+ *
+ * We store the pending websocket instance and its associated
+ * user data in the context then call the connection notifier
+ * callback. If the upper layer wants to accept the connection
+ * it calls wsl_accept_pending. That in turn digs these out from
+ * the context to set up and hook together things properly. If all
+ * goes fine wsl_accept_pending clears pending and pending_user
+ * from the context. If something fails or the upper layer decides
+ * not to accept the connection, pending and pending_user stay
+ * intact in which case we'll reject the client here once the
+ * callback returns.
+ */
+
+ if (ctx->pending != NULL) {
+ mrp_log_error("Multiple pending connections, rejecting.");
+ return LWS_EVENT_DENY;
+ }
+
+
+ proto = (lws_proto_t *)libwebsockets_get_protocol(ws);
+ up = find_context_protocol(ctx, proto->name);
+
+ if (up == NULL) {
+ mrp_debug("unknown protocol '%s' requested, rejecting",
+ proto ? proto->name : "<none>");
+ return LWS_EVENT_DENY;
+ }
+ else
+ mrp_debug("found descriptor %p for protocol '%s'", up, up->name);
+
+ ctx->pending = ws;
+ ctx->pending_user = user;
+ ctx->pending_proto = up;
+
+ wsl_ref_context(ctx);
+ up->cbs.connection(ctx, "XXX TODO dig out peer address", up->name,
+ ctx->user_data, up->proto_data);
+
+ /* XXX TODO
+ * check if sockets gets properly closed and freed if
+ * cb->connection calls close on the 'listening' websocket in
+ * the transport layer...
+ */
+
+ if (ctx->pending == NULL) /* connection accepted */
+ status = LWS_EVENT_OK;
+ else /* connection rejected */
+ status = LWS_EVENT_DENY;
+ wsl_unref_context(ctx);
+
+ return status;
+
+ case LWS_CALLBACK_CLOSED:
+ proto = (lws_proto_t *)libwebsockets_get_protocol(ws);
+ up = find_context_protocol(ctx, proto->name);
+ mrp_debug("websocket %p/%p (%s) closed", ws, user,
+ up ? up->name : "<unknown>");
+
+ sck = *(wsl_sck_t **)user;
+ up = sck ? sck->proto : NULL;
+
+ if (up != NULL) {
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.closed(sck, 0, sck->user_data, up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ check_closed(sck);
+ }
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ mrp_debug("server-handshake completed on websocket %p/%p", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ mrp_debug("client connection failed");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_RECEIVE:
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ mrp_debug("%zu bytes received on websocket %p/%p", len, ws, user);
+ mrp_debug("%zd remaining from this message",
+ libwebsockets_remaining_packet_payload(ws));
+
+ sck = *(wsl_sck_t **)user;
+ up = sck ? sck->proto : NULL;
+
+ if (up != NULL) {
+ if (!up->framed && !mrp_fragbuf_missing(sck->buf)) {
+ /* new packet of an unframed protocol, push message size */
+ total = len + libwebsockets_remaining_packet_payload(ws);
+ mrp_debug("unframed protocol, total message size %u", total);
+
+ total = htobe32(total);
+ mrp_fragbuf_push(sck->buf, &total, sizeof(total));
+ }
+
+ if (mrp_fragbuf_push(sck->buf, in, len)) {
+ data = NULL;
+ size = 0;
+
+ while (mrp_fragbuf_pull(sck->buf, &data, &size)) {
+ mrp_debug("websocket %p/%p has a message of %zd bytes",
+ ws, user, size);
+
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.recv(sck, data, size, sck->user_data,
+ up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ if (check_closed(sck))
+ break;
+ }
+ }
+ else {
+ mrp_log_error("failed to push data to fragment buffer");
+
+ SOCKET_BUSY_REGION(sck, {
+ wsl_close(sck);
+#if 0 /*
+ * XXX Hmm... calling wsl_close instead of this now. Should be tested
+ * if that really works.
+ */
+ sck->closing = TRUE; /* make sure sck gets closed */
+ up->cbs.closed(sck, ENOBUFS, sck->user_data,
+ up->proto_data);
+ libwebsocket_close_and_free_session(ctx->ctx, sck->sck,
+ LWS_INTERNAL_ERROR);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+#endif
+ });
+
+ check_closed(sck);
+ return -1;
+ }
+ }
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = *(wsl_sck_t **)user;
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket server side writeable again");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = *(wsl_sck_t **)user;
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket client side writeable again");
+ return LWS_EVENT_OK;
+
+ default:
+ break;
+ }
+
+ return LWS_EVENT_OK;
+}
+
+
+
+/*
+ * logging
+ */
+
+#ifndef WEBSOCKETS_OLD
+
+#ifdef WEBSOCKETS_LOG_WITH_LEVEL
+static void libwebsockets(int level, const char *line)
+#else
+static void libwebsockets(const char *line)
+#endif
+{
+ const char *ts, *ll;
+ const char *b, *e, *lvl;
+ int l, ls;
+ uint32_t mask;
+
+#ifdef WEBSOCKETS_LOG_WITH_LEVEL
+ MRP_UNUSED(level);
+#else
+ /*
+ * If our (shaky) configure-time check gives a false negative,
+ * we'll expect only line but will be passed both level and line.
+ * Try catching it instead of crashing on it here...
+ */
+ if (line < (const char *)(LLL_CLIENT << 3))
+ return;
+#endif
+
+ if ((mask = mrp_log_get_mask()) == 0)
+ return;
+
+ /*
+ * Notes:
+ * libwebsockets logging infrastructure has independently maskable
+ * log classes and supports overriding its default logger. The log
+ * classes are the regular error, warning, info, and debug classes
+ * plus the libwebsockets-specific parser, header, extension, and
+ * client classes. The logging infra filters the messages based on
+ * their class, then formats the message and passes it on to the
+ * (default builtin, or externally set) logger function. This gets
+ * a fully formatted log message that consists of a timestamp, a
+ * log class prefix and the message itself which typically contains
+ * at least one terminating newline.
+ *
+ * Because of the semantic content of the messages coming from
+ * libwebsockets we'd like to preserve the class of errors and
+ * warnings but convert the rest to debug messages. Additionally,
+ * we'd like to keep the message format as consistent with the
+ * murphy infra as possible with a reasonable effort. This means
+ * stripping the timestamp and log class, as these are provided
+ * by the murphy infra (if configured so). However, for the
+ * libwebsockets-specific parser-, header-, extension-, and client-
+ * classes we want to keep the extra information carried by the
+ * log class as part of the message.
+ *
+ * Because the libwebsockets log messages are terminated by '\n',
+ * we also prepare here to properly bridge multiline messages to
+ * the murphy infra (although I'm not sure the library ever issues
+ * such messages).
+ *
+ * So to sum it up the necessary steps to bridge messages here are:
+ * 1) strip timestamp,
+ * 2) dig out and strip log class
+ * 3) map log class to murphy infra, ie.
+ * keep errors and warnings, squash the rest to debug
+ * 4) break multiline messages to lines
+ * 5) pass each line on to the murphy infra,
+ * for parser-, header-, extension-, and client-messages
+ * prefix each line with the class
+ *
+ */
+
+ lvl = "???";
+ ls = 3;
+
+ ts = strchr(line, '[');
+ ll = ts != NULL ? strchr(ts, ']') : NULL;
+
+ /* strip timestamp, dig out log level, find beginning of the message */
+ if (ll != NULL && ll[1] == ' ') {
+ ll += 2;
+ b = strchr(ll, ':');
+
+ if (b != NULL && b[1] == ' ') {
+ b += 2;
+
+ while (*b == ' ')
+ b++;
+
+ /* map log level: debug, info, err, warn, or other */
+ switch (*ll) {
+ case 'D':
+ if (!(mask & MRP_LOG_MASK_DEBUG))
+ return;
+ lvl = "d";
+ break;
+ case 'I':
+ if (!(mask & MRP_LOG_MASK_INFO))
+ return;
+ lvl = "i";
+ break;
+ case 'W':
+ if (!(mask & MRP_LOG_MASK_WARNING))
+ return;
+ lvl = "w";
+ break;
+ case 'E':
+ if (ll[1] == 'R') {
+ if (!(mask & MRP_LOG_MASK_ERROR))
+ return;
+ lvl = "e";
+ }
+ else {
+ other:
+ if (!(mask & MRP_LOG_MASK_DEBUG))
+ return;
+ lvl = ll;
+ e = strchr(lvl, ':');
+
+ if (e != NULL)
+ ls = e - lvl;
+ else {
+ lvl = "???:";
+ ls = 4;
+ }
+ }
+ break;
+
+ default:
+ goto other;
+ }
+ }
+ else
+ goto unknown;
+ }
+ else {
+ unknown:
+ /* if we get confused with the format, default to logging it all */
+ lvl = NULL;
+ b = line;
+ }
+
+#ifdef WEBSOCKETS_LOG_WITH_LEVEL
+ switch (level) {
+ case LLL_ERR: lvl = "e"; ls = 0; break;
+ case LLL_WARN: lvl = "w"; ls = 0; break;
+ case LLL_INFO: lvl = "i"; ls = 0; break;
+ case LLL_DEBUG: lvl = "d"; ls = 0; break;
+ case LLL_NOTICE: lvl = "d"; ls = 0; break;
+ case LLL_PARSER: lvl = "parser" ; ls = 6; break;
+ case LLL_HEADER: lvl = "header" ; ls = 6; break;
+ case LLL_EXT: lvl = "ext" ; ls = 3; break;
+ case LLL_CLIENT: lvl = "client" ; ls = 6; break;
+ case LLL_LATENCY: lvl = "latency"; ls = 7; break;
+ default: lvl = "???" ; ls = 3; break;
+ }
+
+ b = line;
+ while (*b == ' ' || *b == '\t')
+ b++;
+#endif
+
+ /* break the message to lines and pass it on to the murphy infra */
+ e = strchr(b, '\n');
+ while (e || b) {
+ if (e)
+ l = e - b;
+ else
+ l = strlen(b);
+
+ if (!l)
+ break;
+
+ switch (lvl[0] | (lvl[1] << 8)) {
+ case 'd': mrp_debug("%*.*s", l, l, b); break;
+ case 'i': mrp_debug("%*.*s", l, l, b); break;
+ case 'w': mrp_log_warning("libwebsockets: %*.*s", l, l, b); break;
+ case 'e': mrp_log_error("libwebsockets: %*.*s", l, l, b); break;
+ default: mrp_debug("[%*.*s] %*.*s", ls, ls, lvl, l, l, b);
+ }
+
+ if (e != NULL) {
+ b = e + 1;
+ e = strchr(b, '\n');
+ }
+ else
+ b = NULL;
+ }
+}
+
+
+void wsl_set_loglevel(wsl_loglevel_t mask)
+{
+ lws_set_log_level(mask, libwebsockets);
+}
+
+
+#else /* WEBSOCKETS_OLD */
+
+void wsl_set_loglevel(wsl_loglevel_t mask)
+{
+ MRP_UNUSED(mask);
+
+ mrp_log_warning("libwebsockets too old to redirect logs...");
+}
+
+#endif /* WEBSOCKETS_OLD */
diff --git a/src/common/websocklib.h b/src/common/websocklib.h
new file mode 100644
index 0000000..83dbb95
--- /dev/null
+++ b/src/common/websocklib.h
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_WEBSOCKLIB_H__
+#define __MURPHY_WEBSOCKLIB_H__
+
+#include <sys/socket.h>
+
+#include <libwebsockets.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * websocket context
+ *
+ * A websocket context is basically a libwebsocket_context plus the
+ * additional glue data and code necessary to integrate the context
+ * into our mainloop. For our transport abstraction, we create one
+ * context per transport instance. However, accepted transports do
+ * share their context with the listening transport (ie. the server-
+ * side libwebsocket) they were accepted on.
+ *
+ * XXX TODO We probably need to change this so that we create one
+ * context per address/port (or in libwebsockets case device/port).
+ *
+ */
+
+typedef struct wsl_ctx_s wsl_ctx_t;
+
+
+/*
+ * websocket
+ *
+ * A websocket is a libwebsocket instance together with its
+ * associated websocket context.
+ */
+typedef struct wsl_sck_s wsl_sck_t;
+
+
+/*
+ * websocket event callbacks to the upper transport layer
+ *
+ * These callbacks are used to deliver events from the underlying
+ * websocket transport layer to the upper murphy transport layer.
+ */
+typedef struct {
+ /** Connection attempt on a websocket. */
+ void (*connection)(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data);
+ /** Websocket connection closed by peer. */
+ void (*closed)(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data);
+ /** Data received on websocket. */
+ void (*recv)(wsl_sck_t *sck, void *data, size_t size, void *user_data,
+ void *proto_data);
+ /** Check if transport should be destroyed. */
+ int (*check)(wsl_sck_t *sck, void *user_data, void *proto_data);
+
+ /** HTTP (content) request completed. */
+ void (*http_done)(wsl_sck_t *sck, const char *uri, void *user_data,
+ void *proto_data);
+
+#ifdef LWS_OPENSSL_SUPPORT
+ /** Load extra client or server certificates, if necessary. */
+ void (*load_certs)(wsl_ctx_t *ctx, SSL_CTX *ssl, int is_server);
+#else
+ void (*load_certs)(wsl_ctx_t *, void *, int);
+#endif
+} wsl_callbacks_t;
+
+
+/*
+ * websocket protocol
+ *
+ * A websocket protocol is a protocol name together with protocol-specific
+ * upper-layer callbacks.
+ */
+typedef struct {
+ const char *name; /* protocol name */
+ wsl_callbacks_t cbs; /* event/request callbacks */
+ int framed; /* whether a framed protocol */
+ void *proto_data; /* protocol-specific user data */
+} wsl_proto_t;
+
+
+/*
+ * websocket write modes
+ */
+
+typedef enum {
+ WSL_SEND_TEXT = LWS_WRITE_TEXT, /* text mode */
+ WSL_SEND_BINARY = LWS_WRITE_BINARY, /* binary/blob mode */
+#if 0
+ WSL_SEND_HTTP = LWS_WRITE_HTTP /* HTTP mode */
+#endif
+
+#define WSL_SEND_TEXT WSL_SEND_TEXT
+
+} wsl_sendmode_t;
+
+
+/*
+ * logging levels
+ */
+
+#ifndef WEBSOCKETS_OLD
+
+typedef enum {
+ WSL_LOG_NONE = 0x0,
+ WSL_LOG_ERROR = LLL_ERR,
+ WSL_LOG_WARNING = LLL_WARN,
+ WSL_LOG_INFO = LLL_INFO,
+ WSL_LOG_DEBUG = LLL_DEBUG,
+ WSL_LOG_ALL = LLL_ERR | LLL_WARN | LLL_INFO | LLL_DEBUG,
+ WSL_LOG_PARSER = LLL_PARSER,
+ WSL_LOG_HEADER = LLL_HEADER,
+ WSL_LOG_EXT = LLL_EXT,
+ WSL_LOG_CLIENT = LLL_CLIENT,
+ WSL_LOG_EXTRA = LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT,
+ WSL_LOG_VERBOSE = WSL_LOG_ALL | WSL_LOG_EXTRA
+} wsl_loglevel_t;
+
+#else /* !WEBSOCKETS_OLD */
+
+typedef enum {
+ WSL_LOG_NONE = 0x0,
+ WSL_LOG_ERROR = 0x0,
+ WSL_LOG_WARNING = 0x0,
+ WSL_LOG_INFO = 0x0,
+ WSL_LOG_DEBUG = 0x0,
+ WSL_LOG_ALL = 0x0,
+ WSL_LOG_PARSER = 0x0,
+ WSL_LOG_HEADER = 0x0,
+ WSL_LOG_EXT = 0x0,
+ WSL_LOG_CLIENT = 0x0,
+ WSL_LOG_EXTRA = 0x0,
+ WSL_LOG_VERBOSE = 0x0,
+} wsl_loglevel_t;
+
+#endif /* !WEBSOCKETS_OLD */
+
+typedef enum {
+ WSL_NO_SSL = 0, /* plain connection, no SSL */
+ WSL_SSL = 1, /* SSL, deny self-signed certs */
+ WSL_SSL_SELFSIGNED = 2, /* SSL, allow self-signed certs */
+} wsl_ssl_t;
+
+
+/*
+ * websockets context configuration
+ */
+
+#define WSL_NO_GID -1
+#define WSL_NO_UID -1
+
+typedef struct {
+ struct sockaddr *addr; /* address/port to listen on */
+ wsl_proto_t *protos; /* protocols to serve */
+ int nproto; /* number of protocols */
+ const char *ssl_cert; /* SSL certificate path */
+ const char *ssl_pkey; /* SSL private key path */
+ const char *ssl_ca; /* SSL CA path */
+ const char *ssl_ciphers; /* SSL cipher list */
+ int gid; /* group ID to change to, or -1 */
+ int uid; /* user ID to change to, or -1 */
+ void *user_data; /* opaque user data */
+ int timeout; /* keepalive timeout */
+ int nprobe; /* number of keepalive probes */
+ int interval; /* keepalive probe interval */
+} wsl_ctx_cfg_t;
+
+
+/** Set libwebsock logging level _and_ redirect to murphy logging infra. */
+void wsl_set_loglevel(wsl_loglevel_t mask);
+
+/** Create a websocket context. */
+wsl_ctx_t *wsl_create_context(mrp_mainloop_t *ml, wsl_ctx_cfg_t *cfg);
+
+/** Add a reference to a context. */
+wsl_ctx_t *wsl_ref_context(wsl_ctx_t *ctx);
+
+/** Remove a context reference, destroying it once the last is gone. */
+int wsl_unref_context(wsl_ctx_t *ctx);
+
+/** Create a new websocket connection using a given protocol. */
+wsl_sck_t *wsl_connect(wsl_ctx_t *ctx, struct sockaddr *sa,
+ const char *protocol, wsl_ssl_t ssl, void *user_data);
+
+/** Accept a pending connection. */
+wsl_sck_t *wsl_accept_pending(wsl_ctx_t *ctx, void *user_data);
+
+/** Reject a pending connection. */
+void wsl_reject_pending(wsl_ctx_t *ctx);
+
+/** Close a websocket connection. Return user_data of the associated context. */
+void *wsl_close(wsl_sck_t *sck);
+
+/** Set websocket write mode (binary or text). */
+int wsl_set_sendmode(wsl_sck_t *sck, wsl_sendmode_t mode);
+
+/** Send data over a wbesocket. */
+int wsl_send(wsl_sck_t *sck, void *payload, size_t size);
+
+/** Serve the given file over the given socket. */
+int wsl_serve_http_file(wsl_sck_t *sck, const char *path, const char *mime);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_WEBSOCKLIB_H__ */
diff --git a/src/common/wsck-transport.c b/src/common/wsck-transport.c
new file mode 100644
index 0000000..fe44271
--- /dev/null
+++ b/src/common/wsck-transport.c
@@ -0,0 +1,995 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <arpa/inet.h>
+
+#include <libwebsockets.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/json.h>
+
+#include "websocklib.h"
+#include "wsck-transport.h"
+
+#define WSCKP "wsck" /* websocket transport prefix */
+#define WSCKL 4 /* websocket transport prefix length */
+
+
+/*
+ * a websocket transport instance
+ */
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ wsl_ctx_t *ctx; /* websocket context */
+ wsl_sck_t *sck; /* websocket instance */
+ int send_mode; /* websocket send mode */
+ const char *http_root; /* HTTP content root */
+ mrp_wsck_urimap_t *uri_table; /* URI-to-path table */
+ mrp_wsck_mimemap_t *mime_table; /* suffix to MIME-type table */
+ const char *ssl_cert; /* path to SSL certificate */
+ const char *ssl_pkey; /* path to SSL private key */
+ const char *ssl_ca; /* path to SSL CA */
+ wsl_ssl_t ssl; /* SSL mode (wsl_ssl_t) */
+ char *protocol; /* websocket protocol name */
+ wsl_proto_t proto[2]; /* protocol setup */
+ mrp_list_hook_t http_clients; /* pure HTTP clients */
+} wsck_t;
+
+
+/*
+ * a pure HTTP client instance
+ */
+
+typedef struct {
+ wsl_sck_t *sck; /* websocket towards client */
+ mrp_list_hook_t hook; /* hook to listening socket */
+ const char *http_root; /* HTTP content root */
+ mrp_wsck_urimap_t *uri_table; /* URI to path mapping */
+ mrp_wsck_mimemap_t *mime_table; /* suffix to MIME type mapping */
+} http_client_t;
+
+
+/*
+ * default file suffix to MIME type mapping table
+ */
+
+static mrp_wsck_mimemap_t mime_table[] = {
+ { "js" , "application/javascript" },
+ { "html", "text/html" },
+ { "htm ", "text/html" },
+ { "txt" , "text/plain" },
+ { NULL, NULL }
+};
+
+
+static int resolve_address(const char *str, mrp_wsckaddr_t *wa, socklen_t alen);
+
+static void connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data);
+static void closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data);
+static void recv_cb(wsl_sck_t *sck, void *data, size_t size, void *user_data,
+ void *proto_data);
+static int check_cb(wsl_sck_t *sck, void *user_data, void *proto_data);
+
+static void http_connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data);
+static void http_closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data);
+static void http_req_cb(wsl_sck_t *sck, void *data, size_t size,
+ void *user_data, void *proto_data);
+static int http_check_cb(wsl_sck_t *sck, void *user_data, void *proto_data);
+static void http_done_cb(wsl_sck_t *sck, const char *uri, void *user_data,
+ void *proto_data);
+
+static socklen_t wsck_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ mrp_wsckaddr_t *wa = (mrp_wsckaddr_t *)addr;
+ socklen_t len;
+
+ len = resolve_address(str, wa, size);
+
+ if (len <= 0)
+ return 0;
+ else {
+ if (typep != NULL)
+ *typep = WSCKP;
+
+ return len;
+ }
+}
+
+
+static int wsck_open(mrp_transport_t *mt)
+{
+ wsck_t *t = (wsck_t *)mt;
+
+ mrp_list_init(&t->http_clients);
+ wsl_set_loglevel(WSL_LOG_ALL/* | WSL_LOG_EXTRA*/);
+
+ return TRUE;
+}
+
+
+static int wsck_createfrom(mrp_transport_t *mt, void *conn)
+{
+ wsck_t *t = (wsck_t *)mt;
+
+ MRP_UNUSED(conn);
+
+ mrp_list_init(&t->http_clients);
+
+ return FALSE;
+}
+
+
+static void wsck_close(mrp_transport_t *mt)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_ctx_t *ctx = t->ctx;
+ wsl_sck_t *sck = t->sck;
+ void *user_data;
+
+ t->sck = NULL;
+ t->ctx = NULL;
+ mrp_free(t->protocol);
+ t->protocol = NULL;
+
+ user_data = wsl_close(sck);
+
+ if (user_data == t) /* was our associated context */
+ wsl_unref_context(ctx);
+}
+
+
+static int wsck_setopt(mrp_transport_t *mt, const char *opt, const void *val)
+{
+ wsck_t *t = (wsck_t *)mt;
+ int success;
+
+ if (!strcmp(opt, MRP_WSCK_OPT_SENDMODE) && val != NULL) {
+ if (!strcmp(val, "binary"))
+ t->send_mode = WSL_SEND_BINARY;
+ else if (!strcmp(val, "text"))
+ t->send_mode = WSL_SEND_TEXT;
+ else
+ return FALSE;
+
+ if (t->sck != NULL)
+ return wsl_set_sendmode(t->sck, t->send_mode);
+ else
+ return TRUE;
+ }
+
+ success = TRUE;
+
+ if (!strcmp(opt, MRP_WSCK_OPT_HTTPDIR))
+ t->http_root = val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_MIMEMAP))
+ t->mime_table = (void *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_URIMAP))
+ t->uri_table = (void *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL_CERT))
+ t->ssl_cert = (const char *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL_PKEY))
+ t->ssl_pkey = (const char *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL_CA))
+ t->ssl_ca = (const char *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL))
+ t->ssl = *(wsl_ssl_t *)val;
+ else
+ success = FALSE;
+
+ return success;
+}
+
+
+static int wsck_bind(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_proto_t proto[] = {
+ {
+ .name = "http",
+ .cbs = { .connection = http_connection_cb,
+ .closed = http_closed_cb,
+ .recv = http_req_cb,
+ .check = http_check_cb,
+ .http_done = http_done_cb,
+ .load_certs = NULL, },
+ .framed = FALSE,
+ .proto_data = NULL
+ },
+ {
+ .name = "murphy",
+ .cbs = { .connection = connection_cb,
+ .closed = closed_cb,
+ .recv = recv_cb,
+ .check = check_cb,
+ .http_done = NULL,
+ .load_certs = NULL, },
+ .framed = FALSE,
+ .proto_data = NULL
+ }
+ };
+ wsl_ctx_cfg_t cfg;
+ mrp_wsckaddr_t *wa;
+ struct sockaddr *sa;
+
+ if (addr->any.sa_family != MRP_AF_WSCK || addrlen != sizeof(*wa))
+ return FALSE;
+
+ if (t->ctx != NULL)
+ return FALSE;
+
+ wa = (mrp_wsckaddr_t *)addr;
+
+ switch (wa->wsck_addr.family) {
+ case AF_INET: sa = (struct sockaddr *)&wa->wsck_addr.v4; break;
+ case AF_INET6: sa = (struct sockaddr *)&wa->wsck_addr.v6; break;
+ default:
+ errno = EAFNOSUPPORT;
+ return FALSE;
+ }
+
+ if ((t->protocol = mrp_strdup(wa->wsck_proto)) == NULL)
+ return FALSE;
+
+ t->proto[0] = proto[0];
+ t->proto[1] = proto[1];
+
+ t->proto[1].name = t->protocol;
+
+ mrp_clear(&cfg);
+ cfg.addr = sa;
+ cfg.protos = &t->proto[0];
+ cfg.nproto = MRP_ARRAY_SIZE(t->proto);
+ cfg.ssl_cert = t->ssl_cert;
+ cfg.ssl_pkey = t->ssl_pkey;
+ cfg.ssl_ca = t->ssl_ca;
+ cfg.gid = WSL_NO_GID;
+ cfg.uid = WSL_NO_UID;
+ cfg.user_data = t;
+
+ t->ctx = wsl_create_context(t->ml, &cfg);
+
+ if (t->ctx != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int wsck_listen(mrp_transport_t *mt, int backlog)
+{
+ MRP_UNUSED(mt);
+ MRP_UNUSED(backlog);
+
+ mt->listened = TRUE;
+
+ return TRUE;
+}
+
+
+static int wsck_accept(mrp_transport_t *mt, mrp_transport_t *mlt)
+{
+ wsck_t *lt = (wsck_t *)mlt;
+ wsck_t *t = (wsck_t *)mt;
+
+ t->sck = wsl_accept_pending(lt->ctx, t);
+
+ if (t->sck != NULL) {
+ mrp_debug("accepted websocket connection %p", mlt);
+
+ /* default to mode inherited from listening transport */
+ t->send_mode = lt->send_mode;
+ wsl_set_sendmode(t->sck, t->send_mode);
+
+ /* inherit pure HTTP settings by default */
+ t->http_root = lt->http_root;
+ t->uri_table = lt->uri_table;
+ t->mime_table = lt->mime_table;
+
+ return TRUE;
+ }
+ else {
+ mrp_debug("failed to accept websocket connection on %p", mlt);
+
+ return FALSE;
+ }
+}
+
+
+static int wsck_connect(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_proto_t proto = {
+ .name = "murphy",
+ .cbs = { .connection = connection_cb,
+ .closed = closed_cb,
+ .recv = recv_cb,
+ .check = check_cb, },
+ .framed = FALSE,
+ .proto_data = NULL
+ };
+
+ wsl_ctx_cfg_t cfg;
+ mrp_wsckaddr_t *wa;
+ struct sockaddr *sa;
+ if (addr->any.sa_family != MRP_AF_WSCK || addrlen != sizeof(*wa))
+ return FALSE;
+
+ if (t->ctx != NULL)
+ return FALSE;
+
+ wa = (mrp_wsckaddr_t *)addr;
+
+ switch (wa->wsck_addr.family) {
+ case AF_INET: sa = (struct sockaddr *)&wa->wsck_addr.v4; break;
+ case AF_INET6: sa = (struct sockaddr *)&wa->wsck_addr.v6; break;
+ default:
+ errno = EAFNOSUPPORT;
+ return FALSE;
+ }
+
+ if ((t->protocol = mrp_strdup(wa->wsck_proto)) == NULL)
+ return FALSE;
+
+ proto.name = t->protocol;
+ t->proto[0] = proto;
+
+ mrp_clear(&cfg);
+ cfg.addr = NULL;
+ cfg.protos = &t->proto[0];
+ cfg.nproto = 1;
+ cfg.ssl_cert = t->ssl_cert;
+ cfg.ssl_pkey = t->ssl_pkey;
+ cfg.ssl_ca = t->ssl_ca;
+ cfg.gid = WSL_NO_GID;
+ cfg.uid = WSL_NO_UID;
+ cfg.user_data = t;
+
+ t->ctx = wsl_create_context(t->ml, &cfg);
+
+ if (t->ctx == NULL)
+ return FALSE;
+
+ t->sck = wsl_connect(t->ctx, sa, t->protocol, t->ssl, t);
+
+ if (t->sck != NULL) {
+ t->connected = TRUE;
+
+ return TRUE;
+ }
+ else {
+ wsl_unref_context(t->ctx);
+ t->ctx = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int wsck_disconnect(mrp_transport_t *mt)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_ctx_t *ctx = t->ctx;
+ wsl_sck_t *sck = t->sck;
+ void *user_data;
+
+ t->sck = NULL;
+ t->ctx = NULL;
+
+ user_data = wsl_close(sck);
+
+ if (user_data == t) /* was our associated context */
+ wsl_unref_context(ctx);
+
+ return TRUE;
+}
+
+
+static int wsck_send(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ wsck_t *t = (wsck_t *)mt;
+ void *buf;
+ ssize_t size;
+ int success;
+
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (wsl_send(t->sck, buf, size))
+ success = TRUE;
+ else
+ success = FALSE;
+
+ mrp_free(buf);
+
+ return success;
+}
+
+
+static int wsck_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ wsck_t *t = (wsck_t *)mt;
+
+ return wsl_send(t->sck, data, size);
+}
+
+
+static int wsck_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ wsck_t *t = (wsck_t *)mt;
+ mrp_data_descr_t *type;
+ void *buf;
+ size_t size, reserve;
+ uint16_t *tagp;
+ int status;
+
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ reserve = sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size > 0) {
+ tagp = buf;
+ *tagp = htobe16(tag);
+
+ status = wsl_send(t->sck, buf, size);
+
+ mrp_free(buf);
+ return status;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int wsck_sendcustom(mrp_transport_t *mt, void *data)
+{
+ wsck_t *t = (wsck_t *)mt;
+ mrp_json_t *json = (mrp_json_t *)data;
+ const char *s;
+ int status;
+
+ s = mrp_json_object_to_string(json);
+
+ /*
+ * Notes:
+ * Although json-c internally counts the length of the serialized
+ * object, it does not provide an API to get it out together with
+ * the string. Great...
+ */
+
+ if (s != NULL)
+ status = wsl_send(t->sck, (void *)s, strlen(s));
+ else
+ status = FALSE;
+
+ return status;
+}
+
+
+static inline int looks_ipv4(const char *p)
+{
+ if (isdigit(p[0])) {
+ if (p[1] == '.')
+ return TRUE;
+
+ if (isdigit(p[1])) {
+ if (p[2] == '.')
+ return TRUE;
+
+ if (isdigit(p[2])) {
+ if (p[3] == '.')
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int resolve_address(const char *str, mrp_wsckaddr_t *wa, socklen_t alen)
+{
+ struct addrinfo *ai, hints;
+ const char *node, *port, *proto;
+ char nbuf[256], pbuf[32];
+ int family, status;
+ size_t len;
+
+ if (strncmp(str, WSCKP":", WSCKL + 1) != 0)
+ return 0;
+ else
+ str += WSCKL + 1;
+
+ node = (char *)str;
+
+ if (node[0] == '[') {
+ node++;
+ family = AF_INET6;
+ port = strchr(node, ']');
+ }
+ else if (looks_ipv4(node)) {
+ family = AF_INET;
+ port = strchr(node, ':');
+ }
+ else {
+ family = AF_UNSPEC;
+ port = strrchr(node, ':');
+ }
+
+ if (port == NULL || (*port != ':' && *port != ']')) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = port - node;
+
+ if (len > sizeof(nbuf) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ strncpy(nbuf, node, len);
+ nbuf[len] = '\0';
+
+ if (*port == ']')
+ port++;
+
+ if (*port != ':') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ port++;
+ proto = strchr(port, '/');
+
+ if (proto != NULL) {
+ len = proto - port;
+
+ if (len > sizeof(pbuf) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ strncpy(pbuf, port, len);
+ pbuf[len] = '\0';
+
+ proto++;
+ if (strlen(proto) > sizeof(wa->wsck_proto) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ }
+ else {
+ proto = MRP_WSCK_DEFPROTO;
+ len = strlen(port);
+
+ if (len > sizeof(pbuf) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ strcpy(pbuf, port);
+ }
+
+ mrp_clear(&hints);
+ hints.ai_family = family;
+
+ status = getaddrinfo(nbuf, pbuf, &hints, &ai);
+
+ switch (status) {
+ case 0:
+ if (ai->ai_addrlen <= alen) {
+ wa->wsck_family = MRP_AF_WSCK;
+ memcpy(&wa->wsck_addr, ai->ai_addr, ai->ai_addrlen);
+ strcpy(wa->wsck_proto, proto);
+
+ len = sizeof(*wa);
+ }
+ else {
+ errno = EOVERFLOW;
+ len = -1;
+ }
+
+ freeaddrinfo(ai);
+ return len;
+
+#define MAP_ERROR(ai_err, err) \
+ case EAI_##ai_err: \
+ errno = err; \
+ return -1
+
+ MAP_ERROR(AGAIN , EAGAIN);
+ MAP_ERROR(BADFLAGS , EADDRNOTAVAIL);
+ MAP_ERROR(FAIL , EHOSTUNREACH);
+ MAP_ERROR(FAMILY , EPFNOSUPPORT);
+ MAP_ERROR(MEMORY , ENOMEM);
+ MAP_ERROR(NONAME , EHOSTUNREACH);
+ MAP_ERROR(SERVICE , EAFNOSUPPORT);
+ MAP_ERROR(SOCKTYPE , EHOSTUNREACH);
+ MAP_ERROR(SYSTEM , EHOSTUNREACH);
+#ifdef EAI_ADDRFAMILY
+ MAP_ERROR(ADDRFAMILY, EHOSTUNREACH);
+#endif
+#ifdef EAI_NODATA
+ MAP_ERROR(NODATA , EHOSTUNREACH);
+#endif
+
+ default:
+ errno = EHOSTUNREACH;
+ }
+
+ return -1;
+}
+
+
+#if 0
+static int print_address(char *buf, size_t size, mrp_wsckaddr_t *wa)
+{
+ struct sockaddr *saddr;
+ socklen_t salen;
+ char nbuf[256], pbuf[32], *b, *e;
+ int status;
+
+ if (wa->wsck_family != MRP_AF_WSCK) {
+ invalid:
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (wa->wsck_addr.family) {
+ case AF_INET:
+ saddr = (struct sockaddr *)&wa->wsck_addr.v4;
+ salen = sizeof(wa->wsck_addr.v4);
+ b = "";
+ e = "";
+ break;
+ case AF_INET6:
+ saddr = (struct sockaddr *)&wa->wsck_addr.v6;
+ salen = sizeof(wa->wsck_addr.v6);
+ b = "[";
+ e = "]";
+ break;
+ default:
+ goto invalid;
+ }
+
+ status = getnameinfo(saddr, salen, nbuf, sizeof(nbuf), pbuf, sizeof(pbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (status == 0)
+ return snprintf(buf, size, "wsck:%s%s%s:%s/%s",
+ b, nbuf, e, pbuf, wa->wsck_proto);
+ else {
+ printf("error: %d: %s\n", status, gai_strerror(status));
+
+ errno = EINVAL;
+ return -1;
+ }
+}
+#endif
+
+static void connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(addr);
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("incoming connection (%s) for context %p", protocol, ctx);
+
+ if (t->listened) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.connection((mrp_transport_t *)t, t->user_data);
+ });
+ }
+ else
+ mrp_log_error("connection attempt on non-listened transport %p", t);
+}
+
+
+static void closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("websocket %p closed", sck);
+
+ if (t->evt.closed != NULL)
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.closed((mrp_transport_t *)t, error, t->user_data);
+ });
+}
+
+
+static void recv_cb(wsl_sck_t *sck, void *data, size_t size, void *user_data,
+ void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("%zu bytes on websocket %p", size, sck);
+
+ MRP_TRANSPORT_BUSY(t, {
+ if (t->mode != MRP_TRANSPORT_MODE_CUSTOM)
+ t->recv_data((mrp_transport_t *)t, data, size, NULL, 0);
+ else {
+ mrp_json_t *json = mrp_json_string_to_object(data, size);
+
+ if (json != NULL) {
+ t->recv_data((mrp_transport_t *)t, json, 0, NULL, 0);
+ mrp_json_unref(json);
+ }
+ }
+ });
+}
+
+
+static int check_cb(wsl_sck_t *sck, void *user_data, void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("checking if transport %p (%p) has been destroyed", t, sck);
+
+ if (t != NULL) {
+ if (t->check_destroy((mrp_transport_t *)t)) {
+ mrp_debug("transport has been destroyed");
+ return TRUE;
+ }
+ else
+ mrp_debug("transport has not been destroyed");
+ }
+
+ return FALSE;
+}
+
+
+static http_client_t *http_create_client(wsck_t *lt)
+{
+ http_client_t *c;
+
+ c = mrp_allocz(sizeof(*c));
+
+ if (c != NULL) {
+ mrp_list_init(&c->hook);
+ c->sck = wsl_accept_pending(lt->ctx, c);
+
+ if (c->sck != NULL) {
+ c->http_root = lt->http_root;
+ c->uri_table = lt->uri_table;
+ c->mime_table = lt->mime_table;
+
+ return c;
+ }
+ else {
+ mrp_free(c);
+ c = NULL;
+ }
+ }
+
+ return c;
+}
+
+
+static void http_destroy_client(http_client_t *c)
+{
+ if (c != NULL) {
+ mrp_list_delete(&c->hook);
+ wsl_close(c->sck);
+ mrp_free(c);
+ }
+}
+
+
+const char *http_mapuri(http_client_t *c, const char *uri,
+ char *buf, size_t size)
+{
+ mrp_wsck_urimap_t *um;
+ mrp_wsck_mimemap_t *mm;
+ const char *suff, *root, *r, *s;
+
+ root = c->http_root ? c->http_root : "/";
+
+ if (c->uri_table != NULL) {
+ for (um = c->uri_table; um->uri != NULL; um++) {
+ if (!strcmp(uri, um->uri)) {
+ if (um->path[0] != '/') {
+ r = root;
+ s = "/";
+ }
+ else {
+ r = "";
+ s = "";
+ }
+
+ if (snprintf(buf, size, "%s%s%s", r, s, um->path) < (int)size)
+ return um->type;
+ else
+ return NULL;
+ }
+ }
+ }
+
+ if (c->http_root != NULL) {
+ if (snprintf(buf, size, "%s/%s", root, uri) >= (int)size)
+ return NULL;
+
+ suff = strrchr(uri, '.');
+
+ if (suff == NULL)
+ return "text/plain";
+ else
+ suff++;
+
+ if (c->mime_table != NULL) {
+ for (mm = c->mime_table; mm->suffix != NULL; mm++) {
+ if (!strcmp(mm->suffix, suff))
+ return mm->type;
+ }
+ }
+
+ for (mm = mime_table; mm->suffix != NULL; mm++) {
+ if (!strcmp(mm->suffix, suff))
+ return mm->type;
+ }
+ }
+
+ return NULL;
+}
+
+
+static void http_connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+ http_client_t *c;
+
+ MRP_UNUSED(addr);
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("incoming %s connection for context %p", protocol, ctx);
+
+ if (t->http_root != NULL || t->uri_table != NULL) {
+ c = http_create_client(t);
+
+ if (c != NULL)
+ mrp_debug("accepted pure HTTP client for context %p", ctx);
+ else
+ mrp_log_error("failed to create new HTTP client");
+ }
+ else
+ mrp_debug("rejecting pure HTTP client for context %p", ctx);
+}
+
+
+static void http_closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+ MRP_UNUSED(error);
+
+ if (error)
+ mrp_debug("HTTP client socket %p closed with error %d", sck, error);
+ else
+ mrp_debug("HTTP client socket %p closed", sck);
+
+ http_destroy_client(c);
+}
+
+
+static void http_req_cb(wsl_sck_t *sck, void *data, size_t size,
+ void *user_data, void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+ const char *uri = (const char *)data;
+ const char *type;
+ char path[PATH_MAX];
+
+ MRP_UNUSED(size);
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("HTTP request for URI '%s' on socket %p", uri, c->sck);
+
+ type = http_mapuri(c, uri, path, sizeof(path));
+
+ if (type != NULL) {
+ mrp_debug("mapped to '%s' (%s)", path, type);
+ wsl_serve_http_file(sck, path, type);
+ }
+ else
+ mrp_debug("failed to map URI");
+}
+
+
+static int http_check_cb(wsl_sck_t *sck, void *user_data, void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(sck);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(proto_data);
+
+ return FALSE;
+}
+
+
+static void http_done_cb(wsl_sck_t *sck, const char *uri, void *user_data,
+ void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("HTTP request for '%s' done, closing socket %p.", uri, sck);
+
+ http_destroy_client(c);
+}
+
+
+MRP_REGISTER_TRANSPORT(wsck, WSCKP, wsck_t, wsck_resolve,
+ wsck_open, wsck_createfrom, wsck_close, wsck_setopt,
+ wsck_bind, wsck_listen, wsck_accept,
+ wsck_connect, wsck_disconnect,
+ wsck_send, NULL,
+ wsck_sendraw, NULL,
+ wsck_senddata, NULL,
+ wsck_sendcustom, NULL,
+ NULL, NULL,
+ NULL, NULL);
diff --git a/src/common/wsck-transport.h b/src/common/wsck-transport.h
new file mode 100644
index 0000000..c35f29f
--- /dev/null
+++ b/src/common/wsck-transport.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_WEBSOCKET_TRANSPORT_H__
+#define __MURPHY_WEBSOCKET_TRANSPORT_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/transport.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_AF_WSCK 0xDC /* stolen address family */
+
+
+/*
+ * websocket transport address
+ */
+
+#define MRP_WSCKADDR_BASE \
+ __SOCKADDR_COMMON(wsck_); /* wsck_family: MRP_AF_WSCK */ \
+ union { /* websocket address */ \
+ sa_family_t family; \
+ struct sockaddr_in v4; \
+ struct sockaddr_in6 v6; \
+ } wsck_addr \
+
+typedef struct {
+ MRP_WSCKADDR_BASE;
+} _mrp_wsckaddr_base_t;
+
+
+#define MRP_WSCK_DEFPROTO "murphy"
+#define MRP_WSCK_PROTOLEN (MRP_SOCKADDR_SIZE - sizeof(_mrp_wsckaddr_base_t))
+
+
+typedef struct {
+ MRP_WSCKADDR_BASE; /* websocket address */
+ char wsck_proto[MRP_WSCK_PROTOLEN]; /* websocket protocol */
+} mrp_wsckaddr_t;
+
+
+/*
+ * websocket transport options and values
+ */
+
+#define MRP_WSCK_OPT_SENDMODE "send-mode" /* sendmode option name */
+#define MRP_WSCK_SENDMODE_TEXT "text" /* sendmode text option */
+#define MRP_WSCK_SENDMODE_BINARY "binary" /* sendmode blob option */
+
+
+#define MRP_WSCK_OPT_HTTPDIR "http-dir" /* HTTP content root */
+#define MRP_WSCK_OPT_MIMEMAP "mime-map" /* suffix-MIME table */
+#define MRP_WSCK_OPT_URIMAP "uri-map" /* URI-path table */
+#define MRP_WSCK_OPT_SSL_CERT "ssl-cert" /* path to SSL certificate */
+#define MRP_WSCK_OPT_SSL_PKEY "ssl-pkey" /* path to SSL priv. key */
+#define MRP_WSCK_OPT_SSL_CA "ssl-ca" /* path to SSL CA */
+#define MRP_WSCK_OPT_SSL "ssl" /* whether to connect with SSL */
+
+/*
+ * It is also possible to serve content over HTTP on a websocket transport.
+ *
+ * This is primarily intended for serving javascript API libraries to
+ * clients talking to you via the same websocket transport. The served
+ * libraries hide the details of the underlying communication protocol
+ * and present a more developer-friendly conventional javascript API.
+ *
+ * Currently the websocket transport provides two mechanisms for
+ * configuring HTTP content serving.
+ *
+ * 1) You can put all the files you're willing to expose via HTTP to a
+ * dedicated directory and configure it to the transport as the
+ * MRP_WSCK_OPT_HTTPROOT option. If you serve any other types of
+ * files than HTML (*.htm, *.html), javascript (*.js), or text
+ * (*.txt) files than you should also push down a table to map
+ * the extra file suffices to MIME types. You can do this using
+ * the MRP_SCK_OPT_MIMEMAP transport option.
+ *
+ * 2) You can use a mapping table that maps URIs to file path / mime
+ * type pairs. You can push this table down to the transport as
+ * the MRP_WSCK_URIMAP transport option.
+ *
+ * HTTPROOT takes a char *, URIMAP takes a mrp_wsck_urimap_t *, and
+ * MIMEMAP takes a mrp_wsck_mimemap_t * as their values. Both URI
+ * and MIME type tables need to be NULL-terminated. If you set both
+ * HTTPROOT and URIMAP, URIMAP entries with relative path names will
+ * be treated relative to HTTPROOT.
+ *
+ * Notes:
+ *
+ * If you push down any of these options, the websocket backend
+ * will use the provided values as such __without__ making an
+ * internal copy. IOW, you better make sure that the passed values
+ * are valid throughout the full lifetime of the transport (and
+ * if that is a transport you listen on also the lifetime of all
+ * transports accepted on that transport) otherwise you'll end up
+ * with severe memory corruption.
+ *
+ */
+
+typedef struct {
+ const char *uri; /* exported URI */
+ const char *path; /* path to file */
+ const char *type; /* MIME type to use */
+} mrp_wsck_urimap_t;
+
+typedef struct {
+ const char *suffix; /* filename suffix */
+ const char *type; /* MIME type */
+} mrp_wsck_mimemap_t;
+
+
+
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_WEBSOCKET_TRANSPORT_H__ */