diff --git a/CMakeLists.txt b/CMakeLists.txt index eb112af..5bed1e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,8 @@ cmake_minimum_required(VERSION 2.6) PROJECT(ubox C) ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3) +OPTION(BUILD_LUA "build Lua plugin" ON) + IF(APPLE) INCLUDE_DIRECTORIES(/opt/local/include) LINK_DIRECTORIES(/opt/local/lib) @@ -22,6 +24,8 @@ INSTALL(TARGETS ubox LIBRARY DESTINATION lib ) +ADD_SUBDIRECTORY(lua) + find_library(json json) IF(EXISTS ${json}) ADD_LIBRARY(blobmsg_json SHARED blobmsg_json.c) diff --git a/lua/CMakeLists.txt b/lua/CMakeLists.txt new file mode 100644 index 0000000..04abe0c --- /dev/null +++ b/lua/CMakeLists.txt @@ -0,0 +1,52 @@ +cmake_minimum_required(VERSION 2.6) + +PROJECT(uloop C) + +SET(CMAKE_INSTALL_PREFIX /) + +IF(NOT LUA_CFLAGS) + FIND_PROGRAM(PKG_CONFIG pkg-config) + IF(PKG_CONFIG) + EXECUTE_PROCESS( + COMMAND pkg-config --silence-errors --cflags lua5.1 + OUTPUT_VARIABLE LUA_CFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + ENDIF() +ENDIF() + +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -I.. ${LUA_CFLAGS}) +LINK_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) + +IF(APPLE) + ADD_DEFINITIONS(-I/opt/local/include) + SET(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "${CMAKE_SHARED_MODULE_CREATE_C_FLAGS} -undefined dynamic_lookup") +ENDIF(APPLE) + +IF(NOT LUAPATH) + EXECUTE_PROCESS( + COMMAND lua -e "for k in string.gmatch(package.cpath .. \";\", \"([^;]+)/..so;\") do if k:sub(1,1) == \"/\" then print(k) break end end" + OUTPUT_VARIABLE LUAPATH + RESULT_VARIABLE LUA_CHECK_RES + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + IF(BUILD_LUA) + IF(NOT ${LUA_CHECK_RES} EQUAL 0 OR "${LUAPATH}" EQUAL "") + MESSAGE(SEND_ERROR "Lua was not found on your system") + ENDIF() + ENDIF() +ENDIF() + +IF(BUILD_LUA) + ADD_LIBRARY(uloop_lua MODULE uloop.c) + SET_TARGET_PROPERTIES(uloop_lua PROPERTIES + OUTPUT_NAME uloop + PREFIX "" + ) + TARGET_LINK_LIBRARIES(uloop_lua ubox) + + INSTALL(TARGETS uloop_lua + LIBRARY DESTINATION ${LUAPATH} + ) +ENDIF() diff --git a/lua/uloop.c b/lua/uloop.c new file mode 100644 index 0000000..0f0c878 --- /dev/null +++ b/lua/uloop.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2012 John Crispin + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +struct lua_uloop_timeout { + struct uloop_timeout t; + int r; +}; + +struct lua_uloop_process { + struct uloop_process p; + int r; +}; + +static lua_State *state; + +static void ul_timer_cb(struct uloop_timeout *t) +{ + struct lua_uloop_timeout *tout = container_of(t, struct lua_uloop_timeout, t); + + lua_getglobal(state, "__uloop_cb"); + lua_rawgeti(state, -1, tout->r); + lua_call(state, 0, 0); +} + +static int ul_timer_set(lua_State *L) +{ + struct lua_uloop_timeout *tout; + double set; + + if (!lua_isnumber(L, -1)) { + lua_pushstring(L, "invalid arg list"); + lua_error(L); + + return 0; + } + + set = lua_tointeger(L, -1); + tout = lua_touserdata(L, 1); + uloop_timeout_set(&tout->t, set); + + return 1; +} + +static int ul_timer_free(lua_State *L) +{ + struct lua_uloop_timeout *tout = lua_touserdata(L, 1); + + uloop_timeout_cancel(&tout->t); + lua_getglobal(state, "__uloop_cb"); + luaL_unref(L, -1, tout->r); + + return 1; +} + +static const luaL_Reg timer_m[] = { + { "set", ul_timer_set }, + { "cancel", ul_timer_free }, + { NULL, NULL } +}; + +static int ul_timer(lua_State *L) +{ + struct lua_uloop_timeout *tout; + int set = 0; + int ref; + + if (lua_isnumber(L, -1)) { + set = lua_tointeger(L, -1); + lua_pop(L, 1); + } + + if (!lua_isfunction(L, -1)) { + lua_pushstring(L, "invalid arg list"); + lua_error(L); + + return 0; + } + + lua_getglobal(L, "__uloop_cb"); + lua_pushvalue(L, -2); + ref = luaL_ref(L, -2); + + tout = lua_newuserdata(L, sizeof(*tout)); + lua_createtable(L, 0, 2); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, ul_timer_free); + lua_setfield(L, -2, "__gc"); + lua_pushvalue(L, -1); + lua_setmetatable(L, -3); + lua_pushvalue(L, -2); + luaI_openlib(L, NULL, timer_m, 1); + lua_pushvalue(L, -2); + + memset(tout, 0, sizeof(*tout)); + + tout->r = ref; + tout->t.cb = ul_timer_cb; + if (set) + uloop_timeout_set(&tout->t, set); + + return 1; +} + +static void proc_cb(struct uloop_process *p, int ret) +{ + struct lua_uloop_process *proc = container_of(p, struct lua_uloop_process, p); + + lua_getglobal(state, "__uloop_cb"); + lua_rawgeti(state, -1, proc->r); + luaL_unref(state, -2, proc->r); + lua_pushinteger(state, ret >> 8); + lua_call(state, 1, 0); +} + +static int ul_process(lua_State *L) +{ + struct lua_uloop_process *proc; + pid_t pid; + int ref; + + if (!lua_isfunction(L, -1) || !lua_istable(L, -2) || + !lua_istable(L, -3) || !lua_isstring(L, -4)) { + lua_pushstring(L, "invalid arg list"); + lua_error(L); + + return 0; + } + + pid = fork(); + + if (pid == -1) { + lua_pushstring(L, "failed to fork"); + lua_error(L); + + return 0; + } + + if (pid == 0) { + /* child */ + int argn = lua_objlen(L, -3); + int envn = lua_objlen(L, -2); + char** argp = malloc(sizeof(char*) * (argn + 2)); + char** envp = malloc(sizeof(char*) * envn + 1); + int i = 1; + + argp[0] = (char*) lua_tostring(L, -4); + for (i = 1; i <= argn; i++) { + lua_rawgeti(L, -3, i); + argp[i] = (char*) lua_tostring(L, -1); + lua_pop(L, 1); + } + argp[i] = NULL; + + for (i = 1; i <= envn; i++) { + lua_rawgeti(L, -2, i); + envp[i - 1] = (char*) lua_tostring(L, -1); + lua_pop(L, 1); + } + envp[i - 1] = NULL; + + execve(*argp, argp, envp); + exit(-1); + } + + lua_getglobal(L, "__uloop_cb"); + lua_pushvalue(L, -2); + ref = luaL_ref(L, -2); + + proc = lua_newuserdata(L, sizeof(*proc)); + memset(proc, 0, sizeof(*proc)); + + proc->r = ref; + proc->p.pid = pid; + proc->p.cb = proc_cb; + uloop_process_add(&proc->p); + + return 1; +} + +static int ul_init(lua_State *L) +{ + uloop_init(); + lua_pushboolean(L, 1); + + return 1; +} + +static int ul_run(lua_State *L) +{ + uloop_run(); + lua_pushboolean(L, 1); + + return 1; +} + +static luaL_reg uloop_func[] = { + {"init", ul_init}, + {"run", ul_run}, + {"timer", ul_timer}, + {"process", ul_process}, + {NULL, NULL}, +}; + +int luaopen_uloop(lua_State *L) +{ + state = L; + + lua_createtable(L, 1, 0); + lua_setglobal(L, "__uloop_cb"); + + luaL_openlib(L, "uloop", uloop_func, 0); + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, "1.0"); + lua_rawset(L, -3); + + return 1; +} + +int luaclose_uloop(lua_State *L) +{ + lua_pushstring(L, "Called"); + + return 1; +}