I've got a few more issues to attend to before I'm going to put the LuaRPC code into the eLua trunk. One thing that I'm thinking about that I've not yet come up with a satisfactory solution for is dealing with connection state. The original code used sockets, and therefore the protocol makes some assumptions that, in most cases, data arrives in order with no errors, unless some exception is thrown.
One advantage of all of this is that the code is reasonably simple and is minimal on CPU usage. However, I think it's a little too minimal to handle serial communications where a client might hangup or one simply gets a bad read, and there are only the TXD and RXD lines to indicate physical/data link state. So far what I've done is padded out each buffered read & write (one or many bytes) with a header and tail byte (different) so that either the client or server can complain and jump back to waiting for fresh requests on error. I may consider following HDLC a little more closely in terms of asynchronous framing, but this may be OK for now.
It should be easy to layer checksums, addressing, retransmission or other assurances on top of this implementation by simply wrapping the functions that read and write chunks of data.
I've now both gotten it working on 5.1.4 and have abstracted out the transport layer. As it stands which "transport" one uses is a compile-time option set by enabling or disabling some defines in config.h, but both serial (using pty to simulate real serial) and socket connections seem to be working at the moment for desktop use. It's still somewhat rough around the edges, and I'd like to be able to support multiple transports from a single compilation, but I've not decided how I might want to do that yet (multiple modules? function pointers that allow switching within a single module file?).
The existing module uses C-based exceptions to deal with transport-layer errors (take a look at luarpc_rpc.h if you're interested). I know we're not really using exceptions for most of the eLua modules, but I'm wondering whether I should leave that in place, or change that in order to be friendly to porting. As it is set up, it doesn't have to derive from errno codes defined by system libraries (not sure whether Newlib provides an errno global), so if custom codes or strings are needed they can be set up. I think, though, with the current flow, some exceptions will need to be thrown in order to enable connection resets when the client disappears, or if bad data is sent/received.
Additionally, argument handling for connection setup that varies between transports is included in the "transport" side of the code, so the API to the transport layer includes a few functions that want a lua_State. This feels a bit undesirable to me since some of them push stuff onto the stack depending on what args are given to them, and that number has to be passed back up the chain of called functions, since none of the transport layer functions are called directly from Lua.
Also, beyond whatever was included in the original code, I've not done anything special security-wise to prevent clients from doing nasty things. That said, if you were letting someone connect to a LuaRPC server, you were already letting them run arbitrary code on your server (unless you disable loadstring), so... :-)
I think I'll have an eLua serial backend going sometime in the near future, and I'll certainly mention that when it's ready to be abused.
I've tinkered a bit further with Lua-RPC, but I've still not settled on how I'd like to deal with multiple lower level layers over which it can operate. Inherently, it should be fairly simple because all the modalities mentioned in the title can be treated like files on UNIX/POSIX. This means that once setup is complete, so long as no errors are encountered, all of these links should essentially look the same. With error handling included, perhaps this can be somewhat simplified if we can look up error messages depending on which type of link is in use.
The main point of interest, I think, is in allowing any one of these link types to be configured for the same library. Perhaps the best way to handle this is to have a set of different handle creators (one per link layer), and each one of these takes parameters appropriate for configuration of that link, while leaving a handle that can be used by a common set of utility functions that read and write to the file descriptor set up when the handle was created.
Currently there is one RPC_open function for opening client-side connections to a server. If the namespacing were adjusted so that we have an rpc table, and function entries corresponding to different link types, our adjusted set of "open" functions could look like:
h_sock = rpc.open_socket(address,port)
h_ser = rpc.open_serial(serial_port, baud, etc..)
h_fifo = rpc.open_fifo(file_path)
I'm not sure if after setting up the file descriptor that when read() or write() are subsequently called that anything special need be done to ensure that those functions are the ones that know how to work with the link type that has been set up. I would assume that at least for the serial and fifo situations, where an actual "file" exists to be read and written to that this isn't too much of a problem, but for the socket there's no file or fake file to represent the open socket. Does POSIX/UNIX take care of this for you? I suppose I'll just have to check :-)