X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fubus.git;a=blobdiff_plain;f=lua%2Fubus.c;h=92fb0a151fb9ac445938abc3e68848f4b49fc192;hp=85c12662190df0093fc16e47f472108795aaea03;hb=e52db46c9c5d8eabe5abaccf28468c6e0b99be95;hpb=25b9b4b827f74482743689386b195dca3b81a0df diff --git a/lua/ubus.c b/lua/ubus.c index 85c1266..92fb0a1 100644 --- a/lua/ubus.c +++ b/lua/ubus.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Jo-Philipp Wich + * Copyright (C) 2012 John Crispin * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2.1 @@ -14,12 +15,14 @@ #include #include #include +#include #include +#include +#define MODNAME "ubus" +#define METANAME MODNAME ".meta" -#define MODNAME "ubus" -#define METANAME MODNAME ".meta" - +static lua_State *state; struct ubus_lua_connection { int timeout; @@ -27,6 +30,15 @@ struct ubus_lua_connection { struct ubus_context *ctx; }; +struct ubus_lua_object { + struct ubus_object o; + int r; +}; + +struct ubus_lua_event { + struct ubus_event_handler e; + int r; +}; static int ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table); @@ -60,7 +72,6 @@ ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table) int len; int off = 0; void *data; - char buf[32]; if (!blobmsg_check_attr(attr, false)) return 0; @@ -89,9 +100,7 @@ ubus_lua_parse_blob(lua_State *L, struct blob_attr *attr, bool table) break; case BLOBMSG_TYPE_INT64: - /* NB: Lua cannot handle 64bit, format value as string and push that */ - sprintf(buf, "%lld", (long long int) be64_to_cpu(*(uint64_t *)data)); - lua_pushstring(L, buf); + lua_pushnumber(L, (double) be64_to_cpu(*(uint64_t *)data)); break; case BLOBMSG_TYPE_STRING: @@ -124,9 +133,13 @@ ubus_lua_format_blob_is_array(lua_State *L) /* Find out whether table is array-like */ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { +#ifdef LUA_TINT if (lua_type(L, -2) != LUA_TNUMBER && lua_type(L, -2) != LUA_TINT) +#else + if (lua_type(L, -2) != LUA_TNUMBER) +#endif { - lua_pop(L, 1); + lua_pop(L, 2); return false; } @@ -134,7 +147,7 @@ ubus_lua_format_blob_is_array(lua_State *L) if ((cur - 1) != prv) { - lua_pop(L, 1); + lua_pop(L, 2); return false; } @@ -160,7 +173,9 @@ ubus_lua_format_blob(lua_State *L, struct blob_buf *b, bool table) blobmsg_add_u8(b, key, (uint8_t)lua_toboolean(L, -1)); break; +#ifdef LUA_TINT case LUA_TINT: +#endif case LUA_TNUMBER: blobmsg_add_u32(b, key, (uint32_t)lua_tointeger(L, -1)); break; @@ -220,6 +235,7 @@ ubus_lua_connect(lua_State *L) if ((c = lua_newuserdata(L, sizeof(*c))) != NULL && (c->ctx = ubus_connect(sockpath)) != NULL) { + ubus_add_uloop(c->ctx); c->timeout = timeout; memset(&c->buf, 0, sizeof(c->buf)); luaL_getmetatable(L, METANAME); @@ -263,6 +279,218 @@ ubus_lua_objects(lua_State *L) return 1; } +static int +ubus_method_handler(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct ubus_lua_object *o = container_of(obj, struct ubus_lua_object, o); + int rv = 0; + + lua_getglobal(state, "__ubus_cb"); + lua_rawgeti(state, -1, o->r); + lua_getfield(state, -1, method); + lua_remove(state, -2); + lua_remove(state, -2); + + if (lua_isfunction(state, -1)) { + lua_pushlightuserdata(state, req); + if (!msg) + lua_pushnil(state); + else + ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); + lua_call(state, 2, 1); + if (lua_isnumber(state, -1)) + rv = lua_tonumber(state, -1); + } else + lua_pop(state, 1); + + return rv; +} + +static int lua_gettablelen(lua_State *L, int index) +{ + int cnt = 0; + + lua_pushnil(L); + index -= 1; + while (lua_next(L, index) != 0) { + cnt++; + lua_pop(L, 1); + } + + return cnt; +} + +static int ubus_lua_reply(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + struct ubus_request_data *req; + + luaL_checktype(L, 3, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) + { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + req = lua_touserdata(L, 2); + ubus_send_reply(c->ctx, req, c->buf.head); + + return 0; +} + +static int ubus_lua_load_methods(lua_State *L, struct ubus_method *m) +{ + struct blobmsg_policy *p; + int plen; + int pidx = 0; + + /* get the function pointer */ + lua_pushinteger(L, 1); + lua_gettable(L, -2); + + /* get the policy table */ + lua_pushinteger(L, 2); + lua_gettable(L, -3); + + /* check if the method table is valid */ + if ((lua_type(L, -2) != LUA_TFUNCTION) || + (lua_type(L, -1) != LUA_TTABLE) || + lua_objlen(L, -1)) { + lua_pop(L, 2); + return 1; + } + + /* store function pointer */ + lua_pushvalue(L, -2); + lua_setfield(L, -6, lua_tostring(L, -5)); + + m->name = lua_tostring(L, -4); + m->handler = ubus_method_handler; + + plen = lua_gettablelen(L, -1); + + /* exit if policy table is empty */ + if (!plen) { + lua_pop(L, 2); + return 0; + } + + /* setup the policy pointers */ + p = malloc(sizeof(struct blobmsg_policy) * plen); + memset(p, 0, sizeof(struct blobmsg_policy) * plen); + m->policy = p; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + int val = lua_tointeger(L, -1); + + /* check if the policy is valid */ + if ((lua_type(L, -2) != LUA_TSTRING) || + (lua_type(L, -1) != LUA_TNUMBER) || + (val < 0) || + (val > BLOBMSG_TYPE_LAST)) { + lua_pop(L, 1); + continue; + } + p[pidx].name = lua_tostring(L, -2); + p[pidx].type = val; + lua_pop(L, 1); + pidx++; + } + + m->n_policy = pidx; + lua_pop(L, 2); + + return 0; +} + +static struct ubus_object* ubus_lua_load_object(lua_State *L) +{ + struct ubus_lua_object *obj = NULL; + int mlen = lua_gettablelen(L, -1); + struct ubus_method *m; + int midx = 0; + + /* setup object pointers */ + obj = malloc(sizeof(struct ubus_lua_object)); + memset(obj, 0, sizeof(struct ubus_lua_object)); + obj->o.name = lua_tostring(L, -2); + + /* setup method pointers */ + m = malloc(sizeof(struct ubus_method) * mlen); + memset(m, 0, sizeof(struct ubus_method) * mlen); + obj->o.methods = m; + + /* setup type pointers */ + obj->o.type = malloc(sizeof(struct ubus_object_type)); + memset(obj->o.type, 0, sizeof(struct ubus_object_type)); + obj->o.type->name = lua_tostring(L, -2); + obj->o.type->id = 0; + obj->o.type->methods = obj->o.methods; + + /* create the callback lookup table */ + lua_createtable(L, 1, 0); + lua_getglobal(L, "__ubus_cb"); + lua_pushvalue(L, -2); + obj->r = luaL_ref(L, -2); + lua_pop(L, 1); + + /* scan each method */ + lua_pushnil(L); + while (lua_next(L, -3) != 0) { + /* check if it looks like a method */ + if ((lua_type(L, -2) != LUA_TSTRING) || + (lua_type(L, -1) != LUA_TTABLE) || + !lua_objlen(L, -1)) { + lua_pop(L, 1); + continue; + } + + if (!ubus_lua_load_methods(L, &m[midx])) + midx++; + lua_pop(L, 1); + } + + obj->o.type->n_methods = obj->o.n_methods = midx; + + /* pop the callback table */ + lua_pop(L, 1); + + return &obj->o; +} + +static int ubus_lua_add(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + /* verify top level object */ + if (lua_istable(L, 1)) { + lua_pushstring(L, "you need to pass a table"); + lua_error(L); + return 0; + } + + /* scan each object */ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + struct ubus_object *obj = NULL; + + /* check if the object has a table of methods */ + if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TTABLE)) { + obj = ubus_lua_load_object(L); + + if (obj) + ubus_add_object(c->ctx, obj); + } + lua_pop(L, 1); + } + + return 0; +} static void ubus_lua_signatures_cb(struct ubus_context *c, struct ubus_object_data *o, void *p) @@ -310,7 +538,7 @@ ubus_lua_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) static int ubus_lua_call(lua_State *L) { - int rv; + int rv, top; uint32_t id; struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); const char *path = luaL_checkstring(L, 2); @@ -335,6 +563,7 @@ ubus_lua_call(lua_State *L) return 2; } + top = lua_gettop(L); rv = ubus_invoke(c->ctx, id, func, c->buf.head, ubus_lua_call_cb, L, c->timeout * 1000); if (rv != UBUS_STATUS_OK) @@ -345,9 +574,92 @@ ubus_lua_call(lua_State *L) return 2; } - return 1; + return lua_gettop(L) - top; +} + +static void +ubus_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, + const char *type, struct blob_attr *msg) +{ + struct ubus_lua_event *listener = container_of(ev, struct ubus_lua_event, e); + + lua_getglobal(state, "__ubus_cb_event"); + lua_rawgeti(state, -1, listener->r); + + if (lua_isfunction(state, -1)) { + ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); + lua_call(state, 1, 0); + } } +static struct ubus_event_handler* +ubus_lua_load_event(lua_State *L) +{ + struct ubus_lua_event* event = NULL; + + event = malloc(sizeof(struct ubus_lua_event)); + memset(event, 0, sizeof(struct ubus_lua_event)); + event->e.cb = ubus_event_handler; + + /* update the he callback lookup table */ + lua_getglobal(L, "__ubus_cb_event"); + lua_pushvalue(L, -2); + event->r = luaL_ref(L, -2); + lua_setfield(L, -1, lua_tostring(L, -3)); + + return &event->e; +} + +static int +ubus_lua_listen(lua_State *L) { + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + + /* verify top level object */ + luaL_checktype(L, 2, LUA_TTABLE); + + /* scan each object */ + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + struct ubus_event_handler *listener; + + /* check if the key is a string and the value is a method */ + if ((lua_type(L, -2) == LUA_TSTRING) && (lua_type(L, -1) == LUA_TFUNCTION)) { + listener = ubus_lua_load_event(L); + if(listener != NULL) { + ubus_register_event_handler(c->ctx, listener, lua_tostring(L, -2)); + } + } + lua_pop(L, 1); + } + return 0; +} + +static int +ubus_lua_send(lua_State *L) +{ + struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + const char *event = luaL_checkstring(L, 2); + + if (*event == 0) + return luaL_argerror(L, 2, "no event name"); + + // Event content convert to ubus form + luaL_checktype(L, 3, LUA_TTABLE); + blob_buf_init(&c->buf, 0); + + if (!ubus_lua_format_blob_array(L, &c->buf, true)) { + lua_pushnil(L); + lua_pushinteger(L, UBUS_STATUS_INVALID_ARGUMENT); + return 2; + } + + // Send the event + ubus_send_event(c->ctx, event, c->buf.head); + + return 0; +} + + static int ubus_lua__gc(lua_State *L) @@ -366,13 +678,19 @@ ubus_lua__gc(lua_State *L) static const luaL_Reg ubus[] = { { "connect", ubus_lua_connect }, { "objects", ubus_lua_objects }, + { "add", ubus_lua_add }, + { "reply", ubus_lua_reply }, { "signatures", ubus_lua_signatures }, { "call", ubus_lua_call }, { "close", ubus_lua__gc }, + { "listen", ubus_lua_listen }, + { "send", ubus_lua_send }, { "__gc", ubus_lua__gc }, { NULL, NULL }, }; +/* avoid missing prototype warning */ +int luaopen_ubus(lua_State *L); int luaopen_ubus(lua_State *L) @@ -391,5 +709,34 @@ luaopen_ubus(lua_State *L) /* create module */ luaL_register(L, MODNAME, ubus); + /* set some enum defines */ + lua_pushinteger(L, BLOBMSG_TYPE_ARRAY); + lua_setfield(L, -2, "ARRAY"); + lua_pushinteger(L, BLOBMSG_TYPE_TABLE); + lua_setfield(L, -2, "TABLE"); + lua_pushinteger(L, BLOBMSG_TYPE_STRING); + lua_setfield(L, -2, "STRING"); + lua_pushinteger(L, BLOBMSG_TYPE_INT64); + lua_setfield(L, -2, "INT64"); + lua_pushinteger(L, BLOBMSG_TYPE_INT32); + lua_setfield(L, -2, "INT32"); + lua_pushinteger(L, BLOBMSG_TYPE_INT16); + lua_setfield(L, -2, "INT16"); + lua_pushinteger(L, BLOBMSG_TYPE_INT8); + lua_setfield(L, -2, "INT8"); + lua_pushinteger(L, BLOBMSG_TYPE_BOOL); + lua_setfield(L, -2, "BOOLEAN"); + + /* used in our callbacks */ + state = L; + + /* create the callback table */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb"); + + /* create the event table */ + lua_createtable(L, 1, 0); + lua_setglobal(L, "__ubus_cb_event"); + return 0; }