Erlang how to start an external script in linux - erlang

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.

Related

Using os:cmd to in escript to start Erlang application fails.

I have an Erlang application named tb that runs fine from Erlang command line by doing application:start(tb). Whereas when I try to invoke the same application from inside escript using os:cmd, the application doesn't seem to run. When i do a 'ps | grep beam', I see the beam.smp process running. But the application is not generating any output.What might be the problem? Is there a better way to start another erlang VM from inside escript?
Here's the code snippet:
net_kernel:start([tb_escript, shortnames]),
read_config_file(FName),
Cookie = get(cookie),
Node = get(node),
N = io_lib:format("~p",[Node]),
lists:flatten(N),
C = io_lib:format("~p",[Cookie]),
lists:flatten(C),
EBIN = "~/tb/ebin",
erlang:set_cookie(tb_escript,Cookie),
os:cmd("erl -pa " ++ EBIN ++ " -sname " ++ N ++ " -detached " ++ " -setcookie " ++ C ++ " -s application start tb").
This happens because the args flag to -s wraps the arguments in a list and passes that to module:function/1. -s application start tb will execute application:start([tb]), which would return {error,{bad_application,[ssl]}}. As this is just a normal return value, no error is printed by erl.
From the erl documentation:
-s Mod [Func [Arg1, Arg2, ...]](init flag)
Makes init call the specified function. Func defaults to start. If no arguments are provided, the function is assumed to be of arity 0. Otherwise it is assumed to be of arity 1, taking the list [Arg1,Arg2,...] as argument. All arguments are passed as atoms.
There are two ways to solve this:
Use -eval "application:start(tb)", as you already mentioned in a comment.
Add a start/0 (if not already present) function to tb which calls application:start(tb), and then pass just -s tb to erl. -s with a single argument will call module:start().

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).

No output or error message from Erlang shell

Sometimes when i try to run a command in the Erlang shell (for example calling a function from a module etc) the shell doesn't give any output or error message and just returns. I am sure that i give the command in the right shape (with a dot at the end etc).
To solve this i have to give the following commands in the shell:
'. or ".
And then the shell prints a long atom or what ever and then after that i can run my original command correctly.
Does anyone have seen a same issue? And what is the reason for this?
This looks like there were an initial ' or " in the command line. So the shell is waiting for the second ' or " which closes the atom or string declaration. It prints the evaluation result and is now ready for the next command.
Look at this example in the console (started with erl):
Eshell V10.6.2 (abort with ^G)
1> io:format("Hello world!").
Hello world!ok
2> io:format("Hello world! ).
2> ".
* 2: syntax error before: '.'

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

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.

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