Erlang-Lua:
How to write an Erlang C Node
	
		
		
Robby Raschke
Erlang Solutions Ltd.
@rtraschke
		
		
Erlang Solutions Ltd.
(rtr@127.0.0.1)1> erlang_lua:start_link(foo).
{ok,<0.47.0>}
(rtr@127.0.0.1)2> erlang_lua:lua(foo, <<"return {x=1, y='foo'}">>).
{lua,[[{y,<<"foo">>},{x,1}]]}
(rtr@127.0.0.1)3> erlang_lua:lua(foo, <<" x = 42 + 'fourty two' ">>).
{error,"[string \" x = 42 + 'fourty two' \"]:1: attempt to perform
 arithmetic on a string value"}
(rtr@127.0.0.1)4> erlang_lua:stop(foo).
ok
(rtr@127.0.0.1)5>
open_port/2:ei_x_new(ei_x_buff* x))struct hostent *gethostbyname(char *hostname)) and build the C Node nameei_connect_xinit(...))ei_connect(...)){Port, {data, {noeol, S}}}{Port, {data, {eol, S}}}
{Port, {exit_status, N}}{'EXIT', Port, Reason}
handle_call({exec, Code}, From, #state{mbox=Mbox} = State) ->
    Mbox ! {exec, self(), Code},
    Result = receive
        {error, _Reason} = Error ->
            Error;
        {lua, _Result} = Reply
            Reply
    end,
    {reply, Result, State};
Not quite, remember that we are in a gen_server, and port messages could arrive while we are waiting for our result. For example, an exit of the C Node, due to an error.
handle_call({exec, Code}, From, #state{mbox=Mbox} = State) ->
    Mbox ! {exec, self(), Code},
    Result = receive
        {error, _Reason} = Error ->
            Error;
        {lua, _Result} = Reply
            Reply
    end,
    {reply, Result, State};
Use two-part gen_server calls:
noreplygen_server:reply/2
handle_call({exec, Code}, From,
        #state{mbox=Mbox, from=undefined} = State) ->
    Mbox ! {exec, self(), Code},
    {noreply, State#state{from=From}};
handle_info({error, _Reason} = Error, #state{from=From} = State)
        when From =/= undefined ->
    gen_server:reply(From, Error),
    {noreply, State#state{from=undefined}};
handle_info({lua, _Result} = Reply, #state{from=From} = State)
        when From =/= undefined ->
    gen_server:reply(From, Reply),
    {noreply, State#state{from=undefined}};
int ei_xreceive_msg(int fd, erlang_msg* msg, ei_x_buff* x))ERL_TICK - The Erlang Node sends these regularly to see if the C Node is still aliveERL_ERROR - Some kind of error happened while waiting for a messageERL_MSG - A real message arrived, metadata is in the erlang_msg structure, and the payload is in the ei_x_buffERL_EXIT and ERL_UNLINK - The Erlang Node is disconnecting, in our case this means shut down the C nodeERL_SEND - The actual message sent from the gen_server to the C Node, e.g., {exec, Pid, Code} and {stop, Pid, []}int ei_send(int fd, erlang_pid* to, char* buf, int len)fd in the two calls (ei_xreceive_msg() and ei_send()) was returned to us by the ei_connect() call earlier and is the descriptor of the connection to the Erlang Nodeei_decode_...() functions to disassemble the expected terms
if (ei_decode_version(x_in->buff, &x_in->index, &version) < 0) {
    print("WARNING: Ignoring malformed message (bad version: %d).", version); return -1;
}
if (ei_decode_tuple_header(x_in->buff, &x_in->index, &arity) < 0) {
    print("WARNING: Ignoring malformed message (not tuple)."); return -1;
}
if (arity != 3) {
    print("WARNING: Ignoring malformed message (not 3-arity tuple)."); return -1;
}
if (ei_decode_atom(x_in->buff, &x_in->index, lua_atom) < 0) {
    print("WARNING: Ignoring malformed message (first tuple element not atom)."); return -1;
}
if (ei_decode_pid(x_in->buff, &x_in->index, pid) < 0) {
    print("WARNING: Ignoring malformed message (second tuple element not pid)."); return -1;
}
if (strcmp(lua_atom, "stop") == 0) {
    print("DEBUG: Lua Erlang Node stopping normally.");
    x_out->index = 0;
    return 0;
}
if (strcmp(lua_atom, "exec") == 0) {
    ei_get_type(x_in->buff, &x_in->index, &type, &len);
    code = (char *) calloc(len+1, sizeof(char));
    if (ei_decode_binary(x_in->buff, &x_in->index, code, NULL) < 0) {
        free(code);
        set_error_msg(x_out, "Third tuple element is not a binary.");
        return 1; /* send the error message back to our gen_server */
    }
}
x_out->index = 0;
ei_x_encode_version(x_out);
ei_x_encode_tuple_header(x_out, 2);
ei_x_encode_atom(x_out, "lua");
execute_code(EI_LUA_STATE.L, x_out, code);
luaL_dostring(){error, Reason} reply{lua, ok} reply is sent back{lua, Result} replyei_x_encode_...() functions
(rtr@127.0.0.1)1> erlang_lua:start_link(foo).                      
=INFO REPORT==== 26-Nov-2014::10:50:04 ===
2014-11-26 10:50:04.621 UTC INFO ELua 'foo' starting using command:
lua5.1/bin/lua_enode 'foo' '127.0.0.1' 'rtr@127.0.0.1'
    'BXYEUYSCJRYFVTWCEXAO' '0'
=INFO REPORT==== 26-Nov-2014::10:50:04 ===
2014-11-26 10:50:04.627 UTC DEBUG ELua 'foo' startup message:
Lua Erlang Node 'foo@127.0.0.1' starting.
=INFO REPORT==== 26-Nov-2014::10:50:04 ===
2014-11-26 10:50:04.628 UTC DEBUG ELua 'foo' startup message:
Lua Erlang Node started.
=INFO REPORT==== 26-Nov-2014::10:50:04 ===
2014-11-26 10:50:04.628 UTC INFO ELua 'foo' is ready to accept Lua code.
{ok,<0.47.0>}
(rtr@127.0.0.1)2> 
(rtr@127.0.0.1)2> erlang_lua:lua(foo, <<"return {x=1, y='foo'}">>).
=INFO REPORT==== 26-Nov-2014::10:50:11 ===
2014-11-26 10:50:11.730 UTC DEBUG ELua 'foo' executing:
return {x=1, y='foo'}
{lua,[[{y,<<"foo">>},{x,1}]]}
(rtr@127.0.0.1)3>
(rtr@127.0.0.1)3> erlang_lua:stop(foo). =INFO REPORT==== 26-Nov-2014::11:08:11 === 2014-11-26 11:08:11.179 UTC DEBUG ELua 'foo' is being asked to stop. =INFO REPORT==== 26-Nov-2014::11:08:11 === 2014-11-26 11:08:11.179 UTC INFO ELua 'foo' terminating: normal =INFO REPORT==== 26-Nov-2014::11:08:11 === 2014-11-26 11:08:11.180 UTC DEBUG ELua 'foo': Lua Erlang Node stopping normally. =INFO REPORT==== 26-Nov-2014::11:08:11 === 2014-11-26 11:08:11.180 UTC INFO ELua 'foo': Lua Erlang Node stopped. =INFO REPORT==== 26-Nov-2014::11:08:11 === 2014-11-26 11:08:11.180 UTC INFO ELua 'foo' stopped normally. ok (rtr@127.0.0.1)4>
(rtr@127.0.0.1)2> erlang_lua:lua(foo,
        <<"os.execute('sleep 300') return {x=1, y='foo'}">>).
=INFO REPORT==== 26-Nov-2014::11:12:43 ===
2014-11-26 11:12:43.472 UTC DEBUG ELua 'foo' executing:
os.execute('sleep 300') return {x=1, y='foo'}
=ERROR REPORT==== 26-Nov-2014::11:13:32 ===
** Node 'foo@127.0.0.1' not responding **
** Removing (timedout) connection **
=INFO REPORT==== 26-Nov-2014::11:17:43 ===
2014-11-26 11:17:43.448 UTC DEBUG ELua 'foo':
Lua Erlang Node error in receive: 5 (Input/output error)
=INFO REPORT==== 26-Nov-2014::11:17:43 ===
2014-11-26 11:17:43.448 UTC INFO ELua 'foo':
Lua Erlang Node 'foo@127.0.0.1' reconnecting.
=INFO REPORT==== 26-Nov-2014::11:17:43 ===
2014-11-26 11:17:43.449 UTC INFO ELua 'foo':
Lua Erlang Node reconnected.
	NOTE: The call is now stuck without a return!
kernel net_ticktime config settingei_xreceive_msg() in the C node produces the I/O error
		Call to erlang:is_alive() before sending the reply in the C main_message_loop():
	
case ERL_SEND:
    x_in->index = 0;
    running = handle_msg(&pid);
    if (running == -1) {
        running = 1; /* Ignore messages without a return pid! */
    } else {
        x_rpc_in->index = x_rpc_out->index = 0;
        ei_x_encode_empty_list(x_rpc_in); /*empty param list for erlang:is_alive()*/
        if (ei_rpc(&EI_LUA_STATE.ec, EI_LUA_STATE.fd, "erlang", "is_alive",
                x_rpc_in->buff, x_rpc_in->index, x_rpc_out) < 0) {
            reconnect();
        }
        if (x_out->index > 0 && ei_send(EI_LUA_STATE.fd, &pid,
                x_out->buff, x_out->index) < 0) {
            print("FATAL: Lua Erlang Node error in send to '%s'.", pid.node);
            exit(8);
        }
    }
    break;
(rtr@127.0.0.1)2> erlang_lua:lua(foo,
        <<"os.execute('sleep 300') return {x=1, y='foo'}">>).
=INFO REPORT==== 26-Nov-2014::10:52:45 ===
2014-11-26 10:52:45.791 UTC DEBUG ELua 'foo' executing:
os.execute('sleep 300') return {x=1, y='foo'}
=ERROR REPORT==== 26-Nov-2014::10:53:32 ===
** Node 'foo@127.0.0.1' not responding **
** Removing (timedout) connection **
=INFO REPORT==== 26-Nov-2014::10:57:45 ===
2014-11-26 10:57:45.769 UTC DEBUG ELua 'foo':
Lua Erlang Node error in 'is alive?' rpc to 'rtr@127.0.0.1'.
=INFO REPORT==== 26-Nov-2014::10:57:45 ===
2014-11-26 10:57:45.769 UTC INFO ELua 'foo':
Lua Erlang Node 'foo@127.0.0.1' reconnecting.
=INFO REPORT==== 26-Nov-2014::10:57:45 ===
2014-11-26 10:57:45.770 UTC INFO ELua 'foo':
Lua Erlang Node reconnected.
{lua,[[{y,<<"foo">>},{x,1}]]}
(rtr@127.0.0.1)3>
printf() for logging when started as a Port programhttps://github.com/rtraschke/erlang-lua
Thank You!
Robby Raschke
Erlang Solutions Ltd.
@rtraschke