%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%%-------------------------------------------------------------------
%%% File : loose_node.erl
%%% Author : Rickard Green <[email protected]>
%%% Description : Creation of nodes which are not supervised by
%%% the test_server. Currently needed by init_SUITE
%%% and heart_SUITE (until the test_server can
%%% handle node restart).
%%%
%%% Created : 22 Sep 2004 by Rickard Green <[email protected]>
%%%-------------------------------------------------------------------
-module(loose_node).
-author('[email protected]').
%%
%% Exports
%%
-export([start/3, start/2, stop/1]).
%%
%% Internal exports
%%
-export([loose_node_started/1]).
%%
%% Exported functions for internal use.
%%
%%
%% Defines
%%
-define(L2A, list_to_atom).
-define(A2L, atom_to_list).
-define(I2L, integer_to_list).
%%
%% Exported functions.
%%
stop(Node) when is_atom(Node) ->
rpc:cast(Node, erlang, halt, []),
io:format("Stopped loose node ~p~n", [Node]),
ok.
start(Name, Args) ->
start(Name, Args, -1).
start(Name, Args, TimeOut) when is_atom(Name) ->
start(atom_to_list(Name), Args, TimeOut);
start(Name, Args, TimeOut)
when is_list(Name), is_list(Args), is_integer(TimeOut) ->
Parent = self(),
Ref = make_ref(),
Starter
= fun () ->
Erl = case init:get_argument(progname) of
{ok,[[Prog]]} ->
Prog;
_ ->
"erl"
end,
RegName = until_success(fun () ->
{A, B, C} = now(),
Reg =
?L2A(?A2L(?MODULE)
++ "-" ++ ?I2L(A)
++ "-" ++ ?I2L(B)
++ "-" ++ ?I2L(C)),
true = register(Reg, self()),
Reg
end),
NameCmd = case net_kernel:longnames() of
true -> " -name " ++ Name;
false -> " -sname " ++ Name
end,
Cookie = " -setcookie " ++ atom_to_list(auth:get_cookie()),
Pa = " -pa " ++ filename:dirname(code:which(?MODULE)),
ThisNode = node(),
NodeStarted
= " -run "
++ atom_to_list(?MODULE)
++ " loose_node_started "
++ atom_to_list(RegName)
++ " "
++ atom_to_list(ThisNode)
++ " "
++ integer_to_list(TimeOut),
CrashDump =
" -env ERL_CRASH_DUMP"
++ " erl_crash.dump.loose_node."
++ Name,
Cmd =
Erl
++ " -detached"
++ NameCmd
++ Cookie
++ Pa
++ NodeStarted
++ CrashDump
++ " "
++ Args,
io:format("Trying to start loose node...~n"
" --> ~p~n", [Cmd]),
Res = case open_port({spawn, Cmd}, []) of
P when is_port(P) ->
receive
{loose_node_started,
Node,
{RegName, ThisNode}} ->
io:format("Loose node ~p started.~n",
[Node]),
{ok, Node}
after 10000 ->
io:format("Start of loose node ~p "
"timed out.", [Name]),
{error, timeout}
end;
_ ->
io:format("Start of loose node ~p failed.",
[Name]),
{error, open_port_failed}
end,
Parent ! {Ref, Res}
end,
spawn_opt(Starter, [link, {priority, max}]),
receive
{Ref, Result} ->
Result
end.
%%
%% Exported functions for internal use.
%%
loose_node_started([Name, Node, TimeOutSecs]) when is_list(Name),
is_list(Node),
is_list(TimeOutSecs) ->
spawn_opt(fun () ->
process_flag(trap_exit, true),
Proc = {list_to_atom(Name), list_to_atom(Node)},
Timeout = case catch list_to_integer(TimeOutSecs) of
I when is_integer(I), I >= 0 -> I*1000;
_ -> infinity
end,
wait_until(fun () -> is_alive() end),
Proc ! {loose_node_started, node(), Proc},
receive
after Timeout ->
timeout
end,
erlang:halt("Loose node timeout")
end,
[{priority, max}]),
ok.
%%
%% Internal functions.
%%
until_success(Fun) ->
case catch Fun() of
{'EXIT', _} -> until_success(Fun);
Res -> Res
end.
wait_until(Fun) ->
case Fun() of
true -> true;
_ ->
receive
after 100 ->
wait_until(Fun)
end
end.