C Bindings – Lua

This is the first post of a serie I’m going to write on binding C to other programing languages (or script languages – I’m disregarding the differences here).

High-level languages often offer an API for creating libraries written in C. Why would we want this?
One thing is performance – With C you can write performance-oriented code in order to do a specific calculation/operation much faster, and even incorporate assembly code and other machine-specific accelerations. With C we can also optimize the amount of RAM usage.
Another thing is that languages such as Java or Python are not bound to the machine type or OS, and sometimes you want to use features that are unique to the platform you’re working on, like a specific processor features, OS mechanisms or HW features, and using C you can have access to the system’s resources.

And the downside of this is, of course, that we bind our implementation to a specific platform. We can, obviously, compile our C library for every platform we intend our software to run on, but this can be very hard, up to impossible. For example, creating such a library for Java on an Android platform – If you don’t have administrator privileges, and most users don’t, you won’t be able to even install it.

I’m going to show examples for binding C with a few languages. I will use the client side library of the unix sockets example from a previous post, and I will build a client in the particular language, which can communicate with the unix sockets listener.

The code for this serie of posts can be found on github, and it will be updated as I go and add more implementation for other languages.

Lua

Introduction

Lua┬áis a small-footprint scripting language, intended (mostly) for embedded platforms. Although it has a very compact syntax, it is quite powerful. It is developed by the “Pontifical Catholic University of Rio de Janeiro in Brazil”, and has a complete book, free online, for version 5.1 (currently the latest version is 5.3.3), you can find it in the link.
I’m sure, although I didn’t check, that there’s a Lua library for unix domain sockets, but I’m doing this for the exercise.

Including a dynamic library in Lua

In Lua, we include other libraries using “require”, like so:

local sockets = require('somelib')

“somelib” might be either a “.lua” file or a “.so” file. An so file is a dynamic library, which in Lua’s case, uses Lua’s infrastructure and adhere to some special conventions.
Lua’s interpreter looks for the file in a few predefined locations, depending on where Lua is installed (and also in the calling file’s path).

“Unix Sockets” (client) Library

We would like our API to implement these features:

  • “Open”: Function for opening a socket for communication
    Input: Socket “file” name
    Output: Socket Object when pass ; “nil” on fail
  • Socket Object
    Holds the socket’s file descriptor, and implements the following methods:

    • “Write”: Writes data to socket
      Input: Data
      Output: Pass/Fail
    • “Read”: Read data from socket:
      Input: None
      Output: Data/nil
    • “Close”: Close the socket.
      No input nor output

    I’m actually cheating a little here. Lua doesn’t really have objects in its full sense, so all the above methods will have to take the object itself as the first parameter. This object will be implemented using a Lua table (no Lua metatables here – keeping it simple)

Lua “main”

The function that gets called when you require a Lua dynamic library must be in the form of:

int luaopen_somelib(lua_State *L)

Where “somelib” is the exact name of the so (that is “somelib.so”).

“L” has an important role – It holds the arguments’ stack. So we need to “pop” our inputs from it, and “push” our outputs into it. The actual C return value (int) is the number of argument we’re returning Lua-wise, that is, the number of parameters we pushed into the stack.

Our main looks like this:

static const luaL_Reg unix_sockets_lib[] = {
    {"open",  unix_sockets_open },
    {NULL, NULL}
};

int luaopen_libunixsockets(lua_State *L)
{
    luaL_newlib(L, unix_sockets_lib);

    return 1;
}

“luaL_newlib” hides some functionality here. It creates a new Lua table, adds

the functions from”unix_socket_lib” to this table, and pushes this table into the stack.
We return “1” because there’s one return parameter on the stack.

The list of functions itself – “unix_sockets_lib”, contain one function which is the implementation for “open”, and as Lua defines, it is terminated by {NULL, NULL}.

Open

static int unix_sockets_open(lua_State *L)
{
    const char *name = lua_tostring(L, 1);
    int sock = socket_open(name);
    if (sock < 0) {
        lua_pushnil(L);
        return 1;
    }

    lua_newtable(L);
    luaL_setfuncs(L, socket_methods, 0);
    lua_pushinteger(L, sock);
    lua_setfield(L, 2, "fd");

    return 1;
}

The only input parameter for this function is a string, and “lua_tostring” is the function that brings us this parameter from the stack. Similar functions exist for every other Lua type.
We use “socket_open” from our unix_sockets static library in order to open the socket, and if we fail, we push “nil” to the stack, and return 1 (the number of return values).
If the operation passes, however, we create a new table, using “lua_newtable”. This table is automatically pushed into the stack, and the operation we are going to perform on it will be performed inside the stack.
This table is our “socket object”, so we need to attach its methods, and we do this by “luaL_setfuncs”.
Then we want to save the fd in this table. Inserting a new field to a table is done by pushing the field to the stack, and then “merging” it into the table. You see how it goes there – We push the integer, and then we set the field “fd” of the 2nd object in the stack (the table, the 1st is the integer) to be the pushed value.

Notice that Lua library functions has two different prefixes here. The ones starting with “lua” belong to the basic infrastructure included by “lua.h”. The ones starting with “luaL” belong to an extension library, included by “luaxlib.h”.

Write

static int unix_sockets_write(lua_State *L)
{
    int res;
    int sock = lua_getfield(L, 1, "fd");
    const char *buffer = lua_tostring(L, 2);

    res = socket_write(sock, buffer, strlen(buffer));
    if (res <= 0)
        lua_pushnil(L);
    else
        lua_pushboolean(L, 1);
    return 1;
}

As we defined, “write” takes two parameters. The first is the socket “object”, and the second is a string (data).
First, we take extract the fd from the table, “lua_getfield” will do this for us. Then we take out the string using “lua_tostring”.
Then, we use “socket_write” from our library, and push the proper result to the stack – nil upon failure, “true” upon success.

“read” and “close” are based on the same concepts, you can review the code if you want.

The Lua script – Using the C library

As you’ll see, this is quite straightforward. All the real operation are well hidden in the C layer. All we have to do now is call one of our API functions.

Including the library:

local sockets = require('libunixsockets')

Opening a new socket:

local s = sockets.open(sock_name)

Writing/Reading:

local e = sock:write(v)
    if not e then
    print('Failed sending ' .. v .. ' to listener')
    break -- This happens inside a loop
end

local resp = sock:read()
print('* Sent "' .. v .. '", listener reponse: "' .. tostring(resp) .. '"')

Closing:

s:close()

Testing

Here is the output test run of our script:

Opened socket "/tmp/sock1"
* Sent "Test string #1", listener response: "Recieved 14 Bytes"
* Sent "Another try", listener response: "Recieved 11 Bytes"
* Sent "This is the last message in this test!", listener response: "Recieved 38 Bytes"
Socket closed

On the server’s side we’ll see:

$ ./listenerd /tmp/sock1
[LISTENER] INFO  Starting "Unix Domain Sockets" demo listener
[LISTENER] INFO  * Starting communication with a client *
[LISTENER] INFO  Incoming message: Test string #1
[LISTENER] INFO  Incoming message: Another try
[LISTENER] INFO  Incoming message: This is the last message in this test!
[LISTENER] INFO  Client socket was closed by peer
^C[LISTENER] INFO  Terminating service

That’s it, I hope you liked it. In the next post I’ll demonstrate the same for Java.
Amnon.