aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel/src/net_adm.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/src/net_adm.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/kernel/src/net_adm.erl')
-rw-r--r--lib/kernel/src/net_adm.erl239
1 files changed, 239 insertions, 0 deletions
diff --git a/lib/kernel/src/net_adm.erl b/lib/kernel/src/net_adm.erl
new file mode 100644
index 0000000000..737b1ecee9
--- /dev/null
+++ b/lib/kernel/src/net_adm.erl
@@ -0,0 +1,239 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. 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%
+%%
+-module(net_adm).
+-export([host_file/0,
+ localhost/0,
+ names/0, names/1,
+ ping_list/1,
+ world/0,world/1,
+ world_list/1, world_list/2,
+ dns_hostname/1,
+ ping/1]).
+
+%%------------------------------------------------------------------------
+
+-type verbosity() :: 'silent' | 'verbose'.
+
+%%------------------------------------------------------------------------
+
+%% Try to read .hosts.erlang file in
+%% 1. cwd , 2. $HOME 3. init:root_dir()
+
+-spec host_file() -> [atom()] | {'error',atom() | {integer(),atom(),_}}.
+
+host_file() ->
+ Home = case init:get_argument(home) of
+ {ok, [[H]]} -> [H];
+ _ -> []
+ end,
+ case file:path_consult(["."] ++ Home ++ [code:root_dir()], ".hosts.erlang") of
+ {ok, Hosts, _} -> Hosts;
+ Error -> Error
+ end.
+
+%% Check whether a node is up or down
+%% side effect: set up a connection to Node if there not yet is one.
+
+-spec ping(atom()) -> 'pang' | 'pong'.
+
+ping(Node) when is_atom(Node) ->
+ case catch gen:call({net_kernel, Node},
+ '$gen_call',
+ {is_auth, node()},
+ infinity) of
+ {ok, yes} -> pong;
+ _ ->
+ erlang:disconnect_node(Node),
+ pang
+ end.
+
+-spec localhost() -> string().
+
+localhost() ->
+ {ok, Host} = inet:gethostname(),
+ case inet_db:res_option(domain) of
+ "" -> Host;
+ Domain -> Host ++ "." ++ Domain
+ end.
+
+
+-spec names() -> {'ok', [{string(), integer()}]} | {'error', _}.
+
+names() ->
+ names(localhost()).
+
+-spec names(atom() | string()) -> {'ok', [{string(), integer()}]} | {'error', _}.
+
+names(Hostname) ->
+ case inet:gethostbyname(Hostname) of
+ {ok, {hostent, _Name, _ , _Af, _Size, [Addr | _]}} ->
+ erl_epmd:names(Addr);
+ Else ->
+ Else
+ end.
+
+-spec dns_hostname(atom() | string()) ->
+ {'ok', string()} | {'error', atom() | string()}.
+
+dns_hostname(Hostname) ->
+ case inet:gethostbyname(Hostname) of
+ {ok,{hostent, Name, _ , _Af, _Size, _Addr}} ->
+ {ok, Name};
+ _ ->
+ {error, Hostname}
+ end.
+
+%% A common situation in "life" is to have a configuration file with a list
+%% of nodes, and then at startup, all nodes in the list are ping'ed
+%% this can lead to no end of troubles if two disconnected nodes
+%% simultaneously ping each other.
+%% Use this function in order to do it safely.
+%% It assumes a working global.erl which ensures a fully
+%% connected network.
+%% Had the erlang runtime system been able to fully cope with
+%% the possibility of two simultaneous (unix) connects, this function would
+%% merley be lists:map({net_adm, ping}, [], Nodelist).
+%% It is also assumed, that the same (identical) Nodelist is given to all
+%% nodes which are to perform this call (possibly simultaneously).
+%% Even this code has a flaw, and that is the case where two
+%% nodes simultaneously and without *any* other already
+%% running nodes execute this code. :-(
+
+-spec ping_list([atom()]) -> [atom()].
+
+ping_list(Nodelist) ->
+ net_kernel:monitor_nodes(true),
+ Sofar = ping_first(Nodelist, nodes()),
+ collect_new(Sofar, Nodelist).
+
+ping_first([], _S) ->
+ [];
+ping_first([Node|Nodes], S) ->
+ case lists:member(Node, S) of
+ true -> [Node | ping_first(Nodes, S)];
+ false ->
+ case ping(Node) of
+ pong -> [Node];
+ pang -> ping_first(Nodes, S)
+ end
+ end.
+
+collect_new(Sofar, Nodelist) ->
+ receive
+ {nodeup, Node} ->
+ case lists:member(Node, Nodelist) of
+ true ->
+ collect_new(Sofar, Nodelist);
+ false ->
+ collect_new([Node | Sofar], Nodelist)
+ end
+ after 3000 ->
+ net_kernel:monitor_nodes(false),
+ Sofar
+ end.
+
+%% This function polls a set of hosts according to a file called
+%% .hosts.erlang that need to reside either in the current directory
+%% or in your home directory. (The current directory is tried first.)
+%% world() returns a list of all nodes on the network that can be
+%% found (including ourselves). Note: the $HOME variable is inspected.
+%%
+%% Added possibility to supply a list of hosts instead of reading
+%% the .hosts.erlang file. 971016 [email protected]
+%% e.g.
+%% net_adm:world_list(['elrond.du.etx.ericsson.se', 'thorin.du.etx.ericsson.se']).
+
+-spec world() -> [node()].
+
+world() ->
+ world(silent).
+
+-spec world(verbosity()) -> [node()].
+
+world(Verbose) ->
+ case net_adm:host_file() of
+ {error,R} -> exit({error, R});
+ Hosts -> expand_hosts(Hosts, Verbose)
+ end.
+
+-spec world_list([atom()]) -> [node()].
+
+world_list(Hosts) when is_list(Hosts) ->
+ expand_hosts(Hosts, silent).
+
+-spec world_list([atom()], verbosity()) -> [node()].
+
+world_list(Hosts, Verbose) when is_list(Hosts) ->
+ expand_hosts(Hosts, Verbose).
+
+expand_hosts(Hosts, Verbose) ->
+ lists:flatten(collect_nodes(Hosts, Verbose)).
+
+collect_nodes([], _) -> [];
+collect_nodes([Host|Tail], Verbose) ->
+ case collect_host_nodes(Host, Verbose) of
+ nil ->
+ collect_nodes(Tail, Verbose);
+ L ->
+ [L|collect_nodes(Tail, Verbose)]
+ end.
+
+collect_host_nodes(Host, Verbose) ->
+ case names(Host) of
+ {ok, Namelist} ->
+ do_ping(Namelist, atom_to_list(Host), Verbose);
+ _ ->
+ nil
+ end.
+
+do_ping(Names, Host0, Verbose) ->
+ case longshort(Host0) of
+ ignored -> [];
+ Host -> do_ping_1(Names, Host, Verbose)
+ end.
+
+do_ping_1([], _Host, _Verbose) ->
+ [];
+do_ping_1([{Name, _} | Rest], Host, Verbose) ->
+ Node = list_to_atom(Name ++ "@" ++ longshort(Host)),
+ verbose(Verbose, "Pinging ~w -> ", [Node]),
+ Result = ping(Node),
+ verbose(Verbose, "~p\n", [Result]),
+ case Result of
+ pong ->
+ [Node | do_ping_1(Rest, Host, Verbose)];
+ pang ->
+ do_ping_1(Rest, Host, Verbose)
+ end.
+
+verbose(verbose, Format, Args) ->
+ io:format(Format, Args);
+verbose(_, _, _) ->
+ ok.
+
+longshort(Host) ->
+ case net_kernel:longnames() of
+ false -> uptodot(Host);
+ true -> Host;
+ ignored -> ignored
+ end.
+
+uptodot([$.|_]) -> [];
+uptodot([])-> [];
+uptodot([H|T]) -> [H|uptodot(T)].