Commit 47876dbd by Tim Stewart

Add UNIX Domain Socket support and use by default

parent 0fd1ef57
tcprpc
======
A bare-bones TCP-based RPC mechanism. You may start up a test
instance quickly using rebar3:
A bare-bones RPC mechanism for communicating with a running Erlang
node. Despite the name, this application defaults to setting up a
UNIX Domain Socket at `/tmp/tcprpc`.
You may start up a test instance quickly using rebar3:
$ rebar3 shell
Once started, telnet or netcat to the configured port (defaults to
5555), and simply submit an RPC call in Module:Function(Arg1, Arg2,
...) format:
Once started, use `socat` to submit an RPC call in
Module:Function(Arg1, Arg2, ...) format:
$ echo "erlang:system_info(cpu_topology)." | nc localhost 5555
$ echo "erlang:system_info(cpu_topology)." | socat - /tmp/tcprpc
[{processor,[{thread,{logical,0}},
{thread,{logical,1}},
{thread,{logical,2}},
{thread,{logical,3}}]}]
tcprpc will close the socket once the answer has been sent.
tcprpc will close the connection immediately after the answer has been
sent.
It would be wise to place the socket inside of a directory accessible
only to the UNIX user that should have RPC capabilities. It is not
currently possible to control the permissions or the file mode of the
created socket.
Build
......@@ -25,6 +33,45 @@ Build
$ rebar3 compile
Configuration
-------------
The following configuration parameters are available for the tcprpc
application:
| Parameter | Type | Default | Description |
|-----------------+-----------------------+------------------------+---------------------------------------|
| acceptors | integer | 100 | Number of ranch acceptors to start |
| max_connections | integer | 1024 | Upper limit on concurrent connections |
| ifaddr | inet:socket_address() | {local, "/tmp/tcprpc"} | Socket interface address |
| port | inet:port_number() | 0 | Listening port |
The default will listen on a UNIX domain socket named `/tmp/tcprpc`:
[
{tcprpc, [
{ifaddr, {local, "/tmp/tcprpc"}},
{port, 0},
...
]}
].
You may also configure it for an actual TCP socket:
[
{tcprpc, [
{ifaddr, {127,0,0,1}},
{port, 5555},
...
]}
].
`127.0.0.1` is recommended if using TCP so that you're not exposing
the RPC mechanism to a wide audience. However, the UNIX Domain Socket
is strongly preferred since it may be controlled via filesystem
permissions.
Acknowledgements
----------------
......
......@@ -8,7 +8,13 @@
stdlib,
ranch
]},
{env,[ {listeners, 10}, {port, 5555} ]},
{env, [
{acceptors, 100},
{max_connections, 1024},
{ifaddr, {local, "/tmp/tcprpc"}},
{port, 0}
]},
{modules, []},
{maintainers, []},
......
......@@ -15,18 +15,40 @@
%%====================================================================
start(_StartType, _StartArgs) ->
{ok, Listeners} = application:get_env(tcprpc, listeners),
{ok, Acceptors} = application:get_env(tcprpc, acceptors),
{ok, MaxConns} = application:get_env(tcprpc, max_connections),
{ok, Port} = application:get_env(tcprpc, port),
{ok, _} = ranch:start_listener(
tcprpc, Listeners, ranch_tcp, [{port, Port}],
tcprpc_protocol, []),
tcprpc_sup:start_link().
{ok, IfAddr} = application:get_env(tcprpc, ifaddr),
stop(_State) ->
_ = ranch:stop_listener(tcprpc),
ok = maybe_delete_uds(IfAddr),
%% The {active, false} is critical here since we are bypassing
%% ranch's normal listener setup. If we leave this at true (the
%% default) then this process (we're in the ranch connection
%% supervisor context right now) might receive TCP messages,
%% killing it.
{ok, Socket} = gen_tcp:listen(Port, [{ifaddr, IfAddr}, {active, false}]),
Opts = [ {socket, Socket},
{max_connections, MaxConns} ],
{ok, _} = ranch:start_listener(tcprpc, Acceptors, ranch_tcp, Opts,
tcprpc_protocol, []),
{ok, Pid} = tcprpc_sup:start_link(),
{ok, Pid, IfAddr}.
stop(IfAddr) ->
ok = maybe_delete_uds(IfAddr),
ok = ranch:stop_listener(tcprpc),
ok.
%%====================================================================
%% Internal functions
%%====================================================================
maybe_delete_uds({local, Path}) ->
_ = file:delete(Path),
ok;
maybe_delete_uds(_Other) ->
ok.
......@@ -30,7 +30,8 @@ start_link(Ref, Socket, Transport, Opts) ->
proc_lib:start_link(?MODULE, init, [Ref, Socket, Transport, Opts]).
init(Ref, Socket, Transport, _Opts = []) ->
%% Put items that would normally be in gen_server's init/1 here
%% Put items that would normally be in gen_server's init/1 here,
%% before call to proc_lib:init_ack/1
ok = proc_lib:init_ack({ok, self()}),
ok = ranch:accept_ack(Ref),
ok = Transport:setopts(Socket, [{active, once}, {packet, line}]),
......@@ -55,7 +56,6 @@ handle_cast(_Msg, State) ->
{noreply, State}.
handle_info({tcp, Socket, Data}, State) ->
ok = inet:setopts(Socket, [{active, once}]),
Result = rpc(Data),
ok = ranch_tcp:send(Socket, Result),
ok = ranch_tcp:close(Socket),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment