diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/kernel/src/net_adm.erl | |
download | otp-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.erl | 239 |
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)]. |