How to create a daemon program with erlang? [closed] - erlang

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I prepare to develop one heartbeat program, which need to send udp packet every 5s.
How to sleep 5s in erlang or is there sleep(5) function to be used?
How to make it run in background?

If you want your application to send a udp packet I would recommend you to start with a gen_server(coz. you will obviously be having need to add other functionalities to your application).
1. For sending packets at regular interval.
timer:send_interval(5000,interval),
This will call "handle_call(interval,State)" callback of gen_server every 5 seconds from where you can send your packets
2. Making it run in background.
As already posted use "run_erl". I have used this myself to run my application successfully as a daemon.
run_erl -daemon /tmp "erl"
This will create two pipes "erlang.pipe.1.r" and "erlang.pipe.1.w" under "/tmp" dir of unix and you can write commands to write pipe for starting your application using perl or any scripting lang or even c/c++ :)

Recently I have been learning the erlang programming language. One task I gave myself was to write a linux daemon.
As you probably already know, daemons are used to run unix services. Services commonly controlled by daemons include database servers, web servers, web proxies etc. In this example the server is very simple, the client calls the function "say_hi" and the server responds with "hello".
In the linux environment daemons are controlled by scripts that are stored in places such as /etc/init.d. These scripts respond according to convention to the commands start, stop and restart.
Let us start with the shell script:
#!/bin/sh
EBIN=$HOME/Documents/Erlang/Daemon
ERL=/usr/bin/erl
case $1 in
start|stop|restart)
$ERL -detached -sname mynode \
-run daemon shell_do $1 >> daemon2.log
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
This has to be one of the simplest shell scripts that you have ever seen. Daemon respond to three different commands, stop, start and restart. In this script the command is simply passed through to the daemon. One improvement would be to exit with the return code from the daemon execution.
So how about the daemon? Here it is...
%% PURPOSE
%% Author: Tony Wallace
%%
%% Manage an erlang daemon process as controlled by a shell scripts
%% Allow standard daemon control verbs
%% Start - Starts a daemon in detached mode and exits
%% Stop - Attaches to the daemon, monitors it, sends an EXIT message and waits for it to die
%% Restart - Calls stop and then start
%% Log events
%% Return UNIX compatible codes for functions called from shell scripts
%% Exit shell script calls so as to not stop the scripts from completing
%% Shell scripts expected to use shell_do to execute functions
%%
%% Allow interaction with daemon from other erlang nodes.
%% Erlang processes are expected to call functions directly rather than through shell_do
%%
%% MOTIVATION
%% Erlang is great, but as an application it needs to be managed by system scripts.
%% This is particularly for process that are expected to be running without user initiation.
%%
%% INVOCATION
%% See daemon.sh for details of calling this module from a shell script.
%%
%% TO DO
%% Define and use error handler for spawn call.
-module(daemon).
%-compile([{debug_info}]).
-export [start/0,start/1,stop_daemon/0,say_hi/0,kill/0,shell_do/1].
%%-define (DAEMON_NAME,daemon#blessing).
-define (DAEMON_NAME,list_to_atom("daemon#"++net_adm:localhost())).
-define (UNIX_OKAY_RESULT,0).
-define (TIMEOUT_STARTING_VM,1).
-define (VM_STARTED_WITHOUT_NAME,2).
-define (INVALID_VERB,3).
-define (COULD_NOT_CONNECT,4).
-define (TIMEOUT_WAITING_QUIT,5).
-define (TIMEOUT_STOPPING_VM,6).
wait_vm_start(_,0) -> ?TIMEOUT_STARTING_VM;
wait_vm_start(D,N) ->
net_kernel:connect(D),
Dl = lists:filter(fun(X) -> X==D end,nodes()),
if Dl =:= [] ->
receive after 1000 -> true end,
wait_vm_start(D,N-1);
Dl /= [] -> ?UNIX_OKAY_RESULT
end.
wait_vm_stop(_,0) -> ?TIMEOUT_STOPPING_VM;
wait_vm_stop(D,N) ->
net_kernel:connect(D),
Dl = lists:filter(fun(X) -> X==D end,nodes()),
if Dl /= [] ->
receive after 1000 -> true end,
wait_vm_start(D,N-1);
Dl == [] -> ?UNIX_OKAY_RESULT
end.
flush() ->
receive
_ ->
flush()
after
0 ->
true
end.
sd(Hdl) ->
MyNode=node(),
if
MyNode =:= nonode#nohost ->
info(stdout,"~s","Error: Erlang not started with a name. Use -sname <name>"),
?VM_STARTED_WITHOUT_NAME;
MyNode /= nonode#nohost ->
Atm_daemon = ?DAEMON_NAME,
Connected = net_kernel:connect(Atm_daemon),
case Connected of
true ->
info(Hdl,"~s",["daemon process already started"]),
?UNIX_OKAY_RESULT;
false ->
info(Hdl,"~s",["starting daemon process"]),
StartString = "erl -detached -sname daemon",
os:cmd(StartString),
Vm_daemon = wait_vm_start(Atm_daemon,10),
case Vm_daemon of
?UNIX_OKAY_RESULT ->
info(Hdl,"~s",["spawning main daemon process"]),
spawn(Atm_daemon,?MODULE,start,[]), ?UNIX_OKAY_RESULT;
A -> A
end
end % case Connected %
end.
say_hi() ->
Daemon = ?DAEMON_NAME,
Connected = net_kernel:connect(Daemon),
if Connected ->
{listener,Daemon} ! {hello,self()},
receive
Response -> Response
after 10000 -> timeout end;
not Connected -> could_not_connect
end.
stop_daemon() ->
Daemon = ?DAEMON_NAME,
Connected = net_kernel:connect(Daemon),
if Connected ->
flush(),
{listener,Daemon} ! {quit,self()},
receive
bye -> wait_vm_stop(Daemon,10)
after 10000 -> ?TIMEOUT_WAITING_QUIT
end;
not Connected -> ?COULD_NOT_CONNECT
end.
shell_do(Verb) ->
{A,Hdl} = file:open('daemon_client.log',[append]),
case A of
ok ->
info(Hdl,"~s",[Verb]);
error -> error
end,
Result = handle_verb(Hdl,Verb),
info(Hdl,"Return status ~.10B",[Result]),
init:stop(Result).
%%handle_verb(_,_) -> 0;
handle_verb(Hdl,["start"]) -> sd(Hdl);
handle_verb(_,["stop"]) -> stop_daemon();
handle_verb(Hdl,["restart"]) ->
stop_daemon(),
sd(Hdl);
handle_verb(Hdl,X) ->
info(Hdl,"handle_verb failed to match ~p",[X]),
?INVALID_VERB.
kill() ->
rpc:call(?DAEMON_NAME, init, stop, []).
start(Source) ->
Source ! starting,
start().
start() ->
register(listener,self()),
case {_,Hdl}=file:open("daemon_server.log",[append]) of
{ok,Hdl} -> server(Hdl);
{error,Hdl} -> {error,Hdl}
end.
info(Hdl,Fmt,D)->
io:fwrite(Hdl,"~w"++Fmt++"~n",[erlang:localtime()] ++ D).
server(Hdl) ->
info(Hdl,"~s",["waiting"]),
receive
{hello,Sender} ->
info(Hdl,"~s~w",["hello received from",Sender]),
Sender ! hello,
server(Hdl);
{getpid,Sender} ->
info(Hdl,"~s~w",["pid request from ",Sender]),
Sender ! self(),
server(Hdl);
{quit,Sender} ->
info(Hdl,"~s~w",["quit recevied from ",Sender]),
Sender ! bye,
init:stop();
_ ->
info(Hdl,"~s",["Unknown message received"])
after
50000 ->
server(Hdl)
end.
For the reader not used to reading erlang, there some of this code is run as a result of the shell script we saw above. Other code in this file is the daemon itself. Referring back to the shell script we see that the script calls procedure shell_do. Shell_do writes log entries, calls handle_verb and exits. Handle_verb implements the different behaviours for each verb. Starting the daemon is handled by function sd, which creates the daemon by an operating system call os:cmd, waits for the erlang virtual machine to initialise, and then spawns the server code called start, which in turn calls server.

Sleep is available in erlang, through the timer functions.
http://www.erlang.org/doc/man/timer.html
For the background process, you can use the -detached cli argument.
You can specify an entry point with -s
EDIT
You can also spawn a new process from your main program:
http://www.erlang.org/doc/reference_manual/processes.html

With respect to daemonizing, consider starting your erlang program with the run_erl utility that comes with OTP. Note in particular the -daemon command line flag.

Related

Pass args through rebar shell to erl?

I am using "rebar shell" to test my app. This is documented as:
Start a shell with project and deps preloaded similar to
'erl -pa ebin -pa deps/*/ebin'.
How do I add extra args to the underlying invocation of 'erl'? For
example, I want to add application specific environment variables and
run a Module/Function. I want to invoke something like:
erl -pa ebin -pa deps/*/ebin -browser_spy browser_exe "/my/dir" -run bs_example test
(and I want code:priv_dir to work as it does when using rebar shell,
which the above 'erl' command does not do).
You cannot
rebar shell does not execute erl ... command actually, but only tries to replicate its behaviour.
Actually rebar just turns yourself into the shell along with mimicking -pa by adding paths with code:add_pathz
See here for implementation details:
shell(_Config, _AppFile) ->
true = code:add_pathz(rebar_utils:ebin_dir()),
%% scan all processes for any with references to the old user and save them to
%% update later
NeedsUpdate = [Pid || Pid <- erlang:processes(),
proplists:get_value(group_leader, erlang:process_info(Pid)) == whereis(user)
],
%% terminate the current user
ok = supervisor:terminate_child(kernel_sup, user),
%% start a new shell (this also starts a new user under the correct group)
_ = user_drv:start(),
%% wait until user_drv and user have been registered (max 3 seconds)
ok = wait_until_user_started(3000),
%% set any process that had a reference to the old user's group leader to the
%% new user process
_ = [erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate],
%% enable error_logger's tty output
ok = error_logger:swap_handler(tty),
%% disable the simple error_logger (which may have been added multiple
%% times). removes at most the error_logger added by init and the
%% error_logger added by the tty handler
ok = remove_error_handler(3),
%% this call never returns (until user quits shell)
timer:sleep(infinity).

Erlang how to start an external script in linux

I want to run an external script and get the PID of the process (once it starts) from my erlang program. Later, I will want to send TERM signal to that PID from erlang code. How do I do it?
I tried this
P = os:cmd("myscript &"),
io:format("Pid = ~s ~n",[P]).
It starts the script in background as expected, but I dont get the PID.
Update
I made the below script (loop.pl) for testing:
while(1){
sleep 1;
}
Then tried to spawn the script using open_port. The script runs OK. But, erlang:port_info/2 troughs exception:
2> Port = open_port({spawn, "perl loop.pl"}, []).
#Port<0.504>
3> {os_pid, OsPid} = erlang:port_info(Port, os_pid).
** exception error: bad argument
in function erlang:port_info/2
called as erlang:port_info(#Port<0.504>,os_pid)
I checked the script is running:
$ ps -ef | grep loop.pl
root 10357 10130 0 17:35 ? 00:00:00 perl loop.pl
You can open a port using spawn or spawn_executable, and then use erlang:port_info/2 to get its OS process ID:
1> Port = open_port({spawn, "myscript"}, PortOptions).
#Port<0.530>
2> {os_pid, OsPid} = erlang:port_info(Port, os_pid).
{os_pid,91270}
3> os:cmd("kill " ++ integer_to_list(OsPid)).
[]
Set PortOptions as appropriate for your use case.
As the last line above shows, you can use os:cmd/1 to kill the process if you wish.

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.

Unset all paths before node boot

TLDR;
When starting erlang node (using just erl command for an instance), how could I force it not to use local OTP libraries and get code:get_path() empty?
Rationale.
I want to touch erl_boot_server. Don't doing something certain, just playing. I have built sample release and want to load it via network. Here is it.
[vkovalev#t30nix foobar]$ tree -L 2
.
|-- bin
| |-- foobar
| |-- foobar-0.0.0+build.1.ref307ae38
| |-- install_upgrade.escript
| |-- nodetool
| `-- start_clean.boot
|-- erts-6.1
| |-- bin
| |-- doc
| |-- include
| |-- lib
| |-- man
| `-- src
|-- lib
| |-- foobar-0.1.0
| |-- kernel-3.0.1
| |-- sasl-2.4
| `-- stdlib-2.1
`-- releases
|-- 0.0.0+build.1.ref307ae38
|-- RELEASES
`-- start_erl.data
First I start boot node.
[vkovalev#t30nix foobar]$ erl -sname boot -pa lib/*/ebin -pa releases/0.0.0+build.1.ref307ae38/ -s erl_boot_server start localhost
(boot#t30nix)1> {ok, _, _} = erl_prim_loader:get_file("foobar.boot").
(boot#t30nix)2> {ok, _, _} = erl_prim_loader:get_file("foobar_app.beam").
As you can see, all okay here. Then I start slave node:
[vkovalev#t30nix ~]$ erl -sname slave -loader inet -hosts 127.0.0.1 -boot foobar
{"init terminating in do_boot",{'cannot get bootfile','foobar.boot'}}
Crash dump was written to: erl_crash.dump
init terminating in do_boot ()
I dug into erl_prim_loader and found that stuff. One clause acts when Paths is empty (it just forward requested filename to boot server as is), another acts when Paths is non-empty. In this case (I wonder why) prim loader cripples requested file name with its own (clientside) paths and then ask SERVER to serve this path. In my understanding this is quite weird thing, but okay. Then I checked code:get_path() on slave node, and yes, it has paths to local otp installation.
So, returning to subject. How could I force slave node not to use any local OTP installation (if it already presents)?
UPD: Added more investigation results.
First thing -
https://github.com/erlang/otp/blob/maint/erts/preloaded/src/erl_prim_loader.erl#L669.
erl_prim_loader (in inet mode) for some (unclear for me) reasons tries
to cripple any requested module with local (clientside) paths.
It seems there is no way to force loader on slave node to keep its
paths empty: https://github.com/erlang/otp/blob/maint/erts/preloaded/src/init.erl#L697
Paths in my bootscript looks like
{path,["$ROOT/lib/kernel-4.0/ebin","$ROOT/lib/stdlib-2.5/ebin"]}, so
it seems, if I'll get bootscript loaded, anyway, I won't be able boot
system with it.
What's going on? Is erlang network boot feature broken? Or just my
brains? How could I get node successfully network-booted?
Do you think node of slave type is just named by "slave"?
The following code from the project tsung, file named "ts_os_mon_erlang.erl".
start_beam(Host) ->
Args = ts_utils:erl_system_args(),
?LOGF("Starting os_mon beam on host ~p ~n", [Host], ?NOTICE),
?LOGF("~p Args: ~p~n", [Host, Args], ?DEB),
slave:start(list_to_atom(Host), ?NODE, Args).
In additon, the slave module restriction as follows:
Slave nodes on other hosts than the current one are started with the program rsh. The user must be allowed to rsh to the remote hosts without being prompted for a password. This can be arranged in a number of ways (refer to the rsh documentation for details). A slave node started on the same host as the master inherits certain environment values from the master, such as the current directory and the environment variables. For what can be assumed about the environment when a slave is started on another host, read the documentation for the rsh program.
An alternative to the rsh program can be specified on the command line to erl as follows: -rsh Program.
The slave node should use the same file system at the master. At least, Erlang/OTP should be installed in the same place on both computers and the same version of Erlang should be used.
If you want to start a node with different path, I think you could do it by script with differnt environment variable, for master node, not slave node.
I think rebar project's can help for the similiar purpose. It include how to manipulate the path:
From rebar_core.erl file:
process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
{DirModules, ModuleSetFile}) ->
Config0 = rebar_config:set(Config, current_command, Command),
%% Get the list of modules for "any dir". This is a catch-all list
%% of modules that are processed in addition to modules associated
%% with this directory type. These any_dir modules are processed
%% FIRST.
{ok, AnyDirModules} = application:get_env(rebar, any_dir_modules),
Modules = AnyDirModules ++ DirModules,
%% Invoke 'preprocess' on the modules -- this yields a list of other
%% directories that should be processed _before_ the current one.
{Config1, Predirs} = acc_modules(Modules, preprocess, Config0,
ModuleSetFile),
%% Remember associated pre-dirs (used for plugin lookup)
PredirsAssoc = remember_cwd_predirs(Dir, Predirs),
%% Get the list of plug-in modules from rebar.config. These
%% modules may participate in preprocess and postprocess.
{ok, PluginModules} = plugin_modules(Config1, PredirsAssoc),
{Config2, PluginPredirs} = acc_modules(PluginModules, preprocess,
Config1, ModuleSetFile),
AllPredirs = Predirs ++ PluginPredirs,
?DEBUG("Predirs: ~p\n", [AllPredirs]),
{Config3, DirSet2} = process_each(AllPredirs, Command, Config2,
ModuleSetFile, DirSet),
%% Make sure the CWD is reset properly; processing the dirs may have
%% caused it to change
ok = file:set_cwd(Dir),
%% Check that this directory is not on the skip list
Config7 = case rebar_config:is_skip_dir(Config3, Dir) of
true ->
%% Do not execute the command on the directory, as some
%% module has requested a skip on it.
?INFO("Skipping ~s in ~s\n", [Command, Dir]),
Config3;
false ->
%% Check for and get command specific environments
{Config4, Env} = setup_envs(Config3, Modules),
%% Execute any before_command plugins on this directory
Config5 = execute_pre(Command, PluginModules,
Config4, ModuleSetFile, Env),
%% Execute the current command on this directory
Config6 = execute(Command, Modules ++ PluginModules,
Config5, ModuleSetFile, Env),
%% Execute any after_command plugins on this directory
execute_post(Command, PluginModules,
Config6, ModuleSetFile, Env)
end,
%% Mark the current directory as processed
DirSet3 = sets:add_element(Dir, DirSet2),
%% Invoke 'postprocess' on the modules. This yields a list of other
%% directories that should be processed _after_ the current one.
{Config8, Postdirs} = acc_modules(Modules ++ PluginModules, postprocess,
Config7, ModuleSetFile),
?DEBUG("Postdirs: ~p\n", [Postdirs]),
Res = process_each(Postdirs, Command, Config8,
ModuleSetFile, DirSet3),
%% Make sure the CWD is reset properly; processing the dirs may have
%% caused it to change
ok = file:set_cwd(Dir),
%% Once we're all done processing, reset the code path to whatever
%% the parent initialized it to
restore_code_path(CurrentCodePath),
%% Return the updated {config, dirset} as result
Res.
restore_code_path(no_change) ->
ok;
restore_code_path({added, Paths}) ->
%% Verify that all of the paths still exist -- some dynamically
%% added paths can get blown away during clean.
[code:del_path(F) || F <- Paths, erl_prim_loader_is_file(F)],
ok.
erl_prim_loader_is_file(File) ->
erl_prim_loader:read_file_info(File) =/= error.
Make sure you use the -setcookie option. From the erl -man erl page:
-loader Loader:
Specifies the method used by erl_prim_loader to
load Erlang modules in to the system. See
erl_prim_loader(3). Two Loader methods are supported,
efile and inet. efile means use the local file
system, this is the default. inet means use a boot
server on another machine, and the -id, -hosts and
-setcookie flags must be specified as well. If Loader
is something else, the user supplied Loader port
program is started.

mnesia save out info

how to save mnesia:info() output?
I use remote sh in unix screen and can't to scroll window
Here's a function that you can put in the user_default.erl module on the remote node:
out(Fun, File) ->
G = erlang:group_leader(),
{ok, FD} = file:open(File, [write]),
erlang:group_leader(FD, self()),
Fun(),
erlang:group_leader(G, self()),
file:close(FD).
Then, you can do the following (after recompiling and loading user_default):
1> out(fun () -> mnesia:info() end, "mnesia_info.txt").
Or, just cut-and paste the following into the shell:
F = fun (Fun, File) ->
G = erlang:group_leader(),
{ok, FD} = file:open(File, [write]),
erlang:group_leader(FD, self()),
Fun(),
erlang:group_leader(G, self()),
file:close(FD)
end,
F(fun () -> mnesia:info() end, "mnesia_info.txt").
In cases where you are situated at a terminal without scrolling (if you are on a xterm and see no scrollbar simply switch it on) a tool very useful is screen: it provides virtual vt100 termials, you can switch between terminals even detach from it and come back later (nice for long running programs on remote serversthat need the occasional interaction).
And you can log transcripts to a file and scroll in the output of the virtual terminal.
If you are on a Unix like System you will probably be able to just install a pre-built package, if all else fails you can always pick up the source and build it yourself.
Also look at this article for other solutions.
If you are not able to install screen on the system, a simple but not very comfortable hack that only uses Unix built-in stuff is:
Start erlang shell with tee(1) to redirect the output:
$ erl | tee output.log
Eshell V5.7.5 (abort with ^G)
1> mnesia:info().
===> System info in version {mnesia_not_loaded,nonode#nohost,
{1301,742014,571300}}, debug level = none <===
opt_disc. Directory "/usr/home/peer/Mnesia.nonode#nohost" is NOT used.
use fallback at restart = false
running db nodes = []
stopped db nodes = [nonode#nohost]
ok
2>
Its a bit hard to get out of the shell (you probably have to type ^D to end the input file) but then you have the tty output in the file:
$ cat output.log
Eshell V5.7.5 (abort with ^G)
1> ===> System info in version {mnesia_not_loaded,nonode#nohost,
{1301,742335,572797}}, debug level = none <===
...
I believe you cant. See system_info(all).
Convert to a string:
S = io_lib:format("~p~n", [mnesia:info()]).
Then write it to disk.

Resources