1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1996-2016. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20-module(net_adm). 21-export([host_file/0, 22 localhost/0, 23 names/0, names/1, 24 ping_list/1, 25 world/0,world/1, 26 world_list/1, world_list/2, 27 dns_hostname/1, 28 ping/1]). 29 30%%------------------------------------------------------------------------ 31 32-type verbosity() :: 'silent' | 'verbose'. 33 34%%------------------------------------------------------------------------ 35 36%% Try to read .hosts.erlang file in 37%% 1. cwd , 2. $HOME 3. init:root_dir() 38 39-spec host_file() -> Hosts | {error, Reason} when 40 Hosts :: [Host :: atom()], 41 %% Copied from file:path_consult/2: 42 Reason :: file:posix() | badarg | terminated | system_limit 43 | {Line :: integer(), Mod :: module(), Term :: term()}. 44 45host_file() -> 46 Home = case init:get_argument(home) of 47 {ok, [[H]]} -> [H]; 48 _ -> [] 49 end, 50 case file:path_consult(["."] ++ Home ++ [code:root_dir()], ".hosts.erlang") of 51 {ok, Hosts, _} -> Hosts; 52 Error -> Error 53 end. 54 55%% Check whether a node is up or down 56%% side effect: set up a connection to Node if there not yet is one. 57 58-spec ping(Node) -> pong | pang when 59 Node :: atom(). 60 61ping(Node) when is_atom(Node) -> 62 case catch gen:call({net_kernel, Node}, 63 '$gen_call', 64 {is_auth, node()}, 65 infinity) of 66 {ok, yes} -> pong; 67 _ -> 68 erlang:disconnect_node(Node), 69 pang 70 end. 71 72-spec localhost() -> Name when 73 Name :: string(). 74 75localhost() -> 76 {ok, Host} = inet:gethostname(), 77 case inet_db:res_option(domain) of 78 "" -> Host; 79 Domain -> Host ++ "." ++ Domain 80 end. 81 82 83-spec names() -> {ok, [{Name, Port}]} | {error, Reason} when 84 Name :: string(), 85 Port :: non_neg_integer(), 86 Reason :: address | file:posix(). 87 88names() -> 89 names(localhost()). 90 91 92-spec names(Host) -> {ok, [{Name, Port}]} | {error, Reason} when 93 Host :: atom() | string() | inet:ip_address(), 94 Name :: string(), 95 Port :: non_neg_integer(), 96 Reason :: address | file:posix(). 97 98names(Hostname) -> 99 ErlEpmd = net_kernel:epmd_module(), 100 ErlEpmd:names(Hostname). 101 102-spec dns_hostname(Host) -> {ok, Name} | {error, Host} when 103 Host :: atom() | string(), 104 Name :: string(). 105 106dns_hostname(Hostname) -> 107 case inet:gethostbyname(Hostname) of 108 {ok,{hostent, Name, _ , _Af, _Size, _Addr}} -> 109 {ok, Name}; 110 _ -> 111 {error, Hostname} 112 end. 113 114%% A common situation in "life" is to have a configuration file with a list 115%% of nodes, and then at startup, all nodes in the list are ping'ed 116%% this can lead to no end of troubles if two disconnected nodes 117%% simultaneously ping each other. 118%% Use this function in order to do it safely. 119%% It assumes a working global.erl which ensures a fully 120%% connected network. 121%% Had the erlang runtime system been able to fully cope with 122%% the possibility of two simultaneous (unix) connects, this function would 123%% merley be lists:map({net_adm, ping}, [], Nodelist). 124%% It is also assumed, that the same (identical) Nodelist is given to all 125%% nodes which are to perform this call (possibly simultaneously). 126%% Even this code has a flaw, and that is the case where two 127%% nodes simultaneously and without *any* other already 128%% running nodes execute this code. :-( 129 130-spec ping_list([atom()]) -> [atom()]. 131 132ping_list(Nodelist) -> 133 ok = net_kernel:monitor_nodes(true), 134 Sofar = ping_first(Nodelist, nodes()), 135 collect_new(Sofar, Nodelist). 136 137ping_first([], _S) -> 138 []; 139ping_first([Node|Nodes], S) -> 140 case lists:member(Node, S) of 141 true -> [Node | ping_first(Nodes, S)]; 142 false -> 143 case ping(Node) of 144 pong -> [Node]; 145 pang -> ping_first(Nodes, S) 146 end 147 end. 148 149collect_new(Sofar, Nodelist) -> 150 receive 151 {nodeup, Node} -> 152 case lists:member(Node, Nodelist) of 153 true -> 154 collect_new(Sofar, Nodelist); 155 false -> 156 collect_new([Node | Sofar], Nodelist) 157 end 158 after 3000 -> 159 ok = net_kernel:monitor_nodes(false), 160 Sofar 161 end. 162 163%% This function polls a set of hosts according to a file called 164%% .hosts.erlang that need to reside either in the current directory 165%% or in your home directory. (The current directory is tried first.) 166%% world() returns a list of all nodes on the network that can be 167%% found (including ourselves). Note: the $HOME variable is inspected. 168%% 169%% Added possibility to supply a list of hosts instead of reading 170%% the .hosts.erlang file. 971016 patrik@erix.ericsson.se 171%% e.g. 172%% net_adm:world_list(['elrond.du.etx.ericsson.se', 'thorin.du.etx.ericsson.se']). 173 174-spec world() -> [node()]. 175 176world() -> 177 world(silent). 178 179-spec world(Arg) -> [node()] when 180 Arg :: verbosity(). 181 182world(Verbose) -> 183 case net_adm:host_file() of 184 {error,R} -> exit({error, R}); 185 Hosts -> expand_hosts(Hosts, Verbose) 186 end. 187 188-spec world_list(Hosts) -> [node()] when 189 Hosts :: [atom()]. 190 191world_list(Hosts) when is_list(Hosts) -> 192 expand_hosts(Hosts, silent). 193 194-spec world_list(Hosts, Arg) -> [node()] when 195 Hosts :: [atom()], 196 Arg :: verbosity(). 197 198world_list(Hosts, Verbose) when is_list(Hosts) -> 199 expand_hosts(Hosts, Verbose). 200 201expand_hosts(Hosts, Verbose) -> 202 lists:flatten(collect_nodes(Hosts, Verbose)). 203 204collect_nodes([], _) -> []; 205collect_nodes([Host|Tail], Verbose) -> 206 case collect_host_nodes(Host, Verbose) of 207 nil -> 208 collect_nodes(Tail, Verbose); 209 L -> 210 [L|collect_nodes(Tail, Verbose)] 211 end. 212 213collect_host_nodes(Host, Verbose) -> 214 case names(Host) of 215 {ok, Namelist} -> 216 do_ping(Namelist, atom_to_list(Host), Verbose); 217 _ -> 218 nil 219 end. 220 221do_ping(Names, Host0, Verbose) -> 222 case longshort(Host0) of 223 ignored -> []; 224 Host -> do_ping_1(Names, Host, Verbose) 225 end. 226 227do_ping_1([], _Host, _Verbose) -> 228 []; 229do_ping_1([{Name, _} | Rest], Host, Verbose) -> 230 Node = list_to_atom(Name ++ "@" ++ longshort(Host)), 231 verbose(Verbose, "Pinging ~w -> ", [Node]), 232 Result = ping(Node), 233 verbose(Verbose, "~p\n", [Result]), 234 case Result of 235 pong -> 236 [Node | do_ping_1(Rest, Host, Verbose)]; 237 pang -> 238 do_ping_1(Rest, Host, Verbose) 239 end. 240 241verbose(verbose, Format, Args) -> 242 io:format(Format, Args); 243verbose(_, _, _) -> 244 ok. 245 246longshort(Host) -> 247 case net_kernel:longnames() of 248 false -> uptodot(Host); 249 true -> Host; 250 ignored -> ignored 251 end. 252 253uptodot([$.|_]) -> []; 254uptodot([])-> []; 255uptodot([H|T]) -> [H|uptodot(T)]. 256