1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2018. 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(erl_distribution_SUITE).
21
22-include_lib("common_test/include/ct.hrl").
23
24-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
25	 init_per_group/2,end_per_group/2]).
26
27-export([tick/1, tick_change/1,
28         connect_node/1,
29         nodenames/1, hostnames/1,
30         illegal_nodenames/1, hidden_node/1,
31	 setopts/1,
32	 table_waste/1, net_setuptime/1,
33	 inet_dist_options_options/1,
34
35	 monitor_nodes_nodedown_reason/1,
36	 monitor_nodes_complex_nodedown_reason/1,
37	 monitor_nodes_node_type/1,
38	 monitor_nodes_misc/1,
39	 monitor_nodes_otp_6481/1,
40	 monitor_nodes_errors/1,
41	 monitor_nodes_combinations/1,
42	 monitor_nodes_cleanup/1,
43	 monitor_nodes_many/1]).
44
45%% Performs the test at another node.
46-export([get_socket_priorities/0,
47	 tick_cli_test/1, tick_cli_test1/1,
48	 tick_serv_test/2, tick_serv_test1/1,
49	 run_remote_test/1,
50	 setopts_do/2,
51	 keep_conn/1, time_ping/1]).
52
53-export([init_per_testcase/2, end_per_testcase/2]).
54
55-export([start_node/2]).
56
57-export([pinger/1]).
58
59-define(DUMMY_NODE,dummy@test01).
60
61%%-----------------------------------------------------------------
62%% The distribution is mainly tested in the big old test_suite.
63%% This test only tests the net_ticktime configuration flag.
64%% Should be started in a CC view with:
65%% erl -sname master -rsh ctrsh
66%%-----------------------------------------------------------------
67
68suite() ->
69    [{ct_hooks,[ts_install_cth]},
70     {timetrap,{minutes,4}}].
71
72all() ->
73    [tick, tick_change, nodenames, hostnames, illegal_nodenames,
74     connect_node,
75     hidden_node, setopts,
76     table_waste, net_setuptime, inet_dist_options_options,
77     {group, monitor_nodes}].
78
79groups() ->
80    [{monitor_nodes, [],
81      [monitor_nodes_nodedown_reason,
82       monitor_nodes_complex_nodedown_reason,
83       monitor_nodes_node_type, monitor_nodes_misc,
84       monitor_nodes_otp_6481, monitor_nodes_errors,
85       monitor_nodes_combinations, monitor_nodes_cleanup,
86       monitor_nodes_many]}].
87
88init_per_suite(Config) ->
89    Config.
90
91end_per_suite(_Config) ->
92    [slave:stop(N) || N <- nodes()],
93    ok.
94
95init_per_group(_GroupName, Config) ->
96    Config.
97
98end_per_group(_GroupName, Config) ->
99    Config.
100
101init_per_testcase(TC, Config) when TC == hostnames;
102                                   TC == nodenames ->
103    file:make_dir("hostnames_nodedir"),
104    file:write_file("hostnames_nodedir/ignore_core_files",""),
105    Config;
106init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
107    Config.
108
109end_per_testcase(_Func, _Config) ->
110    ok.
111
112connect_node(Config) when is_list(Config) ->
113    Connected = nodes(connected),
114    true = net_kernel:connect_node(node()),
115    Connected = nodes(connected),
116    ok.
117
118tick(Config) when is_list(Config) ->
119    PaDir = filename:dirname(code:which(erl_distribution_SUITE)),
120
121    %% First check that the normal case is OK!
122    {ok, Node} = start_node(dist_test, "-pa " ++ PaDir),
123    rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [node()]),
124
125    erlang:monitor_node(Node, true),
126    receive
127	{nodedown, Node} ->
128	    ct:fail("nodedown from other node")
129    after 30000 ->
130	    erlang:monitor_node(Node, false),
131	    stop_node(Node)
132    end,
133
134    %% Now, set the net_ticktime for the other node to 12 secs.
135    %% After the sleep(2sec) and cast the other node shall destroy
136    %% the connection as it has not received anything on the connection.
137    %% The nodedown message should arrive within 8 < T < 16 secs.
138
139    %% We must have two slave nodes as the slave mechanism otherwise
140    %% halts the client node after tick timeout (the connection is down
141    %% and the slave node decides to halt !!
142
143    %% Set the ticktime on the server node to 100 secs so the server
144    %% node doesn't tick the client node within the interval ...
145
146    {ok, ServNode} = start_node(dist_test_server,
147				"-kernel net_ticktime 100 "
148				"-pa " ++ PaDir),
149    rpc:call(ServNode, erl_distribution_SUITE, tick_serv_test, [Node, node()]),
150
151    {ok, _} = start_node(dist_test,
152			 "-kernel net_ticktime 12 "
153			 "-pa " ++ PaDir),
154    rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [ServNode]),
155
156    spawn_link(erl_distribution_SUITE, keep_conn, [Node]),
157
158    {tick_serv, ServNode} ! {i_want_the_result, self()},
159
160    monitor_node(ServNode, true),
161    monitor_node(Node, true),
162
163    receive
164	{tick_test, T} when is_integer(T) ->
165	    stop_node(ServNode),
166	    stop_node(Node),
167	    T;
168	{tick_test, Error} ->
169	    stop_node(ServNode),
170	    stop_node(Node),
171	    ct:fail(Error);
172	{nodedown, Node} ->
173	    stop_node(ServNode),
174	    ct:fail("client node died");
175	{nodedown, ServNode} ->
176	    stop_node(Node),
177	    ct:fail("server node died")
178    end,
179    ok.
180
181%% Checks that pinging nonexistyent nodes does not waste space in distribution table.
182table_waste(Config) when is_list(Config) ->
183    {ok, HName} = inet:gethostname(),
184    F = fun(0,_F) -> [];
185	   (N,F) ->
186		Name = list_to_atom("erl_distribution_"++integer_to_list(N)++
187					"@"++HName),
188		pang = net_adm:ping(Name),
189		F(N-1,F)
190	end,
191    F(256,F),
192    {ok, N} = start_node(erl_distribution_300,""),
193    stop_node(N),
194    ok.
195
196%% Test that starting nodes with different legal name part works, and that illegal
197%% ones are filtered
198nodenames(Config) when is_list(Config) ->
199    legal("a1@b"),
200    legal("a-1@b"),
201    legal("a_1@b"),
202
203    illegal("cdé@a"),
204    illegal("te欢st@a").
205
206%% Test that starting nodes with different legal host part works, and that illegal
207%% ones are filtered
208hostnames(Config) when is_list(Config) ->
209    Host = gethostname(),
210    legal([$a,$@|atom_to_list(Host)]),
211    legal("1@b1"),
212    legal("b@b1-c"),
213    legal("c@b1_c"),
214    legal("d@b1#c"),
215    legal("f@::1"),
216    legal("g@1:bc3:4e3f:f20:0:1"),
217
218    case file:native_name_encoding() of
219        latin1 -> ignore;
220        _ -> legal("e@b1é")
221    end,
222    long_hostnames(net_kernel:longnames()),
223
224    illegal("h@testالع"),
225    illegal("i@языtest"),
226    illegal("j@te欢st").
227
228long_hostnames(true) ->
229    legal("k@b.b.c"),
230    legal("l@b.b-c.d"),
231    legal("m@b.b_c.d"),
232    legal("n@127.0.0.1"),
233    legal("o@207.123.456.789");
234long_hostnames(false) ->
235    illegal("k@b.b.c").
236
237legal(Name) ->
238    case test_node(Name) of
239        started ->
240            ok;
241        not_started ->
242            ct:fail("no ~p node started", [Name])
243    end.
244
245illegal(Name) ->
246    case test_node(Name, true) of
247        not_started ->
248            ok;
249        started ->
250            ct:fail("~p node started with illegal name", [Name])
251    end.
252
253test_node(Name) ->
254    test_node(Name, false).
255test_node(Name, Illigal) ->
256    ProgName = ct:get_progname(),
257    Command = ProgName ++ " -noinput " ++ long_or_short() ++ Name ++
258        " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"" ++
259        case Illigal of
260            true ->
261                " -eval \"timer:sleep(10000),init:stop().\"";
262            false ->
263                ""
264        end,
265    net_kernel:monitor_nodes(true),
266    BinCommand = unicode:characters_to_binary(Command, utf8),
267    Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
268    Node = list_to_atom(Name),
269    receive
270        {nodeup, Node} ->
271            net_kernel:monitor_nodes(false),
272            slave:stop(Node),
273            started
274    after 5000 ->
275        net_kernel:monitor_nodes(false),
276        not_started
277    end.
278
279long_or_short() ->
280    case net_kernel:longnames() of
281        true -> " -name ";
282        false -> " -sname "
283    end.
284
285% get the localhost's name, depending on the using name policy
286gethostname() ->
287    Hostname = case net_kernel:longnames() of
288       true->
289           net_adm:localhost();
290       _->
291           {ok, Name}=inet:gethostname(),
292           Name
293    end,
294    list_to_atom(Hostname).
295
296%% Test that pinging an illegal nodename does not kill the node.
297illegal_nodenames(Config) when is_list(Config) ->
298    PaDir = filename:dirname(code:which(erl_distribution_SUITE)),
299    {ok, Node}=start_node(illegal_nodenames, "-pa " ++ PaDir),
300    monitor_node(Node, true),
301    RPid=rpc:call(Node, erlang, spawn,
302		  [?MODULE, pinger, [self()]]),
303    receive
304	{RPid, pinged} ->
305	    ok;
306	{nodedown, Node} ->
307	    ct:fail("Remote node died.")
308    end,
309    stop_node(Node),
310    ok.
311
312pinger(Starter) ->
313    io:format("Starter:~p~n",[Starter]),
314    net_adm:ping(a@b@c),
315    Starter ! {self(), pinged},
316    ok.
317
318
319%% Test that you can set the net_setuptime properly.
320net_setuptime(Config) when is_list(Config) ->
321    %% In this test case, we reluctantly accept shorter times than the given
322    %% setup time, because the connection attempt can end in a
323    %% "Host unreachable" error before the timeout fires.
324
325    Res0 = do_test_setuptime("2"),
326    io:format("Res0 = ~p", [Res0]),
327    true = (Res0 =< 4000),
328    Res1 = do_test_setuptime("0.3"),
329    io:format("Res1 = ~p", [Res1]),
330    true = (Res1 =< 500),
331    ok.
332
333do_test_setuptime(Setuptime) when is_list(Setuptime) ->
334    PaDir = filename:dirname(code:which(?MODULE)),
335    {ok, Node} = start_node(dist_setuptime_test, "-pa " ++ PaDir ++
336				" -kernel net_setuptime " ++ Setuptime),
337    Res = rpc:call(Node,?MODULE,time_ping,[?DUMMY_NODE]),
338    stop_node(Node),
339    Res.
340
341time_ping(Node) ->
342    T0 = erlang:monotonic_time(),
343    pang = net_adm:ping(Node),
344    T1 = erlang:monotonic_time(),
345    erlang:convert_time_unit(T1 - T0, native, millisecond).
346
347%% Keep the connection with the client node up.
348%% This is necessary as the client node runs with much shorter
349%% tick time !!
350keep_conn(Node) ->
351    sleep(1),
352    rpc:cast(Node, erlang, time, []),
353    keep_conn(Node).
354
355tick_serv_test(Node, MasterNode) ->
356    spawn(erl_distribution_SUITE, keep_conn, [MasterNode]),
357    spawn(erl_distribution_SUITE, tick_serv_test1, [Node]).
358
359tick_serv_test1(Node) ->
360    register(tick_serv, self()),
361    TestServer = receive {i_want_the_result, TS} -> TS end,
362    monitor_node(Node, true),
363    receive
364	{nodedown, Node} ->
365	    net_adm:ping(Node), %% Set up the connection again !!
366
367	    {tick_test, Node} ! {whats_the_result, self()},
368	    receive
369		{tick_test, Res} ->
370		    TestServer ! {tick_test, Res}
371	    end
372    end.
373
374tick_cli_test(Node) ->
375    spawn(erl_distribution_SUITE, tick_cli_test1, [Node]).
376
377tick_cli_test1(Node) ->
378    register(tick_test, self()),
379    erlang:monitor_node(Node, true),
380    sleep(2),
381    rpc:call(Node, erlang, time, []), %% simulate action on the connection
382    T1 = erlang:monotonic_time(),
383    receive
384	{nodedown, Node} ->
385	    T2 = erlang:monotonic_time(),
386	    receive
387		{whats_the_result, From} ->
388		    Diff = erlang:convert_time_unit(T2-T1, native,
389						    millisecond),
390		    case Diff of
391			T when T > 8000, T < 16000 ->
392			    From ! {tick_test, T};
393			T ->
394			    From ! {tick_test,
395				    {"T not in interval 8000 < T < 16000",
396				     T}}
397		    end
398	    end
399    end.
400
401setopts(Config) when is_list(Config) ->
402    register(setopts_regname, self()),
403    [N1,N2,N3,N4] = get_nodenames(4, setopts),
404
405    {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test,
406					["setopts_do", atom_to_list(node()), "1", "ping"]),
407    0 = wait_for_port_exit(Port1),
408
409    {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test,
410				 ["setopts_do", atom_to_list(node()), "2", "ping"]),
411    0 = wait_for_port_exit(Port2),
412
413    {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]),
414    {ok, LTcpPort} = inet:port(LSock),
415
416    {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test,
417					["setopts_do", atom_to_list(node()),
418					 "1", integer_to_list(LTcpPort)]),
419    wait_and_connect(LSock, N3F, Port3),
420    0 = wait_for_port_exit(Port3),
421
422    {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test,
423					["setopts_do", atom_to_list(node()),
424					 "2", integer_to_list(LTcpPort)]),
425    wait_and_connect(LSock, N4F, Port4),
426    0 = wait_for_port_exit(Port4),
427
428    ok.
429
430wait_and_connect(LSock, NodeName, NodePort) ->
431    {ok, Sock} = gen_tcp:accept(LSock),
432    {ok, "Connect please"} = gen_tcp:recv(Sock, 0),
433    flush_from_port(NodePort),
434    pong = net_adm:ping(NodeName),
435    gen_tcp:send(Sock, "Connect done"),
436    gen_tcp:close(Sock).
437
438
439flush_from_port(Port) ->
440    flush_from_port(Port, 10).
441
442flush_from_port(Port, Timeout) ->
443    receive
444	{Port,{data,String}} ->
445	    io:format("~p: ~s\n", [Port, String]),
446	    flush_from_port(Port, Timeout)
447    after Timeout ->
448	    timeout
449    end.
450
451wait_for_port_exit(Port) ->
452    case (receive M -> M end) of
453	{Port,{exit_status,Status}} ->
454	    Status;
455	{Port,{data,String}} ->
456	    io:format("~p: ~s\n", [Port, String]),
457	    wait_for_port_exit(Port)
458    end.
459
460run_remote_test([FuncStr, TestNodeStr | Args]) ->
461    Status = try
462	io:format("Node ~p started~n", [node()]),
463	TestNode = list_to_atom(TestNodeStr),
464	io:format("Node ~p spawning function ~p~n", [node(), FuncStr]),
465	{Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]),
466	io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]),
467	receive
468	    {'DOWN', Ref, process, Pid, normal} ->
469		0;
470	    Other ->
471		io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]),
472		1
473	end
474    catch
475	C:E:S ->
476	    io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n",
477		      [node(), C, E, S]),
478	    2
479    end,
480    io:format("Node ~p doing halt(~p).\n",[node(), Status]),
481    erlang:halt(Status).
482
483% Do the actual test on the remote node
484setopts_do(TestNode, [OptNr, ConnectData]) ->
485    [] = nodes(),
486    {Opt, Val} = opt_from_nr(OptNr),
487    ok = net_kernel:setopts(new, [{Opt, Val}]),
488
489    [] = nodes(),
490    {error, noconnection} = net_kernel:getopts(TestNode, [Opt]),
491
492    case ConnectData of
493	"ping" ->  % We connect
494	    net_adm:ping(TestNode);
495	TcpPort -> % Other connect
496	    {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort),
497					 [{active,false},{packet,2}]),
498	    ok = gen_tcp:send(Sock, "Connect please"),
499	    {ok, "Connect done"} = gen_tcp:recv(Sock, 0),
500	    gen_tcp:close(Sock)
501    end,
502    [TestNode] = nodes(),
503    {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
504    {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]),
505
506    NewVal = change_val(Val),
507    ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]),
508    {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]),
509
510    ok = net_kernel:setopts(TestNode, [{Opt, Val}]),
511    {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
512
513    ok.
514
515opt_from_nr("1") -> {nodelay, true};
516opt_from_nr("2") -> {nodelay, false}.
517
518change_val(true)  -> false;
519change_val(false) -> true.
520
521start_node_unconnected(Name, Mod, Func, Args) ->
522    FullName = full_node_name(Name),
523    CmdLine = mk_node_cmdline(Name,Mod,Func,Args),
524    io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
525    case open_port({spawn, CmdLine}, [exit_status]) of
526	Port when is_port(Port) ->
527	    {FullName, Port};
528	Error ->
529	    exit({failed_to_start_node, FullName, Error})
530    end.
531
532full_node_name(PreName) ->
533    HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
534				 atom_to_list(node())),
535    list_to_atom(atom_to_list(PreName) ++ HostSuffix).
536
537mk_node_cmdline(Name,Mod,Func,Args) ->
538    Static = "-noinput",
539    Pa = filename:dirname(code:which(?MODULE)),
540    Prog = case catch init:get_argument(progname) of
541	       {ok,[[P]]} -> P;
542	       _ -> exit(no_progname_argument_found)
543	   end,
544    NameSw = case net_kernel:longnames() of
545		 false -> "-sname ";
546		 true -> "-name ";
547		 _ -> exit(not_distributed_node)
548	     end,
549    {ok, Pwd} = file:get_cwd(),
550    NameStr = atom_to_list(Name),
551    Prog ++ " "
552	++ Static ++ " "
553	++ NameSw ++ " " ++ NameStr
554	++ " -pa " ++ Pa
555	++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr
556	++ " -setcookie " ++ atom_to_list(erlang:get_cookie())
557	++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func)
558	++ " " ++ string:join(Args, " ").
559
560
561%% OTP-4255.
562tick_change(Config) when is_list(Config) ->
563    PaDir = filename:dirname(code:which(?MODULE)),
564    [BN, CN] = get_nodenames(2, tick_change),
565    DefaultTT = net_kernel:get_net_ticktime(),
566    unchanged = net_kernel:set_net_ticktime(DefaultTT, 60),
567    case DefaultTT of
568	I when is_integer(I) -> ok;
569	_                 -> ct:fail(DefaultTT)
570    end,
571
572    %% In case other nodes are connected
573    case nodes(connected) of
574	[] -> net_kernel:set_net_ticktime(10, 0);
575	_ ->  rpc:multicall(nodes([this, connected]), net_kernel,
576			    set_net_ticktime, [10, 5])
577    end,
578
579    wait_until(fun () -> 10 == net_kernel:get_net_ticktime() end),
580    {ok, B} = start_node(BN, "-kernel net_ticktime 10 -pa " ++ PaDir),
581    {ok, C} = start_node(CN, "-kernel net_ticktime 10 -hidden -pa "
582			 ++ PaDir),
583
584    OTE = process_flag(trap_exit, true),
585    case catch begin
586		   run_tick_change_test(B, C, 10, 1, PaDir),
587		   run_tick_change_test(B, C, 1, 10, PaDir)
588	       end of
589	{'EXIT', Reason} ->
590	    stop_node(B),
591	    stop_node(C),
592	    %% In case other nodes are connected
593	    case nodes(connected) of
594		[] -> net_kernel:set_net_ticktime(DefaultTT, 0);
595		_ ->  rpc:multicall(nodes([this, connected]), net_kernel,
596				    set_net_ticktime, [DefaultTT, 10])
597	    end,
598	    wait_until(fun () ->
599			       DefaultTT == net_kernel:get_net_ticktime()
600		       end),
601	    process_flag(trap_exit, OTE),
602	    ct:fail(Reason);
603	_ ->
604	    ok
605    end,
606    process_flag(trap_exit, OTE),
607    stop_node(B),
608    stop_node(C),
609
610    %% In case other nodes are connected
611    case nodes(connected) of
612	[] -> net_kernel:set_net_ticktime(DefaultTT, 0);
613	_ ->  rpc:multicall(nodes([this, connected]), net_kernel,
614			    set_net_ticktime, [DefaultTT, 5])
615    end,
616
617    wait_until(fun () -> DefaultTT == net_kernel:get_net_ticktime() end),
618    ok.
619
620
621wait_for_nodedowns(Tester, Ref) ->
622    receive
623	{nodedown, Node} ->
624	    io:format("~p~n", [{node(), {nodedown, Node}}]),
625	    Tester ! {Ref, {node(), {nodedown, Node}}}
626    end,
627    wait_for_nodedowns(Tester, Ref).
628
629run_tick_change_test(B, C, PrevTT, TT, PaDir) ->
630    [DN, EN] = get_nodenames(2, tick_change),
631
632    Tester = self(),
633    Ref = make_ref(),
634    MonitorNodes = fun (Nodes) ->
635			   lists:foreach(
636			     fun (N) ->
637				     monitor_node(N,true)
638			     end,
639			     Nodes),
640			   wait_for_nodedowns(Tester, Ref)
641		   end,
642
643    {ok, D} = start_node(DN, "-kernel net_ticktime "
644			 ++ integer_to_list(PrevTT) ++ " -pa " ++ PaDir),
645
646    NMA = spawn_link(fun () -> MonitorNodes([B, C, D]) end),
647    NMB = spawn_link(B, fun () -> MonitorNodes([node(), C, D]) end),
648    NMC = spawn_link(C, fun () -> MonitorNodes([node(), B, D]) end),
649
650    MaxTT = case PrevTT > TT of
651		true  -> PrevTT;
652		false -> TT
653	    end,
654
655    CheckResult = make_ref(),
656    spawn_link(fun () ->
657		       receive
658		       after (25 + MaxTT)*1000 ->
659			       Tester ! CheckResult
660		       end
661	       end),
662
663    %% In case other nodes than these are connected
664    case nodes(connected) -- [B, C, D] of
665	[] -> ok;
666	OtherNodes -> rpc:multicall(OtherNodes, net_kernel,
667				    set_net_ticktime, [TT, 20])
668    end,
669
670    change_initiated = net_kernel:set_net_ticktime(TT,20),
671    {ongoing_change_to,_} = net_kernel:set_net_ticktime(TT,20),
672    sleep(3),
673    change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]),
674    sleep(7),
675    change_initiated = rpc:call(C,net_kernel,set_net_ticktime,[TT,10]),
676
677    {ok, E} = start_node(EN, "-kernel net_ticktime "
678			 ++ integer_to_list(TT) ++ " -pa " ++ PaDir),
679    NME  = spawn_link(E, fun () -> MonitorNodes([node(), B, C, D]) end),
680    NMA2 = spawn_link(fun () -> MonitorNodes([E]) end),
681    NMB2 = spawn_link(B, fun () -> MonitorNodes([E]) end),
682    NMC2 = spawn_link(C, fun () -> MonitorNodes([E]) end),
683
684    receive CheckResult -> ok end,
685
686    unlink(NMA),  exit(NMA, kill),
687    unlink(NMB),  exit(NMB, kill),
688    unlink(NMC),  exit(NMC, kill),
689    unlink(NME),  exit(NME, kill),
690    unlink(NMA2), exit(NMA2, kill),
691    unlink(NMB2), exit(NMB2, kill),
692    unlink(NMC2), exit(NMC2, kill),
693
694    %% The node not changing ticktime should have been disconnected from the
695    %% other nodes
696    receive {Ref, {Node, {nodedown, D}}} when Node == node() -> ok
697    after 0 -> exit({?LINE, no_nodedown})
698    end,
699    receive {Ref, {B, {nodedown, D}}} -> ok
700    after 0 -> exit({?LINE, no_nodedown})
701    end,
702    receive {Ref, {C, {nodedown, D}}} -> ok
703    after 0 -> exit({?LINE, no_nodedown})
704    end,
705    receive {Ref, {E, {nodedown, D}}} -> ok
706    after 0 -> exit({?LINE, no_nodedown})
707    end,
708
709    %% No other connections should have been broken
710    receive
711	{Ref, Reason} ->
712	    stop_node(E),
713	    exit({?LINE, Reason});
714	{'EXIT', Pid, Reason} when Pid == NMA;
715				   Pid == NMB;
716				   Pid == NMC;
717				   Pid == NME;
718				   Pid == NMA2;
719				   Pid == NMB2;
720				   Pid == NMC2 ->
721	    stop_node(E),
722
723	    exit({?LINE, {node(Pid), Reason}})
724    after 0 ->
725	    TT = net_kernel:get_net_ticktime(),
726	    TT = rpc:call(B, net_kernel, get_net_ticktime, []),
727	    TT = rpc:call(C, net_kernel, get_net_ticktime, []),
728	    TT = rpc:call(E, net_kernel, get_net_ticktime, []),
729	    stop_node(E),
730	    ok
731    end.
732
733%%
734%% Basic tests of hidden node.
735%%
736%% Basic test of hidden node.
737hidden_node(Config) when is_list(Config) ->
738    PaDir = filename:dirname(code:which(?MODULE)),
739    VArgs = "-pa " ++ PaDir,
740    HArgs = "-hidden -pa " ++ PaDir,
741    {ok, V} = start_node(visible_node, VArgs),
742    VMN = start_monitor_nodes_proc(V),
743    {ok, H} = start_node(hidden_node, HArgs),
744    %% Connect visible_node -> hidden_node
745    connect_nodes(V, H),
746    test_nodes(V, H),
747    stop_node(H),
748    sleep(5),
749    check_monitor_nodes_res(VMN, H),
750    stop_node(V),
751    {ok, H} = start_node(hidden_node, HArgs),
752    HMN = start_monitor_nodes_proc(H),
753    {ok, V} = start_node(visible_node, VArgs),
754    %% Connect hidden_node -> visible_node
755    connect_nodes(H, V),
756    test_nodes(V, H),
757    stop_node(V),
758    sleep(5),
759    check_monitor_nodes_res(HMN, V),
760    stop_node(H),
761    ok.
762
763connect_nodes(A, B) ->
764    %% Check that they haven't already connected.
765    false = lists:member(A, rpc:call(B, erlang, nodes, [connected])),
766    false = lists:member(B, rpc:call(A, erlang, nodes, [connected])),
767    %% Connect them.
768    pong = rpc:call(A, net_adm, ping, [B]).
769
770
771test_nodes(V, H) ->
772    %% No nodes should be visible on hidden_node
773    [] = rpc:call(H, erlang, nodes, []),
774    %% visible_node should be hidden on hidden_node
775    true = lists:member(V, rpc:call(H, erlang, nodes, [hidden])),
776    %% hidden_node node shouldn't be visible on visible_node
777    false = lists:member(H, rpc:call(V, erlang, nodes, [])),
778    %% hidden_node should be hidden on visible_node
779    true = lists:member(H, rpc:call(V, erlang, nodes, [hidden])).
780
781mn_loop(MNs) ->
782    receive
783	{nodeup, N} ->
784	    mn_loop([{nodeup, N}|MNs]);
785	{nodedown, N} ->
786	    mn_loop([{nodedown, N}|MNs]);
787	{monitor_nodes_result, Ref, From} ->
788	    From ! {Ref, MNs};
789	_ ->
790	    mn_loop(MNs)
791    end.
792
793start_monitor_nodes_proc(Node) ->
794    Ref = make_ref(),
795    Starter = self(),
796    Pid = spawn(Node,
797		fun() ->
798			net_kernel:monitor_nodes(true),
799			Starter ! Ref,
800			mn_loop([])
801		end),
802    receive
803	Ref ->
804	    ok
805    end,
806    Pid.
807
808
809check_monitor_nodes_res(Pid, Node) ->
810    Ref = make_ref(),
811    Pid ! {monitor_nodes_result, Ref, self()},
812    receive
813	{Ref, MNs} ->
814	    false = lists:keysearch(Node, 2, MNs)
815    end.
816
817
818
819%% Check the kernel inet_dist_{listen,connect}_options options.
820inet_dist_options_options(Config) when is_list(Config) ->
821    Prio = 1,
822    case gen_udp:open(0, [{priority,Prio}]) of
823	{ok,Socket} ->
824	    case inet:getopts(Socket, [priority]) of
825		{ok,[{priority,Prio}]} ->
826		    ok = gen_udp:close(Socket),
827		    do_inet_dist_options_options(Prio);
828		_ ->
829		    ok = gen_udp:close(Socket),
830		    {skip,
831		     "Can not set priority "++integer_to_list(Prio)++
832			 " on socket"}
833	    end;
834	{error,_} ->
835	    {skip, "Can not set priority on socket"}
836    end.
837
838do_inet_dist_options_options(Prio) ->
839    PriorityString0 = "[{priority,"++integer_to_list(Prio)++"}]",
840    PriorityString =
841	case os:cmd("echo [{a,1}]") of
842	    "[{a,1}]"++_ ->
843		PriorityString0;
844	    _ ->
845		%% Some shells need quoting of [{}]
846		"'"++PriorityString0++"'"
847	end,
848    InetDistOptions =
849	"-hidden "
850	"-kernel inet_dist_connect_options "++PriorityString++" "
851	"-kernel inet_dist_listen_options "++PriorityString,
852    {ok,Node1} =
853	start_node(inet_dist_options_1, InetDistOptions),
854    {ok,Node2} =
855	start_node(inet_dist_options_2, InetDistOptions),
856    %%
857    pong =
858	rpc:call(Node1, net_adm, ping, [Node2]),
859    PrioritiesNode1 =
860	rpc:call(Node1, ?MODULE, get_socket_priorities, []),
861    PrioritiesNode2 =
862	rpc:call(Node2, ?MODULE, get_socket_priorities, []),
863    io:format("PrioritiesNode1 = ~p", [PrioritiesNode1]),
864    io:format("PrioritiesNode2 = ~p", [PrioritiesNode2]),
865    Elevated = [P || P <- PrioritiesNode1, P =:= Prio],
866    Elevated = [P || P <- PrioritiesNode2, P =:= Prio],
867    [_|_] = Elevated,
868    %%
869    stop_node(Node2),
870    stop_node(Node1),
871    ok.
872
873get_socket_priorities() ->
874    [Priority ||
875	{ok,[{priority,Priority}]} <-
876	    [inet:getopts(Port, [priority]) ||
877		Port <- erlang:ports(),
878		element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]].
879
880
881
882%%
883%% Testcase:
884%%   monitor_nodes_nodedown_reason
885%%
886
887monitor_nodes_nodedown_reason(Config) when is_list(Config) ->
888    MonNodeState = monitor_node_state(),
889    ok = net_kernel:monitor_nodes(true),
890    ok = net_kernel:monitor_nodes(true, [nodedown_reason]),
891
892    Names = get_numbered_nodenames(5, node),
893    [NN1, NN2, NN3, NN4, NN5] = Names,
894
895    {ok, N1} = start_node(NN1),
896    {ok, N2} = start_node(NN2),
897    {ok, N3} = start_node(NN3),
898    {ok, N4} = start_node(NN4, "-hidden"),
899
900    receive {nodeup, N1} -> ok end,
901    receive {nodeup, N2} -> ok end,
902    receive {nodeup, N3} -> ok end,
903
904    receive {nodeup, N1, []} -> ok end,
905    receive {nodeup, N2, []} -> ok end,
906    receive {nodeup, N3, []} -> ok end,
907
908    stop_node(N1),
909    stop_node(N4),
910    true = net_kernel:disconnect(N2),
911    TickTime = net_kernel:get_net_ticktime(),
912    SleepTime = TickTime + (TickTime div 2),
913    spawn(N3, fun () ->
914		      block_emu(SleepTime*1000),
915		      halt()
916	      end),
917
918    receive {nodedown, N1} -> ok end,
919    receive {nodedown, N2} -> ok end,
920    receive {nodedown, N3} -> ok end,
921
922    receive {nodedown, N1, [{nodedown_reason, R1}]} -> connection_closed = R1 end,
923    receive {nodedown, N2, [{nodedown_reason, R2}]} -> disconnect = R2 end,
924    receive {nodedown, N3, [{nodedown_reason, R3}]} -> net_tick_timeout = R3 end,
925
926    ok = net_kernel:monitor_nodes(false, [nodedown_reason]),
927
928    {ok, N5} = start_node(NN5),
929    stop_node(N5),
930
931    receive {nodeup, N5} -> ok end,
932    receive {nodedown, N5} -> ok end,
933    print_my_messages(),
934    ok = check_no_nodedown_nodeup(1000),
935    ok = net_kernel:monitor_nodes(false),
936    MonNodeState = monitor_node_state(),
937    ok.
938
939
940monitor_nodes_complex_nodedown_reason(Config) when is_list(Config) ->
941    MonNodeState = monitor_node_state(),
942    Me = self(),
943    ok = net_kernel:monitor_nodes(true, [nodedown_reason]),
944    [Name] = get_nodenames(1, monitor_nodes_complex_nodedown_reason),
945    {ok, Node} = start_node(Name, ""),
946    Pid = spawn(Node,
947		fun() ->
948			Me ! {stuff,
949			      self(),
950			      [make_ref(),
951			       {processes(), erlang:ports()}]}
952		end),
953    receive {nodeup, Node, []} -> ok end,
954    {ok, NodeInfo} = net_kernel:node_info(Node),
955    {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo),
956    ComplexTerm = receive {stuff, Pid, _} = Msg ->
957			  {Msg, term_to_binary(Msg)}
958		  end,
959    exit(Owner, ComplexTerm),
960    receive
961	{nodedown, Node, [{nodedown_reason, NodeDownReason}]} ->
962	    ok
963    end,
964    %% If the complex nodedown_reason messed something up garbage collections
965    %% are likely to dump core
966    garbage_collect(),
967    garbage_collect(),
968    garbage_collect(),
969    ComplexTerm = NodeDownReason,
970    ok = net_kernel:monitor_nodes(false, [nodedown_reason]),
971    no_msgs(),
972    MonNodeState = monitor_node_state(),
973    ok.
974
975
976
977
978%%
979%% Testcase:
980%%   monitor_nodes_node_type
981%%
982
983monitor_nodes_node_type(Config) when is_list(Config) ->
984    MonNodeState = monitor_node_state(),
985    ok = net_kernel:monitor_nodes(true),
986    ok = net_kernel:monitor_nodes(true, [{node_type, all}]),
987    Names = get_numbered_nodenames(9, node),
988    [NN1, NN2, NN3, NN4, NN5, NN6, NN7, NN8, NN9] = Names,
989
990    {ok, N1} = start_node(NN1),
991    {ok, N2} = start_node(NN2),
992    {ok, N3} = start_node(NN3, "-hidden"),
993    {ok, N4} = start_node(NN4, "-hidden"),
994
995    receive {nodeup, N1} -> ok end,
996    receive {nodeup, N2} -> ok end,
997
998    receive {nodeup, N1, [{node_type, visible}]} -> ok end,
999    receive {nodeup, N2, [{node_type, visible}]} -> ok end,
1000    receive {nodeup, N3, [{node_type, hidden}]} -> ok end,
1001    receive {nodeup, N4, [{node_type, hidden}]} -> ok end,
1002
1003    stop_node(N1),
1004    stop_node(N2),
1005    stop_node(N3),
1006    stop_node(N4),
1007
1008    receive {nodedown, N1} -> ok end,
1009    receive {nodedown, N2} -> ok end,
1010
1011    receive {nodedown, N1, [{node_type, visible}]} -> ok end,
1012    receive {nodedown, N2, [{node_type, visible}]} -> ok end,
1013    receive {nodedown, N3, [{node_type, hidden}]} -> ok end,
1014    receive {nodedown, N4, [{node_type, hidden}]} -> ok end,
1015
1016    ok = net_kernel:monitor_nodes(false, [{node_type, all}]),
1017    {ok, N5} = start_node(NN5),
1018
1019    receive {nodeup, N5} -> ok end,
1020    stop_node(N5),
1021    receive {nodedown, N5} -> ok end,
1022
1023    ok = net_kernel:monitor_nodes(true, [{node_type, hidden}]),
1024    {ok, N6} = start_node(NN6),
1025    {ok, N7} = start_node(NN7, "-hidden"),
1026
1027
1028    receive {nodeup, N6} -> ok end,
1029    receive {nodeup, N7, [{node_type, hidden}]} -> ok end,
1030    stop_node(N6),
1031    stop_node(N7),
1032
1033    receive {nodedown, N6} -> ok end,
1034    receive {nodedown, N7, [{node_type, hidden}]} -> ok end,
1035
1036    ok = net_kernel:monitor_nodes(true, [{node_type, visible}]),
1037    ok = net_kernel:monitor_nodes(false, [{node_type, hidden}]),
1038    ok = net_kernel:monitor_nodes(false),
1039
1040    {ok, N8} = start_node(NN8),
1041    {ok, N9} = start_node(NN9, "-hidden"),
1042
1043    receive {nodeup, N8, [{node_type, visible}]} -> ok end,
1044    stop_node(N8),
1045    stop_node(N9),
1046
1047    receive {nodedown, N8, [{node_type, visible}]} -> ok end,
1048    print_my_messages(),
1049    ok = check_no_nodedown_nodeup(1000),
1050    ok = net_kernel:monitor_nodes(false, [{node_type, visible}]),
1051    MonNodeState = monitor_node_state(),
1052    ok.
1053
1054
1055%%
1056%% Testcase:
1057%%   monitor_nodes
1058%%
1059
1060monitor_nodes_misc(Config) when is_list(Config) ->
1061    MonNodeState = monitor_node_state(),
1062    ok = net_kernel:monitor_nodes(true),
1063    ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]),
1064    ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]),
1065    Names = get_numbered_nodenames(3, node),
1066    [NN1, NN2, NN3] = Names,
1067
1068    {ok, N1} = start_node(NN1),
1069    {ok, N2} = start_node(NN2, "-hidden"),
1070
1071    receive {nodeup, N1} -> ok end,
1072
1073    receive {nodeup, N1, [{node_type, visible}]} -> ok end,
1074    receive {nodeup, N1, [{node_type, visible}]} -> ok end,
1075    receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
1076    receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
1077
1078    stop_node(N1),
1079    stop_node(N2),
1080
1081    VisbleDownInfo = lists:sort([{node_type, visible},
1082				 {nodedown_reason, connection_closed}]),
1083    HiddenDownInfo = lists:sort([{node_type, hidden},
1084				 {nodedown_reason, connection_closed}]),
1085
1086    receive {nodedown, N1} -> ok end,
1087
1088    receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end,
1089    receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end,
1090    receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end,
1091    receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end,
1092
1093    ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]),
1094
1095    {ok, N3} = start_node(NN3),
1096    receive {nodeup, N3} -> ok end,
1097    stop_node(N3),
1098    receive {nodedown, N3} -> ok end,
1099    print_my_messages(),
1100    ok = check_no_nodedown_nodeup(1000),
1101    ok = net_kernel:monitor_nodes(false),
1102    MonNodeState = monitor_node_state(),
1103    ok.
1104
1105
1106%% Tests that {nodeup, Node} messages are received before
1107%% messages from Node and that {nodedown, Node} messages are
1108%% received after messages from Node.
1109monitor_nodes_otp_6481(Config) when is_list(Config) ->
1110    io:format("Testing nodedown...~n"),
1111    monitor_nodes_otp_6481_test(Config, nodedown),
1112    io:format("ok~n"),
1113    io:format("Testing nodeup...~n"),
1114    monitor_nodes_otp_6481_test(Config, nodeup),
1115    io:format("ok~n"),
1116    ok.
1117
1118monitor_nodes_otp_6481_test(Config, TestType) when is_list(Config) ->
1119    MonNodeState = monitor_node_state(),
1120    NodeMsg = make_ref(),
1121    Me = self(),
1122    [Name] = get_nodenames(1, monitor_nodes_otp_6481),
1123    case TestType of
1124	nodedown -> ok = net_kernel:monitor_nodes(true);
1125	nodeup -> ok
1126    end,
1127    Seq = lists:seq(1,10000),
1128    MN = spawn_link(
1129	   fun () ->
1130		   lists:foreach(
1131		     fun (_) ->
1132			     ok = net_kernel:monitor_nodes(true)
1133		     end,
1134		     Seq),
1135		   Me ! {mon_set, self()},
1136		   receive after infinity -> ok end
1137	   end),
1138    receive {mon_set, MN} -> ok end,
1139    case TestType of
1140	nodedown -> ok;
1141	nodeup -> ok = net_kernel:monitor_nodes(true)
1142    end,
1143
1144    %% Whitebox:
1145    %% nodedown test: Since this process was the first one monitoring
1146    %%                nodes this process will be the first one notified
1147    %%                on nodedown.
1148    %% nodeup test:   Since this process was the last one monitoring
1149    %%                nodes this process will be the last one notified
1150    %%                on nodeup
1151
1152    %% Verify the monitor_nodes order expected
1153    TestMonNodeState = monitor_node_state(),
1154    %% io:format("~p~n", [TestMonNodeState]),
1155    TestMonNodeState =
1156	case TestType of
1157	    nodedown -> [];
1158	    nodeup -> [{self(), []}]
1159	end
1160	++ lists:map(fun (_) -> {MN, []} end, Seq)
1161	++ case TestType of
1162	       nodedown -> [{self(), []}];
1163	       nodeup -> []
1164	   end
1165	++ MonNodeState,
1166
1167    {ok, Node} = start_node(Name, "", this),
1168    receive {nodeup, Node} -> ok end,
1169
1170    RemotePid = spawn(Node,
1171		      fun () ->
1172			      receive after 1500 -> ok end,
1173			      %% infinite loop of msgs
1174			      %% we want an endless stream of messages and the kill
1175			      %% the node mercilessly.
1176			      %% We then want to ensure that the nodedown message arrives
1177			      %% last ... without garbage after it.
1178			      _ = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end),
1179			      receive {Me, kill_it} -> ok end,
1180			      halt()
1181		      end),
1182
1183    net_kernel:disconnect(Node),
1184    receive {nodedown, Node} -> ok end,
1185
1186    %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message
1187    %% bringing up the connection).
1188    {nodeup, Node} = receive Msg1 -> Msg1 end,
1189    {NodeMsg, N}   = receive Msg2 -> Msg2 end,
1190    %% msg stream has begun, kill the node
1191    RemotePid ! {self(), kill_it},
1192
1193    %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}'
1194    %% message.
1195    {nodedown, Node} = flush_node_msgs(NodeMsg, N+1),
1196    no_msgs(500),
1197
1198    Mon = erlang:monitor(process, MN),
1199    unlink(MN),
1200    exit(MN, bang),
1201    receive {'DOWN', Mon, process, MN, bang} -> ok end,
1202    ok = net_kernel:monitor_nodes(false),
1203    MonNodeState = monitor_node_state(),
1204    ok.
1205
1206flush_node_msgs(NodeMsg, No) ->
1207    case receive Msg -> Msg end of
1208	{NodeMsg, N} when N >= No ->
1209	    flush_node_msgs(NodeMsg, N+1);
1210	OtherMsg ->
1211	    OtherMsg
1212    end.
1213
1214node_loop_send(Pid, Msg, No) ->
1215    Pid ! {Msg, No},
1216    node_loop_send(Pid, Msg, No + 1).
1217
1218monitor_nodes_errors(Config) when is_list(Config) ->
1219    MonNodeState = monitor_node_state(),
1220    error = net_kernel:monitor_nodes(asdf),
1221    {error,
1222     {unknown_options,
1223      [gurka]}} = net_kernel:monitor_nodes(true,
1224					   [gurka]),
1225    {error,
1226     {options_not_a_list,
1227      gurka}} = net_kernel:monitor_nodes(true,
1228					 gurka),
1229    {error,
1230     {option_value_mismatch,
1231      [{node_type,visible},
1232       {node_type,hidden}]}}
1233	= net_kernel:monitor_nodes(true,
1234				   [{node_type,hidden},
1235				    {node_type,visible}]),
1236    {error,
1237     {option_value_mismatch,
1238      [{node_type,visible},
1239       {node_type,all}]}}
1240	= net_kernel:monitor_nodes(true,
1241				   [{node_type,all},
1242				    {node_type,visible}]),
1243    {error,
1244     {bad_option_value,
1245      {node_type,
1246       blaha}}}
1247	= net_kernel:monitor_nodes(true, [{node_type, blaha}]),
1248    MonNodeState = monitor_node_state(),
1249    ok.
1250
1251monitor_nodes_combinations(Config) when is_list(Config) ->
1252    MonNodeState = monitor_node_state(),
1253    monitor_nodes_all_comb(true),
1254    [VisibleName, HiddenName] = get_nodenames(2,
1255					      monitor_nodes_combinations),
1256    {ok, Visible} = start_node(VisibleName, ""),
1257    receive_all_comb_nodeup_msgs(visible, Visible),
1258    no_msgs(),
1259    stop_node(Visible),
1260    receive_all_comb_nodedown_msgs(visible, Visible, connection_closed),
1261    no_msgs(),
1262    {ok, Hidden} = start_node(HiddenName, "-hidden"),
1263    receive_all_comb_nodeup_msgs(hidden, Hidden),
1264    no_msgs(),
1265    stop_node(Hidden),
1266    receive_all_comb_nodedown_msgs(hidden, Hidden, connection_closed),
1267    no_msgs(),
1268    monitor_nodes_all_comb(false),
1269    MonNodeState = monitor_node_state(),
1270    no_msgs(),
1271    ok.
1272
1273monitor_nodes_all_comb(Flag) ->
1274    ok = net_kernel:monitor_nodes(Flag),
1275    ok = net_kernel:monitor_nodes(Flag,
1276				  [nodedown_reason]),
1277    ok = net_kernel:monitor_nodes(Flag,
1278				  [{node_type, hidden}]),
1279    ok = net_kernel:monitor_nodes(Flag,
1280				  [{node_type, visible}]),
1281    ok = net_kernel:monitor_nodes(Flag,
1282				  [{node_type, all}]),
1283    ok = net_kernel:monitor_nodes(Flag,
1284				  [nodedown_reason,
1285				   {node_type, hidden}]),
1286    ok = net_kernel:monitor_nodes(Flag,
1287				  [nodedown_reason,
1288				   {node_type, visible}]),
1289    ok = net_kernel:monitor_nodes(Flag,
1290				  [nodedown_reason,
1291				   {node_type, all}]),
1292    %% There currently are 8 different combinations
1293    8.
1294
1295
1296receive_all_comb_nodeup_msgs(visible, Node) ->
1297    io:format("Receive nodeup visible...~n"),
1298    Exp = [{nodeup, Node},
1299	   {nodeup, Node, []}]
1300	++ mk_exp_mn_all_comb_nodeup_msgs_common(visible, Node),
1301    receive_mn_msgs(Exp),
1302    io:format("ok~n"),
1303    ok;
1304receive_all_comb_nodeup_msgs(hidden, Node) ->
1305    io:format("Receive nodeup hidden...~n"),
1306    Exp = mk_exp_mn_all_comb_nodeup_msgs_common(hidden, Node),
1307    receive_mn_msgs(Exp),
1308    io:format("ok~n"),
1309    ok.
1310
1311mk_exp_mn_all_comb_nodeup_msgs_common(Type, Node) ->
1312    InfoNt = [{node_type, Type}],
1313    [{nodeup, Node, InfoNt},
1314     {nodeup, Node, InfoNt},
1315     {nodeup, Node, InfoNt},
1316     {nodeup, Node, InfoNt}].
1317
1318receive_all_comb_nodedown_msgs(visible, Node, Reason) ->
1319    io:format("Receive nodedown visible...~n"),
1320    Exp = [{nodedown, Node},
1321	   {nodedown, Node, [{nodedown_reason, Reason}]}]
1322	++ mk_exp_mn_all_comb_nodedown_msgs_common(visible,
1323						   Node,
1324						   Reason),
1325    receive_mn_msgs(Exp),
1326    io:format("ok~n"),
1327    ok;
1328receive_all_comb_nodedown_msgs(hidden, Node, Reason) ->
1329    io:format("Receive nodedown hidden...~n"),
1330    Exp = mk_exp_mn_all_comb_nodedown_msgs_common(hidden, Node, Reason),
1331    receive_mn_msgs(Exp),
1332    io:format("ok~n"),
1333    ok.
1334
1335mk_exp_mn_all_comb_nodedown_msgs_common(Type, Node, Reason) ->
1336    InfoNt = [{node_type, Type}],
1337    InfoNdrNt = lists:sort([{nodedown_reason, Reason}]++InfoNt),
1338    [{nodedown, Node, InfoNt},
1339     {nodedown, Node, InfoNt},
1340     {nodedown, Node, InfoNdrNt},
1341     {nodedown, Node, InfoNdrNt}].
1342
1343receive_mn_msgs([]) ->
1344    ok;
1345receive_mn_msgs(Msgs) ->
1346    io:format("Expecting msgs: ~p~n", [Msgs]),
1347    receive
1348	{_Dir, _Node} = Msg ->
1349	    io:format("received ~p~n", [Msg]),
1350	    case lists:member(Msg, Msgs) of
1351		true -> receive_mn_msgs(lists:delete(Msg, Msgs));
1352		false -> ct:fail({unexpected_message, Msg,
1353				  expected_messages, Msgs})
1354	    end;
1355	{Dir, Node, Info} ->
1356	    Msg = {Dir, Node, lists:sort(Info)},
1357	    io:format("received ~p~n", [Msg]),
1358	    case lists:member(Msg, Msgs) of
1359		true -> receive_mn_msgs(lists:delete(Msg, Msgs));
1360		false -> ct:fail({unexpected_message, Msg,
1361				  expected_messages, Msgs})
1362	    end;
1363	Msg ->
1364	    io:format("received ~p~n", [Msg]),
1365	    ct:fail({unexpected_message, Msg,
1366		     expected_messages, Msgs})
1367    end.
1368
1369monitor_nodes_cleanup(Config) when is_list(Config) ->
1370    MonNodeState = monitor_node_state(),
1371    Me = self(),
1372    No = monitor_nodes_all_comb(true),
1373    Inf = spawn(fun () ->
1374			monitor_nodes_all_comb(true),
1375			Me ! {mons_set, self()},
1376			receive after infinity -> ok end
1377		end),
1378    TO = spawn(fun () ->
1379		       monitor_nodes_all_comb(true),
1380		       Me ! {mons_set, self()},
1381		       receive after 500 -> ok end
1382	       end),
1383    receive {mons_set, Inf} -> ok end,
1384    receive {mons_set, TO} -> ok end,
1385    MNLen = length(MonNodeState) + No*3,
1386    MNLen = length(monitor_node_state()),
1387    MonInf = erlang:monitor(process, Inf),
1388    MonTO = erlang:monitor(process, TO),
1389    exit(Inf, bang),
1390    No = monitor_nodes_all_comb(false),
1391    receive {'DOWN', MonInf, process, Inf, bang} -> ok end,
1392    receive {'DOWN', MonTO, process, TO, normal} -> ok end,
1393    MonNodeState = monitor_node_state(),
1394    no_msgs(),
1395    ok.
1396
1397monitor_nodes_many(Config) when is_list(Config) ->
1398    MonNodeState = monitor_node_state(),
1399    [Name] = get_nodenames(1, monitor_nodes_many),
1400    %% We want to perform more than 2^16 net_kernel:monitor_nodes
1401    %% since this will wrap an internal counter
1402    No = (1 bsl 16) + 17,
1403    repeat(fun () -> ok = net_kernel:monitor_nodes(true) end, No),
1404    No = length(monitor_node_state()) - length(MonNodeState),
1405    {ok, Node} = start_node(Name),
1406    repeat(fun () -> receive {nodeup, Node} -> ok end end, No),
1407    stop_node(Node),
1408    repeat(fun () -> receive {nodedown, Node} -> ok end end, No),
1409    ok = net_kernel:monitor_nodes(false),
1410    no_msgs(10),
1411    MonNodeState = monitor_node_state(),
1412    ok.
1413
1414%% Misc. functions
1415
1416monitor_node_state() ->
1417    erts_debug:set_internal_state(available_internal_state, true),
1418    MonitoringNodes = erts_debug:get_internal_state(monitoring_nodes),
1419    erts_debug:set_internal_state(available_internal_state, false),
1420    MonitoringNodes.
1421
1422
1423check_no_nodedown_nodeup(TimeOut) ->
1424    receive
1425	{nodeup, _, _} = Msg -> ct:fail({unexpected_nodeup, Msg});
1426	{nodeup, _} = Msg -> ct:fail({unexpected_nodeup, Msg});
1427	{nodedown, _, _} = Msg -> ct:fail({unexpected_nodedown, Msg});
1428	{nodedown, _} = Msg -> ct:fail({unexpected_nodedown, Msg})
1429    after TimeOut ->
1430	    ok
1431    end.
1432
1433print_my_messages() ->
1434    {messages, Messages} = process_info(self(), messages),
1435    io:format("Messages: ~p~n", [Messages]),
1436    ok.
1437
1438
1439sleep(T) -> receive after T * 1000 -> ok end.
1440
1441start_node(Name, Param, this) ->
1442    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
1443    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]);
1444start_node(Name, Param, "this") ->
1445    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
1446    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]);
1447start_node(Name, Param, Rel) when is_atom(Rel) ->
1448    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
1449    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, atom_to_list(Rel)}]}]);
1450start_node(Name, Param, Rel) when is_list(Rel) ->
1451    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
1452    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, Rel}]}]).
1453
1454start_node(Name, Param) ->
1455    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
1456    test_server:start_node(Name, slave, [{args, NewParam}]).
1457
1458start_node(Name) ->
1459    start_node(Name, "").
1460
1461stop_node(Node) ->
1462    test_server:stop_node(Node).
1463
1464get_nodenames(N, T) ->
1465    get_nodenames(N, T, []).
1466
1467get_nodenames(0, _, Acc) ->
1468    Acc;
1469get_nodenames(N, T, Acc) ->
1470    U = erlang:unique_integer([positive]),
1471    get_nodenames(N-1, T, [list_to_atom(atom_to_list(T)
1472					++ "-"
1473					++ ?MODULE_STRING
1474					++ "-"
1475					++ integer_to_list(U)) | Acc]).
1476
1477get_numbered_nodenames(N, T) ->
1478    get_numbered_nodenames(N, T, []).
1479
1480get_numbered_nodenames(0, _, Acc) ->
1481    Acc;
1482get_numbered_nodenames(N, T, Acc) ->
1483    U = erlang:unique_integer([positive]),
1484    NL = [list_to_atom(atom_to_list(T) ++ integer_to_list(N)
1485		       ++ "-"
1486		       ++ ?MODULE_STRING
1487		       ++ "-"
1488		       ++ integer_to_list(U)) | Acc],
1489    get_numbered_nodenames(N-1, T, NL).
1490
1491wait_until(Fun) ->
1492    case Fun() of
1493	true ->
1494	    ok;
1495	_ ->
1496	    receive
1497	    after 100 ->
1498		    wait_until(Fun)
1499	    end
1500    end.
1501
1502repeat(Fun, 0) when is_function(Fun) ->
1503    ok;
1504repeat(Fun, N) when is_function(Fun), is_integer(N), N > 0 ->
1505    Fun(),
1506    repeat(Fun, N-1).
1507
1508no_msgs(Wait) ->
1509    receive after Wait -> no_msgs() end.
1510
1511no_msgs() ->
1512    {messages, []} = process_info(self(), messages).
1513
1514block_emu(Ms) ->
1515    erts_debug:set_internal_state(available_internal_state, true),
1516    Res = erts_debug:set_internal_state(block, Ms),
1517    erts_debug:set_internal_state(available_internal_state, false),
1518    Res.
1519