/******************************************************************************* * Copyright (c) 2007, 2010 Wind River Systems, Inc. and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and Eclipse Distribution License v1.0 which accompany this distribution. * The Eclipse Public License is available at * http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * You may elect to redistribute this code under either of these licenses. * * Contributors: * Wind River Systems - initial API and implementation *******************************************************************************/ /* * Agent main module. */ #include #include #include #include #if ENABLE_LUA #include #include #include #ifdef __cplusplus extern "C" { #endif #include #include #ifdef __cplusplus } #endif #include #include #include #include #include #include #include #include #include static const char * progname; static lua_State *luastate; struct luaref { int ref; void *owner; struct luaref *next; }; enum { bufst_normal, bufst_normal_optnl, bufst_esc, bufst_esc2, bufst_eol, bufst_eol_optnl }; struct lua_read_command_state { int reqline; int reqdata; struct luaref *refp; AsyncReqInfo req; int eof; int bufrd; int bufwr; int bufst; char buf[1024]; int linemax; int lineind; char *line; }; struct peer_extra { lua_State *L; PeerServer * ps; int managed; }; struct protocol_extra { lua_State *L; Protocol * p; unsigned int ucnt; struct luaref *self_refp; }; struct channel_extra { lua_State *L; Channel * c; struct protocol_extra * pe; struct luaref *self_refp; struct luaref *connect_cbrefp; struct luaref *connecting_cbrefp; struct luaref *connected_cbrefp; struct luaref *receive_cbrefp; struct luaref *disconnected_cbrefp; }; struct command_extra { struct luaref *self_refp; struct luaref *result_cbrefp; ReplyHandlerInfo * replyinfo; }; struct post_event_extra { lua_State *L; struct luaref *self_refp; struct luaref *handler_refp; }; static struct luaref *refroot; static struct luaref *peers_refp; static struct lua_read_command_state lua_read_command_state; static void lua_read_command_fillbuf(struct lua_read_command_state *state); static struct luaref *luaref_new(lua_State *L, void * owner) { struct luaref *refp = (struct luaref *)loc_alloc(sizeof *refp); refp->ref = luaL_ref(L, LUA_REGISTRYINDEX); refp->owner = owner; refp->next = refroot; refroot = refp; return refp; } static void luaref_free(lua_State *L, struct luaref *p) { struct luaref **refpp; struct luaref *refp; refpp = &refroot; while((refp = *refpp) != NULL) { if(refp == p) { *refpp = refp->next; luaL_unref(L, LUA_REGISTRYINDEX, refp->ref); loc_free(refp); return; } refpp = &refp->next; } assert(!"lua reference not found in list"); } static void luaref_owner_free(lua_State *L, void * owner) { struct luaref **refpp; struct luaref *refp; refpp = &refroot; while((refp = *refpp) != NULL) { if(refp->owner == owner) { *refpp = refp->next; luaL_unref(L, LUA_REGISTRYINDEX, refp->ref); loc_free(refp); continue; } refpp = &refp->next; } } #ifndef NDEBUG static int lua_isclass(lua_State *L, int index, const char *name) { int rval; if(!lua_getmetatable(L, index)) return 0; lua_getfield(L, LUA_REGISTRYINDEX, name); rval = lua_rawequal(L, -1, -2); lua_pop(L, 2); return rval; } #endif static struct peer_extra *lua2peer(lua_State *L, int index) { if(luaL_checkudata(L, index, "tcf_peer") == NULL) { return NULL; } return (struct peer_extra *)lua_touserdata(L, index); } static struct protocol_extra *lua2protocol(lua_State *L, int index) { if(luaL_checkudata(L, index, "tcf_protocol") == NULL) { return NULL; } return (struct protocol_extra *)lua_touserdata(L, index); } static struct channel_extra *lua2channel(lua_State *L, int index) { if(luaL_checkudata(L, index, "tcf_channel") == NULL) { return NULL; } return (struct channel_extra *)lua_touserdata(L, index); } static struct post_event_extra *lua2postevent(lua_State *L, int index) { if(luaL_checkudata(L, index, "tcf_post_event") == NULL) { return NULL; } return (struct post_event_extra *)lua_touserdata(L, index); } static void lua_read_command_getline(void *client_data) { int c; lua_State *L = luastate; struct lua_read_command_state *state = (struct lua_read_command_state *)client_data; assert(state->reqline != 0); assert(state->reqdata == 0); while(state->bufrd < state->bufwr) { c = state->buf[state->bufrd++]; switch(state->bufst) { case bufst_normal: case_bufst_normal: if(c == '\\') { state->bufst = bufst_esc; continue; } if(c == '\r') { state->bufst = bufst_eol_optnl; goto eol; } if(c == '\n') { state->bufst = bufst_eol; goto eol; } break; case bufst_normal_optnl: if(c == '\n') { state->bufst = bufst_normal; continue; } goto case_bufst_normal; case bufst_esc: if(c == '\r') { state->bufst = bufst_normal_optnl; c = '\n'; break; } if(c == '\n') { state->bufst = bufst_normal; break; } state->bufst = bufst_esc2; state->bufrd--; c = '\\'; break; case bufst_esc2: state->bufst = bufst_normal; break; case bufst_eol: state->bufst = bufst_normal; goto case_bufst_normal; case bufst_eol_optnl: state->bufst = bufst_normal; if(c == '\n') continue; goto case_bufst_normal; default: assert(!"unexpected state"); } if(state->lineind == state->linemax) { if(state->linemax == 0) { state->linemax = 1024; state->line = (char *)loc_alloc(state->linemax); } else { state->linemax *= 2; state->line = (char *)loc_realloc(state->line, state->linemax); } } state->line[state->lineind++] = (char)c; } eol: if(state->bufst != bufst_eol_optnl && state->bufst != bufst_eol && !state->eof) { assert(state->bufrd == state->bufwr); lua_read_command_fillbuf(state); return; } state->reqline = 0; assert(state->refp != NULL); lua_rawgeti(L, LUA_REGISTRYINDEX, state->refp->ref); luaref_free(L, state->refp); state->refp = NULL; if(state->lineind > 0 || !state->eof) { trace(LOG_LUA, "lua_read_command: %.*s", state->lineind, state->line); lua_pushlstring(L, state->line, state->lineind); state->lineind = 0; } else { trace(LOG_LUA, "lua_read_command: EOF"); lua_pushnil(L); } if(lua_pcall(L, 1, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } return; } static void lua_read_command_done(void *client_data) { AsyncReqInfo *req = (AsyncReqInfo *)client_data; struct lua_read_command_state *state = (struct lua_read_command_state *)req->client_data; assert(state->reqdata != 0); state->reqdata = 0; if(state->req.u.fio.rval > 0) { state->bufwr += state->req.u.fio.rval; state->eof = 0; } else { state->eof = 1; } lua_read_command_getline(state); } static void lua_read_command_fillbuf(struct lua_read_command_state *state) { assert(state->reqdata == 0); assert(state->bufrd == state->bufwr); state->reqdata = 1; state->bufrd = 0; state->bufwr = 0; state->req.done = lua_read_command_done; state->req.client_data = state; state->req.type = AsyncReqRead; state->req.u.fio.bufp = state->buf; state->req.u.fio.bufsz = sizeof state->buf; async_req_post(&state->req); } static int lua_read_command(lua_State *L) { struct lua_read_command_state *state = &lua_read_command_state; assert(L == luastate); assert(state->reqline == 0); if(lua_gettop(L) != 1 || !lua_isfunction(L, 1)) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_read_command start"); lua_pushvalue(L, 1); state->refp = luaref_new(L, state); state->reqline = 1; if(state->bufrd == state->bufwr) { lua_read_command_fillbuf(state); } else { post_event(lua_read_command_getline, state); } return 0; } static struct peer_extra *lookup_pse(lua_State *L, PeerServer *ps) { struct peer_extra *pse; lua_rawgeti(L, LUA_REGISTRYINDEX, peers_refp->ref); lua_pushstring(L, ps->id); /* Key */ lua_rawget(L, -2); if((pse = (struct peer_extra *)lua_touserdata(L, -1)) == NULL) { assert(lua_isnil(L, -1)); lua_pop(L, 2); return NULL; } assert(lua_isclass(L, -1, "tcf_peer")); lua_pop(L, 2); return pse; } static struct peer_extra *lua_alloc_pse(lua_State *L, PeerServer *ps) { struct peer_extra *pse; /* Allocate new LUA object for peer */ pse = (struct peer_extra *)lua_newuserdata(L, sizeof *pse); memset(pse, 0, sizeof *pse); pse->L = L; pse->ps = ps; luaL_getmetatable(L, "tcf_peer"); lua_setmetatable(L, -2); /* Returns userdata for peer on LUA stack */ return pse; } static struct peer_extra *lua_push_pse(lua_State *L, PeerServer *ps) { struct peer_extra *pse; lua_rawgeti(L, LUA_REGISTRYINDEX, peers_refp->ref); lua_pushstring(L, ps->id); /* Key */ lua_rawget(L, -2); if((pse = (struct peer_extra *)lua_touserdata(L, -1)) != NULL) { assert(lua_isclass(L, -1, "tcf_peer")); assert(pse->managed); } else { assert(lua_isnil(L, -1)); lua_pop(L, 1); pse = lua_alloc_pse(L, ps); pse->managed = 1; /* Register mapping from PS to LUA object */ lua_pushstring(L, ps->id); /* Key */ lua_pushvalue(L, -2); /* Value */ lua_rawset(L, -4); /* peers[id] = pse */ } lua_remove(L, -2); /* Remove peers table */ /* Returns userdata for peer on LUA stack */ return pse; } static void peer_server_changes(PeerServer *ps, int changeType, void * client_data) { lua_State *L = (lua_State *)client_data; struct peer_extra *pse = lookup_pse(L, ps); switch(changeType) { case PS_EVENT_ADDED: assert(pse == NULL); break; case PS_EVENT_HEART_BEAT: break; case PS_EVENT_CHANGED: if(pse != NULL) pse->ps = ps; break; case PS_EVENT_REMOVED: if(pse != NULL) pse->ps = NULL; break; default: assert(!"unknown peer change type"); } } static int lua_peer_server_find(lua_State *L) { PeerServer *ps; const char *name; assert(L == luastate); if(lua_gettop(L) != 1 || !lua_isstring(L, 1)) { luaL_error(L, "wrong number or type of arguments"); } name = lua_tostring(L, 1); ps = peer_server_find(name); trace(LOG_LUA, "lua_peer_server_find %s %p", name, ps); if(ps == NULL) { lua_pushnil(L); return 1; } lua_push_pse(L, ps); return 1; } static int lua_peer_server_list_entry(PeerServer * ps, void * client_data) { lua_State *L = (lua_State *)client_data; lua_push_pse(L, ps); lua_rawseti(L, -2, lua_objlen(L, -2) + 1); return 0; } static int lua_peer_server_list(lua_State *L) { assert(L == luastate); if(lua_gettop(L) != 0) { luaL_error(L, "wrong number of arguments"); } trace(LOG_LUA, "lua_peer_server_list"); lua_newtable(L); peer_server_iter(lua_peer_server_list_entry, L); return 1; } static int lua_peer_server_from_url(lua_State *L) { PeerServer *ps; const char *url; assert(L == luastate); if(lua_gettop(L) != 1 || !lua_isstring(L, 1)) { luaL_error(L, "wrong number or type of arguments"); } url = lua_tostring(L, 1); ps = channel_peer_from_url(url); trace(LOG_LUA, "lua_peer_server_from_url %s %p", url, ps); if(ps == NULL) luaL_error(L, "cannot parse url: %s", lua_tostring(L, 1)); if(ps->id == NULL) { peer_server_addprop(ps, loc_strdup("ID"), loc_strdup(lua_tostring(L, 1))); } lua_alloc_pse(L, ps); return 1; } static struct protocol_extra *lua_protocol_ref(struct protocol_extra *pe, int index) { if(pe->ucnt == 0) { assert(lua_touserdata(pe->L, index) == pe); lua_pushvalue(pe->L, index); /* Prevent GC while connection is active */ pe->self_refp = luaref_new(pe->L, pe); } pe->ucnt++; return pe; } static void lua_protocol_unref(struct protocol_extra *pe) { assert(pe->ucnt > 0); assert(pe->self_refp != NULL); pe->ucnt--; if(pe->ucnt == 0) { luaref_free(pe->L, pe->self_refp); pe->self_refp = NULL; } } static int lua_protocol_alloc(lua_State *L) { struct protocol_extra *pe; assert(L == luastate); if(lua_gettop(L) != 0) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_protocol_alloc"); pe = (struct protocol_extra *)lua_newuserdata(L, sizeof *pe); memset(pe, 0, sizeof *pe); pe->L = L; pe->p = protocol_alloc(); luaL_getmetatable(L, "tcf_protocol"); lua_setmetatable(L, -2); return 1; } static int lua_channel_server(lua_State *L) { trace(LOG_LUA, "lua_channel_server"); luaL_error(L, "not implemented"); return 0; } static void channel_connecting(Channel * c) { struct channel_extra *ce = (struct channel_extra *)c->client_data; lua_State *L = ce->L; if(ce->connecting_cbrefp != NULL) { lua_rawgeti(L, LUA_REGISTRYINDEX, ce->connecting_cbrefp->ref); if(lua_pcall(L, 0, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } trace(LOG_LUA, "lua_channel_connecting %p", c); send_hello_message(c); } static void channel_connected(Channel * c) { struct channel_extra *ce = (struct channel_extra *)c->client_data; lua_State *L = ce->L; trace(LOG_LUA, "lua_channel_connected %p", c); if(ce->connected_cbrefp != NULL) { lua_rawgeti(L, LUA_REGISTRYINDEX, ce->connected_cbrefp->ref); if(lua_pcall(L, 0, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } } static void channel_receive(Channel * c) { struct channel_extra *ce = (struct channel_extra *)c->client_data; lua_State *L = ce->L; trace(LOG_LUA, "lua_channel_receive %p", c); if(ce->receive_cbrefp != NULL) { lua_rawgeti(L, LUA_REGISTRYINDEX, ce->receive_cbrefp->ref); if(lua_pcall(L, 0, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } handle_protocol_message(c); } static void channel_disconnected(Channel * c) { struct channel_extra *ce = (struct channel_extra *)c->client_data; lua_State *L = ce->L; trace(LOG_LUA, "lua_channel_disconnected %p", c); if(ce->disconnected_cbrefp != NULL) { lua_rawgeti(L, LUA_REGISTRYINDEX, ce->disconnected_cbrefp->ref); if(lua_pcall(L, 0, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } lua_protocol_unref(ce->pe); ce->pe = NULL; c->client_data = NULL; ce->c = NULL; luaref_owner_free(L, ce); } static void lua_channel_connect_cb(void * client_data, int error, Channel * c) { struct channel_extra *ce = (struct channel_extra *)client_data; lua_State *L = ce->L; trace(LOG_LUA, "lua_channel_connect_cb %p %d", c, error); assert(ce->connect_cbrefp != NULL); lua_rawgeti(L, LUA_REGISTRYINDEX, ce->connect_cbrefp->ref); if(!error) { assert(c != NULL); ce->c = c; c->client_data = ce; c->protocol = ce->pe->p; c->connecting = channel_connecting; c->connected = channel_connected; c->receive = channel_receive; c->disconnected = channel_disconnected; lua_rawgeti(L, LUA_REGISTRYINDEX, ce->self_refp->ref); lua_pushnil(L); } else { assert(c == NULL); luaref_owner_free(L, ce); lua_pushnil(L); lua_pushstring(L, errno_to_str(error)); } if(lua_pcall(L, 2, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } static int lua_channel_connect(lua_State *L) { struct peer_extra *pse = NULL; struct protocol_extra *pe = NULL; struct channel_extra *ce; assert(L == luastate); if(lua_gettop(L) != 3 || (pse = lua2peer(L, 1)) == NULL || (pe = lua2protocol(L, 2)) == NULL || !lua_isfunction(L, 3)) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_channel_connect %p", pse->ps); if(pse->ps == NULL) luaL_error(L, "stale peer"); ce = (struct channel_extra *)lua_newuserdata(L, sizeof *ce); memset(ce, 0, sizeof *ce); lua_pushvalue(L, -1); /* Prevent GC while connection is active */ ce->self_refp = luaref_new(L, ce); ce->L = L; ce->pe = lua_protocol_ref(pe, 2); luaL_getmetatable(L, "tcf_channel"); lua_setmetatable(L, -2); lua_pushvalue(L, 3); ce->connect_cbrefp = luaref_new(L, ce); channel_connect(pse->ps, lua_channel_connect_cb, ce); return 0; } static void lua_post_event_cb(void * client_data) { struct post_event_extra *p = (struct post_event_extra *)client_data; lua_State *L = p->L; assert(p->handler_refp != NULL); trace(LOG_LUA, "lua_post_event_cb %d", p->handler_refp); lua_rawgeti(L, LUA_REGISTRYINDEX, p->handler_refp->ref); luaref_owner_free(L, p); if(lua_pcall(L, 0, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } static int lua_post_event(lua_State *L) { struct post_event_extra *p; unsigned long delay; assert(L == luastate); if(lua_gettop(L) > 2 || !lua_isfunction(L, 1) || (lua_gettop(L) > 1 && !(lua_isnil(L, 2) || lua_isnumber(L, 2)))) { luaL_error(L, "wrong number or type of arguments"); } p = (struct post_event_extra *)lua_newuserdata(L, sizeof *p); memset(p, 0, sizeof *p); p->L = L; lua_pushvalue(L, -1); p->self_refp = luaref_new(L, p); lua_pushvalue(L, 1); p->handler_refp = luaref_new(L, p); trace(LOG_LUA, "lua_post_event %d", p->handler_refp); luaL_getmetatable(L, "tcf_post_event"); lua_setmetatable(L, -2); delay = lua_tointeger(L, 2); if(delay == 0) { post_event(lua_post_event_cb, p); } else { post_event_with_delay(lua_post_event_cb, p, delay); } return 1; } static const luaL_Reg tcffuncs[] = { { "read_command", lua_read_command }, { "peer_server_find", lua_peer_server_find }, { "peer_server_list", lua_peer_server_list }, { "peer_server_from_url",lua_peer_server_from_url }, { "protocol_alloc", lua_protocol_alloc }, { "channel_server", lua_channel_server }, { "channel_connect", lua_channel_connect }, { "post_event", lua_post_event }, { 0 } }; static int lua_protocol_tostring(lua_State *L) { struct protocol_extra *pe = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (pe = lua2protocol(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_protocol_tostring %p", pe->p); lua_pushfstring(L, "tcf_protocol (%p, %d)", pe, pe->ucnt); return 1; } static int lua_protocol_gc(lua_State *L) { struct protocol_extra *pe = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (pe = lua2protocol(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } assert(pe->ucnt == 0); assert(pe->p != NULL); trace(LOG_LUA, "lua_protocol_gc %p", pe->p); protocol_release(pe->p); pe->p = NULL; return 0; } static void protocol_command_handler(char * token, Channel * c, void * client_data) { struct channel_extra *ce = (struct channel_extra *)c->client_data; struct luaref *refp = (struct luaref *)client_data; lua_State *L = ce->L; InputStream * inp = &c->inp; luaL_Buffer msg; int ch; lua_rawgeti(L, LUA_REGISTRYINDEX, refp->ref); lua_pushstring(L, token); luaL_buffinit(L, &msg); while((ch = read_stream(inp)) >= 0) { luaL_addchar(&msg, ch); } luaL_pushresult(&msg); trace(LOG_LUA, "lua_protocol_command %d %s", refp->ref, lua_tostring(L, -1)); if(lua_pcall(L, 2, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } static int lua_protocol_command_handler(lua_State *L) { struct protocol_extra *pe = NULL; struct luaref *refp; const char *service; const char *name; assert(L == luastate); if(lua_gettop(L) != 4 || (pe = lua2protocol(L, 1)) == NULL || !lua_isstring(L, 2) || !lua_isstring(L, 3) || !lua_isfunction(L, 4)) { luaL_error(L, "wrong number or type of arguments"); } lua_pushvalue(L, 4); refp = luaref_new(L, pe); service = lua_tostring(L, 2); name = lua_tostring(L, 3); trace(LOG_LUA, "lua_protocol_command_handler %d %s %s", refp->ref, service, name); add_command_handler2(pe->p, service, name, protocol_command_handler, refp); return 0; } static const luaL_Reg protocolfuncs[] = { { "__tostring", lua_protocol_tostring }, { "__gc", lua_protocol_gc }, { "command_handler", lua_protocol_command_handler }, // { "default_message_handler", lua_protocol_default_message_handler }, { 0 } }; static const char *channel_state_string(Channel * c) { if (c == NULL) return "Disconnected"; switch(c->state) { case ChannelStateStartWait: return "StartWait"; case ChannelStateStarted: return "Started"; case ChannelStateHelloSent: return "HelloSent"; case ChannelStateHelloReceived: return "HelloReceived"; case ChannelStateConnected: return "Connected"; case ChannelStateRedirectSent: return "RedirectSent"; case ChannelStateRedirectReceived: return "RedirectReceived"; case ChannelStateDisconnected: return "Disconnected"; default: return "Unknown"; } } static int lua_channel_tostring(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_channel_tostring %p", ce->c); if(ce->c != NULL) { lua_pushfstring(L, "tcf_channel (%s, %p, %s)", ce->c->peer_name, ce, channel_state_string(ce->c)); } else { lua_pushfstring(L, "tcf_channel (, %p)", ce); } return 1; } static int lua_channel_state(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_channel_state %p", ce->c); lua_pushstring(L, channel_state_string(ce->c)); return 1; } static int lua_channel_close(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_channel_close %p", ce->c); if(ce->c == NULL) luaL_error(L, "disconnected channel"); channel_close(ce->c); return 0; } static void update_callback(lua_State *L, int index, struct luaref **cbrefpp, void * owner) { if(!lua_isfunction(L, index) && !lua_isnil(L, index)) { luaL_error(L, "handler must be function or nil"); } if(*cbrefpp != NULL) { luaref_free(L, *cbrefpp); *cbrefpp = NULL; } if(lua_isfunction(L, index)) { lua_pushvalue(L, index); *cbrefpp = luaref_new(L, owner); } } static int lua_channel_connecting_handler(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 2 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); update_callback(L, 2, &ce->connecting_cbrefp, ce); trace(LOG_LUA, "lua_channel_connecting_handler %p", ce->c, ce->connecting_cbrefp->ref); return 0; } static int lua_channel_connected_handler(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 2 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); update_callback(L, 2, &ce->connected_cbrefp, ce); trace(LOG_LUA, "lua_channel_connected_handler %p", ce->c, ce->connected_cbrefp->ref); return 0; } static int lua_channel_receive_handler(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 2 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); update_callback(L, 2, &ce->receive_cbrefp, ce); trace(LOG_LUA, "lua_channel_receive_handler %p", ce->c, ce->receive_cbrefp->ref); return 0; } static int lua_channel_disconnected_handler(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 2 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); update_callback(L, 2, &ce->disconnected_cbrefp, ce); trace(LOG_LUA, "lua_channel_disconnected_handler %p", ce->c, ce->disconnected_cbrefp->ref); return 0; } static void channel_event_handler(Channel * c, void * client_data) { struct channel_extra *ce = (struct channel_extra *)c->client_data; struct luaref *refp = (struct luaref *)client_data; lua_State *L = ce->L; InputStream * inp = &c->inp; luaL_Buffer msg; int ch; lua_rawgeti(L, LUA_REGISTRYINDEX, refp->ref); luaL_buffinit(L, &msg); while((ch = read_stream(inp)) >= 0) { luaL_addchar(&msg, ch); } luaL_pushresult(&msg); trace(LOG_LUA, "lua_channel_event %p %d %s", c, refp->ref, lua_tostring(L, -1)); if(lua_pcall(L, 1, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } } static int lua_channel_event_handler(lua_State *L) { struct channel_extra *ce = NULL; struct luaref *refp; const char *service; const char *name; assert(L == luastate); if(lua_gettop(L) != 4 || (ce = lua2channel(L, 1)) == NULL || !lua_isstring(L, 2) || !lua_isstring(L, 3) || !lua_isfunction(L, 4)) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); lua_pushvalue(L, 4); refp = luaref_new(L, ce); service = lua_tostring(L, 2); name = lua_tostring(L, 3); trace(LOG_LUA, "lua_channel_event_handler %p %s %s %d", ce->c, service, name, refp->ref); add_event_handler2(ce->c, service, name, channel_event_handler, refp); return 0; } static int lua_channel_start(lua_State *L) { struct channel_extra *ce = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); trace(LOG_LUA, "lua_channel_start %p", ce->c); channel_start(ce->c); return 0; } static int lua_channel_send_message(lua_State *L) { struct channel_extra *ce = NULL; OutputStream *out; const char *s; size_t l; assert(L == luastate); if(lua_gettop(L) != 2 || (ce = lua2channel(L, 1)) == NULL || !lua_isstring(L, 2)) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); s = lua_tolstring(L, 2, &l); trace(LOG_LUA, "lua_channel_send_message %p %.*s", ce->c, l, s); out = &ce->c->out; while(l-- > 0) { write_stream(out, (*s++) & 0xff); } write_stream(out, MARKER_EOM); return 0; } static void channel_send_command_cb(Channel * c, void * client_data, int error) { struct channel_extra *ce = (struct channel_extra *)c->client_data; struct command_extra *cmd = (struct command_extra *)client_data; lua_State *L = ce->L; InputStream * inp = &c->inp; luaL_Buffer msg; int ch; lua_rawgeti(L, LUA_REGISTRYINDEX, cmd->result_cbrefp->ref); if(!error) { luaL_buffinit(L, &msg); while((ch = read_stream(inp)) >= 0) { luaL_addchar(&msg, ch); } luaL_pushresult(&msg); lua_pushnil(L); trace(LOG_LUA, "lua_channel_send_command_reply %p %d %s", c, cmd->result_cbrefp->ref, lua_tostring(L, -2)); } else { lua_pushnil(L); lua_pushstring(L, errno_to_str(error)); trace(LOG_LUA, "lua_channel_send_command_reply %p %d error %d", c, cmd->result_cbrefp->ref, error); } if(lua_pcall(L, 2, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } luaref_owner_free(L, cmd); } static int lua_channel_send_command(lua_State *L) { struct channel_extra *ce = NULL; struct command_extra *cmd; OutputStream *out; const char *s; size_t l; assert(L == luastate); if(lua_gettop(L) != 5 || (ce = lua2channel(L, 1)) == NULL || !lua_isstring(L, 2) || !lua_isstring(L, 3) || !lua_isstring(L, 4) || !lua_isfunction(L, 5)) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); /* Object to track outstanding command */ cmd = (struct command_extra *)lua_newuserdata(L, sizeof *cmd); memset(cmd, 0, sizeof *cmd); luaL_getmetatable(L, "tcf_command"); lua_setmetatable(L, -2); /* Make sure GC don't free until reply is received */ lua_pushvalue(L, -1); cmd->self_refp = luaref_new(L, cmd); lua_pushvalue(L, 5); cmd->result_cbrefp = luaref_new(L, cmd); /* Send command header */ cmd->replyinfo = protocol_send_command(ce->c, lua_tostring(L, 2), lua_tostring(L, 3), channel_send_command_cb, cmd); s = lua_tolstring(L, 4, &l); trace(LOG_LUA, "lua_channel_send_command %p %d %.*s", ce->c, cmd->result_cbrefp->ref, l, s); out = &ce->c->out; while(l-- > 0) { write_stream(out, (*s++) & 0xff); } write_stream(out, MARKER_EOM); return 1; } static int lua_channel_cancel_command(lua_State *L) { trace(LOG_LUA, "lua_channel_cancel_command"); luaL_error(L, "not implemented"); return 0; } static void channel_redirect_cb(Channel * c, void * client_data, int error) { struct channel_extra *ce = (struct channel_extra *)c->client_data; struct command_extra *cmd = (struct command_extra *)client_data; lua_State *L = ce->L; lua_rawgeti(L, LUA_REGISTRYINDEX, cmd->result_cbrefp->ref); if(!error) { lua_pushnil(L); trace(LOG_LUA, "lua_channel_redirect_reply %p %d %s", c, cmd->result_cbrefp->ref, lua_tostring(L, -2)); } else { lua_pushstring(L, errno_to_str(error)); trace(LOG_LUA, "lua_channel_redirect_reply %p %d error %d", c, cmd->result_cbrefp->ref, error); } if(lua_pcall(L, 1, 0, 0) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } luaref_owner_free(L, cmd); } static int lua_channel_redirect(lua_State *L) { struct channel_extra *ce = NULL; struct command_extra *cmd; assert(L == luastate); if(lua_gettop(L) != 3 || (ce = lua2channel(L, 1)) == NULL || !lua_isstring(L, 2) || !lua_isfunction(L, 3)) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); /* Object to track outstanding command */ cmd = (struct command_extra *)lua_newuserdata(L, sizeof *cmd); memset(cmd, 0, sizeof *cmd); luaL_getmetatable(L, "tcf_command"); lua_setmetatable(L, -2); /* Make sure GC don't free until reply is received */ lua_pushvalue(L, -1); cmd->self_refp = luaref_new(L, cmd); lua_pushvalue(L, 3); cmd->result_cbrefp = luaref_new(L, cmd); /* Send command header */ cmd->replyinfo = send_redirect_command(ce->c, lua_tostring(L, 2), channel_redirect_cb, cmd); trace(LOG_LUA, "lua_channel_redirect %p %d %s", ce->c, cmd->result_cbrefp->ref, lua_tostring(L, 2)); return 1; } static int lua_channel_get_services(lua_State *L) { struct channel_extra *ce = NULL; int i; assert(L == luastate); if(lua_gettop(L) != 1 || (ce = lua2channel(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(ce->c == NULL) luaL_error(L, "disconnected channel"); trace(LOG_LUA, "lua_channel_get_services %p", ce->c); lua_newtable(L); for (i = 0; i < ce->c->peer_service_cnt; i++) { lua_pushstring(L, ce->c->peer_service_list[i]); lua_rawseti(L, -2, i + 1); } return 1; } static const luaL_Reg channelfuncs[] = { { "__tostring", lua_channel_tostring }, { "state", lua_channel_state }, { "close", lua_channel_close }, { "connecting_handler", lua_channel_connecting_handler }, { "connected_handler", lua_channel_connected_handler }, { "receive_handler", lua_channel_receive_handler }, { "disconnected_handler",lua_channel_disconnected_handler }, { "event_handler", lua_channel_event_handler }, { "start", lua_channel_start }, { "send_message", lua_channel_send_message }, { "send_command", lua_channel_send_command }, { "cancel_command", lua_channel_cancel_command }, { "redirect", lua_channel_redirect }, { "get_services", lua_channel_get_services }, { 0 } }; static int lua_peer_tostring(lua_State *L) { struct peer_extra *pse = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (pse = lua2peer(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_peer_tostring %p", pse->ps); lua_pushfstring(L, "tcf_peer (%s, %p)", pse->ps ? pse->ps->id : "", pse); return 1; } static int lua_peer_gc(lua_State *L) { struct peer_extra *pse = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (pse = lua2peer(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(pse->managed == 0) { trace(LOG_LUA, "lua_peer_gc %p", pse->ps); peer_server_free(pse->ps); pse->ps = NULL; } return 0; } static int lua_peer_getid(lua_State *L) { struct peer_extra *pse = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (pse = lua2peer(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(pse->ps == NULL) luaL_error(L, "stale peer"); trace(LOG_LUA, "lua_peer_getid %p", pse->ps); lua_pushstring(L, pse->ps->id); return 1; } static int lua_peer_getnames(lua_State *L) { int i; struct peer_extra *pse = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (pse = lua2peer(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(pse->ps == NULL) luaL_error(L, "stale peer"); trace(LOG_LUA, "lua_peer_getnames %p", pse->ps); lua_createtable(L, pse->ps->ind, 0); for(i = 0; i < pse->ps->ind; i++) { lua_pushstring(L, pse->ps->list[i].name); lua_rawseti(L, -2, i+1); } return 1; } static int lua_peer_getvalue(lua_State *L) { struct peer_extra *pse = NULL; const char *s; assert(L == luastate); if(lua_gettop(L) != 2 || (pse = lua2peer(L, 1)) == NULL || !lua_isstring(L, 2)) { luaL_error(L, "wrong number or type of arguments"); } if(pse->ps == NULL) luaL_error(L, "stale peer"); trace(LOG_LUA, "lua_peer_getvalue %p", pse->ps); if((s = peer_server_getprop(pse->ps, lua_tostring(L, 2), NULL)) == NULL) { lua_pushnil(L); return 1; } lua_pushstring(L, s); return 1; } static int lua_peer_setvalue(lua_State *L) { struct peer_extra *pse = NULL; assert(L == luastate); if(lua_gettop(L) != 3 || (pse = lua2peer(L, 1)) == NULL || !lua_isstring(L, 2) || !lua_isstring(L, 3)) { luaL_error(L, "wrong number or type of arguments"); } if(pse->ps == NULL) luaL_error(L, "stale peer"); trace(LOG_LUA, "lua_peer_setvalue %p", pse->ps); peer_server_addprop(pse->ps, loc_strdup(lua_tostring(L, 2)), loc_strdup(lua_tostring(L, 3))); return 0; } static int lua_peer_getflags(lua_State *L) { struct peer_extra *pse = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (pse = lua2peer(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } if(pse->ps == NULL) luaL_error(L, "stale peer"); trace(LOG_LUA, "lua_peer_getflags %p", pse->ps); lua_createtable(L, 0, 0); if(pse->ps->flags & PS_FLAG_LOCAL) { lua_pushstring(L, "local"); lua_pushboolean(L, 1); lua_rawset(L, -3); } if(pse->ps->flags & PS_FLAG_PRIVATE) { lua_pushstring(L, "private"); lua_pushboolean(L, 1); lua_rawset(L, -3); } if(pse->ps->flags & PS_FLAG_DISCOVERABLE) { lua_pushstring(L, "discoverable"); lua_pushboolean(L, 1); lua_rawset(L, -3); } return 1; } static int lua_peer_setflags(lua_State *L) { struct peer_extra *pse = NULL; assert(L == luastate); if(lua_gettop(L) != 2 || (pse = lua2peer(L, 1)) == NULL || !lua_istable(L, 2)) { luaL_error(L, "wrong number or type of arguments"); } if(pse->ps == NULL) luaL_error(L, "stale peer"); trace(LOG_LUA, "lua_peer_setflags %p", pse->ps); lua_pushnil(L); while(lua_next(L, 2) != 0) { if(lua_isstring(L, -2)) { if(strcmp(lua_tostring(L, -2), "local") == 0) { if(lua_toboolean(L, 2)) { pse->ps->flags |= PS_FLAG_LOCAL; } else { pse->ps->flags &= ~PS_FLAG_LOCAL; } } else if(strcmp(lua_tostring(L, -2), "private") == 0) { if(lua_toboolean(L, 2)) { pse->ps->flags |= PS_FLAG_PRIVATE; } else { pse->ps->flags &= ~PS_FLAG_PRIVATE; } } else if(strcmp(lua_tostring(L, -2), "discoverable") == 0) { if(lua_toboolean(L, 2)) { pse->ps->flags |= PS_FLAG_DISCOVERABLE; } else { pse->ps->flags &= ~PS_FLAG_DISCOVERABLE; } } } lua_pop(L, 1); } lua_pop(L, 1); return 0; } static const luaL_Reg peerfuncs[] = { { "__tostring", lua_peer_tostring }, { "__gc", lua_peer_gc }, { "getid", lua_peer_getid }, { "getnames", lua_peer_getnames }, { "getvalue", lua_peer_getvalue }, { "setvalue", lua_peer_setvalue }, { "getflags", lua_peer_getflags }, { "setflags", lua_peer_setflags }, { 0 } }; static int lua_post_event_tostring(lua_State *L) { struct post_event_extra *p = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (p = lua2postevent(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_post_event_tostring %p", p); lua_pushfstring(L, "tcf_post_event (%s, %p)", p->handler_refp ? "pending" : "stale", p); return 1; } static int lua_post_event_cancel(lua_State *L) { struct post_event_extra *p = NULL; assert(L == luastate); if(lua_gettop(L) != 1 || (p = lua2postevent(L, 1)) == NULL) { luaL_error(L, "wrong number or type of arguments"); } trace(LOG_LUA, "lua_post_event_cancel %p", p); p->self_refp = NULL; p->handler_refp = NULL; luaref_owner_free(L, p); cancel_event(lua_post_event_cb, p, 0); return 0; } static const luaL_Reg posteventfuncs[] = { { "__tostring", lua_post_event_tostring }, { "cancel", lua_post_event_cancel }, { 0 } }; /* * main entry point for TCF client * * The client is a simple shell permitting communication with the TCF agent. * By default the client will run in interactive mode. The client accepts * 3 command line options: * -L : specify a log file * -l : logging level see trace.c for more details * -S : script of commands to run - non-interactive mode */ #if defined(_WRS_KERNEL) int tcf_lua(void) { #else int main(int argc, char ** argv) { #endif int c; int ind; int error; int interactive = 1; const char * log_name = "-"; const char * script_name = NULL; char * engine_name; lua_State *L; log_mode = 0; #ifndef WIN32 signal(SIGPIPE, SIG_IGN); #endif ini_mdep(); ini_trace(); ini_events_queue(); ini_asyncreq(); #if defined(_WRS_KERNEL) progname = "tcf"; open_log_file("-"); #else progname = argv[0]; /* Parse arguments */ for (ind = 1; ind < argc; ind++) { const char * s = argv[ind]; if (*s != '-') { break; } s++; while ((c = *s++) != '\0') { switch (c) { case 'b': interactive = 0; break; case 'i': interactive = 1; break; case 'l': case 'L': case 'S': if (*s == '\0') { if (++ind >= argc) { fprintf(stderr, "%s: error: no argument given to option '%c'\n", progname, c); exit(1); } s = argv[ind]; } switch (c) { case 'l': log_mode = strtol(s, 0, 0); break; case 'L': log_name = s; break; case 'S': script_name = s; interactive = 0; break; default: fprintf(stderr, "%s: error: illegal option '%c'\n", progname, c); exit(1); } s = ""; break; default: fprintf(stderr, "%s: error: illegal option '%c'\n", progname, c); exit(1); } } } if (ind >= argc) { fprintf(stderr, "%s: error: no Lua script specified\n", progname); exit(1); } engine_name = argv[ind++]; if (ind < argc) { fprintf(stderr, "%s: error: too many arguments\n", progname); exit(1); } open_log_file(log_name); #endif if (script_name != NULL) { if((lua_read_command_state.req.u.fio.fd = open(script_name, O_RDONLY, 0)) < 0) { fprintf(stderr, "%s: error: cannot open script: %s\n", progname, script_name); exit(1); } } else { lua_read_command_state.req.u.fio.fd = fileno(stdin); } discovery_start(); if((luastate = L = luaL_newstate()) == NULL) { fprintf(stderr, "error from luaL_newstate\n"); exit(1); } luaL_openlibs(L); luaL_register(L, "tcf", tcffuncs); lua_pop(L, 1); /* Peer metatable */ luaL_newmetatable(L, "tcf_peer"); luaL_register(L, NULL, peerfuncs); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); /* m.__index = m */ lua_pushvalue(L, -1); lua_setfield(L, -1, "__metatable"); /* m.__metatable = m */ lua_pop(L, 1); /* Protocol metatable */ luaL_newmetatable(L, "tcf_protocol"); luaL_register(L, NULL, protocolfuncs); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); /* m.__index = m */ lua_pushvalue(L, -1); lua_setfield(L, -1, "__metatable"); /* m.__metatable = m */ lua_pop(L, 1); /* Channel metatable */ luaL_newmetatable(L, "tcf_channel"); luaL_register(L, NULL, channelfuncs); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); /* m.__index = m */ lua_pushvalue(L, -1); lua_setfield(L, -1, "__metatable"); /* m.__metatable = m */ lua_pop(L, 1); /* Post event metatable */ luaL_newmetatable(L, "tcf_post_event"); luaL_register(L, NULL, posteventfuncs); lua_pushvalue(L, -1); lua_setfield(L, -1, "__index"); /* m.__index = m */ lua_pushvalue(L, -1); lua_setfield(L, -1, "__metatable"); /* m.__metatable = m */ lua_pop(L, 1); lua_newtable(L); /* peers = {} */ lua_newtable(L); /* m = {} */ lua_pushstring(L, "v"); /* Values are weak */ lua_setfield(L, -2, "__mode"); /* m.__mode = "v" */ lua_setmetatable(L, -2); /* setmetatable(peer, m) */ peers_refp = luaref_new(L, NULL); peer_server_add_listener(peer_server_changes, L); if((error = luaL_loadfile(L, engine_name)) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } if((error = lua_pcall(L, 0, LUA_MULTRET, 0)) != 0) { fprintf(stderr, "%s\n", lua_tostring(L,1)); exit(1); } /* Process events - must run on the initial thread since ptrace() * returns ECHILD otherwise, thinking we are not the owner. */ run_event_loop(); lua_close(L); return 0; } #else /* ENABLE_LUA */ #if defined(_WRS_KERNEL) int tcf_lua(void) { #else int main(int argc, char ** argv) { #endif fprintf(stderr, "Agent was built without LUA support\n"); return 1; } #endif /* ENABLE_LUA */