mirror of
https://github.com/samba-team/samba.git
synced 2025-01-13 13:18:06 +03:00
c34d5f445a
metze
385 lines
8.3 KiB
C
385 lines
8.3 KiB
C
/*
|
|
* Unix SMB/CIFS implementation.
|
|
* Lua experiments
|
|
* Copyright (C) Volker Lendecke 2006
|
|
*
|
|
* 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 3 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
#include "includes.h"
|
|
#include "utils/net.h"
|
|
|
|
#include "lua-5.1.4/src/lualib.h"
|
|
#include "lua-5.1.4/src/lauxlib.h"
|
|
|
|
#define SOCK_METATABLE "cade1208-9029-4d76-8748-426dfc1436f7"
|
|
|
|
struct sock_userdata {
|
|
int fd;
|
|
};
|
|
|
|
static int sock_userdata_gc(lua_State *L)
|
|
{
|
|
struct sock_userdata *p = (struct sock_userdata *)
|
|
luaL_checkudata(L, 1, SOCK_METATABLE);
|
|
close(p->fd);
|
|
return 0;
|
|
}
|
|
|
|
static int sock_userdata_tostring(lua_State *L)
|
|
{
|
|
struct sock_userdata *p = (struct sock_userdata *)
|
|
luaL_checkudata(L, 1, SOCK_METATABLE);
|
|
|
|
lua_pushfstring(L, "socket: %d", p->fd);
|
|
return 1;
|
|
}
|
|
|
|
static int sock_userdata_connect(lua_State *L)
|
|
{
|
|
struct sock_userdata *p = (struct sock_userdata *)
|
|
luaL_checkudata(L, 1, SOCK_METATABLE);
|
|
const char *hostname;
|
|
int port;
|
|
struct sockaddr_in addr;
|
|
int res;
|
|
|
|
if (!lua_isstring(L, 2)) {
|
|
luaL_error(L, "connect: Expected IP-Address");
|
|
}
|
|
hostname = lua_tostring(L, 2);
|
|
|
|
if (!lua_isnumber(L, 3)) {
|
|
luaL_error(L, "connect: Expected port");
|
|
}
|
|
port = lua_tointeger(L, 3);
|
|
|
|
if (lua_gettop(L) == 4) {
|
|
/*
|
|
* Here we expect an event context in the last argument to
|
|
* make connect() asynchronous.
|
|
*/
|
|
}
|
|
|
|
addr.sin_family = AF_INET;
|
|
inet_aton(hostname, &addr.sin_addr);
|
|
addr.sin_port = htons(port);
|
|
|
|
res = connect(p->fd, (struct sockaddr *)&addr, sizeof(addr));
|
|
if (res == -1) {
|
|
int err = errno;
|
|
lua_pushnil(L);
|
|
lua_pushfstring(L, "connect failed: %s", strerror(err));
|
|
return 2;
|
|
}
|
|
|
|
lua_pushboolean(L, 1);
|
|
return 1;
|
|
}
|
|
|
|
static const struct luaL_Reg sock_methods[] = {
|
|
{"__gc", sock_userdata_gc},
|
|
{"__tostring", sock_userdata_tostring},
|
|
{"connect", sock_userdata_connect},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static const struct {
|
|
const char *name;
|
|
int domain;
|
|
} socket_domains[] = {
|
|
{"PF_UNIX", PF_UNIX},
|
|
{"PF_INET", PF_INET},
|
|
{NULL, 0},
|
|
};
|
|
|
|
static const struct {
|
|
const char *name;
|
|
int type;
|
|
} socket_types[] = {
|
|
{"SOCK_STREAM", SOCK_STREAM},
|
|
{"SOCK_DGRAM", SOCK_DGRAM},
|
|
{NULL, 0},
|
|
};
|
|
|
|
static int sock_userdata_new(lua_State *L)
|
|
{
|
|
struct sock_userdata *result;
|
|
const char *domain_str = luaL_checkstring(L, 1);
|
|
const char *type_str = luaL_checkstring(L, 2);
|
|
int i, domain, type;
|
|
|
|
i = 0;
|
|
while (socket_domains[i].name != NULL) {
|
|
if (strcmp(domain_str, socket_domains[i].name) == 0) {
|
|
break;
|
|
}
|
|
i += 1;
|
|
}
|
|
if (socket_domains[i].name == NULL) {
|
|
return luaL_error(L, "socket domain %s unknown", domain_str);
|
|
}
|
|
domain = socket_domains[i].domain;
|
|
|
|
i = 0;
|
|
while (socket_types[i].name != NULL) {
|
|
if (strcmp(type_str, socket_types[i].name) == 0) {
|
|
break;
|
|
}
|
|
i += 1;
|
|
}
|
|
if (socket_types[i].name == NULL) {
|
|
return luaL_error(L, "socket type %s unknown", type_str);
|
|
}
|
|
type = socket_types[i].type;
|
|
|
|
result = (struct sock_userdata *)lua_newuserdata(L, sizeof(*result));
|
|
ZERO_STRUCTP(result);
|
|
|
|
result->fd = socket(domain, type, 0);
|
|
if (result->fd == -1) {
|
|
int err = errno;
|
|
lua_pushnil(L);
|
|
lua_pushfstring(L, "socket() failed: %s", strerror(errno));
|
|
lua_pushinteger(L, err);
|
|
return 3;
|
|
}
|
|
|
|
luaL_getmetatable(L, SOCK_METATABLE);
|
|
lua_setmetatable(L, -2);
|
|
return 1;
|
|
}
|
|
|
|
static const struct luaL_Reg sock_funcs[] = {
|
|
{"new", sock_userdata_new},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static int sock_lua_init(lua_State *L, const char *libname) {
|
|
luaL_newmetatable(L, SOCK_METATABLE);
|
|
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
luaL_register(L, NULL, sock_methods);
|
|
luaL_register(L, libname, sock_funcs);
|
|
return 1;
|
|
}
|
|
|
|
#define EVT_METATABLE "c42e0642-b24a-40f0-8483-d8eb4aee9ea3"
|
|
|
|
/*
|
|
* The userdata we allocate from lua when a new event context is created
|
|
*/
|
|
struct evt_userdata {
|
|
struct event_context *ev;
|
|
};
|
|
|
|
static bool evt_is_main_thread(lua_State *L) {
|
|
int ret;
|
|
|
|
ret = lua_pushthread(L);
|
|
lua_pop(L, 1);
|
|
return (ret != 0);
|
|
}
|
|
|
|
/*
|
|
* Per event we allocate a struct thread_reference to keep the coroutine from
|
|
* being garbage-collected. This is also the hook to find the right thread to
|
|
* be resumed.
|
|
*/
|
|
|
|
struct thread_reference {
|
|
struct lua_State *L;
|
|
/*
|
|
* Reference to the Thread (i.e. lua_State) this event is hanging on
|
|
*/
|
|
int thread_ref;
|
|
};
|
|
|
|
static int thread_reference_destructor(struct thread_reference *ref)
|
|
{
|
|
luaL_unref(ref->L, LUA_REGISTRYINDEX, ref->thread_ref);
|
|
return 0;
|
|
}
|
|
|
|
static struct thread_reference *evt_reference_thread(TALLOC_CTX *mem_ctx,
|
|
lua_State *L)
|
|
{
|
|
struct thread_reference *result;
|
|
|
|
result = talloc(mem_ctx, struct thread_reference);
|
|
if (result == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
lua_pushthread(L);
|
|
result->thread_ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
result->L = L;
|
|
talloc_set_destructor(result, thread_reference_destructor);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int evt_userdata_gc(lua_State *L)
|
|
{
|
|
struct evt_userdata *p = (struct evt_userdata *)
|
|
luaL_checkudata(L, 1, EVT_METATABLE);
|
|
TALLOC_FREE(p->ev);
|
|
return 0;
|
|
}
|
|
|
|
static int evt_userdata_tostring(lua_State *L) {
|
|
lua_pushstring(L, "event context");
|
|
return 1;
|
|
}
|
|
|
|
static void evt_userdata_sleep_done(struct event_context *event_ctx,
|
|
struct timed_event *te,
|
|
struct timeval now,
|
|
void *priv)
|
|
{
|
|
struct thread_reference *ref = talloc_get_type_abort(
|
|
priv, struct thread_reference);
|
|
lua_resume(ref->L, 0);
|
|
TALLOC_FREE(ref);
|
|
}
|
|
|
|
static int evt_userdata_sleep(lua_State *L)
|
|
{
|
|
struct evt_userdata *p = (struct evt_userdata *)
|
|
luaL_checkudata(L, 1, EVT_METATABLE);
|
|
lua_Integer usecs = luaL_checkint(L, 2);
|
|
struct thread_reference *ref;
|
|
struct timed_event *te;
|
|
|
|
if (evt_is_main_thread(L)) {
|
|
/*
|
|
* Block in the main thread
|
|
*/
|
|
smb_msleep(usecs/1000);
|
|
return 0;
|
|
}
|
|
|
|
ref = evt_reference_thread(p->ev, L);
|
|
if (ref == NULL) {
|
|
return luaL_error(L, "evt_reference_thread failed\n");
|
|
}
|
|
|
|
te = event_add_timed(p->ev, ref, timeval_current_ofs(0, usecs),
|
|
evt_userdata_sleep_done,
|
|
ref);
|
|
|
|
if (te == NULL) {
|
|
TALLOC_FREE(ref);
|
|
return luaL_error(L, "event_add_timed failed");
|
|
}
|
|
|
|
return lua_yield(L, 0);
|
|
}
|
|
|
|
static int evt_userdata_once(lua_State *L)
|
|
{
|
|
struct evt_userdata *p = (struct evt_userdata *)
|
|
luaL_checkudata(L, 1, EVT_METATABLE);
|
|
|
|
if (!evt_is_main_thread(L)) {
|
|
return luaL_error(L, "event_once called from non-base thread");
|
|
}
|
|
|
|
lua_pushinteger(L, event_loop_once(p->ev));
|
|
return 1;
|
|
}
|
|
|
|
static const struct luaL_Reg evt_methods[] = {
|
|
{"__gc", evt_userdata_gc},
|
|
{"__tostring", evt_userdata_tostring},
|
|
{"sleep", evt_userdata_sleep},
|
|
{"once", evt_userdata_once},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static int evt_userdata_new(lua_State *L) {
|
|
struct evt_userdata *result;
|
|
|
|
result = (struct evt_userdata *)lua_newuserdata(L, sizeof(*result));
|
|
ZERO_STRUCTP(result);
|
|
|
|
result->ev = event_context_init(NULL);
|
|
if (result->ev == NULL) {
|
|
return luaL_error(L, "event_context_init failed");
|
|
}
|
|
|
|
luaL_getmetatable(L, EVT_METATABLE);
|
|
lua_setmetatable(L, -2);
|
|
return 1;
|
|
}
|
|
|
|
static const struct luaL_Reg evt_funcs[] = {
|
|
{"new", evt_userdata_new},
|
|
{NULL, NULL}
|
|
};
|
|
|
|
static int evt_lua_init(lua_State *L, const char *libname) {
|
|
luaL_newmetatable(L, EVT_METATABLE);
|
|
|
|
lua_pushvalue(L, -1);
|
|
lua_setfield(L, -2, "__index");
|
|
|
|
luaL_register(L, NULL, evt_methods);
|
|
luaL_register(L, libname, evt_funcs);
|
|
return 1;
|
|
}
|
|
|
|
int net_lua(struct net_context *c, int argc, const char **argv)
|
|
{
|
|
lua_State *state;
|
|
|
|
state = lua_open();
|
|
if (state == NULL) {
|
|
d_fprintf(stderr, "lua_newstate failed\n");
|
|
return -1;
|
|
}
|
|
|
|
luaL_openlibs(state);
|
|
evt_lua_init(state, "event");
|
|
sock_lua_init(state, "socket");
|
|
|
|
while (1) {
|
|
char *line = NULL;
|
|
|
|
line = smb_readline("lua> ", NULL, NULL);
|
|
if (line == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (line[0] == ':') {
|
|
if (luaL_dofile(state, &line[1])) {
|
|
d_printf("luaL_dofile returned an error\n");
|
|
continue;
|
|
}
|
|
} else if (line[0] != '\n') {
|
|
if (luaL_dostring(state, line) != 0) {
|
|
d_printf("luaL_dostring returned an error\n");
|
|
}
|
|
}
|
|
|
|
SAFE_FREE(line);
|
|
}
|
|
|
|
lua_close(state);
|
|
return -1;
|
|
}
|