system-controller: basic plugin, Lua bindings and dispatching infra.
authorKrisztian Litkey <kli@iki.fi>
Mon, 4 Nov 2013 00:36:56 +0000 (02:36 +0200)
committerKrisztian Litkey <krisztian.litkey@intel.com>
Thu, 8 Jan 2015 16:37:11 +0000 (18:37 +0200)
Change-Id: Ic915a2e3ced65e3053ff6808f1ddff455f2cd1b9

configure.ac
src/Makefile.am
src/daemon/murphy.lua
src/plugins/system-controller/Makefile [new file with mode: 0644]
src/plugins/system-controller/plugin-system-controller.c [new file with mode: 0644]
src/plugins/system-controller/system-controller.h [new file with mode: 0644]

index a781c38..cadb449 100644 (file)
@@ -648,6 +648,7 @@ AM_CONDITIONAL(DISABLED_PLUGIN_DOMAIN_CONTROL,
 AM_CONDITIONAL(DISABLED_PLUGIN_SYSTEMD,  [check_if_disabled systemd])
 AM_CONDITIONAL(DISABLED_PLUGIN_TELEPHONY, [check_if_disabled telephony])
 AM_CONDITIONAL(DISABLED_PLUGIN_RESOURCE_ASM,     [check_if_disabled resource-asm])
+AM_CONDITIONAL(DISABLED_PLUGIN_SYSTEMCTL,  [check_if_disabled system-controller])
 
 AM_CONDITIONAL(BUILTIN_PLUGIN_TEST,     [check_if_internal test])
 AM_CONDITIONAL(BUILTIN_PLUGIN_DBUS,     [check_if_internal dbus])
@@ -663,6 +664,7 @@ AM_CONDITIONAL(BUILTIN_PLUGIN_LUA,      [check_if_internal lua])
 AM_CONDITIONAL(BUILTIN_PLUGIN_SYSTEMD,  [check_if_internal systemd])
 AM_CONDITIONAL(BUILTIN_PLUGIN_TELEPHONY, [check_if_internal telephony])
 AM_CONDITIONAL(BUILTIN_PLUGIN_RESOURCE_ASM,      [check_if_internal resource-asm])
+AM_CONDITIONAL(BUILTIN_PLUGIN_SYSTEMCTL, [check_if_internal system-controller])
 
 # Check for Check (unit test framework).
 PKG_CHECK_MODULES(CHECK, 
index bcb1a98..91d17f0 100644 (file)
@@ -1517,6 +1517,28 @@ endif
 endif
 endif
 
+# system-controller
+if SYSTEMCTL_ENABLED
+SYSTEMCTL_PLUGIN_SOURCES = plugins/system-controller/plugin-system-controller.c
+SYSTEMCTL_PLUGIN_CFLAGS  = $(WEBSOCKETS_CFLAGS)
+SYSTEMCTL_PLUGIN_LIBS    =
+
+if !DISABLED_PLUGIN_SYSTEMCTL
+if BUILTIN_PLUGIN_SYSTEMCTL
+BUILTIN_PLUGINS += $(SYSTEMCTL_PLUGIN_SOURCES)
+BUILTIN_CFLAGS  += $(SYSTEMCTL_PLUGIN_CFLAGS)
+BUILTIN_LIBS    += $(SYSTEMCTL_PLUGIN_LIBS)
+else
+plugin_system_controller_la_SOURCES = $(SYSTEMCTL_PLUGIN_SOURCES)
+plugin_system_controller_la_CFLAGS  = $(SYSTEMCTL_PLUGIN_CFLAGS) \
+                                $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_system_controller_la_LDFLAGS = -module -avoid-version
+plugin_system_controller_la_LIBADD  = $(SYSTEMCTL_PLUGIN_LIBS)
+plugin_LTLIBRARIES    += plugin-system-controller.la
+endif
+endif
+endif
+
 
 
 # telephony plugin
index 03112e8..00cb92b 100644 (file)
@@ -101,6 +101,12 @@ else
     m:info("No domain-control plugin found...")
 end
 
+if m:plugin_exists('system-controller') then
+    m:load_plugin('system-controller')
+else
+    m:info("No system-controller plugin found...")
+end
+
 
 -- load IVI resource manager plugin if exists
 if m:plugin_exists('ivi-resource-manager') then
diff --git a/src/plugins/system-controller/Makefile b/src/plugins/system-controller/Makefile
new file mode 100644 (file)
index 0000000..2c0a593
--- /dev/null
@@ -0,0 +1,7 @@
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+       $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+       $(MAKE) -C .. all
+endif
diff --git a/src/plugins/system-controller/plugin-system-controller.c b/src/plugins/system-controller/plugin-system-controller.c
new file mode 100644 (file)
index 0000000..ccdd69e
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ * 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/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/wsck-transport.h>
+#include <murphy/common/json.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/lua-bindings/murphy.h>
+#include <murphy/core/lua-bindings/lua-json.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+
+#include "system-controller.h"
+
+
+#define DEFAULT_ADDRESS "wsck:127.0.0.1:18081/ico_syc_protocol"
+
+/*
+ * plugin argument ids/indices
+ */
+
+enum {
+    ARG_ADDRESS,                         /* transport address to use */
+};
+
+
+/*
+ * system-controller context
+ */
+
+typedef struct sysctl_lua_s sysctl_lua_t;
+
+typedef struct {
+    mrp_context_t    *ctx;                /* murphy context */
+    mrp_transport_t  *lt;                 /* transport we listen on */
+    const char       *addr;               /* address we listen on */
+    mrp_list_hook_t   clients;            /* connected clients */
+    int               id;                 /* next client id */
+    sysctl_lua_t     *scl;                /* singleton Lua object */
+    lua_State        *L;                  /* murphy Lua state */
+    struct {
+        mrp_funcbridge_t *client;
+        mrp_funcbridge_t *generic;
+        mrp_funcbridge_t *window;
+        mrp_funcbridge_t *input;
+        mrp_funcbridge_t *user;
+        mrp_funcbridge_t *resource;
+        mrp_funcbridge_t *inputdev;
+    } handler;
+} sysctl_t;
+
+
+/*
+ * a system-controller client
+ */
+
+typedef struct {
+    int              id;                 /* client id */
+    pid_t            pid;                /* client pid */
+    char            *app;                /* client app id */
+    sysctl_t        *sc;                 /* system controller context */
+    mrp_transport_t *t;                  /* client transport */
+    mrp_list_hook_t  hook;               /* to list of clients */
+} client_t;
+
+
+struct sysctl_lua_s {
+    sysctl_t         *sc;                /* system controller */
+};
+
+typedef enum {
+    SYSCTL_IDX_CLIENT = 1,
+    SYSCTL_IDX_GENERIC,
+    SYSCTL_IDX_WINDOW,
+    SYSCTL_IDX_INPUT,
+    SYSCTL_IDX_USER,
+    SYSCTL_IDX_RESOURCE,
+    SYSCTL_IDX_INPUTDEV,
+} sysctl_lua_index_t;
+
+
+static int  sysctl_lua_create(lua_State *L);
+static void sysctl_lua_destroy(void *data);
+static int  sysctl_lua_getfield(lua_State *L);
+static int  sysctl_lua_setfield(lua_State *L);
+static int  sysctl_lua_stringify(lua_State *L);
+static int  sysctl_lua_send(lua_State *L);
+
+#define SYSCTL_LUA_CLASS MRP_LUA_CLASS(sysctl, lua)
+
+MRP_LUA_METHOD_LIST_TABLE(sysctl_lua_methods,
+                          MRP_LUA_METHOD_CONSTRUCTOR(sysctl_lua_create)
+                          MRP_LUA_METHOD(send_message, sysctl_lua_send));
+
+MRP_LUA_METHOD_LIST_TABLE(sysctl_lua_overrides,
+                          MRP_LUA_OVERRIDE_CALL     (sysctl_lua_create)
+                          MRP_LUA_OVERRIDE_GETFIELD (sysctl_lua_getfield)
+                          MRP_LUA_OVERRIDE_SETFIELD (sysctl_lua_setfield)
+                          MRP_LUA_OVERRIDE_STRINGIFY(sysctl_lua_stringify));
+
+MRP_LUA_CLASS_DEF(sysctl, lua, sysctl_lua_t,
+                  sysctl_lua_destroy, sysctl_lua_methods, sysctl_lua_overrides);
+
+
+
+
+static sysctl_t *scptr;                  /* singleton native object */
+
+
+static client_t *create_client(sysctl_t *sc, mrp_transport_t *lt)
+{
+    client_t *c;
+
+    c = mrp_allocz(sizeof(*c));
+
+    if (c != NULL) {
+        mrp_list_init(&c->hook);
+
+        c->sc = sc;
+        c->t  = mrp_transport_accept(lt, c, MRP_TRANSPORT_REUSEADDR);
+
+        if (c->t != NULL) {
+            mrp_list_append(&sc->clients, &c->hook);
+            c->id = sc->id++;
+
+            return c;
+        }
+
+        mrp_free(c);
+    }
+
+    return NULL;
+}
+
+
+static void destroy_client(client_t *c)
+{
+    if (c != NULL) {
+        mrp_list_delete(&c->hook);
+
+        mrp_transport_disconnect(c->t);
+        mrp_transport_destroy(c->t);
+
+        mrp_free(c);
+    }
+}
+
+
+static client_t *find_client_by_id(sysctl_t *sc, int id)
+{
+    mrp_list_hook_t *p, *n;
+    client_t        *c;
+
+    mrp_list_foreach(&sc->clients, p, n) {
+        c = mrp_list_entry(p, typeof(*c), hook);
+
+        if (c->id == id)
+            return c;
+    }
+
+    return NULL;
+}
+
+
+static client_t *find_client_by_pid(sysctl_t *sc, pid_t pid)
+{
+    mrp_list_hook_t *p, *n;
+    client_t        *c;
+
+    mrp_list_foreach(&sc->clients, p, n) {
+        c = mrp_list_entry(p, typeof(*c), hook);
+
+        if (c->pid == pid)
+            return c;
+    }
+
+    return NULL;
+}
+
+
+static client_t *find_client_by_app(sysctl_t *sc, const char *app)
+{
+    mrp_list_hook_t *p, *n;
+    client_t        *c;
+
+    mrp_list_foreach(&sc->clients, p, n) {
+        c = mrp_list_entry(p, typeof(*c), hook);
+
+        if (c->app != NULL && !strcmp(c->app, app))
+            return c;
+    }
+
+    return NULL;
+}
+
+
+static void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+    sysctl_t *sc = (sysctl_t *)user_data;
+    client_t *c;
+
+    c = create_client(sc, lt);
+
+    if (c != NULL)
+        mrp_log_info("Accepted system controller client connection.");
+    else
+        mrp_log_error("Failed to accept system controller client connection.");
+}
+
+
+static void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+    client_t *c = (client_t *)user_data;
+
+    MRP_UNUSED(t);
+
+    if (error != 0)
+        mrp_log_error("System controller connection closed with error %d (%s).",
+                      error, strerror(error));
+    else
+        mrp_log_info("System controller client #%d closed connection.", c->id);
+
+    destroy_client(c);
+}
+
+
+static int send_message(client_t *c, mrp_json_t *msg)
+{
+    const char *s;
+
+    s = mrp_json_object_to_string(msg);
+
+    mrp_debug("sending system controller message to client #%d:", c->id);
+    mrp_debug("  %s", s);
+
+    return mrp_transport_sendcustom(c->t, msg);
+}
+
+
+static const char *message_name(int code)
+{
+#define MAX_CLASS   0x08
+#define MAX_TYPE    0x30
+#define CLASS(code) (((code) & 0xf0000) >> 16)
+#define TYPE(code)  (((code) & 0x0ffff))
+    static char *names[MAX_CLASS + 1][MAX_TYPE + 1];
+    static int   setup = 1;
+
+    int   c = CLASS(code), t = TYPE(code);
+    char *n;
+
+    if (setup) {
+        memset(names, 0, sizeof(names));
+
+#define MAP(msg, code) names[CLASS(code)][TYPE(code)] = #msg;
+        MAP(MSG_CMD_SEND_APPID          , 0x00000001);
+        MAP(MSG_CMD_CREATE              , 0x00010001);
+        MAP(MSG_CMD_DESTROY             , 0x00010002);
+        MAP(MSG_CMD_SHOW                , 0x00010003);
+        MAP(MSG_CMD_HIDE                , 0x00010004);
+        MAP(MSG_CMD_MOVE                , 0x00010005);
+        MAP(MSG_CMD_CHANGE_ACTIVE       , 0x00010006);
+        MAP(MSG_CMD_CHANGE_LAYER        , 0x00010007);
+        MAP(MSG_CMD_CHANGE_ATTR         , 0x00010008);
+        MAP(MSG_CMD_NAME                , 0x00010009);
+        MAP(MSG_CMD_MAP_THUMB           , 0x00010011);
+        MAP(MSG_CMD_UNMAP_THUMB         , 0x00010012);
+        MAP(MSG_CMD_SHOW_LAYER          , 0x00010020);
+        MAP(MSG_CMD_HIDE_LAYER          , 0x00010021);
+        MAP(MSG_CMD_CHANGE_LAYER_ATTR   , 0x00010022);
+        MAP(MSG_CMD_ADD_INPUT           , 0x00020001);
+        MAP(MSG_CMD_DEL_INPUT           , 0x00020002);
+        MAP(MSG_CMD_SEND_INPUT          , 0x00020003);
+        MAP(MSG_CMD_CHANGE_USER         , 0x00030001);
+        MAP(MSG_CMD_GET_USERLIST        , 0x00030002);
+        MAP(MSG_CMD_GET_LASTINFO        , 0x00030003);
+        MAP(MSG_CMD_SET_LASTINFO        , 0x00030004);
+        MAP(MSG_CMD_ACQUIRE_RES         , 0x00040001);
+        MAP(MSG_CMD_RELEASE_RES         , 0x00040002);
+        MAP(MSG_CMD_DEPRIVE_RES         , 0x00040003);
+        MAP(MSG_CMD_WAITING_RES         , 0x00040004);
+        MAP(MSG_CMD_REVERT_RES          , 0x00040005);
+        MAP(MSG_CMD_SET_REGION          , 0x00050001);
+        MAP(MSG_CMD_UNSET_REGION        , 0x00050002);
+        MAP(MSG_CMD_CREATE_RES          , 0x00040011);
+        MAP(MSG_CMD_DESTORY_RES         , 0x00040012);
+        MAP(MSG_CMD_NOTIFY_CHANGED_STATE, 0x00060001);
+#undef MAP
+
+        setup = false;
+    }
+
+    if (c > MAX_CLASS || t > MAX_TYPE || (n = names[c][t]) == NULL)
+        return "<unknown message>";
+    else
+        return n;
+}
+
+
+static void recv_evt(mrp_transport_t *t, void *data, void *user_data)
+{
+    client_t         *c   = (client_t *)user_data;
+    sysctl_t         *sc  = c->sc;
+    mrp_json_t       *req = (mrp_json_t *)data;
+    int               cmd, type, pid;
+    const char       *app;
+    mrp_funcbridge_t *handlers[] = {
+        [0] = sc->handler.client,
+        [1] = sc->handler.window,
+        [2] = sc->handler.input,
+        [3] = sc->handler.user,
+        [4] = sc->handler.resource,
+        [5] = sc->handler.inputdev
+    }, *h;
+    mrp_funcbridge_value_t args[4];
+    mrp_funcbridge_value_t ret;
+    char                   rt;
+
+    MRP_UNUSED(t);
+
+    {
+        const char *s = mrp_json_object_to_string(req);
+        mrp_debug("system controller received message from client #%d:", c->id);
+        mrp_debug("  %s", s);
+    }
+
+    /*
+     * emit a connection message if this is the fist message with an appid
+     */
+
+    if (mrp_json_get_integer(req, "pid", &pid))
+        c->pid = pid;
+
+    if (mrp_json_get_string(req, "appid", &app) && c->app == NULL) {
+        mrp_free(c->app);
+        c->app = mrp_strdup(app);
+
+        mrp_debug("client #%d: pid %u, appid '%s'", c->id, c->pid,
+                  c->app ? c->app : "");
+
+        h = sc->handler.client;
+
+        if (h != NULL) {
+            args[0].pointer = sc->scl;
+            args[1].integer = c->id;
+            args[2].pointer = mrp_json_lua_wrap(sc->L, req);
+
+            if (!mrp_funcbridge_call_from_c(sc->L, h, "odo", &args[0],
+                                            &rt, &ret)) {
+                mrp_log_error("Failed to dispatch client-connection (%s).",
+                              ret.string ? ret.string : "<unknown error>");
+                mrp_free((void *)ret.string);
+            }
+        }
+    }
+
+
+    if (!mrp_json_get_integer(req, "command", &cmd)) {
+        h    = sc->handler.generic;
+        type = 0;
+    }
+    else {
+        type = (cmd & 0xffff0000) >> 16;
+
+        if (0 <= type && type <= 5)
+            h = handlers[type];
+        else
+            h = NULL;
+    }
+
+    if (h != NULL || (h = sc->handler.generic) != NULL) {
+        args[0].pointer = sc->scl;
+        args[1].integer = c->id;
+        args[2].pointer = mrp_json_lua_wrap(sc->L, req);
+
+        mrp_json_add_string(req, "MESSAGE", message_name(cmd));
+
+        if (!mrp_funcbridge_call_from_c(sc->L, h, "odo", &args[0], &rt, &ret)) {
+            mrp_log_error("Failed to dispatch system-controller message (%s).",
+                          ret.string ? ret.string : "<unknown error>");
+            mrp_free((void *)ret.string);
+        }
+    }
+    else
+        mrp_debug("No handler for system-controller message of type 0x%x.",
+                  type);
+}
+
+
+static int transport_create(sysctl_t *sc)
+{
+    static mrp_transport_evt_t evt = {
+        { .recvcustom     = recv_evt },
+        { .recvcustomfrom = NULL     },
+        .connection       = connection_evt,
+        .closed           = closed_evt,
+    };
+
+    mrp_mainloop_t *ml = sc->ctx->ml;
+    mrp_sockaddr_t  addr;
+    socklen_t       len;
+    const char     *type, *opt, *val;
+    int             flags;
+
+    len = mrp_transport_resolve(NULL, sc->addr, &addr, sizeof(addr), &type);
+
+    if (len > 0) {
+        flags    = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_MODE_CUSTOM;
+        sc->lt = mrp_transport_create(ml, type, &evt, sc, flags);
+
+        if (sc->lt != NULL) {
+            if (mrp_transport_bind(sc->lt, &addr, len) &&
+                mrp_transport_listen(sc->lt, 0)) {
+                mrp_log_info("Listening on transport '%s'...", sc->addr);
+
+                opt = MRP_WSCK_OPT_SENDMODE;
+                val = MRP_WSCK_SENDMODE_TEXT;
+                mrp_transport_setopt(sc->lt, opt, val);
+
+                return TRUE;
+            }
+
+            mrp_transport_destroy(sc->lt);
+            sc->lt = NULL;
+        }
+    }
+    else
+        mrp_log_error("Failed to resolve transport address '%s'.", sc->addr);
+
+    return FALSE;
+}
+
+
+static void transport_destroy(sysctl_t *sc)
+{
+    mrp_transport_destroy(sc->lt);
+    sc->lt = NULL;
+}
+
+
+static int sysctl_lua_create(lua_State *L)
+{
+    static sysctl_lua_t *scl = NULL;
+
+    if (scl == NULL)
+        scl = (sysctl_lua_t *)mrp_lua_create_object(L, SYSCTL_LUA_CLASS,
+                                                    NULL, 0);
+    scl->sc    = scptr;
+    scptr->scl = scl;
+
+    mrp_lua_push_object(L, scl);
+
+    return 1;
+}
+
+
+static void sysctl_lua_destroy(void *data)
+{
+    MRP_UNUSED(data);
+}
+
+
+static sysctl_lua_t *sysctl_lua_check(lua_State *L, int idx)
+{
+    return (sysctl_lua_t *)mrp_lua_check_object(L, SYSCTL_LUA_CLASS, idx);
+}
+
+
+static int name_to_index(const char *name)
+{
+#define MAP(_name, _idx) if (!strcmp(name, _name)) return SYSCTL_IDX_##_idx;
+    MAP("client_handler"  , CLIENT);
+    MAP("generic_handler" , GENERIC);
+    MAP("window_handler"  , WINDOW);
+    MAP("input_handler"   , INPUT);
+    MAP("user_handler"    , USER);
+    MAP("resource_handler", RESOURCE);
+    MAP("inputdev_handler", INPUTDEV);
+    return 0;
+#undef MAP
+}
+
+
+static int sysctl_lua_getfield(lua_State *L)
+{
+    sysctl_lua_t       *scl = sysctl_lua_check(L, 1);
+    sysctl_lua_index_t  idx = name_to_index(lua_tolstring(L, 2, NULL));
+
+    lua_pop(L, 1);
+
+    switch (idx) {
+    case SYSCTL_IDX_CLIENT:
+        mrp_funcbridge_push(L, scl->sc->handler.client);
+        break;
+    case SYSCTL_IDX_GENERIC:
+        mrp_funcbridge_push(L, scl->sc->handler.generic);
+        break;
+    case SYSCTL_IDX_WINDOW:
+        mrp_funcbridge_push(L, scl->sc->handler.window);
+        break;
+    case SYSCTL_IDX_INPUT:
+        mrp_funcbridge_push(L, scl->sc->handler.input);
+        break;
+    case SYSCTL_IDX_USER:
+        mrp_funcbridge_push(L, scl->sc->handler.user);
+        break;
+    case SYSCTL_IDX_RESOURCE:
+        mrp_funcbridge_push(L, scl->sc->handler.resource);
+        break;
+    case SYSCTL_IDX_INPUTDEV:
+        mrp_funcbridge_push(L, scl->sc->handler.inputdev);
+        break;
+    default:
+        lua_pushnil(L);
+    }
+
+    return 1;
+}
+
+
+static int sysctl_lua_setfield(lua_State *L)
+{
+    sysctl_lua_t        *scl  = sysctl_lua_check(L, 1);
+    const char          *name = lua_tostring(L, 2);
+    sysctl_lua_index_t   idx  = name_to_index(name);
+    mrp_funcbridge_t   **hptr = NULL;
+
+    switch (idx) {
+    case SYSCTL_IDX_CLIENT:   hptr = &scl->sc->handler.client;   break;
+    case SYSCTL_IDX_GENERIC:  hptr = &scl->sc->handler.generic;  break;
+    case SYSCTL_IDX_WINDOW:   hptr = &scl->sc->handler.window;   break;
+    case SYSCTL_IDX_INPUT:    hptr = &scl->sc->handler.input;    break;
+    case SYSCTL_IDX_USER:     hptr = &scl->sc->handler.user;     break;
+    case SYSCTL_IDX_RESOURCE: hptr = &scl->sc->handler.resource; break;
+    case SYSCTL_IDX_INPUTDEV: hptr = &scl->sc->handler.inputdev; break;
+    default:
+        luaL_error(L, "unknown system-controller handler '%s'", name);
+    }
+
+    if (lua_type(L, -1) != LUA_TNIL)
+        *hptr = mrp_funcbridge_create_luafunc(L, -1);
+    else
+        *hptr = NULL;
+
+    mrp_debug("handler %s (#%d) of %p set to %p", name, idx, scl, *hptr);
+
+    return 0;
+}
+
+
+static int sysctl_lua_stringify(lua_State *L)
+{
+    sysctl_lua_t *scl = sysctl_lua_check(L, 1);
+
+    if (scl != NULL)
+        lua_pushfstring(L, "<system-controller %p>", scl);
+    else
+        lua_pushstring(L, "<error (not a system-controller)>");
+
+    return 1;
+}
+
+
+static int sysctl_lua_send(lua_State *L)
+{
+    sysctl_lua_t *scl = sysctl_lua_check(L, 1);
+    client_t     *c;
+    mrp_json_t   *msg;
+    int           success;
+
+    switch (lua_type(L, 2)) {
+    case LUA_TSTRING:
+        c = find_client_by_app(scl->sc, lua_tostring(L, 2));
+        break;
+
+    case LUA_TNUMBER:
+        c = find_client_by_id(scl->sc, lua_tointeger(L, 2));
+        break;
+
+    default:
+        return luaL_error(L, "invalid argument, client id or name expected");
+    }
+
+    msg = mrp_json_lua_get(L, 3);
+
+    lua_pop(L, 3);
+
+    if (c != NULL && msg != NULL) {
+        mrp_debug("sending message %s to client #%d",
+                  mrp_json_object_to_string(msg), c->id);
+        success = send_message(c, msg);
+    }
+    else
+        success = FALSE;
+
+    mrp_json_unref(msg);
+    lua_pushboolean(L, success);
+
+    return 1;
+}
+
+
+static int sc_lua_get(lua_State *L)
+{
+    sysctl_lua_t *scl = mrp_lua_create_object(L, SYSCTL_LUA_CLASS, NULL, 0);
+
+    scl->sc = scptr;
+
+    mrp_lua_push_object(L, scl);
+
+    return 1;
+}
+
+
+static int register_lua_bindings(sysctl_t *sc)
+{
+    static luaL_reg methods[] = {
+        { "get_system_controller", sc_lua_get },
+        { NULL, NULL }
+    };
+    static mrp_lua_bindings_t bindings = {
+        .meta    = "murphy",
+        .methods = methods,
+    };
+
+    if ((sc->L = mrp_lua_get_lua_state()) == NULL)
+        return FALSE;
+
+    mrp_lua_create_object_class(sc->L, SYSCTL_LUA_CLASS);
+
+    return mrp_lua_register_murphy_bindings(&bindings);
+}
+
+
+static int plugin_init(mrp_plugin_t *plugin)
+{
+    sysctl_t *sc;
+
+    MRP_UNUSED(find_client_by_pid);
+
+    sc = mrp_allocz(sizeof(*sc));
+
+    if (sc != NULL) {
+        mrp_list_init(&sc->clients);
+
+        sc->id   = 1;
+        sc->ctx  = plugin->ctx;
+        sc->addr = plugin->args[ARG_ADDRESS].str;
+
+        if (!transport_create(sc))
+            goto fail;
+
+        if (!register_lua_bindings(sc))
+            goto fail;
+
+        scptr = sc;
+
+       return TRUE;
+    }
+
+
+ fail:
+    if (sc != NULL) {
+        transport_destroy(sc);
+
+        mrp_free(sc);
+    }
+
+    return FALSE;
+}
+
+
+static void plugin_exit(mrp_plugin_t *plugin)
+{
+    sysctl_t *sc = (sysctl_t *)plugin->data;
+
+    scptr = NULL;
+
+    transport_destroy(sc);
+
+    mrp_free(sc);
+}
+
+
+
+#define PLUGIN_DESCRIPTION "Murphy system controller plugin."
+#define PLUGIN_HELP        "Murphy system controller plugin."
+#define PLUGIN_AUTHORS     "Krisztian Litkey <kli@iki.fi>"
+#define PLUGIN_VERSION     MRP_VERSION_INT(0, 0, 1)
+
+static mrp_plugin_arg_t plugin_args[] = {
+    MRP_PLUGIN_ARGIDX(ARG_ADDRESS, STRING, "address", DEFAULT_ADDRESS)
+};
+
+MURPHY_REGISTER_PLUGIN("system-controller",
+                       PLUGIN_VERSION, PLUGIN_DESCRIPTION, PLUGIN_AUTHORS,
+                       PLUGIN_HELP, MRP_SINGLETON, plugin_init, plugin_exit,
+                       plugin_args, MRP_ARRAY_SIZE(plugin_args),
+                       NULL, 0,
+                       NULL, 0,
+                       NULL);
diff --git a/src/plugins/system-controller/system-controller.h b/src/plugins/system-controller/system-controller.h
new file mode 100644 (file)
index 0000000..8c31bb5
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * 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_PLUGIN_SYSTEM_CONTROLLER_H__
+#define __MURPHY_PLUGIN_SYSTEM_CONTROLLER_H__
+
+
+
+#endif /* __MURPHY_PLUGIN_SYSTEM_CONTROLLER_H__ */