December 19, 2008

Talking to Executables in Erlang.

Lately I have been working on a TCP server that works similar to D. J. Bernstein’s ucspi-tcp. Basically, it is a TCP server that sits in front of a executable of some sort and plays a middleman between the TCP client and the actual code that is performing functions on the data sent to the TCP server. More on ucspi-tcp in later post(s). The first portion of my TCP server code can be found here and blog post here. The portion of code for today is for sending and receiving messages to/from the executable in Erlang. This is done using an Erlang port, the Erlang docs have a good interoperability tutorial that should help anyone start out.

erl_portc.erl

-module(erl_portc).
-export([start/1, call_port/1, init/1, port_loop/1]).

start(Cmd) ->
% spawn a process connected to the executable
spawn(?MODULE, init, [Cmd]).

init(Cmd) ->
% register it and set things up
register(port, self()),
process_flag(trap_exit, true),
Port = open_port({spawn, Cmd}, []),
port_loop(Port).

call_port(Msg) ->
% send messages to the executable
port ! {call, self(), Msg},
receive
{port, Result} ->
Result
end.

port_loop(Port) ->
% recieve results from the executable
receive
{call, Caller, Msg} ->
Port ! {self(), {command, Msg}},
receive
{Port, {data, Data}} ->
Caller ! {port, Data};
stop ->
Port ! {self(), close},
receive
{Port, closed} ->
exit(normal)
end;
{‘EXIT’, Port, Reason} ->
exit(port_terminated)
end,
port_loop(Port)
end.

Here is an example of its usage using DJB’s qmail-smtpd executable.

[joe@box01 ~]$ erl
Erlang (BEAM) emulator version 5.6.3 [source] [smp:4] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.6.3 (abort with ^G)
1> erl_portc:start(‘/var/qmail/bin/qmail-smtpd’).
<0.32.0>
2> erl_portc:call_port(“”).
“220 mail.someaddress.com ESMTP\r\n”
3> erl_portc:call_port(“HELO mail.someaddress.com\r\n”).
“250 mail.someaddress.com\r\n”
4> erl_portc:call_port(“MAIL FROM: joe@someaddress.com\r\n”).
“250 ok\r\n”
5> erl_portc:call_port(“RCPT TO: joe@someaddress.com\r\n”).
CHKUSER accepted rcpt: from remote rcpt : found existing recipient
“250 ok\r\n”
6> erl_portc:call_port(“DATA\r\n”).
“354 go ahead\r\n”
7> erl_portc:call_port(“Subject: TEST123\r\n.\r\n”).
“250 ok 1229721933 qp 10176\r\n”
8> erl_portc:call_port(“QUIT\r\n”).
“221 mail.someaddress.com\r\n”

Shortly after running the above directives I received an email in my inbox with the subject “TEST123″. So it seems to be interfacing with the executable properly.

As with most of the code on this blog, this example can be checked out from subversion and viewed in WebSVN. Enjoy!

2 Comments

  1. Nick Gerakines Dec 27, 2008 4:59 pm

    Have you ever thought about using cnodes here instead of ports for a problem like this?

  2. joe Dec 27, 2008 5:21 pm

    cnodes need to be written using the Erlang C library calls, right? A cnode would probably work but I wanted to write something that I could drop in executables without needing to write/change already existing C code or etc. This way I can interface with things I am already using. Like in my above example I wanted to talk to qmail’s SMTP server binary from Erlang but didn’t want to make any changes to qmail-smtpd. Hopefully it makes sense, if doesn’t let me know. :)

Leave a Comment

(required)

(will not be published) (required)