695 lines
16 KiB
C
695 lines
16 KiB
C
/* (C) 2017-2018 by Holger Hans Peter Freyther
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <lua.h>
|
|
#include <lualib.h>
|
|
#include <lauxlib.h>
|
|
|
|
#include <osmocom/bb/common/osmocom_data.h>
|
|
#include <osmocom/bb/mobile/app_mobile.h>
|
|
#include <osmocom/bb/common/logging.h>
|
|
|
|
#include <osmocom/bb/mobile/primitives.h>
|
|
|
|
#include <osmocom/core/select.h>
|
|
#include <osmocom/vty/misc.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
|
|
struct timer_userdata {
|
|
int cb_ref;
|
|
};
|
|
|
|
struct fd_userdata {
|
|
struct lua_State *state;
|
|
struct osmo_fd fd;
|
|
int cb_ref;
|
|
};
|
|
|
|
static char lua_prim_key[] = "osmocom.org-mobile-prim";
|
|
|
|
static struct mobile_prim_intf *get_primitive(lua_State *L)
|
|
{
|
|
struct mobile_prim_intf *intf;
|
|
|
|
lua_pushlightuserdata(L, lua_prim_key);
|
|
lua_gettable(L, LUA_REGISTRYINDEX);
|
|
intf = (void *) lua_topointer(L, -1);
|
|
lua_pop(L, 1);
|
|
return intf;
|
|
}
|
|
|
|
static int lua_osmo_do_log(lua_State *L, int loglevel)
|
|
{
|
|
int argc = lua_gettop(L);
|
|
lua_Debug ar = { 0, };
|
|
int i;
|
|
|
|
lua_getstack(L, 1, &ar);
|
|
lua_getinfo(L, "nSl", &ar);
|
|
|
|
LOGPSRC(DLUA, loglevel, ar.source, ar.currentline, "%s", "");
|
|
for (i = 1; i <= argc; ++i) {
|
|
if (!lua_isstring(L, i))
|
|
continue;
|
|
LOGPSRCC(DLUA, loglevel, ar.source, ar.currentline, 1,
|
|
"%s%s", i > 1 ? "\t" : "", lua_tostring(L, i));
|
|
}
|
|
LOGPC(DLUA, loglevel, "\n");
|
|
return 0;
|
|
}
|
|
|
|
static int lua_osmo_print(lua_State *L)
|
|
{
|
|
return lua_osmo_do_log(L, LOGL_NOTICE);
|
|
}
|
|
|
|
static int lua_osmo_debug(lua_State *L)
|
|
{
|
|
return lua_osmo_do_log(L, LOGL_DEBUG);
|
|
}
|
|
|
|
static int lua_osmo_error(lua_State *L)
|
|
{
|
|
return lua_osmo_do_log(L, LOGL_ERROR);
|
|
}
|
|
|
|
static int lua_osmo_fatal(lua_State *L)
|
|
{
|
|
return lua_osmo_do_log(L, LOGL_FATAL);
|
|
}
|
|
|
|
static const struct luaL_Reg global_runtime[] = {
|
|
{ "print", lua_osmo_print },
|
|
{ "log_notice", lua_osmo_print },
|
|
{ "log_debug", lua_osmo_debug },
|
|
{ "log_error", lua_osmo_error },
|
|
{ "log_fatal", lua_osmo_fatal },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
/* Push table and function. Stack+=2 */
|
|
static bool load_cb(struct osmocom_ms *ms, const char *cb_name)
|
|
{
|
|
struct lua_State *L = ms->lua_state;
|
|
|
|
if (ms->lua_cb_ref == LUA_REFNIL)
|
|
return false;
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, ms->lua_cb_ref);
|
|
lua_pushstring(L, cb_name);
|
|
lua_gettable(L, -2);
|
|
if (lua_isnil(L, -1)) {
|
|
LOGP(DLUA, LOGL_DEBUG, "No handler for %s\n", cb_name);
|
|
lua_pop(L, 2);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/* Call callback. Stack-=func + args. func/args popped by lua_pcall */
|
|
static void call_cb(lua_State *L, int args)
|
|
{
|
|
int err = lua_pcall(L, args, 0, 0);
|
|
if (err) {
|
|
LOGP(DLUA, LOGL_ERROR, "lua error: %s\n", lua_tostring(L, -1));
|
|
lua_pop(L, 2);
|
|
}
|
|
}
|
|
|
|
static void handle_timeout(struct mobile_prim_intf *intf, struct mobile_timer_param *param)
|
|
{
|
|
struct timer_userdata *timer = (void *)(intptr_t) param->timer_id;
|
|
lua_State *L = intf->ms->lua_state;
|
|
int err;
|
|
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, timer->cb_ref);
|
|
luaL_unref(L, LUA_REGISTRYINDEX, timer->cb_ref);
|
|
timer->cb_ref = LUA_NOREF;
|
|
|
|
err = lua_pcall(L, 0, 0, 0);
|
|
if (err) {
|
|
LOGP(DLUA, LOGL_ERROR, "lua error: %s\n", lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
}
|
|
}
|
|
|
|
static void handle_started(struct mobile_prim_intf *intf, struct mobile_started_param *param)
|
|
{
|
|
struct lua_State *L = intf->ms->lua_state;
|
|
|
|
if (!load_cb(intf->ms, "Started"))
|
|
return;
|
|
|
|
lua_pushinteger(L, param->started);
|
|
|
|
call_cb(L, 1);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static void handle_shutdown(struct mobile_prim_intf *intf, struct mobile_shutdown_param *param)
|
|
{
|
|
struct lua_State *L = intf->ms->lua_state;
|
|
|
|
if (!load_cb(intf->ms, "Shutdown"))
|
|
return;
|
|
|
|
lua_pushinteger(L, param->old_state);
|
|
lua_pushinteger(L, param->new_state);
|
|
|
|
call_cb(L, 2);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static void handle_sms(struct mobile_prim_intf *intf, struct mobile_sms_param *param)
|
|
{
|
|
struct lua_State *L = intf->ms->lua_state;
|
|
|
|
if (!load_cb(intf->ms, "Sms"))
|
|
return;
|
|
|
|
lua_createtable(L, 0, 11);
|
|
|
|
lua_pushinteger(L, param->sms.validity_minutes);
|
|
lua_setfield(L, -2, "validity_minutes");
|
|
|
|
lua_pushinteger(L, param->sms.reply_path_req);
|
|
lua_setfield(L, -2, "reply_path_req");
|
|
|
|
lua_pushinteger(L, param->sms.status_rep_req);
|
|
lua_setfield(L, -2, "status_rep_req");
|
|
|
|
lua_pushinteger(L, param->sms.ud_hdr_ind);
|
|
lua_setfield(L, -2, "ud_hdr_ind");
|
|
|
|
lua_pushinteger(L, param->sms.protocol_id);
|
|
lua_setfield(L, -2, "protocol_id");
|
|
|
|
lua_pushinteger(L, param->sms.data_coding_scheme);
|
|
lua_setfield(L, -2, "data_coding_scheme");
|
|
|
|
lua_pushinteger(L, param->sms.msg_ref);
|
|
lua_setfield(L, -2, "msg_ref");
|
|
|
|
lua_pushstring(L, param->sms.address);
|
|
lua_setfield(L, -2, "address");
|
|
|
|
lua_pushinteger(L, param->sms.time);
|
|
lua_setfield(L, -2, "time");
|
|
|
|
lua_pushlstring(L, (char *) param->sms.user_data, param->sms.user_data_len);
|
|
lua_setfield(L, -2, "user_data");
|
|
|
|
lua_pushstring(L, param->sms.text);
|
|
lua_setfield(L, -2, "text");
|
|
|
|
lua_pushinteger(L, param->cause_valid);
|
|
lua_pushinteger(L, param->cause);
|
|
|
|
call_cb(L, 3);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static void handle_mm(struct mobile_prim_intf *intf, struct mobile_mm_param *param)
|
|
{
|
|
lua_State *L = intf->ms->lua_state;
|
|
|
|
if (!load_cb(intf->ms, "Mm"))
|
|
return;
|
|
|
|
lua_pushinteger(L, param->state);
|
|
lua_pushinteger(L, param->substate);
|
|
lua_pushinteger(L, param->prev_substate);
|
|
|
|
call_cb(L, 3);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static int lua_osmo_timeout(lua_State *L)
|
|
{
|
|
struct mobile_prim *prim;
|
|
struct timer_userdata *timer;
|
|
|
|
if(lua_gettop(L) != 2) {
|
|
lua_pushliteral(L, "Need two arguments");
|
|
lua_error(L);
|
|
return 0;
|
|
}
|
|
|
|
luaL_argcheck(L, lua_isnumber(L, -2), 1, "Timeout needs to be a number");
|
|
luaL_argcheck(L, lua_isfunction(L, -1), 2, "Callback needs to be a function");
|
|
|
|
/*
|
|
* Create a handle to prevent the function to be GCed while we run the
|
|
* timer. Add a metatable to the object so itself will be GCed properly.
|
|
*/
|
|
timer = lua_newuserdata(L, sizeof(*timer));
|
|
luaL_getmetatable(L, "Timer");
|
|
lua_setmetatable(L, -2);
|
|
|
|
lua_pushvalue(L, -2);
|
|
timer->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
/* Take the address of the user data... */
|
|
prim = mobile_prim_alloc(PRIM_MOB_TIMER, PRIM_OP_REQUEST);
|
|
prim->u.timer.timer_id = (intptr_t) timer;
|
|
prim->u.timer.seconds = lua_tonumber(L, -3);
|
|
mobile_prim_intf_req(get_primitive(L), prim);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int lua_timer_cancel(lua_State *L)
|
|
{
|
|
struct mobile_prim *prim;
|
|
struct timer_userdata *timer;
|
|
|
|
luaL_argcheck(L, lua_isuserdata(L, -1), 1, "No userdata");
|
|
timer = lua_touserdata(L, -1);
|
|
if (timer->cb_ref != LUA_NOREF) {
|
|
luaL_unref(L, LUA_REGISTRYINDEX, timer->cb_ref);
|
|
timer->cb_ref = LUA_NOREF;
|
|
}
|
|
|
|
prim = mobile_prim_alloc(PRIM_MOB_TIMER_CANCEL, PRIM_OP_REQUEST);
|
|
prim->u.timer.timer_id = (intptr_t) timer;
|
|
mobile_prim_intf_req(get_primitive(L), prim);
|
|
return 0;
|
|
}
|
|
|
|
static const struct luaL_Reg timer_funcs[] = {
|
|
{ "cancel", lua_timer_cancel },
|
|
{ "__gc", lua_timer_cancel },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
static int lua_osmo_ms(lua_State *L)
|
|
{
|
|
lua_pushlightuserdata(L, get_primitive(L)->ms);
|
|
luaL_getmetatable(L, "MS");
|
|
lua_setmetatable(L, -2);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int lua_ms_imei(lua_State *L)
|
|
{
|
|
struct osmocom_ms *ms = get_primitive(L)->ms;
|
|
|
|
luaL_argcheck(L, lua_isuserdata(L, -1), 1, "No userdata");
|
|
lua_pushstring(L, ms->settings.imei);
|
|
return 1;
|
|
}
|
|
static int lua_ms_imsi(lua_State *L)
|
|
{
|
|
struct osmocom_ms *ms = get_primitive(L)->ms;
|
|
|
|
luaL_argcheck(L, lua_isuserdata(L, -1), 1, "No userdata");
|
|
lua_pushstring(L, ms->subscr.imsi);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_ms_shutdown_state(lua_State *L)
|
|
{
|
|
struct osmocom_ms *ms = get_primitive(L)->ms;
|
|
|
|
lua_pushinteger(L, ms->shutdown);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_ms_started(lua_State *L)
|
|
{
|
|
struct osmocom_ms *ms = get_primitive(L)->ms;
|
|
|
|
lua_pushinteger(L, ms->started);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_ms_register(lua_State *L)
|
|
{
|
|
struct osmocom_ms *ms = get_primitive(L)->ms;
|
|
|
|
/* callbacks must be a table */
|
|
luaL_checktype(L, 2, LUA_TTABLE);
|
|
|
|
if (ms->lua_cb_ref != LUA_REFNIL)
|
|
luaL_unref(L, LUA_REGISTRYINDEX, ms->lua_cb_ref);
|
|
ms->lua_cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
return 0;
|
|
}
|
|
|
|
static int lua_ms_no_shutdown(lua_State *L)
|
|
{
|
|
struct osmocom_ms *ms = get_primitive(L)->ms;
|
|
char *name;
|
|
int res;
|
|
|
|
res = mobile_start(ms, &name);
|
|
lua_pushinteger(L, res);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_ms_shutdown(lua_State *L)
|
|
{
|
|
struct osmocom_ms *ms = get_primitive(L)->ms;
|
|
int argc = lua_gettop(L);
|
|
int force = 0;
|
|
int res;
|
|
|
|
if (argc >= 1) {
|
|
luaL_argcheck(L, lua_isboolean(L, -1), 1, "Force");
|
|
force = lua_toboolean(L, -1);
|
|
}
|
|
|
|
res = mobile_stop(ms, force);
|
|
lua_pushinteger(L, res);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_ms_sms_send_simple(lua_State *L)
|
|
{
|
|
const char *sms_sca, *number, *text;
|
|
struct mobile_prim *prim;
|
|
struct gsm_sms *sms;
|
|
int msg_ref;
|
|
|
|
luaL_argcheck(L, lua_isnumber(L, -1), 4, "msg_ref needs to be a number");
|
|
luaL_argcheck(L, lua_isstring(L, -2), 3, "text must be a string");
|
|
luaL_argcheck(L, lua_isstring(L, -3), 2, "number must be a string");
|
|
luaL_argcheck(L, lua_isstring(L, -4), 1, "sms_sca must be a string");
|
|
|
|
msg_ref = (int) lua_tonumber(L, -1);
|
|
text = lua_tostring(L, -2);
|
|
number = lua_tostring(L, -3);
|
|
sms_sca = lua_tostring(L, -4);
|
|
|
|
prim = mobile_prim_alloc(PRIM_MOB_SMS, PRIM_OP_REQUEST);
|
|
|
|
sms = sms_from_text(number, 0, text);
|
|
if (!sms) {
|
|
lua_pushinteger(L, -ENOMEM);
|
|
msgb_free(prim->hdr.msg);
|
|
return 1;
|
|
}
|
|
|
|
prim->u.sms.sms = *sms;
|
|
prim->u.sms.sms.msg_ref = msg_ref;
|
|
osmo_strlcpy(prim->u.sms.sca, sms_sca, sizeof(prim->u.sms.sca));
|
|
mobile_prim_intf_req(get_primitive(L), prim);
|
|
|
|
lua_pushinteger(L, 0);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_ms_name(lua_State *L)
|
|
{
|
|
lua_pushstring(L, get_primitive(L)->ms->name);
|
|
return 1;
|
|
}
|
|
|
|
static int lua_reselect_network(lua_State *L)
|
|
{
|
|
struct mobile_prim *prim;
|
|
|
|
prim = mobile_prim_alloc(PRIM_MOB_NETWORK_RESELECT, PRIM_OP_REQUEST);
|
|
mobile_prim_intf_req(get_primitive(L), prim);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* Expect a fd on the stack and enable SO_PASSCRED */
|
|
static int lua_unix_passcred(lua_State *L)
|
|
{
|
|
int one = 1;
|
|
int fd, rc;
|
|
|
|
luaL_argcheck(L, lua_isnumber(L, -1), 1, "needs to be a filedescriptor");
|
|
fd = (int) lua_tonumber(L, -1);
|
|
|
|
rc = setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
|
|
if (rc != 0)
|
|
LOGP(DLUA, LOGL_ERROR, "Failed to set SO_PASSCRED: %s\n",
|
|
strerror(errno));
|
|
lua_pushinteger(L, rc);
|
|
return 1;
|
|
}
|
|
|
|
static void lua_fd_do_unregister(lua_State *L, struct fd_userdata *fdu) {
|
|
/* Unregister the fd and forget about the callback */
|
|
osmo_fd_unregister(&fdu->fd);
|
|
if (fdu->cb_ref != LUA_NOREF) {
|
|
luaL_unref(L, LUA_REGISTRYINDEX, fdu->cb_ref);
|
|
fdu->cb_ref = LUA_NOREF;
|
|
}
|
|
}
|
|
|
|
static int lua_fd_cb(struct osmo_fd *fd, unsigned int what) {
|
|
struct fd_userdata *fdu;
|
|
lua_State *L;
|
|
int cb_ref;
|
|
int err;
|
|
|
|
if (!fd->data) {
|
|
LOGP(DLUA, LOGL_ERROR,
|
|
"fd callback for fd(%d) but no lua callback\n", fd->fd);
|
|
return 0;
|
|
}
|
|
|
|
fdu = fd->data;
|
|
L = fdu->state;
|
|
cb_ref = fdu->cb_ref;
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref);
|
|
|
|
err = lua_pcall(L, 0, 1, 0);
|
|
if (err) {
|
|
LOGP(DLUA, LOGL_ERROR, "lua error: %s\n", lua_tostring(L, -1));
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
/* Check if we should continue */
|
|
luaL_argcheck(L, lua_isnumber(L, -1), 1, "needs to be a number");
|
|
if (lua_tonumber(L, -1) == 1)
|
|
return 0;
|
|
|
|
lua_fd_do_unregister(L, fdu);
|
|
return 0;
|
|
}
|
|
|
|
/* Register the fd */
|
|
static int lua_register_fd(lua_State *L)
|
|
{
|
|
struct fd_userdata *fdu;
|
|
int fd;
|
|
|
|
/* fd, cb */
|
|
luaL_argcheck(L, lua_isnumber(L, -2), 1, "needs to be a filedescriptor");
|
|
luaL_argcheck(L, lua_isfunction(L, -1), 2, "Callback needs to be a function");
|
|
|
|
/* Create a table so a user can unregister (and unregister on GC) */
|
|
fdu = lua_newuserdata(L, sizeof(*fdu));
|
|
fdu->state = L;
|
|
fdu->fd.fd = -1;
|
|
luaL_getmetatable(L, "Fd");
|
|
lua_setmetatable(L, -2);
|
|
|
|
/* Set the filedescriptor */
|
|
fd = (int) lua_tonumber(L, -3);
|
|
osmo_fd_setup(&fdu->fd, fd, OSMO_FD_READ, lua_fd_cb, fdu, 0);
|
|
|
|
/* Assuming that an error here will lead to a GC */
|
|
if (osmo_fd_register(&fdu->fd) != 0) {
|
|
fdu->cb_ref = LUA_NOREF;
|
|
lua_pushliteral(L, "Can't register the fd");
|
|
lua_error(L);
|
|
return 0;
|
|
}
|
|
|
|
/* Take the callback and keep a reference to it */
|
|
lua_pushvalue(L, -2);
|
|
fdu->cb_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int lua_fd_unregister(lua_State *L) {
|
|
struct fd_userdata *fdu;
|
|
|
|
luaL_argcheck(L, lua_isuserdata(L, -1), 1, "No userdata");
|
|
fdu = lua_touserdata(L, -1);
|
|
|
|
lua_fd_do_unregister(L, fdu);
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const struct luaL_Reg fd_funcs[] = {
|
|
{ "unregister", lua_fd_unregister },
|
|
{ "__gc", lua_fd_unregister },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
static const struct luaL_Reg ms_funcs[] = {
|
|
{ "imsi", lua_ms_imsi },
|
|
{ "imei", lua_ms_imei },
|
|
{ "shutdown_state", lua_ms_shutdown_state },
|
|
{ "started", lua_ms_started },
|
|
{ "register", lua_ms_register },
|
|
{ "start", lua_ms_no_shutdown },
|
|
{ "stop", lua_ms_shutdown },
|
|
{ "sms_send_simple", lua_ms_sms_send_simple },
|
|
{ "number", lua_ms_name },
|
|
{ "reselect_network", lua_reselect_network },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
|
|
static const struct luaL_Reg osmo_funcs[] = {
|
|
{ "timeout", lua_osmo_timeout },
|
|
{ "unix_passcred", lua_unix_passcred },
|
|
{ "register_fd", lua_register_fd },
|
|
{ "ms", lua_osmo_ms },
|
|
{ NULL, NULL },
|
|
};
|
|
|
|
static void lua_prim_ind(struct mobile_prim_intf *intf, struct mobile_prim *prim)
|
|
{
|
|
switch (OSMO_PRIM_HDR(&prim->hdr)) {
|
|
case OSMO_PRIM(PRIM_MOB_TIMER, PRIM_OP_INDICATION):
|
|
handle_timeout(intf, (struct mobile_timer_param *) &prim->u.timer);
|
|
break;
|
|
case OSMO_PRIM(PRIM_MOB_STARTED, PRIM_OP_INDICATION):
|
|
handle_started(intf, (struct mobile_started_param *) &prim->u.started);
|
|
break;
|
|
case OSMO_PRIM(PRIM_MOB_SHUTDOWN, PRIM_OP_INDICATION):
|
|
handle_shutdown(intf, (struct mobile_shutdown_param *) &prim->u.shutdown);
|
|
break;
|
|
case OSMO_PRIM(PRIM_MOB_SMS, PRIM_OP_INDICATION):
|
|
handle_sms(intf, (struct mobile_sms_param *) &prim->u.sms);
|
|
break;
|
|
case OSMO_PRIM(PRIM_MOB_MM, PRIM_OP_INDICATION):
|
|
handle_mm(intf, (struct mobile_mm_param *) &prim->u.mm);
|
|
break;
|
|
default:
|
|
LOGP(DLUA, LOGL_ERROR, "Unknown primitive: %d\n", OSMO_PRIM_HDR(&prim->hdr));
|
|
};
|
|
}
|
|
|
|
/*
|
|
* Add functions to the global lua scope. Technically these are
|
|
* included in the _G table. The following lua code can be used
|
|
* to inspect it.
|
|
*
|
|
* > for n in pairs(_G) do print(n) end
|
|
*/
|
|
static void add_globals(lua_State *L)
|
|
{
|
|
lua_getglobal(L, "_G");
|
|
luaL_setfuncs(L, global_runtime, 0);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static void create_meta_table(lua_State *L, const char *name, const luaL_Reg *regs)
|
|
{
|
|
luaL_newmetatable(L, name);
|
|
lua_pushliteral(L, "__index");
|
|
lua_pushvalue(L, -2);
|
|
lua_rawset(L, -3);
|
|
luaL_setfuncs(L, regs, 0);
|
|
lua_pop(L, 1);
|
|
}
|
|
|
|
static void add_runtime(lua_State *L, struct mobile_prim_intf *intf)
|
|
{
|
|
add_globals(L);
|
|
|
|
/* Add a osmo module/table. Seems an oldschool module? */
|
|
lua_newtable(L);
|
|
luaL_setfuncs(L, osmo_funcs, 0);
|
|
lua_setglobal(L, "osmo");
|
|
|
|
/* Create metatables so we can GC objects... */
|
|
create_meta_table(L, "Timer", timer_funcs);
|
|
create_meta_table(L, "MS", ms_funcs);
|
|
create_meta_table(L, "Fd", fd_funcs);
|
|
|
|
/* Remember the primitive pointer... store it in the registry */
|
|
lua_pushlightuserdata(L, lua_prim_key);
|
|
lua_pushlightuserdata(L, intf);
|
|
lua_settable(L, LUA_REGISTRYINDEX);
|
|
|
|
}
|
|
|
|
static void *talloc_lua_alloc(void *ctx, void *ptr, size_t osize, size_t nsize)
|
|
{
|
|
if (nsize == 0) {
|
|
talloc_free(ptr);
|
|
return NULL;
|
|
}
|
|
return talloc_realloc_size(ctx, ptr, nsize);
|
|
}
|
|
|
|
int script_lua_close(struct osmocom_ms *ms)
|
|
{
|
|
struct mobile_prim_intf *intf;
|
|
|
|
if (!ms->lua_state)
|
|
return 0;
|
|
|
|
intf = get_primitive(ms->lua_state);
|
|
lua_close(ms->lua_state);
|
|
mobile_prim_intf_free(intf);
|
|
ms->lua_state = NULL;
|
|
return 0;
|
|
}
|
|
|
|
int script_lua_load(struct vty *vty, struct osmocom_ms *ms, const char *filename)
|
|
{
|
|
struct mobile_prim_intf *intf;
|
|
int err;
|
|
|
|
script_lua_close(ms);
|
|
ms->lua_state = lua_newstate(talloc_lua_alloc, ms);
|
|
if (!ms->lua_state)
|
|
return -1;
|
|
|
|
ms->lua_cb_ref = LUA_REFNIL;
|
|
luaL_openlibs(ms->lua_state);
|
|
|
|
intf = mobile_prim_intf_alloc(ms);
|
|
intf->indication = lua_prim_ind;
|
|
add_runtime(ms->lua_state, intf);
|
|
|
|
err = luaL_loadfilex(ms->lua_state, filename, NULL);
|
|
if (err) {
|
|
vty_out(vty, "%% LUA load error: %s%s",
|
|
lua_tostring(ms->lua_state, -1), VTY_NEWLINE);
|
|
lua_pop(ms->lua_state, 1);
|
|
return -2;
|
|
}
|
|
|
|
|
|
err = lua_pcall(ms->lua_state, 0, 0, 0);
|
|
if (err) {
|
|
vty_out(vty, "%% LUA execute error: %s%s",
|
|
lua_tostring(ms->lua_state, -1), VTY_NEWLINE);
|
|
lua_pop(ms->lua_state, 1);
|
|
return -3;
|
|
}
|
|
|
|
return 0;
|
|
}
|