diff options
Diffstat (limited to 'lib/kernel')
-rw-r--r-- | lib/kernel/doc/src/code.xml | 10 | ||||
-rw-r--r-- | lib/kernel/doc/src/inet.xml | 9 | ||||
-rw-r--r-- | lib/kernel/doc/src/kernel_app.xml | 2 | ||||
-rw-r--r-- | lib/kernel/doc/src/net_kernel.xml | 36 | ||||
-rw-r--r-- | lib/kernel/include/dist_util.hrl | 8 | ||||
-rw-r--r-- | lib/kernel/src/code_server.erl | 26 | ||||
-rw-r--r-- | lib/kernel/src/dist_util.erl | 71 | ||||
-rw-r--r-- | lib/kernel/src/erl_epmd.erl | 6 | ||||
-rw-r--r-- | lib/kernel/src/inet.erl | 1 | ||||
-rw-r--r-- | lib/kernel/src/inet6_tcp_dist.erl | 7 | ||||
-rw-r--r-- | lib/kernel/src/inet_tcp_dist.erl | 29 | ||||
-rw-r--r-- | lib/kernel/src/net_kernel.erl | 128 | ||||
-rw-r--r-- | lib/kernel/test/erl_distribution_SUITE.erl | 163 | ||||
-rw-r--r-- | lib/kernel/test/gen_sctp_SUITE.erl | 148 | ||||
-rw-r--r-- | lib/kernel/test/init_SUITE.erl | 61 |
15 files changed, 592 insertions, 113 deletions
diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index d3611d6a03..21f8a2d54a 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -651,6 +651,11 @@ ok = code:finish_loading(Prepared), <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old. If some processes still linger in the old code, these processes are killed before the code is removed.</p> + <note><p>As of ERTS version 9.0, a process is only considered + to be lingering in the code if it has direct references to the code. + For more information see documentation of + <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>, + which is used in order to determine this.</p></note> <p>Returns <c>true</c> if successful and any process is needed to be killed, otherwise <c>false</c>.</p> </desc> @@ -661,6 +666,11 @@ ok = code:finish_loading(Prepared), <desc> <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old, but only if no processes linger in it.</p> + <note><p>As of ERTS version 9.0, a process is only considered + to be lingering in the code if it has direct references to the code. + For more information see documentation of + <seealso marker="erts:erlang#check_process_code/3"><c>erlang:check_process_code/3</c></seealso>, + which is used in order to determine this.</p></note> <p>Returns <c>false</c> if the module cannot be purged because of processes lingering in old code, otherwise <c>true</c>.</p> </desc> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index c0dce2f50c..8cc21bf3e2 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -151,6 +151,12 @@ fe80::204:acff:fe17:bf38 <name name="socket_address"/> </datatype> <datatype> + <name name="socket_getopt"/> + </datatype> + <datatype> + <name name="socket_setopt"/> + </datatype> + <datatype> <name name="returned_non_ip_address"/> <desc> <p> @@ -327,8 +333,6 @@ fe80::204:acff:fe17:bf38 <func> <name name="getopts" arity="2"/> <fsummary>Get one or more options for a socket.</fsummary> - <type name="socket_getopt"/> - <type name="socket_setopt"/> <desc> <p>Gets one or more options for a socket. For a list of available options, see @@ -580,7 +584,6 @@ get_tcpi_sacked(Sock) -> <func> <name name="setopts" arity="2"/> <fsummary>Set one or more options for a socket.</fsummary> - <type name="socket_setopt"/> <desc> <p>Sets one or more options for a socket.</p> <p>The following options are available:</p> diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 9e6fb60bb7..dc0291babe 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -215,12 +215,14 @@ <p>Defines the <c>First..Last</c> port range for the listener socket of a distributed Erlang node.</p> </item> + <marker id="inet_dist_listen_options"></marker> <tag><c>{inet_dist_listen_options, Opts}</c></tag> <item> <p>Defines a list of extra socket options to be used when opening the listening socket for a distributed Erlang node. See <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>.</p> </item> + <marker id="inet_dist_connect_options"></marker> <tag><c>{inet_dist_connect_options, Opts}</c></tag> <item> <p>Defines a list of extra socket options to be used when connecting to diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index f48a534d4f..3c1b3d5190 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -116,6 +116,21 @@ $ <input>erl -sname foobar</input></pre> </func> <func> + <name name="getopts" arity="2"/> + <fsummary>Get distribution socket options.</fsummary> + <desc> + <p>Get one or more options for the distribution socket + connected to <c><anno>Node</anno></c>.</p> + <p>If <c><anno>Node</anno></c> is a connected node + the return value is the same as from + <seealso marker="inet#getopts/2"><c>inet:getopts(Sock, Options)</c></seealso> + where <c>Sock</c> is the distribution socket for <c><anno>Node</anno></c>.</p> + <p>Returns <c>ignored</c> if the local node is not alive or + <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not connected.</p> + </desc> + </func> + + <func> <name name="monitor_nodes" arity="1"/> <name name="monitor_nodes" arity="2"/> <fsummary>Subscribe to node status change messages.</fsummary> @@ -289,6 +304,27 @@ $ <input>erl -sname foobar</input></pre> </func> <func> + <name name="setopts" arity="2"/> + <fsummary>Set distribution socket options.</fsummary> + <desc> + <p>Set one or more options for distribution sockets. + Argument <c><anno>Node</anno></c> can be either one node name + or the atom <c>new</c> to affect the distribution sockets of all + future connected nodes.</p> + <p>The return value is the same as from + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso> + or <c>{error, noconnection}</c> if <c><anno>Node</anno></c> is not + a connected node or <c>new</c>.</p> + <p>If <c><anno>Node</anno></c> is <c>new</c> the <c><anno>Options</anno></c> + will then also be added to kernel configration parameters + <seealso marker="kernel:kernel_app#inet_dist_listen_options">inet_dist_listen_options</seealso> + and + <seealso marker="kernel:kernel_app#inet_dist_connect_options">inet_dist_connect_options</seealso>.</p> + <p>Returns <c>ignored</c> if the local node is not alive.</p> + </desc> + </func> + + <func> <name>start([Name]) -> {ok, pid()} | {error, Reason}</name> <name>start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name> <name>start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name> diff --git a/lib/kernel/include/dist_util.hrl b/lib/kernel/include/dist_util.hrl index 43e50d4325..320e916c04 100644 --- a/lib/kernel/include/dist_util.hrl +++ b/lib/kernel/include/dist_util.hrl @@ -63,7 +63,7 @@ f_getll, %% Get low level port or pid. f_address, %% The address of the "socket", %% generated from Socket,Node - %% These two are used in the tick loop, + %% These three are used in the tick loop, %% so they are not fun's to avoid holding old code. mf_tick, %% Takes the socket as parameters and %% sends a tick, this is no fun, it @@ -74,7 +74,11 @@ %% {ok, RecvCnt, SendCnt, SendPend} for %% a given socket. This is a {M,F}, %% returning {error, Reason on failure} - request_type = normal + request_type = normal, + + %% New in kernel-5.1 (OTP 19.1): + mf_setopts, %% netkernel:setopts on active connection + mf_getopts %% netkernel:getopts on active connection }). diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 6174136507..48541ec500 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -135,10 +135,14 @@ split_paths([], _S, Path, Paths) -> -spec call(term()) -> term(). call(Req) -> + Ref = erlang:monitor(process, ?MODULE), ?MODULE ! {code_call, self(), Req}, receive {?MODULE, Reply} -> - Reply + erlang:demonitor(Ref,[flush]), + Reply; + {'DOWN',Ref,process,_,_} -> + exit({'DOWN',code_server,Req}) end. reply(Pid, Res) -> @@ -933,14 +937,20 @@ del_ebin(Dir) -> filename:join(del_ebin_1(filename:split(Dir))). del_ebin_1([Parent,App,"ebin"]) -> - Ext = archive_extension(), - case filename:basename(Parent, Ext) of - Parent -> - %% Plain directory. + case filename:basename(Parent) of + [] -> + %% Parent is the root directory [Parent,App]; - Archive -> - %% Archive. - [Archive] + _ -> + Ext = archive_extension(), + case filename:basename(Parent, Ext) of + Parent -> + %% Plain directory. + [Parent,App]; + Archive -> + %% Archive. + [Archive] + end end; del_ebin_1([H|T]) -> [H|del_ebin_1(T)]; diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index 47d0c1b861..8d2fc4d4b7 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -143,7 +143,11 @@ handshake_other_started(#hs_data{request_type=ReqType}=HSData0) -> ChallengeB = recv_challenge_reply(HSData, ChallengeA, MyCookie), send_challenge_ack(HSData, gen_digest(ChallengeB, HisCookie)), ?debug({dist_util, self(), accept_connection, Node}), - connection(HSData). + connection(HSData); + +handshake_other_started(OldHsData) when element(1,OldHsData) =:= hs_data -> + handshake_other_started(convert_old_hsdata(OldHsData)). + %% %% check if connecting node is allowed to connect @@ -330,7 +334,20 @@ handshake_we_started(#hs_data{request_type=ReqType, gen_digest(ChallengeA,HisCookie)), reset_timer(NewHSData#hs_data.timer), recv_challenge_ack(NewHSData, MyChallenge, MyCookie), - connection(NewHSData). + connection(NewHSData); + +handshake_we_started(OldHsData) when element(1,OldHsData) =:= hs_data -> + handshake_we_started(convert_old_hsdata(OldHsData)). + +convert_old_hsdata({hs_data, KP, ON, TN, S, T, TF, A, OV, OF, OS, FS, FR, + FS_PRE, FS_POST, FG, FA, MFT, MFG, RT}) -> + #hs_data{ + kernel_pid = KP, other_node = ON, this_node = TN, socket = S, timer = T, + this_flags = TF, allowed = A, other_version = OV, other_flags = OF, + other_started = OS, f_send = FS, f_recv = FR, f_setopts_pre_nodeup = FS_PRE, + f_setopts_post_nodeup = FS_POST, f_getll = FG, f_address = FA, + mf_tick = MFT, mf_getstat = MFG, request_type = RT}. + %% -------------------------------------------------------------- %% The connection has been established. @@ -350,15 +367,15 @@ connection(#hs_data{other_node = Node, mark_nodeup(HSData,Address), case FPostNodeup(Socket) of ok -> - con_loop(HSData#hs_data.kernel_pid, - Node, - Socket, - Address, - HSData#hs_data.this_node, - PType, - #tick{}, - HSData#hs_data.mf_tick, - HSData#hs_data.mf_getstat); + con_loop({HSData#hs_data.kernel_pid, + Node, + Socket, + PType, + HSData#hs_data.mf_tick, + HSData#hs_data.mf_getstat, + HSData#hs_data.mf_setopts, + HSData#hs_data.mf_getopts}, + #tick{}); _ -> ?shutdown2(Node, connection_setup_failed) end; @@ -454,8 +471,8 @@ mark_nodeup(#hs_data{kernel_pid = Kernel, ?shutdown(Node) end. -con_loop(Kernel, Node, Socket, TcpAddress, - MyNode, Type, Tick, MFTick, MFGetstat) -> +con_loop({Kernel, Node, Socket, Type, MFTick, MFGetstat, MFSetOpts, MFGetOpts}=ConData, + Tick) -> receive {tcp_closed, Socket} -> ?shutdown2(Node, connection_closed); @@ -468,15 +485,12 @@ con_loop(Kernel, Node, Socket, TcpAddress, _ -> ignore_it end, - con_loop(Kernel, Node, Socket, TcpAddress, MyNode, Type, - Tick, MFTick, MFGetstat); + con_loop(ConData, Tick); {Kernel, tick} -> case send_tick(Socket, Tick, Type, MFTick, MFGetstat) of {ok, NewTick} -> - con_loop(Kernel, Node, Socket, TcpAddress, - MyNode, Type, NewTick, MFTick, - MFGetstat); + con_loop(ConData, NewTick); {error, not_responding} -> error_msg("** Node ~p not responding **~n" "** Removing (timedout) connection **~n", @@ -489,13 +503,24 @@ con_loop(Kernel, Node, Socket, TcpAddress, case MFGetstat(Socket) of {ok, Read, Write, _} -> From ! {self(), get_status, {ok, Read, Write}}, - con_loop(Kernel, Node, Socket, TcpAddress, - MyNode, - Type, Tick, - MFTick, MFGetstat); + con_loop(ConData, Tick); _ -> ?shutdown2(Node, get_status_failed) - end + end; + {From, Ref, {setopts, Opts}} -> + Ret = case MFSetOpts of + undefined -> {error, enotsup}; + _ -> MFSetOpts(Socket, Opts) + end, + From ! {Ref, Ret}, + con_loop(ConData, Tick); + {From, Ref, {getopts, Opts}} -> + Ret = case MFGetOpts of + undefined -> {error, enotsup}; + _ -> MFGetOpts(Socket, Opts) + end, + From ! {Ref, Ret}, + con_loop(ConData, Tick) end. diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index f8ef4a475d..7bc9e2ede3 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -103,6 +103,10 @@ names(EpmdAddr) -> register_node(Name, PortNo) -> register_node(Name, PortNo, inet). +register_node(Name, PortNo, inet_tcp) -> + register_node(Name, PortNo, inet); +register_node(Name, PortNo, inet6_tcp) -> + register_node(Name, PortNo, inet6); register_node(Name, PortNo, Family) -> gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity). @@ -403,8 +407,6 @@ select_best_version(L1, _H1, _L2, H2) when L1 > H2 -> 0; select_best_version(_L1, H1, L2, _H2) when L2 > H1 -> 0; -select_best_version(_L1, H1, L2, _H2) when L2 > H1 -> - 0; select_best_version(_L1, H1, _L2, H2) -> erlang:min(H1, H2). diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index a91a6ed517..75dd800c6b 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -75,6 +75,7 @@ -export_type([address_family/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, local_address/0, socket_address/0, returned_non_ip_address/0, + socket_setopt/0, socket_getopt/0, posix/0, socket/0, stat_option/0]). %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). diff --git a/lib/kernel/src/inet6_tcp_dist.erl b/lib/kernel/src/inet6_tcp_dist.erl index 3aa61973af..9b6c2745d5 100644 --- a/lib/kernel/src/inet6_tcp_dist.erl +++ b/lib/kernel/src/inet6_tcp_dist.erl @@ -24,6 +24,7 @@ -export([listen/1, accept/1, accept_connection/5, setup/5, close/1, select/1, is_node_name/1]). +-export([setopts/2, getopts/2]). %% ------------------------------------------------------------ %% Select this protocol based on node name @@ -72,3 +73,9 @@ close(Socket) -> is_node_name(Node) when is_atom(Node) -> inet_tcp_dist:is_node_name(Node). + +setopts(S, Opts) -> + inet_tcp_dist:setopts(S, Opts). + +getopts(S, Opts) -> + inet_tcp_dist:getopts(S, Opts). diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl index f91d7ef7c3..3084bd599a 100644 --- a/lib/kernel/src/inet_tcp_dist.erl +++ b/lib/kernel/src/inet_tcp_dist.erl @@ -24,13 +24,16 @@ -export([listen/1, accept/1, accept_connection/5, setup/5, close/1, select/1, is_node_name/1]). +%% Optional +-export([setopts/2, getopts/2]). + %% Generalized dist API -export([gen_listen/2, gen_accept/2, gen_accept_connection/6, gen_setup/6, gen_select/2]). %% internal exports --export([accept_loop/3,do_accept/7,do_setup/7,getstat/1]). +-export([accept_loop/3,do_accept/7,do_setup/7,getstat/1,tick/2]). -import(error_logger,[error_msg/2]). @@ -74,7 +77,7 @@ gen_listen(Driver, Name) -> TcpAddress = get_tcp_address(Driver, Socket), {_,Port} = TcpAddress#net_address.address, ErlEpmd = net_kernel:epmd_module(), - case ErlEpmd:register_node(Name, Port) of + case ErlEpmd:register_node(Name, Port, Driver) of {ok, Creation} -> {ok, {Socket, TcpAddress, Creation}}; Error -> @@ -215,8 +218,10 @@ do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) -> inet:getll(S) end, f_address = fun(S, Node) -> get_remote_id(Driver, S, Node) end, - mf_tick = fun(S) -> tick(Driver, S) end, - mf_getstat = fun ?MODULE:getstat/1 + mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end, + mf_getstat = fun ?MODULE:getstat/1, + mf_setopts = fun ?MODULE:setopts/2, + mf_getopts = fun ?MODULE:getopts/2 }, dist_util:handshake_other_started(HSData); {false,IP} -> @@ -320,6 +325,7 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> {packet, 4}, nodelay()]) end, + f_getll = fun inet:getll/1, f_address = fun(_,_) -> @@ -329,9 +335,11 @@ do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) -> protocol = tcp, family = AddressFamily} end, - mf_tick = fun(S) -> tick(Driver, S) end, + mf_tick = fun(S) -> ?MODULE:tick(Driver, S) end, mf_getstat = fun ?MODULE:getstat/1, - request_type = Type + request_type = Type, + mf_setopts = fun ?MODULE:setopts/2, + mf_getopts = fun ?MODULE:getopts/2 }, dist_util:handshake_we_started(HSData); _ -> @@ -492,3 +500,12 @@ split_stat([], R, W, P) -> {ok, R, W, P}. +setopts(S, Opts) -> + case [Opt || {K,_}=Opt <- Opts, + K =:= active orelse K =:= deliver orelse K =:= packet] of + [] -> inet:setopts(S,Opts); + Opts1 -> {error, {badopts,Opts1}} + end. + +getopts(S, Opts) -> + inet:getopts(S, Opts). diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index ac19f4935b..0c679e7349 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -59,6 +59,8 @@ connect_node/1, monitor_nodes/1, monitor_nodes/2, + setopts/2, + getopts/2, start/1, stop/0]). @@ -111,7 +113,7 @@ }). -record(listen, { - listen, %% listen pid + listen, %% listen socket accept, %% accepting pid address, %% #net_address module %% proto module @@ -384,7 +386,7 @@ init({Name, LongOrShortNames, TickT, CleanHalt}) -> connections = ets:new(sys_dist,[named_table, protected, - {keypos, 2}]), + {keypos, #connection.node}]), listen = Listeners, allowed = [], verbose = 0 @@ -554,6 +556,38 @@ handle_call({new_ticktime,_T,_TP}, #state{tick = #tick_change{time = T}} = State) -> async_reply({reply, {ongoing_change_to, T}, State}, From); +handle_call({setopts, new, Opts}, From, State) -> + Ret = setopts_new(Opts, State), + async_reply({reply, Ret, State}, From); + +handle_call({setopts, Node, Opts}, From, State) -> + Return = + case ets:lookup(sys_dist, Node) of + [Conn] when Conn#connection.state =:= up -> + case call_owner(Conn#connection.owner, {setopts, Opts}) of + {ok, Ret} -> Ret; + _ -> {error, noconnection} + end; + + _ -> + {error, noconnection} + end, + async_reply({reply, Return, State}, From); + +handle_call({getopts, Node, Opts}, From, State) -> + Return = + case ets:lookup(sys_dist, Node) of + [Conn] when Conn#connection.state =:= up -> + case call_owner(Conn#connection.owner, {getopts, Opts}) of + {ok, Ret} -> Ret; + _ -> {error, noconnection} + end; + + _ -> + {error, noconnection} + end, + async_reply({reply, Return, State}, From); + handle_call(_Msg, _From, State) -> {noreply, State}. @@ -1608,3 +1642,93 @@ async_gen_server_reply(From, Msg) -> {'EXIT', _} -> ok end. + +call_owner(Owner, Msg) -> + Mref = monitor(process, Owner), + Owner ! {self(), Mref, Msg}, + receive + {Mref, Reply} -> + erlang:demonitor(Mref, [flush]), + {ok, Reply}; + {'DOWN', Mref, _, _, _} -> + error + end. + + +-spec setopts(Node, Options) -> ok | {error, Reason} | ignored when + Node :: node() | new, + Options :: [inet:socket_setopt()], + Reason :: inet:posix() | noconnection. + +setopts(Node, Opts) when is_atom(Node), is_list(Opts) -> + request({setopts, Node, Opts}). + +setopts_new(Opts, State) -> + %% First try setopts on listening socket(s) + %% Bail out on failure. + %% If successful, we are pretty sure Opts are ok + %% and we continue with config params and pending connections. + case setopts_on_listen(Opts, State#state.listen) of + ok -> + setopts_new_1(Opts); + Fail -> Fail + end. + +setopts_on_listen(_, []) -> ok; +setopts_on_listen(Opts, [#listen {listen = LSocket, module = Mod} | T]) -> + try Mod:setopts(LSocket, Opts) of + ok -> + setopts_on_listen(Opts, T); + Fail -> Fail + catch + error:undef -> {error, enotsup} + end. + +setopts_new_1(Opts) -> + ConnectOpts = case application:get_env(kernel, inet_dist_connect_options) of + {ok, CO} -> CO; + _ -> [] + end, + application:set_env(kernel, inet_dist_connect_options, + merge_opts(Opts,ConnectOpts)), + ListenOpts = case application:get_env(kernel, inet_dist_listen_options) of + {ok, LO} -> LO; + _ -> [] + end, + application:set_env(kernel, inet_dist_listen_options, + merge_opts(Opts, ListenOpts)), + case lists:keyfind(nodelay, 1, Opts) of + {nodelay, ND} when is_boolean(ND) -> + application:set_env(kernel, dist_nodelay, ND); + _ -> ignore + end, + + %% Update any pending connections + PendingConns = ets:select(sys_dist, [{'_', + [{'=/=',{element,#connection.state,'$_'},up}], + ['$_']}]), + lists:foreach(fun(#connection{state = pending, owner = Owner}) -> + call_owner(Owner, {setopts, Opts}); + (#connection{state = up_pending, pending_owner = Owner}) -> + call_owner(Owner, {setopts, Opts}); + (_) -> ignore + end, PendingConns), + ok. + +merge_opts([], B) -> + B; +merge_opts([H|T], B0) -> + {Key, _} = H, + B1 = lists:filter(fun({K,_}) -> K =/= Key end, B0), + merge_opts(T, [H | B1]). + +-spec getopts(Node, Options) -> + {'ok', OptionValues} | {'error', Reason} | ignored when + Node :: node(), + Options :: [inet:socket_getopt()], + OptionValues :: [inet:socket_setopt()], + Reason :: inet:posix() | noconnection. + +getopts(Node, Opts) when is_atom(Node), is_list(Opts) -> + request({getopts, Node, Opts}). + diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl index eb58e92224..e43be77428 100644 --- a/lib/kernel/test/erl_distribution_SUITE.erl +++ b/lib/kernel/test/erl_distribution_SUITE.erl @@ -25,6 +25,7 @@ init_per_group/2,end_per_group/2]). -export([tick/1, tick_change/1, illegal_nodenames/1, hidden_node/1, + setopts/1, table_waste/1, net_setuptime/1, inet_dist_options_options/1, @@ -42,6 +43,8 @@ -export([get_socket_priorities/0, tick_cli_test/1, tick_cli_test1/1, tick_serv_test/2, tick_serv_test1/1, + run_remote_test/1, + setopts_do/2, keep_conn/1, time_ping/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -66,6 +69,7 @@ suite() -> all() -> [tick, tick_change, illegal_nodenames, hidden_node, + setopts, table_waste, net_setuptime, inet_dist_options_options, {group, monitor_nodes}]. @@ -282,6 +286,165 @@ tick_cli_test1(Node) -> end end. +setopts(Config) when is_list(Config) -> + register(setopts_regname, self()), + [N1,N2,N3,N4] = get_nodenames(4, setopts), + + {_N1F,Port1} = start_node_unconnected(N1, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), "1", "ping"]), + 0 = wait_for_port_exit(Port1), + + {_N2F,Port2} = start_node_unconnected(N2, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), "2", "ping"]), + 0 = wait_for_port_exit(Port2), + + {ok, LSock} = gen_tcp:listen(0, [{packet,2}, {active,false}]), + {ok, LTcpPort} = inet:port(LSock), + + {N3F,Port3} = start_node_unconnected(N3, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), + "1", integer_to_list(LTcpPort)]), + wait_and_connect(LSock, N3F, Port3), + 0 = wait_for_port_exit(Port3), + + {N4F,Port4} = start_node_unconnected(N4, ?MODULE, run_remote_test, + ["setopts_do", atom_to_list(node()), + "2", integer_to_list(LTcpPort)]), + wait_and_connect(LSock, N4F, Port4), + 0 = wait_for_port_exit(Port4), + + ok. + +wait_and_connect(LSock, NodeName, NodePort) -> + {ok, Sock} = gen_tcp:accept(LSock), + {ok, "Connect please"} = gen_tcp:recv(Sock, 0), + flush_from_port(NodePort), + pong = net_adm:ping(NodeName), + gen_tcp:send(Sock, "Connect done"), + gen_tcp:close(Sock). + + +flush_from_port(Port) -> + flush_from_port(Port, 10). + +flush_from_port(Port, Timeout) -> + receive + {Port,{data,String}} -> + io:format("~p: ~s\n", [Port, String]), + flush_from_port(Port, Timeout) + after Timeout -> + timeout + end. + +wait_for_port_exit(Port) -> + case (receive M -> M end) of + {Port,{exit_status,Status}} -> + Status; + {Port,{data,String}} -> + io:format("~p: ~s\n", [Port, String]), + wait_for_port_exit(Port) + end. + +run_remote_test([FuncStr, TestNodeStr | Args]) -> + Status = try + io:format("Node ~p started~n", [node()]), + TestNode = list_to_atom(TestNodeStr), + io:format("Node ~p spawning function ~p~n", [node(), FuncStr]), + {Pid,Ref} = spawn_monitor(?MODULE, list_to_atom(FuncStr), [TestNode, Args]), + io:format("Node ~p waiting for function ~p~n", [node(), FuncStr]), + receive + {'DOWN', Ref, process, Pid, normal} -> + 0; + Other -> + io:format("Node ~p got unexpected msg: ~p\n",[node(), Other]), + 1 + end + catch + C:E -> + io:format("Node ~p got EXCEPTION ~p:~p\nat ~p\n", + [node(), C, E, erlang:get_stacktrace()]), + 2 + end, + io:format("Node ~p doing halt(~p).\n",[node(), Status]), + erlang:halt(Status). + +% Do the actual test on the remote node +setopts_do(TestNode, [OptNr, ConnectData]) -> + [] = nodes(), + {Opt, Val} = opt_from_nr(OptNr), + ok = net_kernel:setopts(new, [{Opt, Val}]), + + [] = nodes(), + {error, noconnection} = net_kernel:getopts(TestNode, [Opt]), + + case ConnectData of + "ping" -> % We connect + net_adm:ping(TestNode); + TcpPort -> % Other connect + {ok, Sock} = gen_tcp:connect("localhost", list_to_integer(TcpPort), + [{active,false},{packet,2}]), + ok = gen_tcp:send(Sock, "Connect please"), + {ok, "Connect done"} = gen_tcp:recv(Sock, 0), + gen_tcp:close(Sock) + end, + [TestNode] = nodes(), + {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), + {error, noconnection} = net_kernel:getopts('pixie@fairyland', [Opt]), + + NewVal = change_val(Val), + ok = net_kernel:setopts(TestNode, [{Opt, NewVal}]), + {ok, [{Opt,NewVal}]} = net_kernel:getopts(TestNode, [Opt]), + + ok = net_kernel:setopts(TestNode, [{Opt, Val}]), + {ok, [{Opt,Val}]} = net_kernel:getopts(TestNode, [Opt]), + + ok. + +opt_from_nr("1") -> {nodelay, true}; +opt_from_nr("2") -> {nodelay, false}. + +change_val(true) -> false; +change_val(false) -> true. + +start_node_unconnected(Name, Mod, Func, Args) -> + FullName = full_node_name(Name), + CmdLine = mk_node_cmdline(Name,Mod,Func,Args), + io:format("Starting node ~p: ~s~n", [FullName, CmdLine]), + case open_port({spawn, CmdLine}, [exit_status]) of + Port when is_port(Port) -> + {FullName, Port}; + Error -> + exit({failed_to_start_node, FullName, Error}) + end. + +full_node_name(PreName) -> + HostSuffix = lists:dropwhile(fun ($@) -> false; (_) -> true end, + atom_to_list(node())), + list_to_atom(atom_to_list(PreName) ++ HostSuffix). + +mk_node_cmdline(Name,Mod,Func,Args) -> + Static = "-noinput", + Pa = filename:dirname(code:which(?MODULE)), + Prog = case catch init:get_argument(progname) of + {ok,[[P]]} -> P; + _ -> exit(no_progname_argument_found) + end, + NameSw = case net_kernel:longnames() of + false -> "-sname "; + true -> "-name "; + _ -> exit(not_distributed_node) + end, + {ok, Pwd} = file:get_cwd(), + NameStr = atom_to_list(Name), + Prog ++ " " + ++ Static ++ " " + ++ NameSw ++ " " ++ NameStr + ++ " -pa " ++ Pa + ++ " -env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ NameStr + ++ " -setcookie " ++ atom_to_list(erlang:get_cookie()) + ++ " -run " ++ atom_to_list(Mod) ++ " " ++ atom_to_list(Func) + ++ " " ++ string:join(Args, " "). + %% OTP-4255. tick_change(Config) when is_list(Config) -> diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index f836b2aa94..620ab235a0 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -117,7 +117,11 @@ xfer_min(Config) when is_list(Config) -> Stream = 0, Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>, Loopback = {127,0,0,1}, + StatOpts = + [recv_avg,recv_cnt,recv_max,recv_oct, + send_avg,send_cnt,send_max,send_oct], {ok,Sb} = gen_sctp:open([{type,seqpacket}]), + {ok,SbStat1} = inet:getstat(Sb, StatOpts), {ok,Pb} = inet:port(Sb), ok = gen_sctp:listen(Sb, true), @@ -212,6 +216,8 @@ xfer_min(Config) when is_list(Config) -> assoc_id=SbAssocId}} = recv_event(log_ok(gen_sctp:recv(Sb, infinity))), ok = gen_sctp:close(Sa), + {ok,SbStat2} = inet:getstat(Sb, StatOpts), + [] = filter_stat_eq(SbStat1, SbStat2), ok = gen_sctp:close(Sb), receive @@ -220,6 +226,18 @@ xfer_min(Config) when is_list(Config) -> end, ok. +filter_stat_eq([], []) -> + []; +filter_stat_eq([{Tag,Val1}=Stat|SbStat1], [{Tag,Val2}|SbStat2]) -> + if + Val1 == Val2 -> + [Stat|filter_stat_eq(SbStat1, SbStat2)]; + true -> + filter_stat_eq(SbStat1, SbStat2) + end. + + + %% Minimal data transfer in active mode. xfer_active(Config) when is_list(Config) -> Timeout = 2000, @@ -383,26 +401,28 @@ def_sndrcvinfo(Config) when is_list(Config) -> assoc_id=S2AssocId} = S2AssocChange = log_ok(gen_sctp:connect(S2, Loopback, P1, [])), ?LOGVAR(S2AssocChange), - case recv_event(log_ok(gen_sctp:recv(S1))) of - {Loopback,P2, - #sctp_assoc_change{ - state=comm_up, - error=0, - assoc_id=S1AssocId}} -> - ?LOGVAR(S1AssocId); - {Loopback,P2, - #sctp_paddr_change{ - state=addr_confirmed, - error=0, - assoc_id=S1AssocId}} -> - ?LOGVAR(S1AssocId), + S1AssocId = + case recv_event(log_ok(gen_sctp:recv(S1))) of {Loopback,P2, #sctp_assoc_change{ state=comm_up, error=0, - assoc_id=S1AssocId}} = - recv_event(log_ok(gen_sctp:recv(S1))) - end, + assoc_id=AssocId}} -> + AssocId; + {Loopback,P2, + #sctp_paddr_change{ + state=addr_confirmed, + error=0, + assoc_id=AssocId}} -> + {Loopback,P2, + #sctp_assoc_change{ + state=comm_up, + error=0, + assoc_id=AssocId}} = + recv_event(log_ok(gen_sctp:recv(S1))), + AssocId + end, + ?LOGVAR(S1AssocId), #sctp_sndrcvinfo{ ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} = @@ -1055,6 +1075,7 @@ peeloff(Config, SockOpts) when is_list(Config) -> Addr = {127,0,0,1}, Stream = 0, Timeout = 333, + StartTime = timestamp(), S1 = socket_open([{ifaddr,Addr}|SockOpts], Timeout), ?LOGVAR(S1), P1 = socket_call(S1, get_port), @@ -1077,7 +1098,7 @@ peeloff(Config, SockOpts) when is_list(Config) -> state=comm_up, assoc_id=AssocId2}}} -> AssocId2 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, ?LOGVAR(S2Ai), S1Ai = @@ -1087,7 +1108,7 @@ peeloff(Config, SockOpts) when is_list(Config) -> state=comm_up, assoc_id=AssocId1}}} -> AssocId1 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, ?LOGVAR(S1Ai), %% @@ -1095,13 +1116,13 @@ peeloff(Config, SockOpts) when is_list(Config) -> receive {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}), receive {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, %% S3 = socket_peeloff(Socket1, S1Ai, SockOpts, Timeout), @@ -1120,31 +1141,31 @@ peeloff(Config, SockOpts) when is_list(Config) -> receive {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok after Timeout -> - socket_bailout([S1,S2,S3]) + socket_bailout([S1,S2,S3], StartTime) end, socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}), receive {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok after Timeout -> - socket_bailout([S1,S2,S3]) + socket_bailout([S1,S2,S3], StartTime) end, %% inet:i(sctp), - socket_close_verbose(S1), - socket_close_verbose(S2), + socket_close_verbose(S1, StartTime), + socket_close_verbose(S2, StartTime), receive {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} -> match_unless_solaris(S3Ai, S3Ai_X) after Timeout -> - socket_bailout([S3]) + socket_bailout([S3], StartTime) end, receive {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp, assoc_id=S3Ai}}} -> ok after Timeout -> - socket_bailout([S3]) + socket_bailout([S3], StartTime) end, - socket_close_verbose(S3), + socket_close_verbose(S3, StartTime), [] = flush(), ok. @@ -1156,6 +1177,7 @@ buffers(Config) when is_list(Config) -> Addr = {127,0,0,1}, Stream = 1, Timeout = 3333, + StartTime = timestamp(), S1 = socket_open([{ip,Addr}], Timeout), ?LOGVAR(S1), P1 = socket_call(S1, get_port), @@ -1174,7 +1196,7 @@ buffers(Config) when is_list(Config) -> state=comm_up, assoc_id=AssocId2}}} -> AssocId2 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, S1Ai = receive @@ -1183,7 +1205,7 @@ buffers(Config) when is_list(Config) -> state=comm_up, assoc_id=AssocId1}}} -> AssocId1 after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, %% socket_call(S1, {setopts,[{recbuf,Limit}]}), @@ -1197,22 +1219,22 @@ buffers(Config) when is_list(Config) -> receive {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok after Timeout -> - socket_bailout([S1,S2]) + socket_bailout([S1,S2], StartTime) end, %% - socket_close_verbose(S1), + socket_close_verbose(S1, StartTime), receive {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok after Timeout -> - socket_bailout([S2]) + socket_bailout([S2], StartTime) end, receive {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp, assoc_id=S2Ai}}} -> ok after Timeout -> - socket_bailout([S2]) + socket_bailout([S2], StartTime) end, - socket_close_verbose(S2), + socket_close_verbose(S2, StartTime), [] = flush(), ok. @@ -1521,8 +1543,8 @@ socket_peeloff(Socket, AssocId, SocketOpts, Timeout) -> end, s_start(Starter, Timeout). -socket_close_verbose(S) -> - History = socket_history(socket_close(S)), +socket_close_verbose(S, StartTime) -> + History = socket_history(socket_close(S), StartTime), io:format("socket_close ~p:~n ~p.~n", [S,History]), History. @@ -1535,19 +1557,19 @@ socket_call(S, Request) -> %% socket_get(S, Key) -> %% s_req(S, {get,Key}). -socket_bailout([S|Ss]) -> - History = socket_history(socket_close(S)), +socket_bailout([S|Ss], StartTime) -> + History = socket_history(socket_close(S), StartTime), io:format("bailout ~p:~n ~p.~n", [S,History]), - socket_bailout(Ss); -socket_bailout([]) -> + socket_bailout(Ss, StartTime); +socket_bailout([], _) -> io:format("flush: ~p.~n", [flush()]), ct:fail(socket_bailout). -socket_history({State,Flush}) -> +socket_history({State,Flush}, StartTime) -> {lists:keysort( 2, lists:flatten( - [[{Key,Val} || Val <- Vals] + [[{Key,{TS-StartTime,Val}} || {TS,Val} <- Vals] || {Key,Vals} <- gb_trees:to_list(State)])), Flush}. @@ -1610,14 +1632,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> {Parent,Ref,exit} -> ok = gen_sctp:close(Socket), Key = exit, - Val = {now(),Socket}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, Socket, State), Parent ! {self(),Ref,{NewState,flush()}}; {Parent,Ref,{Msg}} -> Result = Handler(Msg), Key = req, - Val = {now(),{Msg,Result}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Msg,Result}, State), Parent ! {self(),Ref,Result}, s_loop(Socket, Timeout, Parent, Handler, NewState); %% {Parent,Ref,{get,Key}} -> @@ -1627,16 +1647,15 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> {[#sctp_sndrcvinfo{stream=Stream,assoc_id=AssocId}=SRI],Data}} when not is_tuple(Data) -> case gb_get({assoc_change,AssocId}, State) of - [{_,{Addr,Port, - #sctp_assoc_change{ - state=comm_up, - inbound_streams=Is}}}|_] + [{Addr,Port, + #sctp_assoc_change{ + state=comm_up, + inbound_streams=Is}}|_] when 0 =< Stream, Stream < Is-> ok; [] -> ok end, Key = {msg,AssocId,Stream}, - Val = {now(),{Addr,Port,SRI,Data}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SRI,Data}, State), Parent ! {self(),{Addr,Port,AssocId,Stream,Data}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1647,13 +1666,12 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, Key = {assoc_change,AssocId}, - Val = {now(),{Addr,Port,SAC}}, case {gb_get(Key, State),St} of {[],_} -> ok; - {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} + {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} when St =:= comm_lost; St =:= shutdown_comp -> ok end, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SAC}, State), Parent ! {self(),{Addr,Port,SAC}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1667,14 +1685,13 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, case {gb_get({assoc_change,AssocId}, State),St} of - {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],_} + {[{Addr,Port,#sctp_assoc_change{state=comm_up}}|_],_} when St =:= addr_available; St =:= addr_confirmed -> ok; {[],addr_confirmed} -> ok end, Key = {paddr_change,AssocId}, - Val = {now(),{Addr,Port,SPC}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port,SPC}, State), again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); {sctp,Socket,Addr,Port, @@ -1684,12 +1701,11 @@ s_loop(Socket, Timeout, Parent, Handler, State) -> [] -> ok end, case gb_get({assoc_change,AssocId}, State) of - [{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_] -> ok; + [{Addr,Port,#sctp_assoc_change{state=comm_up}}|_] -> ok; [] -> ok end, Key = {shutdown_event,AssocId}, - Val = {now(),{Addr,Port}}, - NewState = gb_push(Key, Val, State), + NewState = gb_push(Key, {Addr,Port}, State), Parent ! {self(), {Addr,Port,SSE}}, again(Socket), s_loop(Socket, Timeout, Parent, Handler, NewState); @@ -1707,11 +1723,12 @@ again(Socket) -> end. gb_push(Key, Val, GBT) -> + TS = timestamp(), case gb_trees:lookup(Key, GBT) of none -> - gb_trees:insert(Key, [Val], GBT); + gb_trees:insert(Key, [{TS,Val}], GBT); {value,V} -> - gb_trees:update(Key, [Val|V], GBT) + gb_trees:update(Key, [{TS,Val}|V], GBT) end. gb_get(Key, GBT) -> @@ -1719,7 +1736,7 @@ gb_get(Key, GBT) -> none -> []; {value,V} -> - V + [Val || {_TS,Val} <- V] end. match_unless_solaris(A, B) -> @@ -1727,3 +1744,6 @@ match_unless_solaris(A, B) -> {unix,sunos} -> B; _ -> A = B end. + +timestamp() -> + erlang:monotonic_time(). diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 1370e23195..2b59eb2bfe 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -27,7 +27,8 @@ -export([get_arguments/1, get_argument/1, boot_var/1, restart/1, many_restarts/0, many_restarts/1, get_plain_arguments/1, - reboot/1, stop_status/1, stop/1, get_status/1, script_id/1]). + reboot/1, stop_status/1, stop/1, get_status/1, script_id/1, + find_system_processes/0]). -export([boot1/1, boot2/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -355,12 +356,16 @@ wait_for(N,Node,EHPid) -> restart(Config) when is_list(Config) -> Args = args(), + Pa = " -pa " ++ filename:dirname(code:which(?MODULE)), + %% Currently test_server:start_node cannot be used. The restarted %% node immediately halts due to the implementation of %% test_server:start_node. - {ok, Node} = loose_node:start(init_test, Args, ?DEFAULT_TIMEOUT_SEC), + {ok, Node} = loose_node:start(init_test, Args ++ Pa, ?DEFAULT_TIMEOUT_SEC), %% Ok, the node is up, now the real test test begins. erlang:monitor_node(Node, true), + SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), + [InitPid, PurgerPid, LitCollectorPid, DirtyCodePid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -375,6 +380,9 @@ restart(Config) when is_list(Config) -> end, ok = wait_restart(30, Node), + SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), + [InitPid1, PurgerPid1, LitCollectorPid1, DirtyCodePid1] = SysProcs1, + %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), InitP = pid_to_list(InitPid), @@ -385,8 +393,24 @@ restart(Config) when is_list(Config) -> PurgerP = pid_to_list(PurgerPid), PurgerP = pid_to_list(PurgerPid1), + %% and same literal area collector process! + case LitCollectorPid of + undefined -> undefined = LitCollectorPid1; + _ -> + LitCollectorP = pid_to_list(LitCollectorPid), + LitCollectorP = pid_to_list(LitCollectorPid1) + end, + + %% and same dirty process code checker process! + case DirtyCodePid of + undefined -> undefined = DirtyCodePid1; + _ -> + DirtyCodeP = pid_to_list(DirtyCodePid), + DirtyCodeP = pid_to_list(DirtyCodePid1) + end, + NewProcs0 = rpc:call(Node, erlang, processes, []), - NewProcs = NewProcs0 -- [InitPid1, PurgerPid1], + NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of true -> ok; @@ -406,6 +430,37 @@ restart(Config) when is_list(Config) -> loose_node:stop(Node), ok. +-record(sys_procs, {init, + code_purger, + literal_collector, + dirty_proc_checker}). + +find_system_processes() -> + find_system_procs(processes(), #sys_procs{}). + +find_system_procs([], SysProcs) -> + [SysProcs#sys_procs.init, + SysProcs#sys_procs.code_purger, + SysProcs#sys_procs.literal_collector, + SysProcs#sys_procs.dirty_proc_checker]; +find_system_procs([P|Ps], SysProcs) -> + case process_info(P, initial_call) of + {initial_call,{otp_ring0,start,2}} -> + undefined = SysProcs#sys_procs.init, + find_system_procs(Ps, SysProcs#sys_procs{init = P}); + {initial_call,{erts_code_purger,start,0}} -> + undefined = SysProcs#sys_procs.code_purger, + find_system_procs(Ps, SysProcs#sys_procs{code_purger = P}); + {initial_call,{erts_literal_area_collector,start,0}} -> + undefined = SysProcs#sys_procs.literal_collector, + find_system_procs(Ps, SysProcs#sys_procs{literal_collector = P}); + {initial_call,{erts_dirty_process_code_checker,start,0}} -> + undefined = SysProcs#sys_procs.dirty_proc_checker, + find_system_procs(Ps, SysProcs#sys_procs{dirty_proc_checker = P}); + _ -> + find_system_procs(Ps, SysProcs) + end. + wait_restart(0, _Node) -> ct:fail(not_restarted); wait_restart(N, Node) -> |