Erlang escript launching application with start parameters

771 Views Asked by At

Currently my Erlang application is started within an escript (TCP server) and all works fine since it uses the default port I provided. Now I want to pass the port via the escript to the application but I have no idea how. (The app runs a supervisor)

script.escript

!/usr/bin/env escript
%% -*- erlang -*-

-export([main/1]).

main([UDPort, TCPort]) ->
   U = list_to_integer(UDPort),
   T = list_to_integer(TCPort),

    app:start(), %% Want to pass T into the startup.
  receive
    _ -> ok
  end;

...

app.erl

-module(app).
-behaviour(application).

-export([start/0, start/2, stop/0, stop/1]).

-define(PORT, 4300).

start () -> application:start(?MODULE). %% This is called by the escript.
stop () -> application:stop(?MODULE).

start (_StartType, _StartArgs) -> supervisor:start(?PORT).
stop (_State) -> ok.

I'm honestly not sure if this is possible with using application but I thought it best to just ask.

1

There are 1 best solutions below

0
On BEST ANSWER

The common way is to start things from whatever shell just calling

erl -run foo

But you can also do

erl -appname key value

to set an environment value and then

application:get_env(appname, key)

to get the value you are looking for.

That said...

I like to have service applications be things that don't have to shut down to be (re)configured. I usually include some message protocol like {config, Aspect, Setting} or similar that can alter the basic state of a service on the fly. Because I often do this I usually just wind up having whatever script starts up the application also send a configuration message to it.

So with this in mind, consider this rough conceptual example:

!/usr/bin/env escript
%% -*- erlang -*-

-export([main/1]).

main([UDPort, TCPort]) ->
    U = list_to_integer(UDPort),
    T = list_to_integer(TCPort),
    ok = case whereis(app) of
        undefined ->  app:start();
        _Pid      ->  ok
    end,
    ok = set_ports(U, T).

%% Just an illustration.
%% Making this a synchronous gen_server/gen_fsm call is way better.
set_ports(U, T) ->
    app ! {config, listen, {tcp, T}},
    app ! {config, listen, {udp, U}},
    ok.

Now not only is the startup script a startup script, it is also a config script. The point isn't to have a startup script, it is to have a service running on the ports you designated. This isn't a conceptual fit for all tools, of course, but it should give you some ideas. There is also the practice of putting a config file somewhere the application knows to look and just reading terms from it, among other techniques (like including ports in the application specification, etc.).

Edit

I just realized you are doing this in an escript which will spawn a new node every time you call it. To make the technique above work properly you would need to make the escript name a node for the service to run on, and locate it if it already exists.