Distributed Erlang: Learn about remote host and start function - erlang

I am trying to implement a really simple example to get a first look into Distributed Erlang. So, I start two nodes on the same machine:
erl -sname dilbert
erl -sname dogbert
The task is that dogbert makes dilbert output Hello, World! using a simple fun () -> io:format("Hello, World!") end on node dilbert. I thought this would be easy with dogbert's shell:
(dogbert#centraldogma.fubar)1> spawn(dilbert, fun () -> io:format("HELLO!") end).
<0.39.0>
=ERROR REPORT==== 13-Jun-2012::17:49:04 ===
** Can not start erlang:apply,[#Fun<erl_eval.20.82930912>,[]] on dilbert **
In the same shell, using nodes(). outputs []. Well, dogbert obviously doesn't know about dilbert, but why is that the case? Or, how do I make nodes in distributed erlang get to know each other?

There are two problems:
You must set a common cookie for both nodes so they can interact.
erl -sname dilbert -setcookie pointyhairedboss
erl -sname dogbert -setcookie pointyhairedboss
You must specify the hostname of the node you want to connect to.
spawn('dogbert#yourhostname', fun () -> io:format("HELLO!") end).

Related

How can I pass command-line arguments to a Erlang program?

I'm working on a Erlang. How can I pass command line parameters to it?
Program File-
-module(program).
-export([main/0]).
main() ->
io:fwrite("Hello, world!\n").
Compilation Command:
erlc Program.erl
Execution Command-
erl -noshell -s program main -s init stop
I need to pass arguments through execution command and want to access them inside main written in program's main.
$ cat program.erl
-module(program).
-export([main/1]).
main(Args) ->
io:format("Args: ~p\n", [Args]).
$ erlc program.erl
$ erl -noshell -s program main foo bar -s init stop
Args: [foo,bar]
$ erl -noshell -run program main foo bar -s init stop
Args: ["foo","bar"]
It is documented in erl man page.
I would recommend using escript for this purpose because it has a simpler invocation.
These are not really commandline-parameters, but if you want to use environment-variables, the os-module might help. os:getenv() gives you a list of all environment variables. os:getenv(Var) gives you the value of the variable as a string, or returns false if Var is not an environment-variable.
These env-variables should be set before you start the application.
I always use an idiom like this to start (on a bash-shell):
export PORT=8080 && erl -noshell -s program main
If you want "named" argument, with possible default values, you can use this command line (from a toy appli I made):
erl -pa "./ebin" -s lavie -noshell -detach -width 100 -height 80 -zoom 6
lavie:start does nothing more than starting an erlang application:
-module (lavie).
-export ([start/0]).
start() -> application:start(lavie).
which in turn start the application where I defined default value for parameters, here is the app.src (rebar build):
{application, lavie,
[
{description, "Le jeu de la vie selon Conway"},
{vsn, "1.3.0"},
{registered, [lavie_sup,lavie_wx,lavie_fsm,lavie_server,rule_wx]},
{applications, [
kernel,
stdlib
]},
{mod, { lavie_app, [200,50,2]}}, %% with default parameters
{env, []}
]}.
then, in the application code, you can use init:get_argument/1 to get the value associated to each option if it was defined in the command line.
-module(lavie_app).
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%% ===================================================================
%% Application callbacks
%% ===================================================================
start(_StartType, [W1,H1,Z1]) ->
W = get(width,W1),
H = get(height,H1),
Z = get(zoom,Z1),
lavie_sup:start_link([W,H,Z]).
stop(_State) ->
% init:stop().
ok.
get(Name,Def) ->
case init:get_argument(Name) of
{ok,[[L]]} -> list_to_integer(L);
_ -> Def
end.
Definitively more complex than #Hynek proposal, but it gives you more flexibility, and I find the command line less opaque.

How do I connect to an Erlang node which uses a short name?

When I run erl -sname foo, the node name created uses the hostname, rather than "localhost", so it is generated as, for example, foo#roger-pc.
I then register(shell, self()), and I can send messages to it from another node (erl -sname bar) as follows:
{shell, 'foo#roger-pc'} ! {hello, world}.
But it doesn't work if I use {shell, foo} ! {knock, knock}. The message is never received.
How do I connect to an Erlang node on the same PC which is using a short name? Or: how do I derive the "#roger-pc" part of the destination node name? Or: should I just use erl -name foo#localhost to register a long name?
Some background: I'm writing a an escript which spawns an erl process, and I'd like to be able to send messages from that OS process back to the original script.
you can specify 'localhost' explicitly for sname.
first shell
erl -sname ax#localhost
register(rcvr, self()).
second shell
erl -sname bx#localhost
net_kernel:connect_node(ax#localhost).
{rcvr, ax#localhost} ! hello.
and first shell again
(ax#localhost)7> flush().
Shell got hello
ok
sname still requires the '#' notation. You just don't have to input the full uri when sending messages.
c:\>erl -sname short
(short#hostname)1> {process, 'short#hostname'} ! Message.
c:\>erl -name short
(short#hostname.more.stuff)1> {process, 'short#hostname.more.stuff'} ! Message.
short is not the full name. sname and name merely decide how much of the rest of the path is required.
A node is an executing Erlang runtime system which has been given a name, using the command line flag -name (long names) or -sname (short names).
The format of the node name is an atom name#host where name is the name given by the user and host is the full host name if long names are used, or the first part of the host name if short names are used. node() returns the name of the node. Example:
% erl -name dilbert
(dilbert#uab.ericsson.se)1> node().
'dilbert#uab.ericsson.se'
% erl -sname dilbert
(dilbert#uab)1> node().
dilbert#uab

erlang R16B01 and Windows CMD

I'm windows 7 user and try to learn erlang and here's the time when I get to the paragraph where I have to deal with creating and naming nodes. I used to start erlang emulator from shortcut and don't know how to start it from command prompt window to use -name or -sname commands, and when I try to use
net_kernel:start([node,shortnames]).
command I get
{error,
{{shutdown,
{failed_to_start_child,net_kernel,{'EXIT',nodistribution}}},
{child,undefined,net_sup_dynamic,
{erl_distribution,start_link,[[node,shortnames]]},
permanent,1000,supervisor,
[erl_distribution]}}}
=INFO REPORT==== 23-Aug-2013::15:28:03 ===
Protocol: "inet_tcp": register/listen error: econnrefused
error. I thought it was because of windows firewall and turned it off, but nothing has changed. So can someone answer what's going on?? And if you don't mind teach me how can I start erlang from CMD and use -name and -sname commands.
1) make sure you have in the path, program files \ erlang \ bin \ erl
2) open the command prompt and type
3) erl -sname dilbert
-name = for fully qualified name [dilbert#mypc.maydomain.com]
-sname for short name [dilbert#mypc]
this will give you
% erl -sname dilbert
(dilbert#mypc)1> node().
dilbert#mypc
I am, most of the time, a windows user, and as it is not natural in this environment to use command windows, I used to write some command files to invoke werl (the usual VM interface in windows). But I stopped that, because it is less convenient and less flexible than the command window, and also because I very often need other command line tools like ping, ipconfig, or git...
I join an example of the code you can use:
launch_werl.bat:
echo off
echo Select a project:
echo 1: project 1
echo 2: project 2
echo 3: project 3
set /p proj=
goto label%proj%
:label1
d:
cd "\git\project1"
set prname=proj1
goto end
:label2
d:
cd "\documents and Settings\user\My Documents\path\to\some\code"
set prname=proj2
goto end
:label3
d:
cd "\git\proj3"
set prname=PLC
goto end
:end
start werl -sname %prname% -pa "./ebin"
echo on

How to run erlang (rebar build) application

I am new to Erlang world and currently can't figure out how to start my dummy erlang application. Probably, I am just missing something... So, I created an application with rebar (rebar create-app appid=dummys).
Currently I have
rebar.config
src/dummys.app.src
src/dummys_app.erl
src/dummys_sup.erl
I have found that in order to run an application during a development it is better to create an additional start method which should call application:start(module).
I added some basic logging to my start methods..
start() ->
error_logger:info_msg("Starting app(dev)..~n"),
application:start(dummys_app).
start(_StartType, _StartArgs) ->
error_logger:info_msg("Starting app..~n"),
dummys_sup:start_link().
If I try
erl -noshell -pa ebin -s application start dummys
erl -noshell -pa ebin -s application start dummys_app
there are no output..
If I try
erl -noshell -pa ebin -s dummys start
erl crashes with an error..
If I try
erl -noshell -pa ebin -s dummys_app start
it outputs just "Starting app(dev).." and that's all. But I also expect to see "Starting app.."
What I am missing or doing wrong??
=============
And another question: How to add a new module to my dummy application correctly? For example I have an additional module called "*dummys_cool*" which has a "start" method. How to tell my application to run that "dummys_cool#start" method?
Thank you!
For quick development, if you just want to ensure your appliction can start, start a shell, then start the application:
erl -pa ebin
1> dummys_app:start().
That will give you a clean indication of what is wrong and right without the shell bombing out after.
Since you're making an application to run, rather than just a library to share, you'll want to make a release. Rebar can get you most of the way there:
mkdir rel
cd rel
rebar create-node nodeid=dummysnode
After you've compiled your application, you can create a release:
rebar generate
This will build a portable release which includes all the required libraries and even the erlang runtime system. This is put by default in the rel/ directory; in your case rel/dummys.
Within that directory there will be a control script that you can use to start, stop, and attach to the application:
rel/dummys/bin/dummys start
rel/dummys/bin/dummys stop
rel/dummys/bin/dummys start
rel/dummys/bin/dummys attach
Have a look at your dummys.app.src file. The meaning of all the directives is explained in the 'app' manpage, but the one I suspect is missing here is mod, which indicates the name of your application callback module. So make sure that this line is present:
{mod, {dummys_app, []}}
The empty list in there will be passed as the StartArgs argument to dummys_app:start/2.
To make a new module start along with your application, add it to the supervision tree in dummys_sup:init. This function should look something like:
init(_) ->
{ok, {{one_for_one, 10, 10},
[{dummys_cool, {dummys_cool, start_link, []},
permanent, brutal_kill, worker, [dummys_cool]}]}.
This is described in the 'supervisor' manpage, but basically this means that on startup, this supervisor will start one child process. dummys_cool:start_link() will be called, and that function is expected to spawn a new process, link to it, and return its process id. If you need more processes, just add more child specifications to the list.
erl -noshell -pa ebin -s application start dummys
The code above will not work because application:start([dummys]) will be called.
You can take a reference of the Erlang documentation for details.
For your case,
erl -noshell -pa ebin -s dummys
I ran into this problem, and this was the first answer on Google.
If you are using rebar3, the standard configuration will have a shell command that compiles your project and opens a shell:
$ rebar3 shell
===> Analyzing applications...
===> Compiling myapp
Erlang/OTP 21 [erts-10.2.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1]
Eshell V10.2.4 (abort with ^G)
1> ===> Booted myapp

An error with Erlang router

I try to make a comet-server using Mochiweb like there.
If I do it from erlang shell, it's alright.
But If try to start 1st node with mochiweb from command line:
erl -pa ebin edit deps/*/ebin -boot start_sasl -sname n1 -s server +K true -setcookie secret_key -s reloader
And 2nd one with router:
erl -pa ebin edit deps/*/ebin -boot start_sasl -sname router -setcookie secret_key -eval 'net_adm:ping(n1#localhost), router:start_link().'
If I connect to mochiweb from browser I have this error
=CRASH REPORT==== 18-Jul-2012::19:04:45 ===
crasher:
initial call: mochiweb_acceptor:init/3
pid: <0.73.0>
registered_name: []
exception exit: {noproc,
{gen_server,call,
[undefined,{login,"234567e",<0.73.0>}]}}
in function gen_server:call/2
in call from server_web:loop/2
in call from mochiweb_http:headers/5
ancestors: [server_web,server_sup,<0.55.0>]
messages: []
links: [<0.57.0>,#Port<0.1406>]
dictionary: [{mochiweb_request_qs,
[{"session_id","234567e"},{"obj_id","page"}]},
{mochiweb_request_path,"/polling"}]
trap_exit: false
status: running
heap_size: 2584
stack_size: 24
reductions: 1516
I think that problem is in this string from router.erl:
-define(SERVER, global:whereis_name(?MODULE)).
Because first part of -eval (net_adm:ping(n1#localhost)) was started without errors and I could see n1#localhost in nodes().. But the second part of -eval (router:start_link()) was not available for ?MODULE.
How can I resolve this problem?
Looks like the gen_server that was supposed to be globally registered with name of current module (?MODULE is replaced with it) just has not been started yet. global:whereis_name(?MODULE) will return undefined right in this case, like you see in log you posted.
Find the part of your code that's supposed to do that (by grepping for ?MODULe and/or gen_server:start_link in that module) and make sure it was executed on the first node.

Resources