1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1997-2021. 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-include_lib("kernel/include/dist.hrl").
24
25-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
26	 init_per_group/2,end_per_group/2]).
27
28-export([tick/1, tick_change/1,
29         connect_node/1,
30         nodenames/1, hostnames/1,
31         illegal_nodenames/1, hidden_node/1,
32         dyn_node_name/1,
33         epmd_reconnect/1,
34	 setopts/1,
35	 table_waste/1, net_setuptime/1,
36	 inet_dist_options_options/1,
37
38	 monitor_nodes_nodedown_reason/1,
39	 monitor_nodes_complex_nodedown_reason/1,
40	 monitor_nodes_node_type/1,
41	 monitor_nodes_misc/1,
42	 monitor_nodes_otp_6481/1,
43	 monitor_nodes_errors/1,
44	 monitor_nodes_combinations/1,
45	 monitor_nodes_cleanup/1,
46	 monitor_nodes_many/1,
47         monitor_nodes_down_up/1,
48         dist_ctrl_proc_smoke/1,
49         dist_ctrl_proc_reject/1,
50         erl_uds_dist_smoke_test/1,
51         erl_1424/1, differing_cookies/1,
52         cmdline_setcookie_2/1, connection_cookie/1,
53         dyn_differing_cookies/1]).
54
55%% Performs the test at another node.
56-export([get_socket_priorities/0,
57	 tick_cli_test/1, tick_cli_test1/1,
58	 tick_serv_test/2, tick_serv_test1/1,
59	 run_remote_test/1,
60         dyn_node_name_do/2,
61         epmd_reconnect_do/2,
62	 setopts_do/2,
63	 keep_conn/1, time_ping/1,
64         dyn_differing_cookies/2]).
65
66-export([init_per_testcase/2, end_per_testcase/2]).
67
68-export([dist_cntrlr_output_test_size/2]).
69
70-export([pinger/1]).
71
72-export([start_uds_rpc_server/1]).
73
74-define(DUMMY_NODE,dummy@test01).
75-define(ALT_EPMD_PORT, "12321").
76-define(ALT_EPMD_CMD, "epmd -port "++?ALT_EPMD_PORT).
77
78%%-----------------------------------------------------------------
79%% The distribution is mainly tested in the big old test_suite.
80%% This test only tests the net_ticktime configuration flag.
81%% Should be started in a CC view with:
82%% erl -sname master -rsh ctrsh
83%%-----------------------------------------------------------------
84
85suite() ->
86    [{ct_hooks,[ts_install_cth]},
87     {timetrap,{minutes,12}}].
88
89all() ->
90    [dist_ctrl_proc_smoke,
91     dist_ctrl_proc_reject,
92     tick, tick_change, nodenames, hostnames, illegal_nodenames,
93     connect_node,
94     dyn_node_name,
95     epmd_reconnect,
96     hidden_node, setopts,
97     table_waste, net_setuptime, inet_dist_options_options,
98     {group, monitor_nodes},
99     erl_uds_dist_smoke_test,
100     erl_1424,
101     {group, differing_cookies}].
102
103groups() ->
104    [{monitor_nodes, [],
105      [monitor_nodes_nodedown_reason,
106       monitor_nodes_complex_nodedown_reason,
107       monitor_nodes_node_type, monitor_nodes_misc,
108       monitor_nodes_otp_6481, monitor_nodes_errors,
109       monitor_nodes_combinations, monitor_nodes_cleanup,
110       monitor_nodes_many,
111       monitor_nodes_down_up]},
112     {differing_cookies, [],
113      [differing_cookies,
114       cmdline_setcookie_2,
115       connection_cookie,
116       dyn_differing_cookies]}].
117
118init_per_suite(Config) ->
119    start_gen_tcp_dist_test_type_server(),
120    Config.
121
122end_per_suite(_Config) ->
123    [slave:stop(N) || N <- nodes()],
124    kill_gen_tcp_dist_test_type_server(),
125    ok.
126
127init_per_group(_GroupName, Config) ->
128    Config.
129
130end_per_group(_GroupName, Config) ->
131    Config.
132
133init_per_testcase(TC, Config) when TC == hostnames;
134                                   TC == nodenames ->
135    file:make_dir("hostnames_nodedir"),
136    file:write_file("hostnames_nodedir/ignore_core_files",""),
137    Config;
138init_per_testcase(epmd_reconnect, Config) ->
139    [] = os:cmd(?ALT_EPMD_CMD++" -relaxed_command_check -daemon"),
140    Config;
141init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) ->
142    Config.
143
144end_per_testcase(epmd_reconnect, _Config) ->
145    os:cmd(?ALT_EPMD_CMD++" -kill"),
146    ok;
147end_per_testcase(_Func, _Config) ->
148    ok.
149
150connect_node(Config) when is_list(Config) ->
151    Connected = nodes(connected),
152    true = net_kernel:connect_node(node()),
153    Connected = nodes(connected),
154    ok.
155
156tick(Config) when is_list(Config) ->
157    run_dist_configs(fun tick/2, Config).
158
159tick(DCfg, _Config) ->
160    %% First check that the normal case is OK!
161    [Name1, Name2] = get_nodenames(2, dist_test),
162    {ok, Node} = start_node(DCfg, Name1),
163    rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [node()]),
164
165    erlang:monitor_node(Node, true),
166    receive
167	{nodedown, Node} ->
168	    ct:fail("nodedown from other node")
169    after 30000 ->
170	    erlang:monitor_node(Node, false),
171	    stop_node(Node)
172    end,
173
174    %% Now, set the net_ticktime for the other node to 12 secs.
175    %% After the sleep(2sec) and cast the other node shall destroy
176    %% the connection as it has not received anything on the connection.
177    %% The nodedown message should arrive within 8 < T < 16 secs.
178
179    %% We must have two slave nodes as the slave mechanism otherwise
180    %% halts the client node after tick timeout (the connection is down
181    %% and the slave node decides to halt !!
182
183    %% Set the ticktime on the server node to 100 secs so the server
184    %% node doesn't tick the client node within the interval ...
185
186    {ok, ServNode} = start_node(DCfg, Name2,
187				"-kernel net_ticktime 100"),
188    rpc:call(ServNode, erl_distribution_SUITE, tick_serv_test, [Node, node()]),
189
190    {ok, Node} = start_node(DCfg, Name1,
191			 "-kernel net_ticktime 12"),
192    rpc:call(Node, erl_distribution_SUITE, tick_cli_test, [ServNode]),
193
194    spawn_link(erl_distribution_SUITE, keep_conn, [Node]),
195
196    {tick_serv, ServNode} ! {i_want_the_result, self()},
197
198    monitor_node(ServNode, true),
199    monitor_node(Node, true),
200
201    receive
202	{tick_test, T} when is_integer(T) ->
203	    stop_node(ServNode),
204	    stop_node(Node),
205	    T;
206	{tick_test, Error} ->
207	    stop_node(ServNode),
208	    stop_node(Node),
209	    ct:fail(Error);
210	{nodedown, Node} ->
211	    stop_node(ServNode),
212	    ct:fail("client node died");
213	{nodedown, ServNode} ->
214	    stop_node(Node),
215	    ct:fail("server node died")
216    end,
217    ok.
218
219%% Checks that pinging nonexistyent nodes does not waste space in distribution table.
220table_waste(Config) when is_list(Config) ->
221    run_dist_configs(fun table_waste/2, Config).
222
223table_waste(DCfg, _Config) ->
224    {ok, HName} = inet:gethostname(),
225    F = fun(0,_F) -> [];
226	   (N,F) ->
227		Name = list_to_atom("erl_distribution_"++integer_to_list(N)++
228					"@"++HName),
229		pang = net_adm:ping(Name),
230		F(N-1,F)
231	end,
232    F(256,F),
233    {ok, N} = start_node(DCfg, erl_distribution_300),
234    stop_node(N),
235    ok.
236
237%% Test that starting nodes with different legal name part works, and that illegal
238%% ones are filtered
239nodenames(Config) when is_list(Config) ->
240    legal("a1@b"),
241    legal("a-1@b"),
242    legal("a_1@b"),
243
244    %% Test that giving two -sname works as it should
245    started = test_node("a_1@b", false, long_or_short() ++ "a_0@b"),
246
247    illegal("cdé@a"),
248    illegal("te欢st@a").
249
250%% Test that starting nodes with different legal host part works, and that illegal
251%% ones are filtered
252hostnames(Config) when is_list(Config) ->
253    Host = gethostname(),
254    legal([$a,$@|atom_to_list(Host)]),
255    legal("1@b1"),
256    legal("b@b1-c"),
257    legal("c@b1_c"),
258    legal("d@b1#c"),
259    legal("f@::1"),
260    legal("g@1:bc3:4e3f:f20:0:1"),
261
262    case file:native_name_encoding() of
263        latin1 -> ignore;
264        _ -> legal("e@b1é")
265    end,
266    long_hostnames(net_kernel:longnames()),
267
268    illegal("h@testالع"),
269    illegal("i@языtest"),
270    illegal("j@te欢st").
271
272long_hostnames(true) ->
273    legal("k@b.b.c"),
274    legal("l@b.b-c.d"),
275    legal("m@b.b_c.d"),
276    legal("n@127.0.0.1"),
277    legal("o@207.123.456.789");
278long_hostnames(false) ->
279    illegal("k@b.b.c").
280
281legal(Name) ->
282    case test_node(Name) of
283        started ->
284            ok;
285        not_started ->
286            ct:fail("no ~p node started", [Name])
287    end.
288
289illegal(Name) ->
290    case test_node(Name, true) of
291        not_started ->
292            ok;
293        started ->
294            ct:fail("~p node started with illegal name", [Name])
295    end.
296
297test_node(Name) ->
298    test_node(Name, false).
299test_node(Name, Illigal) ->
300    test_node(Name, Illigal, "").
301test_node(Name, Illigal, ExtraArgs) ->
302    ProgName = ct:get_progname(),
303    Command = ProgName ++ " -noinput " ++
304        long_or_short() ++ Name ++ ExtraArgs ++
305        " -eval \"net_adm:ping('" ++ atom_to_list(node()) ++ "')\"" ++
306        case Illigal of
307            true ->
308                " -eval \"timer:sleep(10000),init:stop().\"";
309            false ->
310                ""
311        end,
312    net_kernel:monitor_nodes(true),
313    BinCommand = unicode:characters_to_binary(Command, utf8),
314    _Prt = open_port({spawn, BinCommand}, [stream,{cd,"hostnames_nodedir"}]),
315    Node = list_to_atom(Name),
316    receive
317        {nodeup, Node} ->
318            net_kernel:monitor_nodes(false),
319            slave:stop(Node),
320            started
321    after 5000 ->
322        net_kernel:monitor_nodes(false),
323        not_started
324    end.
325
326long_or_short() ->
327    case net_kernel:longnames() of
328        true -> " -name ";
329        false -> " -sname "
330    end.
331
332% get the localhost's name, depending on the using name policy
333gethostname() ->
334    Hostname = case net_kernel:longnames() of
335       true->
336           net_adm:localhost();
337       _->
338           {ok, Name}=inet:gethostname(),
339           Name
340    end,
341    list_to_atom(Hostname).
342
343%% Test that pinging an illegal nodename does not kill the node.
344illegal_nodenames(Config) when is_list(Config) ->
345    run_dist_configs(fun illegal_nodenames/2, Config).
346
347illegal_nodenames(DCfg, _Config) ->
348    {ok, Node}=start_node(DCfg, illegal_nodenames),
349    monitor_node(Node, true),
350    RPid=rpc:call(Node, erlang, spawn,
351		  [?MODULE, pinger, [self()]]),
352    receive
353	{RPid, pinged} ->
354            monitor_node(Node, false),
355	    ok;
356	{nodedown, Node} ->
357	    ct:fail("Remote node died.")
358    end,
359    stop_node(Node),
360    ok.
361
362pinger(Starter) ->
363    io:format("Starter:~p~n",[Starter]),
364    net_adm:ping(a@b@c),
365    Starter ! {self(), pinged},
366    ok.
367
368
369%% Test that you can set the net_setuptime properly.
370net_setuptime(Config) when is_list(Config) ->
371    run_dist_configs(fun net_setuptime/2, Config).
372
373net_setuptime(DCfg, _Config) ->
374
375    %% In this test case, we reluctantly accept shorter times than the given
376    %% setup time, because the connection attempt can end in a
377    %% "Host unreachable" error before the timeout fires.
378
379    Res0 = do_test_setuptime(DCfg, "2"),
380    io:format("Res0 = ~p", [Res0]),
381    true = (Res0 =< 4000),
382    Res1 = do_test_setuptime(DCfg, "0.3"),
383    io:format("Res1 = ~p", [Res1]),
384    true = (Res1 =< 500),
385    ok.
386
387do_test_setuptime(DCfg, Setuptime) when is_list(Setuptime) ->
388    {ok, Node} = start_node(DCfg, dist_setuptime_test,
389                            "-kernel net_setuptime " ++ Setuptime),
390    Res = rpc:call(Node,?MODULE,time_ping,[?DUMMY_NODE]),
391    stop_node(Node),
392    Res.
393
394time_ping(Node) ->
395    T0 = erlang:monotonic_time(),
396    pang = net_adm:ping(Node),
397    T1 = erlang:monotonic_time(),
398    erlang:convert_time_unit(T1 - T0, native, millisecond).
399
400%% Keep the connection with the client node up.
401%% This is necessary as the client node runs with much shorter
402%% tick time !!
403keep_conn(Node) ->
404    sleep(1),
405    rpc:cast(Node, erlang, time, []),
406    keep_conn(Node).
407
408tick_serv_test(Node, MasterNode) ->
409    spawn(erl_distribution_SUITE, keep_conn, [MasterNode]),
410    spawn(erl_distribution_SUITE, tick_serv_test1, [Node]).
411
412tick_serv_test1(Node) ->
413    register(tick_serv, self()),
414    TestServer = receive {i_want_the_result, TS} -> TS end,
415    monitor_node(Node, true),
416    receive
417	{nodedown, Node} ->
418	    net_adm:ping(Node), %% Set up the connection again !!
419
420	    {tick_test, Node} ! {whats_the_result, self()},
421	    receive
422		{tick_test, Res} ->
423		    TestServer ! {tick_test, Res}
424	    end
425    end.
426
427tick_cli_test(Node) ->
428    spawn(erl_distribution_SUITE, tick_cli_test1, [Node]).
429
430tick_cli_test1(Node) ->
431    register(tick_test, self()),
432    erlang:monitor_node(Node, true),
433    sleep(2),
434    rpc:call(Node, erlang, time, []), %% simulate action on the connection
435    T1 = erlang:monotonic_time(),
436    receive
437	{nodedown, Node} ->
438	    T2 = erlang:monotonic_time(),
439	    receive
440		{whats_the_result, From} ->
441		    Diff = erlang:convert_time_unit(T2-T1, native,
442						    millisecond),
443		    case Diff of
444			T when T > 8000, T < 16000 ->
445			    From ! {tick_test, T};
446			T ->
447			    From ! {tick_test,
448				    {"T not in interval 8000 < T < 16000",
449				     T}}
450		    end
451	    end
452    end.
453
454epmd_reconnect(Config) when is_list(Config) ->
455    NodeNames = [N1,N2,N3] = get_nodenames(3, ?FUNCTION_NAME),
456    Nodes = [atom_to_list(full_node_name(NN)) || NN <- NodeNames],
457
458    DCfg = "-epmd_port "++?ALT_EPMD_PORT,
459
460    {_N1F,Port1} = start_node_unconnected(DCfg, N1, ?MODULE, run_remote_test,
461					["epmd_reconnect_do", atom_to_list(node()), "1" | Nodes]),
462    {_N2F,Port2} = start_node_unconnected(DCfg, N2, ?MODULE, run_remote_test,
463					["epmd_reconnect_do", atom_to_list(node()), "2" | Nodes]),
464    {_N3F,Port3} = start_node_unconnected(DCfg, N3, ?MODULE, run_remote_test,
465					["epmd_reconnect_do", atom_to_list(node()), "3" | Nodes]),
466    Ports = [Port1, Port2, Port3],
467
468    ok = reap_ports(Ports),
469
470    ok.
471
472reap_ports([]) ->
473    ok;
474reap_ports(Ports) ->
475    case (receive M -> M end) of
476	{Port, Message} ->
477            case lists:member(Port, Ports) andalso Message of
478                {data,String} ->
479                    io:format("~p: ~s\n", [Port, String]),
480                    reap_ports(Ports);
481                {exit_status,0} ->
482                    reap_ports(Ports -- [Port])
483            end
484    end.
485
486epmd_reconnect_do(_Node, ["1", Node1, Node2, Node3]) ->
487    Names = [Name || Name <- [hd(string:tokens(Node, "@")) || Node <- [Node1, Node2, Node3]]],
488    %% wait until all nodes are registered
489    ok = wait_for_names(Names),
490    "Killed" ++_ = os:cmd(?ALT_EPMD_CMD++" -kill"),
491    open_port({spawn, ?ALT_EPMD_CMD}, []),
492    %% check that all nodes reregister with epmd
493    ok = wait_for_names(Names),
494    lists:foreach(fun(Node) ->
495                          ANode = list_to_atom(Node),
496                          pong = net_adm:ping(ANode),
497                          {epmd_reconnect_do, ANode} ! {stop, Node1, Node}
498                  end, [Node2, Node3]),
499    ok;
500epmd_reconnect_do(_Node, ["2", Node1, Node2, _Node3]) ->
501    register(epmd_reconnect_do, self()),
502    receive {stop, Node1, Node2} ->
503            ok
504    after 7000 ->
505            exit(timeout)
506    end;
507epmd_reconnect_do(_Node, ["3", Node1, _Node2, Node3]) ->
508    register(epmd_reconnect_do, self()),
509    receive {stop, Node1, Node3} ->
510            ok
511    after 7000 ->
512            exit(timeout)
513    end.
514
515wait_for_names(Names) ->
516    %% wait for up to 3 seconds (the current retry timer in erl_epmd is 2s)
517    wait_for_names(lists:sort(Names), 30, 100).
518
519wait_for_names(Names, N, Wait) when N > 0 ->
520    try
521        {ok, Info} = erl_epmd:names(),
522        Names = lists:sort([Name || {Name, _Port} <- Info]),
523        ok
524    catch
525        error:{badmatch, _} ->
526            timer:sleep(Wait),
527            wait_for_names(Names, N-1, Wait)
528    end.
529
530
531dyn_node_name(Config) when is_list(Config) ->
532    %%run_dist_configs(fun dyn_node_name/2, Config).
533    dyn_node_name("", Config).
534
535dyn_node_name(DCfg, _Config) ->
536    {_N1F,Port1} = start_node_unconnected(DCfg ++ " -dist_listen false",
537                                          undefined, ?MODULE, run_remote_test,
538                                          ["dyn_node_name_do", atom_to_list(node())]),
539    0 = wait_for_port_exit(Port1),
540    ok.
541
542dyn_node_name_do(TestNode, _Args) ->
543    nonode@nohost = node(),
544    [] = nodes(),
545    [] = nodes(hidden),
546    net_kernel:monitor_nodes(true, [{node_type,all}]),
547    net_kernel:connect_node(TestNode),
548    [] = nodes(),
549    [TestNode] = nodes(hidden),
550    MyName = node(),
551    check([MyName], rpc:call(TestNode, erlang, nodes, [hidden])),
552
553    {nodeup, MyName, [{node_type, visible}]} = receive_any(0),
554    {nodeup, TestNode, [{node_type, hidden}]} = receive_any(0),
555
556    true = net_kernel:disconnect(TestNode),
557
558    {nodedown, TestNode, [{node_type, hidden}]} = receive_any(0),
559    [] = nodes(hidden),
560    {nodedown, MyName, [{node_type, visible}]} = receive_any(1000),
561
562    nonode@nohost = node(),
563
564    net_kernel:connect_node(TestNode),
565    [] = nodes(),
566    [TestNode] = nodes(hidden),
567    MyName = node(),
568    check([MyName], rpc:call(TestNode, erlang, nodes, [hidden])),
569
570    {nodeup, MyName, [{node_type, visible}]} = receive_any(0),
571    {nodeup, TestNode, [{node_type, hidden}]} = receive_any(0),
572
573    true = rpc:cast(TestNode, net_kernel, disconnect, [MyName]),
574
575    {nodedown, TestNode, [{node_type, hidden}]} = receive_any(1000),
576    [] = nodes(hidden),
577    {nodedown, MyName, [{node_type, visible}]} = receive_any(1000),
578    nonode@nohost = node(),
579
580    ok.
581
582check(X, X) -> ok.
583
584setopts(Config) when is_list(Config) ->
585    run_dist_configs(fun setopts/2, Config).
586
587setopts(DCfg, _Config) ->
588    register(setopts_regname, self()),
589    [N1,N2,N3,N4] = get_nodenames(4, setopts),
590
591    {_N1F,Port1} = start_node_unconnected(DCfg, N1, ?MODULE, run_remote_test,
592					["setopts_do", atom_to_list(node()), "1", "ping"]),
593    0 = wait_for_port_exit(Port1),
594
595    {_N2F,Port2} = start_node_unconnected(DCfg, N2, ?MODULE, run_remote_test,
596				 ["setopts_do", atom_to_list(node()), "2", "ping"]),
597    0 = wait_for_port_exit(Port2),
598
599    {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]),
600    {ok, LTcpPort} = inet:port(LSock),
601
602    {N3F,Port3} = start_node_unconnected(DCfg, N3, ?MODULE, run_remote_test,
603					["setopts_do", atom_to_list(node()),
604					 "1", integer_to_list(LTcpPort)]),
605    wait_and_connect(LSock, N3F, Port3),
606    0 = wait_for_port_exit(Port3),
607
608    {N4F,Port4} = start_node_unconnected(DCfg, N4, ?MODULE, run_remote_test,
609					["setopts_do", atom_to_list(node()),
610					 "2", integer_to_list(LTcpPort)]),
611    wait_and_connect(LSock, N4F, Port4),
612    0 = wait_for_port_exit(Port4),
613
614    unregister(setopts_regname),
615    ok.
616
617wait_and_connect(LSock, NodeName, NodePort) ->
618    {ok, Sock} = gen_tcp:accept(LSock),
619    {ok, "Connect please"} = gen_tcp:recv(Sock, 0),
620    flush_from_port(NodePort),
621    pong = net_adm:ping(NodeName),
622    gen_tcp:send(Sock, "Connect done"),
623    gen_tcp:close(Sock).
624
625
626flush_from_port(Port) ->
627    flush_from_port(Port, 10).
628
629flush_from_port(Port, Timeout) ->
630    receive
631	{Port,{data,String}} ->
632	    io:format("~p: ~s\n", [Port, String]),
633	    flush_from_port(Port, Timeout)
634    after Timeout ->
635	    timeout
636    end.
637
638wait_for_port_exit(Port) ->
639    case (receive M -> M end) of
640	{Port,{exit_status,Status}} ->
641	    Status;
642	{Port,{data,String}} ->
643	    io:format("~p: ~s\n", [Port, String]),
644	    wait_for_port_exit(Port)
645    end.
646
647run_remote_test([FuncStr, TestNodeStr | Args]) ->
648    Status = try
649	io:format("Node ~p started~n", [node()]),
650	TestNode = list_to_atom(TestNodeStr),
651	io:format("Node ~p spawning function ~p~n", [node(), FuncStr]),
652	{Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]),
653	io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]),
654	receive
655	    {'DOWN', Ref, process, Pid, normal} ->
656		0;
657	    Other ->
658		io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]),
659		1
660	end
661    catch
662	C:E:S ->
663	    io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n",
664		      [node(), C, E, S]),
665	    2
666    end,
667    io:format("Node ~p doing halt(~p).\n",[node(), Status]),
668    erlang:halt(Status).
669
670% Do the actual test on the remote node
671setopts_do(TestNode, [OptNr, ConnectData]) ->
672    [] = nodes(),
673    {Opt, Val} = opt_from_nr(OptNr),
674    ok = net_kernel:setopts(new, [{Opt, Val}]),
675
676    [] = nodes(),
677    {error, noconnection} = net_kernel:getopts(TestNode, [Opt]),
678
679    case ConnectData of
680	"ping" ->  % We connect
681	    net_adm:ping(TestNode);
682	TcpPort -> % Other connect
683	    {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort),
684					 [{active,false},{packet,2}]),
685	    ok = gen_tcp:send(Sock, "Connect please"),
686	    {ok, "Connect done"} = gen_tcp:recv(Sock, 0),
687	    gen_tcp:close(Sock)
688    end,
689    [TestNode] = nodes(),
690    {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
691    {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]),
692
693    NewVal = change_val(Val),
694    ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]),
695    {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]),
696
697    ok = net_kernel:setopts(TestNode, [{Opt, Val}]),
698    {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]),
699
700    ok.
701
702opt_from_nr("1") -> {nodelay, true};
703opt_from_nr("2") -> {nodelay, false}.
704
705change_val(true)  -> false;
706change_val(false) -> true.
707
708
709start_node_unconnected(DCfg, Name, Mod, Func, Args) ->
710    start_node_unconnected(DCfg, Name, erlang:get_cookie(), Mod, Func, Args).
711
712start_node_unconnected(DCfg, Name, Cookie, Mod, Func, Args) ->
713    FullName = full_node_name(Name),
714    CmdLine =
715        mk_node_cmdline(DCfg, Name, Cookie, Mod, Func, Args),
716    io:format("Starting node ~p: ~s~n", [FullName, CmdLine]),
717    case open_port({spawn, CmdLine}, [exit_status]) of
718	Port when is_port(Port) ->
719	    {FullName, Port};
720	Error ->
721	    exit({failed_to_start_node, FullName, Error})
722    end.
723
724full_node_name(PreName) when is_atom(PreName) ->
725    full_node_name(atom_to_list(PreName));
726full_node_name(PreNameL) when is_list(PreNameL) ->
727    HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end,
728				 atom_to_list(node())),
729    list_to_atom(PreNameL ++ HostSuffix).
730
731mk_node_cmdline(DCfg, Name, Cookie, Mod, Func, Args) ->
732    Static = "-noinput",
733    Pa = filename:dirname(code:which(?MODULE)),
734    Prog = case catch init:get_argument(progname) of
735	       {ok,[[P]]} -> P;
736	       _ -> exit(no_progname_argument_found)
737	   end,
738    NameSw = case net_kernel:longnames() of
739		 false -> "-sname ";
740		 true -> "-name ";
741		 _ -> exit(not_distributed_node)
742	     end,
743    {ok, Pwd} = file:get_cwd(),
744    NameStr = atom_to_list(Name),
745    Prog ++ " "
746	++ Static ++ " "
747	++ NameSw ++ " " ++ NameStr
748        ++ " " ++ DCfg
749	++ " -pa " ++ Pa
750	++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr
751	++ " -setcookie " ++ atom_to_list(Cookie)
752	++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func)
753	++ " " ++ string:join(Args, " ").
754
755
756%% OTP-4255.
757tick_change(Config) when is_list(Config) ->
758    run_dist_configs(fun tick_change/2, Config).
759
760tick_change(DCfg, _Config) ->
761    [BN, CN] = get_nodenames(2, tick_change),
762    DefaultTT = net_kernel:get_net_ticktime(),
763    unchanged = net_kernel:set_net_ticktime(DefaultTT, 60),
764    case DefaultTT of
765	I when is_integer(I) -> ok;
766	_                 -> ct:fail(DefaultTT)
767    end,
768
769    %% In case other nodes are connected
770    case nodes(connected) of
771	[] -> net_kernel:set_net_ticktime(10, 0);
772	_ ->  rpc:multicall(nodes([this, connected]), net_kernel,
773			    set_net_ticktime, [10, 5])
774    end,
775
776    wait_until(fun () -> 10 == net_kernel:get_net_ticktime() end),
777    {ok, B} = start_node(DCfg, BN, "-kernel net_ticktime 10"),
778    {ok, C} = start_node(DCfg, CN, "-kernel net_ticktime 10 -hidden"),
779
780    OTE = process_flag(trap_exit, true),
781    case catch begin
782		   run_tick_change_test(DCfg, B, C, 10, 1),
783		   run_tick_change_test(DCfg, B, C, 1, 10)
784	       end of
785	{'EXIT', Reason} ->
786	    stop_node(B),
787	    stop_node(C),
788	    %% In case other nodes are connected
789	    case nodes(connected) of
790		[] -> net_kernel:set_net_ticktime(DefaultTT, 0);
791		_ ->  rpc:multicall(nodes([this, connected]), net_kernel,
792				    set_net_ticktime, [DefaultTT, 10])
793	    end,
794	    wait_until(fun () ->
795			       DefaultTT == net_kernel:get_net_ticktime()
796		       end),
797	    process_flag(trap_exit, OTE),
798	    ct:fail(Reason);
799	_ ->
800	    ok
801    end,
802    process_flag(trap_exit, OTE),
803    stop_node(B),
804    stop_node(C),
805
806    %% In case other nodes are connected
807    case nodes(connected) of
808	[] -> net_kernel:set_net_ticktime(DefaultTT, 0);
809	_ ->  rpc:multicall(nodes([this, connected]), net_kernel,
810			    set_net_ticktime, [DefaultTT, 5])
811    end,
812
813    wait_until(fun () -> DefaultTT == net_kernel:get_net_ticktime() end),
814    ok.
815
816
817wait_for_nodedowns(Tester, Ref) ->
818    receive
819	{nodedown, Node} ->
820	    io:format("~p~n", [{node(), {nodedown, Node}}]),
821	    Tester ! {Ref, {node(), {nodedown, Node}}}
822    end,
823    wait_for_nodedowns(Tester, Ref).
824
825run_tick_change_test(DCfg, B, C, PrevTT, TT) ->
826    [DN, EN] = get_nodenames(2, tick_change),
827
828    Tester = self(),
829    Ref = make_ref(),
830    MonitorNodes = fun (Nodes) ->
831			   lists:foreach(
832			     fun (N) ->
833				     monitor_node(N,true)
834			     end,
835			     Nodes),
836			   wait_for_nodedowns(Tester, Ref)
837		   end,
838
839    {ok, D} = start_node(DCfg, DN, "-kernel net_ticktime "
840			 ++ integer_to_list(PrevTT)),
841
842    NMA = spawn_link(fun () -> MonitorNodes([B, C, D]) end),
843    NMB = spawn_link(B, fun () -> MonitorNodes([node(), C, D]) end),
844    NMC = spawn_link(C, fun () -> MonitorNodes([node(), B, D]) end),
845
846    MaxTT = case PrevTT > TT of
847		true  -> PrevTT;
848		false -> TT
849	    end,
850
851    CheckResult = make_ref(),
852    spawn_link(fun () ->
853		       receive
854		       after (25 + MaxTT)*1000 ->
855			       Tester ! CheckResult
856		       end
857	       end),
858
859    %% In case other nodes than these are connected
860    case nodes(connected) -- [B, C, D] of
861	[] -> ok;
862	OtherNodes -> rpc:multicall(OtherNodes, net_kernel,
863				    set_net_ticktime, [TT, 20])
864    end,
865
866    change_initiated = net_kernel:set_net_ticktime(TT,20),
867    {ongoing_change_to,_} = net_kernel:set_net_ticktime(TT,20),
868    sleep(3),
869    change_initiated = rpc:call(B,net_kernel,set_net_ticktime,[TT,15]),
870    sleep(7),
871    change_initiated = rpc:call(C,net_kernel,set_net_ticktime,[TT,10]),
872
873    {ok, E} = start_node(DCfg, EN, "-kernel net_ticktime "
874			 ++ integer_to_list(TT)),
875    NME  = spawn_link(E, fun () -> MonitorNodes([node(), B, C, D]) end),
876    NMA2 = spawn_link(fun () -> MonitorNodes([E]) end),
877    NMB2 = spawn_link(B, fun () -> MonitorNodes([E]) end),
878    NMC2 = spawn_link(C, fun () -> MonitorNodes([E]) end),
879
880    receive CheckResult -> ok end,
881
882    unlink(NMA),  exit(NMA, kill),
883    unlink(NMB),  exit(NMB, kill),
884    unlink(NMC),  exit(NMC, kill),
885    unlink(NME),  exit(NME, kill),
886    unlink(NMA2), exit(NMA2, kill),
887    unlink(NMB2), exit(NMB2, kill),
888    unlink(NMC2), exit(NMC2, kill),
889
890    %% The node not changing ticktime should have been disconnected from the
891    %% other nodes
892    receive {Ref, {Node, {nodedown, D}}} when Node == node() -> ok
893    after 0 -> exit({?LINE, no_nodedown})
894    end,
895    receive {Ref, {B, {nodedown, D}}} -> ok
896    after 0 -> exit({?LINE, no_nodedown})
897    end,
898    receive {Ref, {C, {nodedown, D}}} -> ok
899    after 0 -> exit({?LINE, no_nodedown})
900    end,
901    receive {Ref, {E, {nodedown, D}}} -> ok
902    after 0 -> exit({?LINE, no_nodedown})
903    end,
904
905    %% No other connections should have been broken
906    receive
907	{Ref, Reason} ->
908	    stop_node(E),
909	    exit({?LINE, Reason});
910	{'EXIT', Pid, Reason} when Pid == NMA;
911				   Pid == NMB;
912				   Pid == NMC;
913				   Pid == NME;
914				   Pid == NMA2;
915				   Pid == NMB2;
916				   Pid == NMC2 ->
917	    stop_node(E),
918
919	    exit({?LINE, {node(Pid), Reason}})
920    after 0 ->
921	    TT = net_kernel:get_net_ticktime(),
922	    TT = rpc:call(B, net_kernel, get_net_ticktime, []),
923	    TT = rpc:call(C, net_kernel, get_net_ticktime, []),
924	    TT = rpc:call(E, net_kernel, get_net_ticktime, []),
925	    stop_node(E),
926	    ok
927    end.
928
929%%
930%% Basic tests of hidden node.
931%%
932%% Basic test of hidden node.
933hidden_node(Config) when is_list(Config) ->
934    run_dist_configs(fun hidden_node/2, Config).
935
936hidden_node(DCfg, _Config) ->
937    HArgs = "-hidden",
938    {ok, V} = start_node(DCfg, visible_node),
939    VMN = start_monitor_nodes_proc(V),
940    {ok, H} = start_node(DCfg, hidden_node, HArgs),
941    %% Connect visible_node -> hidden_node
942    connect_nodes(V, H),
943    test_nodes(V, H),
944    stop_node(H),
945    sleep(5),
946    check_monitor_nodes_res(VMN, H),
947    stop_node(V),
948    {ok, H} = start_node(DCfg, hidden_node, HArgs),
949    HMN = start_monitor_nodes_proc(H),
950    {ok, V} = start_node(DCfg, visible_node),
951    %% Connect hidden_node -> visible_node
952    connect_nodes(H, V),
953    test_nodes(V, H),
954    stop_node(V),
955    sleep(5),
956    check_monitor_nodes_res(HMN, V),
957    stop_node(H),
958    ok.
959
960connect_nodes(A, B) ->
961    %% Check that they haven't already connected.
962    false = lists:member(A, rpc:call(B, erlang, nodes, [connected])),
963    false = lists:member(B, rpc:call(A, erlang, nodes, [connected])),
964    %% Connect them.
965    pong = rpc:call(A, net_adm, ping, [B]).
966
967
968test_nodes(V, H) ->
969    %% No nodes should be visible on hidden_node
970    [] = rpc:call(H, erlang, nodes, []),
971    %% visible_node should be hidden on hidden_node
972    true = lists:member(V, rpc:call(H, erlang, nodes, [hidden])),
973    %% hidden_node node shouldn't be visible on visible_node
974    false = lists:member(H, rpc:call(V, erlang, nodes, [])),
975    %% hidden_node should be hidden on visible_node
976    true = lists:member(H, rpc:call(V, erlang, nodes, [hidden])).
977
978mn_loop(MNs) ->
979    receive
980	{nodeup, N} ->
981	    mn_loop([{nodeup, N}|MNs]);
982	{nodedown, N} ->
983	    mn_loop([{nodedown, N}|MNs]);
984	{monitor_nodes_result, Ref, From} ->
985	    From ! {Ref, MNs};
986	_ ->
987	    mn_loop(MNs)
988    end.
989
990start_monitor_nodes_proc(Node) ->
991    Ref = make_ref(),
992    Starter = self(),
993    Pid = spawn(Node,
994		fun() ->
995			net_kernel:monitor_nodes(true),
996			Starter ! Ref,
997			mn_loop([])
998		end),
999    receive
1000	Ref ->
1001	    ok
1002    end,
1003    Pid.
1004
1005
1006check_monitor_nodes_res(Pid, Node) ->
1007    Ref = make_ref(),
1008    Pid ! {monitor_nodes_result, Ref, self()},
1009    receive
1010	{Ref, MNs} ->
1011	    false = lists:keysearch(Node, 2, MNs)
1012    end.
1013
1014
1015
1016%% Check the kernel inet_dist_{listen,connect}_options options.
1017inet_dist_options_options(Config) when is_list(Config) ->
1018    Prio = 1,
1019    case gen_udp:open(0, [{priority,Prio}]) of
1020	{ok,Socket} ->
1021	    case inet:getopts(Socket, [priority]) of
1022		{ok,[{priority,Prio}]} ->
1023		    ok = gen_udp:close(Socket),
1024		    do_inet_dist_options_options(Prio);
1025		_ ->
1026		    ok = gen_udp:close(Socket),
1027		    {skip,
1028		     "Can not set priority "++integer_to_list(Prio)++
1029			 " on socket"}
1030	    end;
1031	{error,_} ->
1032	    {skip, "Can not set priority on socket"}
1033    end.
1034
1035do_inet_dist_options_options(Prio) ->
1036    PriorityString0 = "[{priority,"++integer_to_list(Prio)++"}]",
1037    PriorityString =
1038	case os:cmd("echo [{a,1}]") of
1039	    "[{a,1}]"++_ ->
1040		PriorityString0;
1041	    _ ->
1042		%% Some shells need quoting of [{}]
1043		"'"++PriorityString0++"'"
1044	end,
1045    InetDistOptions =
1046	"-hidden "
1047	"-kernel inet_dist_connect_options "++PriorityString++" "
1048	"-kernel inet_dist_listen_options "++PriorityString,
1049    {ok,Node1} =
1050	start_node("", inet_dist_options_1, InetDistOptions),
1051    {ok,Node2} =
1052	start_node("", inet_dist_options_2, InetDistOptions),
1053    %%
1054    pong =
1055	rpc:call(Node1, net_adm, ping, [Node2]),
1056    PrioritiesNode1 =
1057	rpc:call(Node1, ?MODULE, get_socket_priorities, []),
1058    PrioritiesNode2 =
1059	rpc:call(Node2, ?MODULE, get_socket_priorities, []),
1060    io:format("PrioritiesNode1 = ~p", [PrioritiesNode1]),
1061    io:format("PrioritiesNode2 = ~p", [PrioritiesNode2]),
1062    Elevated = [P || P <- PrioritiesNode1, P =:= Prio],
1063    Elevated = [P || P <- PrioritiesNode2, P =:= Prio],
1064    [_|_] = Elevated,
1065    %%
1066    stop_node(Node2),
1067    stop_node(Node1),
1068    ok.
1069
1070get_socket_priorities() ->
1071    [Priority ||
1072	{ok,[{priority,Priority}]} <-
1073	    [inet:getopts(Port, [priority]) ||
1074		Port <- erlang:ports(),
1075		element(2, erlang:port_info(Port, name)) =:= "tcp_inet"]].
1076
1077
1078
1079%%
1080%% Testcase:
1081%%   monitor_nodes_nodedown_reason
1082%%
1083
1084monitor_nodes_nodedown_reason(Config) when is_list(Config) ->
1085    run_dist_configs(fun monitor_nodes_nodedown_reason/2, Config).
1086
1087monitor_nodes_nodedown_reason(DCfg, _Config) ->
1088    MonNodeState = monitor_node_state(),
1089    ok = net_kernel:monitor_nodes(true),
1090    ok = net_kernel:monitor_nodes(true, [nodedown_reason]),
1091
1092    Names = get_numbered_nodenames(5, node),
1093    [NN1, NN2, NN3, NN4, NN5] = Names,
1094
1095    {ok, N1} = start_node(DCfg, NN1),
1096    {ok, N2} = start_node(DCfg, NN2),
1097    {ok, N3} = start_node(DCfg, NN3),
1098    {ok, N4} = start_node(DCfg, NN4, "-hidden"),
1099
1100    receive {nodeup, N1} -> ok end,
1101    receive {nodeup, N2} -> ok end,
1102    receive {nodeup, N3} -> ok end,
1103
1104    receive {nodeup, N1, []} -> ok end,
1105    receive {nodeup, N2, []} -> ok end,
1106    receive {nodeup, N3, []} -> ok end,
1107
1108    stop_node(N1),
1109    stop_node(N4),
1110    true = net_kernel:disconnect(N2),
1111    TickTime = net_kernel:get_net_ticktime(),
1112    SleepTime = TickTime + (TickTime div 2),
1113    spawn(N3, fun () ->
1114		      block_emu(SleepTime*1000),
1115		      halt()
1116	      end),
1117
1118    receive {nodedown, N1} -> ok end,
1119    receive {nodedown, N2} -> ok end,
1120    receive {nodedown, N3} -> ok end,
1121
1122    receive {nodedown, N1, [{nodedown_reason, R1}]} -> connection_closed = R1 end,
1123    receive {nodedown, N2, [{nodedown_reason, R2}]} -> disconnect = R2 end,
1124    receive {nodedown, N3, [{nodedown_reason, R3}]} -> net_tick_timeout = R3 end,
1125
1126    ok = net_kernel:monitor_nodes(false, [nodedown_reason]),
1127
1128    {ok, N5} = start_node(DCfg, NN5),
1129    stop_node(N5),
1130
1131    receive {nodeup, N5} -> ok end,
1132    receive {nodedown, N5} -> ok end,
1133    print_my_messages(),
1134    ok = check_no_nodedown_nodeup(1000),
1135    ok = net_kernel:monitor_nodes(false),
1136    MonNodeState = monitor_node_state(),
1137    ok.
1138
1139
1140monitor_nodes_complex_nodedown_reason(Config) when is_list(Config) ->
1141    run_dist_configs(fun monitor_nodes_complex_nodedown_reason/2, Config).
1142
1143monitor_nodes_complex_nodedown_reason(DCfg, _Config) ->
1144    MonNodeState = monitor_node_state(),
1145    Me = self(),
1146    ok = net_kernel:monitor_nodes(true, [nodedown_reason]),
1147    [Name] = get_nodenames(1, monitor_nodes_complex_nodedown_reason),
1148    {ok, Node} = start_node(DCfg, Name, ""),
1149    Pid = spawn(Node,
1150		fun() ->
1151			Me ! {stuff,
1152			      self(),
1153			      [make_ref(),
1154			       {processes(), erlang:ports()}]}
1155		end),
1156    receive {nodeup, Node, []} -> ok end,
1157    {ok, NodeInfo} = net_kernel:node_info(Node),
1158    {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo),
1159    ComplexTerm = receive {stuff, Pid, _} = Msg ->
1160			  {Msg, term_to_binary(Msg)}
1161		  end,
1162    exit(Owner, ComplexTerm),
1163    receive
1164	{nodedown, Node, [{nodedown_reason, NodeDownReason}]} ->
1165	    ok
1166    end,
1167    %% If the complex nodedown_reason messed something up garbage collections
1168    %% are likely to dump core
1169    garbage_collect(),
1170    garbage_collect(),
1171    garbage_collect(),
1172    ComplexTerm = NodeDownReason,
1173    ok = net_kernel:monitor_nodes(false, [nodedown_reason]),
1174    no_msgs(),
1175    MonNodeState = monitor_node_state(),
1176    ok.
1177
1178
1179
1180
1181%%
1182%% Testcase:
1183%%   monitor_nodes_node_type
1184%%
1185
1186monitor_nodes_node_type(Config) when is_list(Config) ->
1187    run_dist_configs(fun monitor_nodes_node_type/2, Config).
1188
1189monitor_nodes_node_type(DCfg, _Config) ->
1190    MonNodeState = monitor_node_state(),
1191    ok = net_kernel:monitor_nodes(true),
1192    ok = net_kernel:monitor_nodes(true, [{node_type, all}]),
1193    Names = get_numbered_nodenames(9, node),
1194    [NN1, NN2, NN3, NN4, NN5, NN6, NN7, NN8, NN9] = Names,
1195
1196    {ok, N1} = start_node(DCfg, NN1),
1197    {ok, N2} = start_node(DCfg, NN2),
1198    {ok, N3} = start_node(DCfg, NN3, "-hidden"),
1199    {ok, N4} = start_node(DCfg, NN4, "-hidden"),
1200
1201    receive {nodeup, N1} -> ok end,
1202    receive {nodeup, N2} -> ok end,
1203
1204    receive {nodeup, N1, [{node_type, visible}]} -> ok end,
1205    receive {nodeup, N2, [{node_type, visible}]} -> ok end,
1206    receive {nodeup, N3, [{node_type, hidden}]} -> ok end,
1207    receive {nodeup, N4, [{node_type, hidden}]} -> ok end,
1208
1209    stop_node(N1),
1210    stop_node(N2),
1211    stop_node(N3),
1212    stop_node(N4),
1213
1214    receive {nodedown, N1} -> ok end,
1215    receive {nodedown, N2} -> ok end,
1216
1217    receive {nodedown, N1, [{node_type, visible}]} -> ok end,
1218    receive {nodedown, N2, [{node_type, visible}]} -> ok end,
1219    receive {nodedown, N3, [{node_type, hidden}]} -> ok end,
1220    receive {nodedown, N4, [{node_type, hidden}]} -> ok end,
1221
1222    ok = net_kernel:monitor_nodes(false, [{node_type, all}]),
1223    {ok, N5} = start_node(DCfg, NN5),
1224
1225    receive {nodeup, N5} -> ok end,
1226    stop_node(N5),
1227    receive {nodedown, N5} -> ok end,
1228
1229    ok = net_kernel:monitor_nodes(true, [{node_type, hidden}]),
1230    {ok, N6} = start_node(DCfg, NN6),
1231    {ok, N7} = start_node(DCfg, NN7, "-hidden"),
1232
1233
1234    receive {nodeup, N6} -> ok end,
1235    receive {nodeup, N7, [{node_type, hidden}]} -> ok end,
1236    stop_node(N6),
1237    stop_node(N7),
1238
1239    receive {nodedown, N6} -> ok end,
1240    receive {nodedown, N7, [{node_type, hidden}]} -> ok end,
1241
1242    ok = net_kernel:monitor_nodes(true, [{node_type, visible}]),
1243    ok = net_kernel:monitor_nodes(false, [{node_type, hidden}]),
1244    ok = net_kernel:monitor_nodes(false),
1245
1246    {ok, N8} = start_node(DCfg, NN8),
1247    {ok, N9} = start_node(DCfg, NN9, "-hidden"),
1248
1249    receive {nodeup, N8, [{node_type, visible}]} -> ok end,
1250    stop_node(N8),
1251    stop_node(N9),
1252
1253    receive {nodedown, N8, [{node_type, visible}]} -> ok end,
1254    print_my_messages(),
1255    ok = check_no_nodedown_nodeup(1000),
1256    ok = net_kernel:monitor_nodes(false, [{node_type, visible}]),
1257    MonNodeState = monitor_node_state(),
1258    ok.
1259
1260
1261%%
1262%% Testcase:
1263%%   monitor_nodes
1264%%
1265
1266monitor_nodes_misc(Config) when is_list(Config) ->
1267    run_dist_configs(fun monitor_nodes_misc/2, Config).
1268
1269monitor_nodes_misc(DCfg, _Config) ->
1270    MonNodeState = monitor_node_state(),
1271    ok = net_kernel:monitor_nodes(true),
1272    ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]),
1273    ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]),
1274    Names = get_numbered_nodenames(3, node),
1275    [NN1, NN2, NN3] = Names,
1276
1277    {ok, N1} = start_node(DCfg, NN1),
1278    {ok, N2} = start_node(DCfg, NN2, "-hidden"),
1279
1280    receive {nodeup, N1} -> ok end,
1281
1282    receive {nodeup, N1, [{node_type, visible}]} -> ok end,
1283    receive {nodeup, N1, [{node_type, visible}]} -> ok end,
1284    receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
1285    receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
1286
1287    stop_node(N1),
1288    stop_node(N2),
1289
1290    VisbleDownInfo = lists:sort([{node_type, visible},
1291				 {nodedown_reason, connection_closed}]),
1292    HiddenDownInfo = lists:sort([{node_type, hidden},
1293				 {nodedown_reason, connection_closed}]),
1294
1295    receive {nodedown, N1} -> ok end,
1296
1297    receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end,
1298    receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end,
1299    receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end,
1300    receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end,
1301
1302    ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]),
1303
1304    {ok, N3} = start_node(DCfg, NN3),
1305    receive {nodeup, N3} -> ok end,
1306    stop_node(N3),
1307    receive {nodedown, N3} -> ok end,
1308    print_my_messages(),
1309    ok = check_no_nodedown_nodeup(1000),
1310    ok = net_kernel:monitor_nodes(false),
1311    MonNodeState = monitor_node_state(),
1312    ok.
1313
1314
1315%% Tests that {nodeup, Node} messages are received before
1316%% messages from Node and that {nodedown, Node} messages are
1317%% received after messages from Node.
1318monitor_nodes_otp_6481(Config) when is_list(Config) ->
1319    run_dist_configs(fun monitor_nodes_otp_6481/2, Config).
1320
1321monitor_nodes_otp_6481(DCfg, Config) ->
1322    io:format("Testing nodedown...~n"),
1323    monitor_nodes_otp_6481_test(DCfg, Config, nodedown),
1324    io:format("ok~n"),
1325    io:format("Testing nodeup...~n"),
1326    monitor_nodes_otp_6481_test(DCfg, Config, nodeup),
1327    io:format("ok~n"),
1328    ok.
1329
1330monitor_nodes_otp_6481_test(DCfg, Config, TestType) when is_list(Config) ->
1331    MonNodeState = monitor_node_state(),
1332    NodeMsg = make_ref(),
1333    Me = self(),
1334    [Name] = get_nodenames(1, monitor_nodes_otp_6481),
1335    case TestType of
1336	nodedown -> ok = net_kernel:monitor_nodes(true);
1337	nodeup -> ok
1338    end,
1339    Seq = lists:seq(1,10000),
1340    MN = spawn_link(
1341	   fun () ->
1342		   lists:foreach(
1343		     fun (_) ->
1344			     ok = net_kernel:monitor_nodes(true)
1345		     end,
1346		     Seq),
1347		   Me ! {mon_set, self()},
1348		   receive after infinity -> ok end
1349	   end),
1350    receive {mon_set, MN} -> ok end,
1351    case TestType of
1352	nodedown -> ok;
1353	nodeup -> ok = net_kernel:monitor_nodes(true)
1354    end,
1355
1356    %% Whitebox:
1357    %% nodedown test: Since this process was the first one monitoring
1358    %%                nodes this process will be the first one notified
1359    %%                on nodedown.
1360    %% nodeup test:   Since this process was the last one monitoring
1361    %%                nodes this process will be the last one notified
1362    %%                on nodeup
1363
1364    %% Verify the monitor_nodes order expected
1365    TestMonNodeState = monitor_node_state(),
1366    %% io:format("~p~n", [TestMonNodeState]),
1367    TestMonNodeState =
1368	case TestType of
1369	    nodedown -> [];
1370	    nodeup -> [{self(), []}]
1371	end
1372	++ lists:map(fun (_) -> {MN, []} end, Seq)
1373	++ case TestType of
1374	       nodedown -> [{self(), []}];
1375	       nodeup -> []
1376	   end
1377	++ MonNodeState,
1378
1379    {ok, Node} = start_node(DCfg, Name, "", this),
1380    receive {nodeup, Node} -> ok end,
1381
1382    RemotePid = spawn(Node,
1383		      fun () ->
1384			      receive after 1500 -> ok end,
1385			      %% infinite loop of msgs
1386			      %% we want an endless stream of messages and the kill
1387			      %% the node mercilessly.
1388			      %% We then want to ensure that the nodedown message arrives
1389			      %% last ... without garbage after it.
1390			      _ = spawn(fun() -> node_loop_send(Me, NodeMsg, 1) end),
1391			      receive {Me, kill_it} -> ok end,
1392			      halt()
1393		      end),
1394
1395    net_kernel:disconnect(Node),
1396    receive {nodedown, Node} -> ok end,
1397
1398    %% Verify that '{nodeup, Node}' comes before '{NodeMsg, 1}' (the message
1399    %% bringing up the connection).
1400    {nodeup, Node} = receive Msg1 -> Msg1 end,
1401    {NodeMsg, N}   = receive Msg2 -> Msg2 end,
1402    %% msg stream has begun, kill the node
1403    RemotePid ! {self(), kill_it},
1404
1405    %% Verify that '{nodedown, Node}' comes after the last '{NodeMsg, N}'
1406    %% message.
1407    {nodedown, Node} = flush_node_msgs(NodeMsg, N+1),
1408    no_msgs(500),
1409
1410    Mon = erlang:monitor(process, MN),
1411    unlink(MN),
1412    exit(MN, bang),
1413    receive {'DOWN', Mon, process, MN, bang} -> ok end,
1414    ok = net_kernel:monitor_nodes(false),
1415    MonNodeState = monitor_node_state(),
1416    ok.
1417
1418flush_node_msgs(NodeMsg, No) ->
1419    case receive Msg -> Msg end of
1420	{NodeMsg, N} when N >= No ->
1421	    flush_node_msgs(NodeMsg, N+1);
1422	OtherMsg ->
1423	    OtherMsg
1424    end.
1425
1426node_loop_send(Pid, Msg, No) ->
1427    Pid ! {Msg, No},
1428    node_loop_send(Pid, Msg, No + 1).
1429
1430monitor_nodes_errors(Config) when is_list(Config) ->
1431    MonNodeState = monitor_node_state(),
1432    error = net_kernel:monitor_nodes(asdf),
1433    {error,
1434     {unknown_options,
1435      [gurka]}} = net_kernel:monitor_nodes(true,
1436					   [gurka]),
1437    {error,
1438     {options_not_a_list,
1439      gurka}} = net_kernel:monitor_nodes(true,
1440					 gurka),
1441    {error,
1442     {option_value_mismatch,
1443      [{node_type,visible},
1444       {node_type,hidden}]}}
1445	= net_kernel:monitor_nodes(true,
1446				   [{node_type,hidden},
1447				    {node_type,visible}]),
1448    {error,
1449     {option_value_mismatch,
1450      [{node_type,visible},
1451       {node_type,all}]}}
1452	= net_kernel:monitor_nodes(true,
1453				   [{node_type,all},
1454				    {node_type,visible}]),
1455    {error,
1456     {bad_option_value,
1457      {node_type,
1458       blaha}}}
1459	= net_kernel:monitor_nodes(true, [{node_type, blaha}]),
1460    MonNodeState = monitor_node_state(),
1461    ok.
1462
1463monitor_nodes_combinations(Config) when is_list(Config) ->
1464    run_dist_configs(fun monitor_nodes_combinations/2, Config).
1465
1466monitor_nodes_combinations(DCfg, _Config) ->
1467    MonNodeState = monitor_node_state(),
1468    monitor_nodes_all_comb(true),
1469    [VisibleName, HiddenName] = get_nodenames(2,
1470					      monitor_nodes_combinations),
1471    {ok, Visible} = start_node(DCfg, VisibleName, ""),
1472    receive_all_comb_nodeup_msgs(visible, Visible),
1473    no_msgs(),
1474    stop_node(Visible),
1475    receive_all_comb_nodedown_msgs(visible, Visible, connection_closed),
1476    no_msgs(),
1477    {ok, Hidden} = start_node(DCfg, HiddenName, "-hidden"),
1478    receive_all_comb_nodeup_msgs(hidden, Hidden),
1479    no_msgs(),
1480    stop_node(Hidden),
1481    receive_all_comb_nodedown_msgs(hidden, Hidden, connection_closed),
1482    no_msgs(),
1483    monitor_nodes_all_comb(false),
1484    MonNodeState = monitor_node_state(),
1485    no_msgs(),
1486    ok.
1487
1488monitor_nodes_all_comb(Flag) ->
1489    ok = net_kernel:monitor_nodes(Flag),
1490    ok = net_kernel:monitor_nodes(Flag,
1491				  [nodedown_reason]),
1492    ok = net_kernel:monitor_nodes(Flag,
1493				  [{node_type, hidden}]),
1494    ok = net_kernel:monitor_nodes(Flag,
1495				  [{node_type, visible}]),
1496    ok = net_kernel:monitor_nodes(Flag,
1497				  [{node_type, all}]),
1498    ok = net_kernel:monitor_nodes(Flag,
1499				  [nodedown_reason,
1500				   {node_type, hidden}]),
1501    ok = net_kernel:monitor_nodes(Flag,
1502				  [nodedown_reason,
1503				   {node_type, visible}]),
1504    ok = net_kernel:monitor_nodes(Flag,
1505				  [nodedown_reason,
1506				   {node_type, all}]),
1507    %% There currently are 8 different combinations
1508    8.
1509
1510
1511receive_all_comb_nodeup_msgs(visible, Node) ->
1512    io:format("Receive nodeup visible...~n"),
1513    Exp = [{nodeup, Node},
1514	   {nodeup, Node, []}]
1515	++ mk_exp_mn_all_comb_nodeup_msgs_common(visible, Node),
1516    receive_mn_msgs(Exp),
1517    io:format("ok~n"),
1518    ok;
1519receive_all_comb_nodeup_msgs(hidden, Node) ->
1520    io:format("Receive nodeup hidden...~n"),
1521    Exp = mk_exp_mn_all_comb_nodeup_msgs_common(hidden, Node),
1522    receive_mn_msgs(Exp),
1523    io:format("ok~n"),
1524    ok.
1525
1526mk_exp_mn_all_comb_nodeup_msgs_common(Type, Node) ->
1527    InfoNt = [{node_type, Type}],
1528    [{nodeup, Node, InfoNt},
1529     {nodeup, Node, InfoNt},
1530     {nodeup, Node, InfoNt},
1531     {nodeup, Node, InfoNt}].
1532
1533receive_all_comb_nodedown_msgs(visible, Node, Reason) ->
1534    io:format("Receive nodedown visible...~n"),
1535    Exp = [{nodedown, Node},
1536	   {nodedown, Node, [{nodedown_reason, Reason}]}]
1537	++ mk_exp_mn_all_comb_nodedown_msgs_common(visible,
1538						   Node,
1539						   Reason),
1540    receive_mn_msgs(Exp),
1541    io:format("ok~n"),
1542    ok;
1543receive_all_comb_nodedown_msgs(hidden, Node, Reason) ->
1544    io:format("Receive nodedown hidden...~n"),
1545    Exp = mk_exp_mn_all_comb_nodedown_msgs_common(hidden, Node, Reason),
1546    receive_mn_msgs(Exp),
1547    io:format("ok~n"),
1548    ok.
1549
1550mk_exp_mn_all_comb_nodedown_msgs_common(Type, Node, Reason) ->
1551    InfoNt = [{node_type, Type}],
1552    InfoNdrNt = lists:sort([{nodedown_reason, Reason}]++InfoNt),
1553    [{nodedown, Node, InfoNt},
1554     {nodedown, Node, InfoNt},
1555     {nodedown, Node, InfoNdrNt},
1556     {nodedown, Node, InfoNdrNt}].
1557
1558receive_mn_msgs([]) ->
1559    ok;
1560receive_mn_msgs(Msgs) ->
1561    io:format("Expecting msgs: ~p~n", [Msgs]),
1562    receive
1563	{_Dir, _Node} = Msg ->
1564	    io:format("received ~p~n", [Msg]),
1565	    case lists:member(Msg, Msgs) of
1566		true -> receive_mn_msgs(lists:delete(Msg, Msgs));
1567		false -> ct:fail({unexpected_message, Msg,
1568				  expected_messages, Msgs})
1569	    end;
1570	{Dir, Node, Info} ->
1571	    Msg = {Dir, Node, lists:sort(Info)},
1572	    io:format("received ~p~n", [Msg]),
1573	    case lists:member(Msg, Msgs) of
1574		true -> receive_mn_msgs(lists:delete(Msg, Msgs));
1575		false -> ct:fail({unexpected_message, Msg,
1576				  expected_messages, Msgs})
1577	    end;
1578	Msg ->
1579	    io:format("received ~p~n", [Msg]),
1580	    ct:fail({unexpected_message, Msg,
1581		     expected_messages, Msgs})
1582    end.
1583
1584monitor_nodes_cleanup(Config) when is_list(Config) ->
1585    MonNodeState = monitor_node_state(),
1586    Me = self(),
1587    No = monitor_nodes_all_comb(true),
1588    Inf = spawn(fun () ->
1589			monitor_nodes_all_comb(true),
1590			Me ! {mons_set, self()},
1591			receive after infinity -> ok end
1592		end),
1593    TO = spawn(fun () ->
1594		       monitor_nodes_all_comb(true),
1595		       Me ! {mons_set, self()},
1596		       receive after 500 -> ok end
1597	       end),
1598    receive {mons_set, Inf} -> ok end,
1599    receive {mons_set, TO} -> ok end,
1600    MNLen = length(MonNodeState) + No*3,
1601    MNLen = length(monitor_node_state()),
1602    MonInf = erlang:monitor(process, Inf),
1603    MonTO = erlang:monitor(process, TO),
1604    exit(Inf, bang),
1605    No = monitor_nodes_all_comb(false),
1606    receive {'DOWN', MonInf, process, Inf, bang} -> ok end,
1607    receive {'DOWN', MonTO, process, TO, normal} -> ok end,
1608    MonNodeState = monitor_node_state(),
1609    no_msgs(),
1610    ok.
1611
1612monitor_nodes_many(Config) when is_list(Config) ->
1613    run_dist_configs(fun monitor_nodes_many/2, Config).
1614
1615monitor_nodes_many(DCfg, _Config) ->
1616    MonNodeState = monitor_node_state(),
1617    [Name] = get_nodenames(1, monitor_nodes_many),
1618    %% We want to perform more than 2^16 net_kernel:monitor_nodes
1619    %% since this will wrap an internal counter
1620    No = (1 bsl 16) + 17,
1621    repeat(fun () -> ok = net_kernel:monitor_nodes(true) end, No),
1622    No = length(monitor_node_state()) - length(MonNodeState),
1623    {ok, Node} = start_node(DCfg, Name),
1624    repeat(fun () -> receive {nodeup, Node} -> ok end end, No),
1625    stop_node(Node),
1626    repeat(fun () -> receive {nodedown, Node} -> ok end end, No),
1627    ok = net_kernel:monitor_nodes(false),
1628    no_msgs(10),
1629    MonNodeState = monitor_node_state(),
1630    ok.
1631
1632%% Test order of messages nodedown and nodeup.
1633monitor_nodes_down_up(Config) when is_list(Config) ->
1634    [An] = get_nodenames(1, monitor_nodeup),
1635    {ok, A} = ct_slave:start(An),
1636
1637    try
1638        monitor_nodes_yoyo(A)
1639    after
1640        catch ct_slave:stop(A)
1641    end.
1642
1643monitor_nodes_yoyo(A) ->
1644    net_kernel:monitor_nodes(true),
1645    Papa = self(),
1646
1647    %% Spawn lots of processes doing one erlang:monitor_node(A,true) each
1648    %% just to get lots of other monitors to fire when connection goes down
1649    %% and thereby give time for {nodeup,A} to race before {nodedown,A}.
1650    NodeMonCnt = 10000,
1651    NodeMons = [my_spawn_opt(fun F() ->
1652                                     monitor_node = receive_any(),
1653                                     monitor_node(A, true),
1654                                     Papa ! ready,
1655                                     {nodedown, A} =  receive_any(),
1656                                     F()
1657                             end,
1658                             [link, monitor, {priority, low}])
1659                ||
1660                   _ <- lists:seq(1, NodeMonCnt)],
1661
1662    %% Spawn message spamming process to trigger new connection setups
1663    %% as quick as possible.
1664    Spammer = my_spawn_opt(fun F() ->
1665                                   {dummy, A} ! trigger_auto_connect,
1666                                   F()
1667                           end,
1668                           [link, monitor]),
1669
1670    %% Now bring connection down and verify we get {nodedown,A} before {nodeup,A}.
1671    Yoyos = 20,
1672    [begin
1673         [P ! monitor_node || P <- NodeMons],
1674         [receive ready -> ok end || _ <- NodeMons],
1675
1676         Owner = get_conn_owner(A),
1677         exit(Owner, kill),
1678
1679         {nodedown, A} = receive_any(),
1680         {nodeup, A} = receive_any()
1681     end
1682     || _ <- lists:seq(1,Yoyos)],
1683
1684    unlink(Spammer),
1685    exit(Spammer, die),
1686    receive {'DOWN',_,process,Spammer,_} -> ok end,
1687
1688    [begin unlink(P), exit(P, die) end || P <- NodeMons],
1689    [receive {'DOWN',_,process,P,_} -> ok end || P <- NodeMons],
1690
1691    net_kernel:monitor_nodes(false),
1692    ok.
1693
1694receive_any() ->
1695    receive_any(infinity).
1696
1697receive_any(Timeout) ->
1698    receive
1699        M -> M
1700    after
1701        Timeout -> timeout
1702    end.
1703
1704my_spawn_opt(Fun, Opts) ->
1705    case spawn_opt(Fun, Opts) of
1706        {Pid, _Mref} -> Pid;
1707        Pid -> Pid
1708    end.
1709
1710get_conn_owner(Node) ->
1711    {ok, NodeInfo} = net_kernel:node_info(Node),
1712    {value,{owner, Owner}} = lists:keysearch(owner, 1, NodeInfo),
1713    Owner.
1714
1715dist_ctrl_proc_smoke(Config) when is_list(Config) ->
1716    dist_ctrl_proc_test(get_nodenames(2, ?FUNCTION_NAME)).
1717
1718dist_ctrl_proc_reject(Config) when is_list(Config) ->
1719    ToReject = combinations(dist_util:rejectable_flags()),
1720    lists:map(fun(Flags) ->
1721                      ct:log("Try to reject ~p",[Flags]),
1722                      dist_ctrl_proc_test(get_nodenames(2, ?FUNCTION_NAME),
1723                        "-gen_tcp_dist_reject_flags " ++ integer_to_list(Flags))
1724              end, ToReject).
1725
1726combinations([H | T]) ->
1727    lists:flatten([[(1 bsl H) bor C || C <- combinations(T)] | combinations(T)]);
1728combinations([]) ->
1729    [0];
1730combinations(BitField) ->
1731    lists:sort(combinations(bits(BitField, 0))).
1732
1733bits(0, _) ->
1734    [];
1735bits(BitField, Cnt) when BitField band 1 == 1 ->
1736    [Cnt | bits(BitField bsr 1, Cnt + 1)];
1737bits(BitField, Cnt) ->
1738    bits(BitField bsr 1, Cnt + 1).
1739
1740dist_ctrl_proc_test(Nodes) ->
1741    dist_ctrl_proc_test(Nodes,"").
1742
1743dist_ctrl_proc_test([Name1,Name2], Extra) ->
1744    ThisNode = node(),
1745    GenTcpOptProlog = "-proto_dist gen_tcp "
1746        "-gen_tcp_dist_output_loop " ++ atom_to_list(?MODULE) ++ " " ++
1747        "dist_cntrlr_output_test_size " ++ Extra,
1748    {ok, Node1} = start_node("", Name1, "-proto_dist gen_tcp"),
1749    {ok, Node2} = start_node("", Name2, GenTcpOptProlog),
1750    NL = lists:sort([ThisNode, Node1, Node2]),
1751    wait_until(fun () ->
1752                       NL == lists:sort([node()|nodes()])
1753               end),
1754    wait_until(fun () ->
1755                       NL == lists:sort([rpc:call(Node1,erlang, node, [])
1756                                         | rpc:call(Node1, erlang, nodes, [])])
1757               end),
1758    wait_until(fun () ->
1759                       NL == lists:sort([rpc:call(Node2,erlang, node, [])
1760                                         | rpc:call(Node2, erlang, nodes, [])])
1761               end),
1762
1763    smoke_communicate(Node1, gen_tcp_dist, dist_cntrlr_output_loop),
1764    smoke_communicate(Node2, erl_distribution_SUITE, dist_cntrlr_output_loop_size),
1765
1766    stop_node(Node1),
1767    stop_node(Node2),
1768    ok.
1769
1770smoke_communicate(Node, OLoopMod, OLoopFun) ->
1771    %% Verify that we actually are executing the distribution
1772    %% module we expect and also massage message passing over
1773    %% the connection a bit...
1774    Ps = rpc:call(Node, erlang, processes, []),
1775    try
1776        lists:foreach(
1777          fun (P) ->
1778                  case rpc:call(Node, erlang, process_info, [P, current_stacktrace]) of
1779                      undefined ->
1780                          ok;
1781                      {current_stacktrace, StkTrace} ->
1782                          lists:foreach(fun ({Mod, Fun, 2, _}) when Mod == OLoopMod,
1783                                                                    Fun == OLoopFun ->
1784                                                io:format("~p ~p~n", [P, StkTrace]),
1785                                                throw(found_it);
1786                                            (_) ->
1787                                                ok
1788                                        end, StkTrace)
1789                  end
1790          end, Ps),
1791        exit({missing, {OLoopMod, OLoopFun}})
1792    catch
1793        throw:found_it -> ok
1794    end,
1795    Bin = list_to_binary(lists:duplicate(1000,100)),
1796    BitStr = <<0:7999>>,
1797    List = [[Bin], atom, [BitStr|Bin], make_ref(), [[[BitStr|"hopp"]]],
1798            4711, 111122222211111111111111,"hej", fun () -> ok end, BitStr,
1799            self(), fun erlang:node/1],
1800    Pid = spawn_link(Node, fun () -> receive {From1, Msg1} -> From1 ! Msg1 end,
1801                                     receive {From2, Msg2} -> From2 ! Msg2 end
1802                           end),
1803    R = make_ref(),
1804    Pid ! {self(), [R, List]},
1805    receive [R, L1] -> List = L1 end,
1806
1807    %% Send a huge message in order to trigger message fragmentation if enabled
1808    FragBin = <<0:(2*(1024*64*8))>>,
1809    Pid ! {self(), [R, List, FragBin]},
1810    receive [R, L2, B] -> List = L2, FragBin = B end,
1811
1812    unlink(Pid),
1813    exit(Pid, kill),
1814    ok.
1815
1816
1817erl_uds_dist_smoke_test(Config) when is_list(Config) ->
1818    case os:type() of
1819        {win32,_} ->
1820            {skipped, "Not on Windows"};
1821        _ ->
1822            do_erl_uds_dist_smoke_test()
1823    end.
1824
1825do_erl_uds_dist_smoke_test() ->
1826    [Node1, Node2] = lists:map(fun (Name) ->
1827                                       list_to_atom(atom_to_list(Name) ++ "@localhost")
1828                               end,
1829                               get_nodenames(2, erl_uds_dist_smoke_test)),
1830    {LPort, Acceptor} = uds_listen(),
1831    start_uds_node(Node1, LPort),
1832    start_uds_node(Node2, LPort),
1833    receive
1834        {uds_nodeup, N1} ->
1835            io:format("~p is up~n", [N1])
1836    end,
1837    receive
1838        {uds_nodeup, N2} ->
1839            io:format("~p is up~n", [N2])
1840    end,
1841
1842    io:format("Testing ping net_adm:ping(~p) on ~p~n", [Node2, Node1]),
1843    Node1 ! {self(), {net_adm, ping, [Node2]}},
1844    receive
1845        {Node1, PingRes} ->
1846            io:format("~p~n", [PingRes]),
1847            pong = PingRes
1848    end,
1849
1850    io:format("Testing nodes() on ~p~n", [Node1]),
1851    Node1 ! {self(), {erlang, nodes, []}},
1852    receive
1853        {Node1, N1List} ->
1854            io:format("~p~n", [N1List]),
1855            [Node2] = N1List
1856    end,
1857
1858    io:format("Testing nodes() on ~p~n", [Node2]),
1859    Node2 ! {self(), {erlang, nodes, []}},
1860    receive
1861        {Node2, N2List} ->
1862            io:format("~p~n", [N2List]),
1863            [Node1] = N2List
1864    end,
1865
1866    io:format("Shutting down~n", []),
1867
1868    Node1 ! {self(), close},
1869    Node2 ! {self(), close},
1870
1871    receive {Node1, C1} -> ok = C1 end,
1872    receive {Node2, C2} -> ok = C2 end,
1873
1874    unlink(Acceptor),
1875    exit(Acceptor, kill),
1876
1877    io:format("ok~n", []),
1878
1879    ok.
1880
1881%% Helpers for testing the erl_uds_dist example
1882
1883uds_listen() ->
1884    Me = self(),
1885    {ok, LSock} = gen_tcp:listen(0, [binary, {packet, 4}, {active, false}]),
1886    {ok, LPort} = inet:port(LSock),
1887    {LPort, spawn_link(fun () ->
1888                               uds_accept_loop(LSock, Me)
1889                       end)}.
1890
1891uds_accept_loop(LSock, TestProc) ->
1892    {ok, Sock} = gen_tcp:accept(LSock),
1893    _ = spawn_link(fun () ->
1894                           uds_rpc_client_init(Sock, TestProc)
1895                   end),
1896    uds_accept_loop(LSock, TestProc).
1897
1898uds_rpc(Sock, MFA) ->
1899    ok = gen_tcp:send(Sock, term_to_binary(MFA)),
1900    case gen_tcp:recv(Sock, 0) of
1901        {error, Reason} ->
1902            error({recv_failed, Reason});
1903        {ok, Packet} ->
1904            binary_to_term(Packet)
1905    end.
1906
1907uds_rpc_client_init(Sock, TestProc) ->
1908    case uds_rpc(Sock, {erlang, node, []}) of
1909        nonode@nohost ->
1910            %% Wait for distribution to come up...
1911            receive after 100 -> ok end,
1912            uds_rpc_client_init(Sock, TestProc);
1913        Node when is_atom(Node) ->
1914            register(Node, self()),
1915            TestProc ! {uds_nodeup, Node},
1916            uds_rpc_client_loop(Sock, Node)
1917    end.
1918
1919uds_rpc_client_loop(Sock, Node) ->
1920    receive
1921        {From, close} ->
1922            ok = gen_tcp:send(Sock, term_to_binary(close)),
1923            From ! {Node, gen_tcp:close(Sock)},
1924            exit(normal);
1925        {From, ApplyData} ->
1926            From ! {Node, uds_rpc(Sock, ApplyData)},
1927            uds_rpc_client_loop(Sock, Node)
1928    end.
1929
1930uds_rpc_server_loop(Sock) ->
1931    case gen_tcp:recv(Sock, 0) of
1932        {error, Reason} ->
1933            error({recv_failed, Reason});
1934        {ok, Packet} ->
1935            case binary_to_term(Packet) of
1936                {M, F, A} when is_atom(M), is_atom(F), is_list(A) ->
1937                    ok = gen_tcp:send(Sock, term_to_binary(apply(M, F, A)));
1938                {F, A} when is_function(F), is_list(A) ->
1939                    ok = gen_tcp:send(Sock, term_to_binary(apply(F, A)));
1940                close ->
1941                    ok = gen_tcp:close(Sock),
1942                    exit(normal);
1943                Other ->
1944                    error({unexpected_data, Other})
1945            end
1946    end,
1947    uds_rpc_server_loop(Sock).
1948
1949start_uds_rpc_server([PortString]) ->
1950    Port = list_to_integer(PortString),
1951    {Pid, Mon} = spawn_monitor(fun () ->
1952                                       {ok, Sock} = gen_tcp:connect({127,0,0,1}, Port,
1953                                                                    [binary, {packet, 4},
1954                                                                     {active, false}]),
1955                                       uds_rpc_server_loop(Sock)
1956                               end),
1957    receive
1958        {'DOWN', Mon, process, Pid, Reason} ->
1959            if Reason == normal ->
1960                    halt();
1961               true ->
1962                    EStr = lists:flatten(io_lib:format("uds rpc server crashed: ~p", [Reason])),
1963                    (catch file:write_file("uds_rpc_server_crash."++os:getpid(), EStr)),
1964                    halt(EStr)
1965            end
1966    end.
1967
1968start_uds_node(NodeName, LPort) ->
1969    Static = "-detached -noinput -proto_dist erl_uds",
1970    Pa = filename:dirname(code:which(?MODULE)),
1971    Prog = case catch init:get_argument(progname) of
1972	       {ok,[[P]]} -> P;
1973	       _ -> error(no_progname_argument_found)
1974	   end,
1975    {ok, Pwd} = file:get_cwd(),
1976    NameStr = atom_to_list(NodeName),
1977    CmdLine = Prog ++ " "
1978	++ Static
1979	++ " -sname " ++ NameStr
1980	++ " -pa " ++ Pa
1981	++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr
1982	++ " -setcookie " ++ atom_to_list(erlang:get_cookie())
1983        ++ " -run " ++ atom_to_list(?MODULE) ++ " start_uds_rpc_server "
1984        ++ integer_to_list(LPort),
1985    io:format("Starting: ~p~n", [CmdLine]),
1986    case open_port({spawn, CmdLine}, []) of
1987	Port when is_port(Port) ->
1988	    unlink(Port),
1989	    erlang:port_close(Port);
1990	Error ->
1991	    error({open_port_failed, Error})
1992    end,
1993    ok.
1994
1995erl_1424(Config) when is_list(Config) ->
1996    {error, Reason} = erl_epmd:names("."),
1997    {comment, lists:flatten(io_lib:format("Reason: ~p", [Reason]))}.
1998
1999differing_cookies(Config) when is_list(Config) ->
2000    test_server:timetrap({minutes, 1}),
2001    Node = node(),
2002    true = Node =/= nonode@nohost,
2003    [] = nodes(),
2004    BaseName = atom_to_list(?FUNCTION_NAME),
2005
2006    %% Use -hidden nodes to avoid global connecting all nodes
2007
2008    %% Start node A with different cookie
2009    NodeAName = BaseName++"_nodeA",
2010    NodeA = full_node_name(NodeAName),
2011    NodeACookieL = BaseName++"_cookieA",
2012    NodeACookie = list_to_atom(NodeACookieL),
2013    true = erlang:set_cookie( NodeA, NodeACookie ),
2014    { ok, NodeA } =
2015        start_node( "-hidden", NodeAName, "-setcookie "++NodeACookieL ),
2016    try
2017
2018        %% Verify the cluster
2019        [ NodeA ] = nodes(hidden),
2020        [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ),
2021
2022        %% Start node B with another different cookie
2023        NodeBName = BaseName++"_nodeB",
2024        NodeB = full_node_name(NodeBName),
2025        NodeBCookieL = BaseName++"_cookieB",
2026        NodeBCookie = list_to_atom(NodeBCookieL),
2027        true = erlang:set_cookie( NodeB, NodeBCookie ),
2028        { ok, NodeB } =
2029            start_node( "-hidden", NodeBName, "-setcookie "++NodeBCookieL ),
2030        try
2031
2032            %% Verify the cluster
2033            equal_sets( [NodeA, NodeB], nodes(hidden) ),
2034            [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ),
2035            [ Node ] = rpc:call( NodeB, erlang, nodes, [hidden] ),
2036
2037            %% Verify that the nodes can not connect
2038            %% before correcting the cookie configuration
2039            pang = rpc:call( NodeA, net_adm, ping, [NodeB] ),
2040            pang = rpc:call( NodeB, net_adm, ping, [NodeA] ),
2041
2042            %% Configure cookie and connect node A -> B
2043            true = rpc:call( NodeA, erlang, set_cookie, [NodeB, NodeBCookie] ),
2044            pong = rpc:call( NodeA, net_adm, ping, [NodeB] ),
2045
2046            %% Verify the cluster
2047            NodeACookie = rpc:call( NodeA, erlang, get_cookie, []),
2048            NodeBCookie = rpc:call( NodeB, erlang, get_cookie, []),
2049            equal_sets( [NodeA, NodeB], nodes(hidden) ),
2050            equal_sets( [Node, NodeB],
2051                        rpc:call( NodeA, erlang, nodes, [hidden] )),
2052            equal_sets( [Node, NodeA],
2053                        rpc:call( NodeB, erlang, nodes, [hidden] )),
2054
2055            %% Disconnect node A from B
2056            true = rpc:call( NodeB, net_kernel, disconnect, [NodeA] ),
2057
2058            %% Verify the cluster
2059            equal_sets( [NodeA, NodeB], nodes(hidden) ),
2060            [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ),
2061            [ Node ] = rpc:call( NodeB, erlang, nodes, [hidden] ),
2062
2063            %% Reconnect, now node B -> A
2064            pong = rpc:call( NodeB, net_adm, ping, [NodeA] ),
2065
2066            %% Verify the cluster
2067            equal_sets( [NodeA, NodeB], nodes(hidden) ),
2068            equal_sets( [Node, NodeB],
2069                        rpc:call( NodeA, erlang, nodes, [hidden] )),
2070            equal_sets( [Node, NodeA],
2071                        rpc:call( NodeB, erlang, nodes, [hidden] ))
2072
2073        after
2074            _ = stop_node(NodeB)
2075        end
2076    after
2077        _ = stop_node(NodeA)
2078    end,
2079    [] = nodes(hidden),
2080    ok.
2081
2082cmdline_setcookie_2(Config) when is_list(Config) ->
2083    test_server:timetrap({minutes, 1}),
2084    Node = node(),
2085    true = Node =/= nonode@nohost,
2086    [] = nodes(),
2087    NodeL = atom_to_list(Node),
2088    BaseName = atom_to_list(?FUNCTION_NAME),
2089    NodeCookie = erlang:get_cookie(),
2090    NodeCookieL = atom_to_list(NodeCookie),
2091
2092    %% Use -hidden nodes to avoid global connecting all nodes
2093
2094    %% Start node A with different cookie
2095    %% and cookie configuration of mother node
2096    NodeAName = BaseName++"_nodeA",
2097    NodeA = full_node_name(NodeAName),
2098    NodeACookieL = BaseName++"_cookieA",
2099    NodeACookie = list_to_atom(NodeACookieL),
2100    { ok, NodeA } =
2101        start_node(
2102          "-hidden", NodeAName,
2103          "-setcookie "++NodeL++" "++NodeCookieL ),
2104
2105    try
2106
2107        %% Verify the cluster
2108        [ NodeA ] = nodes(hidden),
2109        [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ),
2110        NodeCookie = rpc:call( NodeA, erlang, get_cookie, []),
2111
2112        true = rpc:call( NodeA, erlang, set_cookie, [NodeACookie] ),
2113
2114        %% Start node B with different cookie
2115        %% and cookie configuration of mother node and node A
2116        NodeBName = BaseName++"_nodeB",
2117        NodeB = full_node_name(NodeBName),
2118        NodeBCookieL = BaseName++"_cookieB",
2119        NodeBCookie = list_to_atom(NodeBCookieL),
2120        { ok, NodeB } =
2121            start_node(
2122              "-hidden", NodeBName,
2123              "-setcookie "++NodeBCookieL++" "
2124              "-setcookie "++NodeL++" "++NodeCookieL++" "
2125              "-setcookie "++atom_to_list(NodeA)++" "++NodeACookieL ),
2126
2127        try
2128
2129            %% Verify the cluster
2130            NodeACookie = rpc:call( NodeA, erlang, get_cookie, []),
2131            NodeBCookie = rpc:call( NodeB, erlang, get_cookie, []),
2132            equal_sets( [NodeA, NodeB], nodes(hidden) ),
2133            [ Node ] = rpc:call( NodeA, erlang, nodes, [hidden] ),
2134            [ Node ] = rpc:call( NodeB, erlang, nodes, [hidden] ),
2135
2136            %% Connect the nodes
2137            pong = rpc:call( NodeA, net_adm, ping, [NodeB] ),
2138
2139            %% Verify the cluster
2140            NodeACookie = rpc:call( NodeA, erlang, get_cookie, []),
2141            NodeBCookie = rpc:call( NodeB, erlang, get_cookie, []),
2142            equal_sets( [NodeA, NodeB], nodes(hidden) ),
2143            equal_sets( [Node, NodeB],
2144                        rpc:call( NodeA, erlang, nodes, [hidden] )),
2145            equal_sets( [Node, NodeA],
2146                        rpc:call( NodeB, erlang, nodes, [hidden] ))
2147
2148        after
2149            _ = stop_node(NodeB)
2150        end
2151    after
2152        _ = stop_node(NodeA)
2153    end,
2154    [] = nodes(hidden),
2155    ok.
2156
2157connection_cookie(Config) when is_list(Config) ->
2158    test_server:timetrap({minutes, 1}),
2159    Node = node(),
2160    true = Node =/= nonode@nohost,
2161    [] = nodes(),
2162    NodeL = atom_to_list(Node),
2163    BaseName = atom_to_list(?FUNCTION_NAME),
2164
2165    %% Start node A with dedicated connection cookie
2166    NodeAName = BaseName++"_nodeA",
2167    NodeA = full_node_name(NodeAName),
2168    NodeACookieL = BaseName++"_cookieA",
2169    NodeACookie = list_to_atom(NodeACookieL),
2170    true = NodeACookie =/= erlang:get_cookie(),
2171    ConnectionCookieL = BaseName++"_connectionCookie",
2172    ConnectionCookie = list_to_atom(ConnectionCookieL),
2173    true = erlang:set_cookie( NodeA, ConnectionCookie ),
2174    { ok, NodeA } =
2175        start_node(
2176          "", NodeAName,
2177          "-setcookie "++NodeACookieL++" "
2178          "-setcookie "++NodeL++" "++ConnectionCookieL ),
2179
2180    try
2181
2182        %% Verify the cluster
2183        [ NodeA ] = nodes(),
2184        [ Node ] = rpc:call( NodeA, erlang, nodes, [] ),
2185        NodeACookie = rpc:call( NodeA, erlang, get_cookie, []),
2186        ConnectionCookie = rpc:call( NodeA, auth, get_cookie, [Node]),
2187        ConnectionCookie = erlang:get_cookie( NodeA )
2188
2189    after
2190        _ = stop_node(NodeA)
2191    end,
2192    [] = nodes(),
2193    ok.
2194
2195dyn_differing_cookies(Config) when is_list(Config) ->
2196    test_server:timetrap({minutes, 1}),
2197    MotherNode = node(),
2198    true = MotherNode =/= nonode@nohost,
2199    [] = nodes(hidden),
2200    MotherNodeL = atom_to_list(MotherNode),
2201    BaseName = atom_to_list(?FUNCTION_NAME),
2202    MotherNodeCookie = erlang:get_cookie(),
2203    MotherNodeCookieL = atom_to_list(MotherNodeCookie),
2204    register(?FUNCTION_NAME, self()),
2205
2206    %% Start node A with different cookie
2207    %% and cookie configuration of mother node
2208    DynNodeCookieL = BaseName++"_cookieA",
2209    DynNodeCookie = list_to_atom(DynNodeCookieL),
2210    {_NF, Port} =
2211        start_node_unconnected(
2212          "-setcookie "++MotherNodeL++" "++MotherNodeCookieL,
2213          undefined, DynNodeCookie,
2214          ?MODULE, run_remote_test,
2215          [atom_to_list(?FUNCTION_NAME), MotherNodeL] ),
2216
2217    dyn_differing_cookies(MotherNode, MotherNodeCookie, DynNodeCookie, Port).
2218
2219dyn_differing_cookies(MotherNode, MotherNodeCookie, DynNodeCookie, Port) ->
2220    receive
2221        { MotherNode, MotherNodeCookie, DynNodeCookie, DynNode } ->
2222            [ DynNode ] = nodes(hidden),
2223            [ MotherNode ] = rpc:call( DynNode, erlang, nodes, [hidden] ),
2224            DynNodeCookie = rpc:call( DynNode, erlang, get_cookie, [] ),
2225            MotherNodeCookie =
2226                rpc:call( DynNode, erlang, get_cookie, [MotherNode] ),
2227            {?FUNCTION_NAME, DynNode} !
2228                {MotherNode, MotherNodeCookie, DynNode},
2229
2230            0 = wait_for_port_exit(Port),
2231
2232            [] = nodes(hidden),
2233            ok;
2234        {Port, {data, Data}} ->
2235            io:format("~p: ~s", [Port, Data]),
2236            dyn_differing_cookies(
2237              MotherNode, MotherNodeCookie, DynNodeCookie, Port);
2238        Other ->
2239            error({unexpected, Other})
2240    end.
2241
2242dyn_differing_cookies(MotherNode, _Args) ->
2243    nonode@nohost = node(),
2244    [] = nodes(hidden),
2245    true = net_kernel:connect_node( MotherNode ),
2246    [ MotherNode ] = nodes(hidden),
2247    DynNode = node(),
2248    [ DynNode ] = rpc:call( MotherNode, erlang, nodes, [hidden] ),
2249    MotherNodeCookie = erlang:get_cookie( MotherNode ),
2250    MotherNodeCookie = rpc:call( MotherNode, erlang, get_cookie, [] ),
2251    %% Here we get the mother node's default cookie
2252    MotherNodeCookie = rpc:call( MotherNode, erlang, get_cookie, [DynNode] ),
2253    DynNodeCookie = erlang:get_cookie(),
2254    register( ?FUNCTION_NAME, self() ),
2255    {?FUNCTION_NAME, MotherNode} !
2256        {MotherNode, MotherNodeCookie, DynNodeCookie, DynNode},
2257    receive
2258        { MotherNode, MotherNodeCookie, DynNode } ->
2259            true = disconnect_node( MotherNode ),
2260            [] = nodes(hidden),
2261            ok;
2262        Other ->
2263            error({unexpected, Other})
2264    end.
2265
2266
2267%% Misc. functions
2268
2269equal_sets(A, B) ->
2270    S = lists:sort(A),
2271    case lists:sort(B) of
2272        S ->
2273            ok;
2274        _ ->
2275            erlang:error({not_equal_sets, A, B})
2276    end.
2277
2278run_dist_configs(Func, Config) ->
2279    GetOptProlog = "-proto_dist gen_tcp -gen_tcp_dist_output_loop "
2280        ++ atom_to_list(?MODULE) ++ " ",
2281    GenTcpDistTest = case get_gen_tcp_dist_test_type() of
2282                         default ->
2283                             {"gen_tcp_dist", "-proto_dist gen_tcp"};
2284                         size ->
2285                             {"gen_tcp_dist (get_size)",
2286                              GetOptProlog ++ "dist_cntrlr_output_test_size"}
2287                     end,
2288    lists:map(fun ({DCfgName, DCfg}) ->
2289                      io:format("~n~n=== Running ~s configuration ===~n~n",
2290                                [DCfgName]),
2291                      Func(DCfg, Config)
2292              end,
2293              [{"default", ""}, GenTcpDistTest]).
2294
2295start_gen_tcp_dist_test_type_server() ->
2296    Me = self(),
2297    Go = make_ref(),
2298    io:format("STARTING: gen_tcp_dist_test_type_server~n",[]),
2299    {P, M} = spawn_monitor(fun () ->
2300                                   register(gen_tcp_dist_test_type_server, self()),
2301                                   Me ! Go,
2302                                   gen_tcp_dist_test_type_server()
2303                           end),
2304    receive
2305        Go ->
2306            ok;
2307        {'DOWN', M, process, P, _} ->
2308            start_gen_tcp_dist_test_type_server()
2309    end.
2310
2311kill_gen_tcp_dist_test_type_server() ->
2312    case whereis(gen_tcp_dist_test_type_server) of
2313        undefined ->
2314            ok;
2315        Pid ->
2316            exit(Pid,kill),
2317            %% Sync death, before continuing...
2318            false = erlang:is_process_alive(Pid)
2319    end.
2320
2321gen_tcp_dist_test_type_server() ->
2322    Type = case abs(erlang:monotonic_time(second)) rem 2 of
2323               0 -> default;
2324               1 -> size
2325           end,
2326    gen_tcp_dist_test_type_server(Type).
2327
2328gen_tcp_dist_test_type_server(Type) ->
2329    receive
2330        {From, Ref} ->
2331            From ! {Ref, Type},
2332            NewType = case Type of
2333                          default -> size;
2334                          size -> default
2335                      end,
2336            gen_tcp_dist_test_type_server(NewType)
2337    end.
2338
2339get_gen_tcp_dist_test_type() ->
2340    Ref = make_ref(),
2341    try
2342        gen_tcp_dist_test_type_server ! {self(), Ref},
2343        receive
2344            {Ref, Type} ->
2345                Type
2346        end
2347    catch
2348        error:badarg ->
2349            start_gen_tcp_dist_test_type_server(),
2350            get_gen_tcp_dist_test_type()
2351    end.
2352
2353dist_cntrlr_output_test_size(DHandle, Socket) ->
2354    false = erlang:dist_ctrl_get_opt(DHandle, get_size),
2355    false = erlang:dist_ctrl_set_opt(DHandle, get_size, true),
2356    true = erlang:dist_ctrl_get_opt(DHandle, get_size),
2357    true = erlang:dist_ctrl_set_opt(DHandle, get_size, false),
2358    false = erlang:dist_ctrl_get_opt(DHandle, get_size),
2359    false = erlang:dist_ctrl_set_opt(DHandle, get_size, true),
2360    true = erlang:dist_ctrl_get_opt(DHandle, get_size),
2361    dist_cntrlr_output_loop_size(DHandle, Socket).
2362
2363dist_cntrlr_output_loop_size(DHandle, Socket) ->
2364    receive
2365        dist_data ->
2366            %% Outgoing data from this node...
2367            dist_cntrlr_send_data_size(DHandle, Socket);
2368        _ ->
2369            ok %% Drop garbage message...
2370    end,
2371    dist_cntrlr_output_loop_size(DHandle, Socket).
2372
2373dist_cntrlr_send_data_size(DHandle, Socket) ->
2374    case erlang:dist_ctrl_get_data(DHandle) of
2375        none ->
2376            erlang:dist_ctrl_get_data_notification(DHandle);
2377        {Size, Data} ->
2378            ok = ensure_iovec(Data),
2379            Size = erlang:iolist_size(Data),
2380            ok = gen_tcp:send(Socket, Data),
2381            dist_cntrlr_send_data_size(DHandle, Socket)
2382    end.
2383
2384ensure_iovec([]) ->
2385    ok;
2386ensure_iovec([X|Y]) when is_binary(X) ->
2387    ensure_iovec(Y).
2388
2389monitor_node_state() ->
2390    erts_debug:set_internal_state(available_internal_state, true),
2391    MonitoringNodes = erts_debug:get_internal_state(monitoring_nodes),
2392    erts_debug:set_internal_state(available_internal_state, false),
2393    MonitoringNodes.
2394
2395
2396check_no_nodedown_nodeup(TimeOut) ->
2397    receive
2398	{nodeup, _, _} = Msg -> ct:fail({unexpected_nodeup, Msg});
2399	{nodeup, _} = Msg -> ct:fail({unexpected_nodeup, Msg});
2400	{nodedown, _, _} = Msg -> ct:fail({unexpected_nodedown, Msg});
2401	{nodedown, _} = Msg -> ct:fail({unexpected_nodedown, Msg})
2402    after TimeOut ->
2403	    ok
2404    end.
2405
2406print_my_messages() ->
2407    {messages, Messages} = process_info(self(), messages),
2408    io:format("Messages: ~p~n", [Messages]),
2409    ok.
2410
2411
2412sleep(T) -> receive after T * 1000 -> ok end.
2413
2414start_node(_DCfg, Name, Param, this) ->
2415    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)),
2416    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]);
2417start_node(DCfg, Name, Param, "this") ->
2418    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg,
2419    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [this]}]);
2420start_node(DCfg, Name, Param, Rel) when is_atom(Rel) ->
2421    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg,
2422    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, atom_to_list(Rel)}]}]);
2423start_node(DCfg, Name, Param, Rel) when is_list(Rel) ->
2424    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg,
2425    test_server:start_node(Name, peer, [{args, NewParam}, {erl, [{release, Rel}]}]).
2426
2427start_node(DCfg, Name, Param) ->
2428    NewParam = Param ++ " -pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++ DCfg,
2429    test_server:start_node(Name, slave, [{args, NewParam}]).
2430
2431start_node(DCfg, Name) ->
2432    start_node(DCfg, Name, "").
2433
2434stop_node(Node) ->
2435    test_server:stop_node(Node).
2436
2437get_nodenames(N, T) ->
2438    get_nodenames(N, T, []).
2439
2440get_nodenames(0, _, Acc) ->
2441    Acc;
2442get_nodenames(N, T, Acc) ->
2443    U = erlang:unique_integer([positive]),
2444    get_nodenames(N-1, T, [list_to_atom(atom_to_list(T)
2445					++ "-"
2446					++ ?MODULE_STRING
2447					++ "-"
2448					++ integer_to_list(U)) | Acc]).
2449
2450get_numbered_nodenames(N, T) ->
2451    get_numbered_nodenames(N, T, []).
2452
2453get_numbered_nodenames(0, _, Acc) ->
2454    Acc;
2455get_numbered_nodenames(N, T, Acc) ->
2456    U = erlang:unique_integer([positive]),
2457    NL = [list_to_atom(atom_to_list(T) ++ integer_to_list(N)
2458		       ++ "-"
2459		       ++ ?MODULE_STRING
2460		       ++ "-"
2461		       ++ integer_to_list(U)) | Acc],
2462    get_numbered_nodenames(N-1, T, NL).
2463
2464wait_until(Fun) ->
2465    case Fun() of
2466	true ->
2467	    ok;
2468	_ ->
2469	    receive
2470	    after 100 ->
2471		    wait_until(Fun)
2472	    end
2473    end.
2474
2475repeat(Fun, 0) when is_function(Fun) ->
2476    ok;
2477repeat(Fun, N) when is_function(Fun), is_integer(N), N > 0 ->
2478    Fun(),
2479    repeat(Fun, N-1).
2480
2481no_msgs(Wait) ->
2482    receive after Wait -> no_msgs() end.
2483
2484no_msgs() ->
2485    {messages, []} = process_info(self(), messages).
2486
2487block_emu(Ms) ->
2488    erts_debug:set_internal_state(available_internal_state, true),
2489    Res = erts_debug:set_internal_state(block, Ms),
2490    erts_debug:set_internal_state(available_internal_state, false),
2491    Res.
2492
2493
2494