aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/gen_sctp.xml54
-rw-r--r--lib/kernel/doc/src/kernel_app.xml11
-rw-r--r--lib/kernel/doc/src/net_kernel.xml4
-rw-r--r--lib/kernel/src/application_controller.erl19
-rw-r--r--lib/kernel/src/code_server.erl33
-rw-r--r--lib/kernel/src/gen_sctp.erl32
-rw-r--r--lib/kernel/src/inet.erl43
-rw-r--r--lib/kernel/src/inet6_sctp.erl17
-rw-r--r--lib/kernel/src/inet6_tcp.erl8
-rw-r--r--lib/kernel/src/inet6_udp.erl6
-rw-r--r--lib/kernel/src/inet_int.hrl16
-rw-r--r--lib/kernel/src/inet_sctp.erl21
-rw-r--r--lib/kernel/src/inet_tcp.erl8
-rw-r--r--lib/kernel/src/inet_udp.erl4
-rw-r--r--lib/kernel/src/kernel.appup.src28
-rw-r--r--lib/kernel/test/application_SUITE.erl30
-rw-r--r--lib/kernel/test/application_SUITE_data/Makefile.src5
-rw-r--r--lib/kernel/test/application_SUITE_data/deadlock/deadlock.app8
-rw-r--r--lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl69
-rw-r--r--lib/kernel/test/erl_boot_server_SUITE.erl2
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl952
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl69
-rw-r--r--lib/kernel/test/kernel_SUITE.erl64
23 files changed, 1265 insertions, 238 deletions
diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml
index 688cd0f78f..418bfae4b8 100644
--- a/lib/kernel/doc/src/gen_sctp.xml
+++ b/lib/kernel/doc/src/gen_sctp.xml
@@ -45,7 +45,15 @@
SUSE Linux Enterprise Server 10 (x86_64) kernel 2.6.16.27-0.6-smp,
with lksctp-tools-1.0.6, briefly on Solaris 10, and later on
SUSE Linux Enterprise Server 10 Service Pack 1 (x86_64)
- kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7.</p>
+ kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7,
+ and later also on FreeBSD 8.2.
+ </p>
+ <p>
+ This module was written for one-to-many style sockets
+ (type <c>seqpacket</c>). With the addition of
+ <seealso marker="#peeloff/2">peeloff/2</seealso>, one-to-one style
+ sockets (type <c>stream</c>) were introduced.
+ </p>
<p>Record definitions for the <c>gen_sctp</c> module can be found using:</p>
<pre> -include_lib("kernel/include/inet_sctp.hrl"). </pre>
<p>These record definitions use the "new" spelling 'adaptation',
@@ -254,15 +262,19 @@
</desc>
</func>
<func>
- <name name="listen" arity="2"/>
+ <name name="listen" arity="2" clause_i="1"/>
+ <name name="listen" arity="2" clause_i="2"/>
<fsummary>Set up a socket to listen.</fsummary>
<desc>
<p>Sets up a socket to listen on the IP address and port number
- it is bound to. <c><anno>IsServer</anno></c> must be <c>true</c>
- or <c>false</c>.
- In the contrast to TCP, in SCTP there is no listening queue length.
- If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e.
- it will become an SCTP server socket.</p>
+ it is bound to.</p>
+ <p>For type <c>seqpacket</c> sockets (the default)
+ <c><anno>IsServer</anno></c> must be <c>true</c> or <c>false</c>.
+ In the contrast to TCP, in SCTP there is no listening queue length.
+ If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e.
+ it will become an SCTP server socket.</p>
+ <p>For type <c>stream</c> sockets <anno>Backlog</anno> defines
+ the backlog queue length just like in TCP.</p>
</desc>
</func>
<func>
@@ -295,12 +307,40 @@
is used. In particular, the socket is opened in
<seealso marker="#option-binary">binary</seealso> and
<seealso marker="#option-active">passive</seealso> mode,
+ with <anno>SockType</anno> <c>seqpacket</c>,
and with reasonably large
<seealso marker="#option-sndbuf">kernel</seealso> and driver
<seealso marker="#option-buffer">buffers.</seealso></p>
</desc>
</func>
<func>
+ <name name="peeloff" arity="2"/>
+ <fsummary>
+ Peel off a type <c>stream</c> socket from a type <c>seqpacket</c> one
+ </fsummary>
+ <desc>
+ <p>
+ Branch off an existing association <anno>Assoc</anno>
+ in a socket <anno>Socket</anno> of type <c>seqpacket</c>
+ (one-to-may style) into
+ a new socket <anno>NewSocket</anno> of type <c>stream</c>
+ (one-to-one style).
+ </p>
+ <p>
+ The existing association argument <anno>Assoc</anno>
+ can be either a
+ <seealso marker="#record-sctp_assoc_change">
+ #sctp_assoc_change{}
+ </seealso>
+ record as returned from e.g
+ <seealso marker="#recv-2">recv/*</seealso>,
+ <seealso marker="#connect-5">connect/*</seealso> or
+ from a listening socket in active mode. Or it can be just
+ the field <c>assoc_id</c> integer from such a record.
+ </p>
+ </desc>
+ </func>
+ <func>
<name name="recv" arity="1"/>
<name name="recv" arity="2"/>
<fsummary>Receive a message from a socket</fsummary>
diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml
index bf513b7815..0f71a4f0f2 100644
--- a/lib/kernel/doc/src/kernel_app.xml
+++ b/lib/kernel/doc/src/kernel_app.xml
@@ -4,7 +4,7 @@
<appref>
<header>
<copyright>
- <year>1996</year><year>2009</year>
+ <year>1996</year><year>2011</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -231,6 +231,15 @@ MaxT = TickTime + TickTime / 4</code>
<p><em>Note:</em> Normally, a terminating node is detected
immediately.</p>
</item>
+ <tag><c>shutdown_timeout = integer() | infinity</c></tag>
+ <item>
+ <p>Specifies the time <c>application_controller</c> will wait
+ for an application to terminate during node shutdown. If the
+ timer expires, <c>application_controller</c> will brutally
+ kill <c>application_master</c> of the hanging
+ application. If this parameter is undefined, it defaults
+ to <c>infinity</c>.</p>
+ </item>
<tag><c>sync_nodes_mandatory = [NodeName]</c></tag>
<item>
<p>Specifies which other nodes <em>must</em> be alive in order
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index 3b7a710664..e54a427ff0 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.xml
@@ -210,6 +210,10 @@
<p><c>net_kernel</c> is currently changing
<c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> seconds.</p>
</item>
+ <tag><c>ignored</c></tag>
+ <item>
+ <p>The local node is not alive.</p>
+ </item>
</taglist>
</desc>
</func>
diff --git a/lib/kernel/src/application_controller.erl b/lib/kernel/src/application_controller.erl
index 42f527f400..ebfe84463a 100644
--- a/lib/kernel/src/application_controller.erl
+++ b/lib/kernel/src/application_controller.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -1180,10 +1180,27 @@ terminate(Reason, S) ->
_ ->
ok
end,
+ ShutdownTimeout =
+ case application:get_env(kernel, shutdown_timeout) of
+ undefined -> infinity;
+ {ok,T} -> T
+ end,
foreach(fun({_AppName, Id}) when is_pid(Id) ->
+ Ref = erlang:monitor(process, Id),
+ unlink(Id),
exit(Id, shutdown),
receive
+ %% Proc died before link
{'EXIT', Id, _} -> ok
+ after 0 ->
+ receive
+ {'DOWN', Ref, process, Id, _} -> ok
+ after ShutdownTimeout ->
+ exit(Id, kill),
+ receive
+ {'DOWN', Ref, process, Id, _} -> ok
+ end
+ end
end;
(_) -> ok
end,
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index e3d22e7999..32a12e2b52 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -32,6 +32,8 @@
-import(lists, [foreach/2]).
+-define(ANY_NATIVE_CODE_LOADED, any_native_code_loaded).
+
-record(state, {supervisor,
root,
path,
@@ -97,6 +99,8 @@ init(Ref, Parent, [Root,Mode0]) ->
State0
end,
+ put(?ANY_NATIVE_CODE_LOADED, false),
+
Parent ! {Ref,{ok,self()}},
loop(State#state{supervisor = Parent}).
@@ -1278,20 +1282,35 @@ load_native_code(Mod, Bin) ->
%% Therefore we must test for that the loader modules are available
%% before trying to to load native code.
case erlang:module_loaded(hipe_unified_loader) of
- false -> no_native;
- true -> hipe_unified_loader:load_native_code(Mod, Bin)
+ false ->
+ no_native;
+ true ->
+ Result = hipe_unified_loader:load_native_code(Mod, Bin),
+ case Result of
+ {module,_} ->
+ put(?ANY_NATIVE_CODE_LOADED, true);
+ _ ->
+ ok
+ end,
+ Result
end.
hipe_result_to_status(Result) ->
case Result of
- {module,_} -> Result;
- _ -> {error,Result}
+ {module,_} ->
+ put(?ANY_NATIVE_CODE_LOADED, true),
+ Result;
+ _ ->
+ {error,Result}
end.
post_beam_load(Mod) ->
- case erlang:module_loaded(hipe_unified_loader) of
- false -> ok;
- true -> hipe_unified_loader:post_beam_load(Mod)
+ %% post_beam_load/1 can potentially be very expensive because it
+ %% blocks multi-scheduling; thus we want to avoid the call if we
+ %% know that it is not needed.
+ case get(?ANY_NATIVE_CODE_LOADED) of
+ true -> hipe_unified_loader:post_beam_load(Mod);
+ false -> ok
end.
int_list([H|T]) when is_integer(H) -> int_list(T);
diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl
index 6cebb7ab97..77ca26b845 100644
--- a/lib/kernel/src/gen_sctp.erl
+++ b/lib/kernel/src/gen_sctp.erl
@@ -27,7 +27,8 @@
-include("inet_sctp.hrl").
-export([open/0,open/1,open/2,close/1]).
--export([listen/2,connect/4,connect/5,connect_init/4,connect_init/5]).
+-export([listen/2,peeloff/2]).
+-export([connect/4,connect/5,connect_init/4,connect_init/5]).
-export([eof/2,abort/2]).
-export([send/3,send/4,recv/1,recv/2]).
-export([error_string/1]).
@@ -109,9 +110,11 @@ open() ->
| {ifaddr,IP}
| inet:address_family()
| {port,Port}
+ | {type,SockType}
| option(),
IP :: inet:ip_address() | any | loopback,
Port :: inet:port_number(),
+ SockType :: seqpacket | stream,
Socket :: sctp_socket().
open(Opts) when is_list(Opts) ->
@@ -134,9 +137,11 @@ open(X) ->
| {ifaddr,IP}
| inet:address_family()
| {port,Port}
+ | {type,SockType}
| option(),
IP :: inet:ip_address() | any | loopback,
Port :: inet:port_number(),
+ SockType :: seqpacket | stream,
Socket :: sctp_socket().
open(Port, Opts) when is_integer(Port), is_list(Opts) ->
@@ -161,17 +166,38 @@ close(S) ->
-spec listen(Socket, IsServer) -> ok | {error, Reason} when
Socket :: sctp_socket(),
IsServer :: boolean(),
+ Reason :: term();
+ (Socket, Backlog) -> ok | {error, Reason} when
+ Socket :: sctp_socket(),
+ Backlog :: integer(),
Reason :: term().
-listen(S, Flag) when is_port(S), is_boolean(Flag) ->
+listen(S, Backlog)
+ when is_port(S), is_boolean(Backlog);
+ is_port(S), is_integer(Backlog) ->
case inet_db:lookup_socket(S) of
{ok,Mod} ->
- Mod:listen(S, Flag);
+ Mod:listen(S, Backlog);
Error -> Error
end;
listen(S, Flag) ->
erlang:error(badarg, [S,Flag]).
+-spec peeloff(Socket, Assoc) -> {ok, NewSocket} | {error, Reason} when
+ Socket :: sctp_socket(),
+ Assoc :: #sctp_assoc_change{} | assoc_id(),
+ NewSocket :: sctp_socket(),
+ Reason :: term().
+
+peeloff(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) ->
+ peeloff(S, AssocId);
+peeloff(S, AssocId) when is_port(S), is_integer(AssocId) ->
+ case inet_db:lookup_socket(S) of
+ {ok,Mod} ->
+ Mod:peeloff(S, AssocId);
+ Error -> Error
+ end.
+
-spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, inet:posix()} when
Socket :: sctp_socket(),
Addr :: inet:ip_address() | inet:hostname(),
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 48a6f3db65..b60c68e3a1 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -36,7 +36,7 @@
-export([i/0, i/1, i/2]).
--export([getll/1, getfd/1, open/7, fdopen/5]).
+-export([getll/1, getfd/1, open/8, fdopen/6]).
-export([tcp_controlling_process/2, udp_controlling_process/2,
tcp_close/1, udp_close/1]).
@@ -115,7 +115,8 @@
'mtu' | 'netmask' | 'flags' |'hwaddr'.
-type address_family() :: 'inet' | 'inet6'.
--type protocol_option() :: 'tcp' | 'udp' | 'sctp'.
+-type socket_protocol() :: 'tcp' | 'udp' | 'sctp'.
+-type socket_type() :: 'stream' | 'dgram' | 'seqpacket'.
-type stat_option() ::
'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' |
'send_cnt' | 'send_max' | 'send_avg' | 'send_oct' | 'send_pend'.
@@ -748,6 +749,8 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
sctp_opt(Opts, Mod, R#sctp_opts{port=P}, As);
Error -> Error
end;
+ {type,Type} when Type =:= seqpacket; Type =:= stream ->
+ sctp_opt(Opts, Mod, R#sctp_opts{type=Type}, As);
binary -> sctp_opt (Opts, Mod, R, As, mode, binary);
list -> sctp_opt (Opts, Mod, R, As, mode, list);
{sctp_module,_} -> sctp_opt (Opts, Mod, R, As); % Done with
@@ -996,13 +999,14 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) ->
Addr :: ip_address(),
Port :: port_number(),
Opts :: [socket_setopt()],
- Protocol :: protocol_option(),
- Family :: 'inet' | 'inet6',
+ Protocol :: socket_protocol(),
+ Family :: address_family(),
+ Type :: socket_type(),
Module :: atom()) ->
{'ok', socket()} | {'error', posix()}.
-open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->
- case prim_inet:open(Protocol, Family) of
+open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
+ case prim_inet:open(Protocol, Family, Type) of
{ok,S} ->
case prim_inet:setopts(S, Opts) of
ok ->
@@ -1029,18 +1033,19 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Module) when Fd < 0 ->
Error ->
Error
end;
-open(Fd, _Addr, _Port, Opts, Protocol, Family, Module) ->
- fdopen(Fd, Opts, Protocol, Family, Module).
+open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) ->
+ fdopen(Fd, Opts, Protocol, Family, Type, Module).
-spec fdopen(Fd :: non_neg_integer(),
Opts :: [socket_setopt()],
- Protocol :: protocol_option(),
+ Protocol :: socket_protocol(),
Family :: address_family(),
+ Type :: socket_type(),
Module :: atom()) ->
{'ok', socket()} | {'error', posix()}.
-fdopen(Fd, Opts, Protocol, Family, Module) ->
- case prim_inet:fdopen(Protocol, Fd, Family) of
+fdopen(Fd, Opts, Protocol, Family, Type, Module) ->
+ case prim_inet:fdopen(Protocol, Family, Type, Fd) of
{ok, S} ->
case prim_inet:setopts(S, Opts) of
ok ->
@@ -1056,18 +1061,24 @@ fdopen(Fd, Opts, Protocol, Family, Module) ->
%% socket stat
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-i() -> i(tcp), i(udp).
+i() -> i(tcp), i(udp), i(sctp).
i(Proto) -> i(Proto, [port, module, recv, sent, owner,
- local_address, foreign_address, state]).
+ local_address, foreign_address, state, type]).
i(tcp, Fs) ->
ii(tcp_sockets(), Fs, tcp);
i(udp, Fs) ->
- ii(udp_sockets(), Fs, udp).
+ ii(udp_sockets(), Fs, udp);
+i(sctp, Fs) ->
+ ii(sctp_sockets(), Fs, sctp).
ii(Ss, Fs, Proto) ->
- LLs = [h_line(Fs) | info_lines(Ss, Fs, Proto)],
+ LLs =
+ case info_lines(Ss, Fs, Proto) of
+ [] -> [];
+ InfoLines -> [h_line(Fs) | InfoLines]
+ end,
Maxs = foldl(
fun(Line,Max0) -> smax(Max0,Line) end,
duplicate(length(Fs),0),LLs),
@@ -1135,6 +1146,7 @@ info(S, F, Proto) ->
case prim_inet:gettype(S) of
{ok,{_,stream}} -> "STREAM";
{ok,{_,dgram}} -> "DGRAM";
+ {ok,{_,seqpacket}} -> "SEQPACKET";
_ -> " "
end;
fd ->
@@ -1186,6 +1198,7 @@ fmt_port(N, Proto) ->
%% Return a list of all tcp sockets
tcp_sockets() -> port_list("tcp_inet").
udp_sockets() -> port_list("udp_inet").
+sctp_sockets() -> port_list("sctp_inet").
%% Return all ports having the name 'Name'
port_list(Name) ->
diff --git a/lib/kernel/src/inet6_sctp.erl b/lib/kernel/src/inet6_sctp.erl
index 5bf3fca647..c47483bbdd 100644
--- a/lib/kernel/src/inet6_sctp.erl
+++ b/lib/kernel/src/inet6_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -32,7 +32,8 @@
-define(FAMILY, inet6).
-export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
+-export([open/1,close/1,listen/2,peeloff/2,connect/5]).
+-export([sendmsg/3,send/4,recv/2]).
@@ -54,8 +55,8 @@ translate_ip(IP) ->
open(Opts) ->
case inet:sctp_options(Opts, ?MODULE) of
- {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,opts=SOs}} ->
- inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, ?MODULE);
+ {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} ->
+ inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE);
Error -> Error
end.
@@ -65,6 +66,14 @@ close(S) ->
listen(S, Flag) ->
prim_inet:listen(S, Flag).
+peeloff(S, AssocId) ->
+ case prim_inet:peeloff(S, AssocId) of
+ {ok, NewS}=Result ->
+ inet_db:register_socket(NewS, ?MODULE),
+ Result;
+ Error -> Error
+ end.
+
connect(S, Addr, Port, Opts, Timer) ->
inet_sctp:connect(S, Addr, Port, Opts, Timer).
diff --git a/lib/kernel/src/inet6_tcp.erl b/lib/kernel/src/inet6_tcp.erl
index cc45f6c7f6..c714b2bee0 100644
--- a/lib/kernel/src/inet6_tcp.erl
+++ b/lib/kernel/src/inet6_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -93,7 +93,7 @@ do_connect(Addr = {A,B,C,D,E,F,G,H}, Port, Opts, Time) when
port=BPort,
opts=SockOpts}}
when ?ip6(Ab,Bb,Cb,Db,Eb,Fb,Gb,Hb), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of
{ok, S} ->
case prim_inet:connect(S, Addr, Port, Time) of
ok -> {ok,S};
@@ -115,7 +115,7 @@ listen(Port, Opts) ->
port=BPort,
opts=SockOpts}=R}
when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet6,stream,?MODULE) of
{ok, S} ->
case prim_inet:listen(S, R#listen_opts.backlog) of
ok -> {ok, S};
@@ -149,5 +149,5 @@ accept(L,Timeout) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- inet:fdopen(Fd, Opts, tcp, inet6, ?MODULE).
+ inet:fdopen(Fd, Opts, tcp, inet6, stream, ?MODULE).
diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl
index e81d417151..ca43c94211 100644
--- a/lib/kernel/src/inet6_udp.erl
+++ b/lib/kernel/src/inet6_udp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -45,7 +45,7 @@ open(Port, Opts) ->
port=BPort,
opts=SockOpts}}
when ?ip6(A,B,C,D,E,F,G,H), ?port(BPort) ->
- inet:open(Fd,BAddr,BPort,SockOpts,udp,inet6,?MODULE);
+ inet:open(Fd,BAddr,BPort,SockOpts,udp,inet6,dgram,?MODULE);
{ok, _} -> exit(badarg)
end.
@@ -84,4 +84,4 @@ controlling_process(Socket, NewOwner) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- inet:fdopen(Fd, Opts, udp, inet6, ?MODULE).
+ inet:fdopen(Fd, Opts, udp, inet6, dgram, ?MODULE).
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 6f1688c6a2..f8984b13fe 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -29,7 +29,7 @@
-define(INET_AF_ANY, 3). % Fake for ANY in any address family
-define(INET_AF_LOOPBACK, 4). % Fake for LOOPBACK in any address family
-%% type codes (gettype, INET_REQ_GETTYPE)
+%% type codes to open and gettype - INET_REQ_GETTYPE
-define(INET_TYPE_STREAM, 1).
-define(INET_TYPE_DGRAM, 2).
-define(INET_TYPE_SEQPACKET, 3).
@@ -83,16 +83,19 @@
-define(INET_REQ_IFSET, 23).
-define(INET_REQ_SUBSCRIBE, 24).
-define(INET_REQ_GETIFADDRS, 25).
+-define(INET_REQ_ACCEPT, 26).
+-define(INET_REQ_LISTEN, 27).
%% TCP requests
--define(TCP_REQ_ACCEPT, 40).
--define(TCP_REQ_LISTEN, 41).
+%%-define(TCP_REQ_ACCEPT, 40). MOVED
+%%-define(TCP_REQ_LISTEN, 41). MERGED
-define(TCP_REQ_RECV, 42).
-define(TCP_REQ_UNRECV, 43).
-define(TCP_REQ_SHUTDOWN, 44).
%% UDP and SCTP requests
-define(PACKET_REQ_RECV, 60).
--define(SCTP_REQ_LISTEN, 61).
+%%-define(SCTP_REQ_LISTEN, 61). MERGED
-define(SCTP_REQ_BINDX, 62). %% Multi-home SCTP bind
+-define(SCTP_REQ_PEELOFF, 63).
%% subscribe codes, INET_REQ_SUBSCRIBE
-define(INET_SUBS_EMPTY_OUT_Q, 1).
@@ -100,7 +103,7 @@
%% reply codes for *_REQ_*
-define(INET_REP_ERROR, 0).
-define(INET_REP_OK, 1).
--define(INET_REP_SCTP, 2).
+-define(INET_REP, 2).
%% INET, TCP and UDP options:
-define(INET_OPT_REUSEADDR, 0).
@@ -399,6 +402,7 @@
ifaddr,
port = 0,
fd = -1,
+ type = seqpacket,
opts = [{mode, binary},
{buffer, ?SCTP_DEF_BUFSZ},
{sndbuf, ?SCTP_DEF_BUFSZ},
diff --git a/lib/kernel/src/inet_sctp.erl b/lib/kernel/src/inet_sctp.erl
index de74b573bd..2d799d79fa 100644
--- a/lib/kernel/src/inet_sctp.erl
+++ b/lib/kernel/src/inet_sctp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2007-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -31,7 +31,8 @@
-define(FAMILY, inet).
-export([getserv/1,getaddr/1,getaddr/2,translate_ip/1]).
--export([open/1,close/1,listen/2,connect/5,sendmsg/3,send/4,recv/2]).
+-export([open/1,close/1,listen/2,peeloff/2,connect/5]).
+-export([sendmsg/3,send/4,recv/2]).
@@ -53,8 +54,8 @@ translate_ip(IP) ->
open(Opts) ->
case inet:sctp_options(Opts, ?MODULE) of
- {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,opts=SOs}} ->
- inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, ?MODULE);
+ {ok,#sctp_opts{fd=Fd,ifaddr=Addr,port=Port,type=Type,opts=SOs}} ->
+ inet:open(Fd, Addr, Port, SOs, sctp, ?FAMILY, Type, ?MODULE);
Error -> Error
end.
@@ -64,6 +65,14 @@ close(S) ->
listen(S, Flag) ->
prim_inet:listen(S, Flag).
+peeloff(S, AssocId) ->
+ case prim_inet:peeloff(S, AssocId) of
+ {ok, NewS}=Result ->
+ inet_db:register_socket(NewS, ?MODULE),
+ Result;
+ Error -> Error
+ end.
+
%% A non-blocking connect is implemented when the initial call is to
%% gen_sctp:connect_init which passes the value nowait as the Timer
connect(S, Addr, Port, Opts, Timer) ->
@@ -102,7 +111,7 @@ connect(S, Addr, Port, Opts, Timer) ->
connect_get_assoc(S, Addr, Port, false, Timer) ->
case recv(S, inet:timeout(Timer)) of
- {ok, {Addr, Port, [], #sctp_assoc_change{state=St}=Ev}} ->
+ {ok, {Addr, Port, _, #sctp_assoc_change{state=St}=Ev}} ->
if St =:= comm_up ->
%% Yes, successfully connected, return the whole
%% sctp_assoc_change event (containing, in particular,
@@ -123,7 +132,7 @@ connect_get_assoc(S, Addr, Port, false, Timer) ->
connect_get_assoc(S, Addr, Port, Active, Timer) ->
Timeout = inet:timeout(Timer),
receive
- {sctp,S,Addr,Port,{[],#sctp_assoc_change{state=St}=Ev}} ->
+ {sctp,S,Addr,Port,{_,#sctp_assoc_change{state=St}=Ev}} ->
case Active of
once ->
prim_inet:setopt(S, active, once);
diff --git a/lib/kernel/src/inet_tcp.erl b/lib/kernel/src/inet_tcp.erl
index 6dadccd6a9..4c2db16ce3 100644
--- a/lib/kernel/src/inet_tcp.erl
+++ b/lib/kernel/src/inet_tcp.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -95,7 +95,7 @@ do_connect({A,B,C,D}, Port, Opts, Time) when ?ip(A,B,C,D), ?port(Port) ->
port=BPort,
opts=SockOpts}}
when ?ip(Ab,Bb,Cb,Db), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of
{ok, S} ->
case prim_inet:connect(S, {A,B,C,D}, Port, Time) of
ok -> {ok,S};
@@ -117,7 +117,7 @@ listen(Port, Opts) ->
port=BPort,
opts=SockOpts}=R}
when ?ip(A,B,C,D), ?port(BPort) ->
- case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,?MODULE) of
+ case inet:open(Fd,BAddr,BPort,SockOpts,tcp,inet,stream,?MODULE) of
{ok, S} ->
case prim_inet:listen(S, R#listen_opts.backlog) of
ok -> {ok, S};
@@ -150,4 +150,4 @@ accept(L,Timeout) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- inet:fdopen(Fd, Opts, tcp, inet, ?MODULE).
+ inet:fdopen(Fd, Opts, tcp, inet, stream, ?MODULE).
diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl
index 60bd96f332..80d930fe10 100644
--- a/lib/kernel/src/inet_udp.erl
+++ b/lib/kernel/src/inet_udp.erl
@@ -52,7 +52,7 @@ open(Port, Opts) ->
ifaddr=BAddr={A,B,C,D},
port=BPort,
opts=SockOpts}} when ?ip(A,B,C,D), ?port(BPort) ->
- inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,?MODULE);
+ inet:open(Fd,BAddr,BPort,SockOpts,udp,inet,dgram,?MODULE);
{ok, _} -> exit(badarg)
end.
@@ -93,7 +93,7 @@ controlling_process(Socket, NewOwner) ->
fdopen(Fd, Opts) ->
inet:fdopen(Fd,
optuniquify([{recbuf, ?RECBUF} | Opts]),
- udp, inet, ?MODULE).
+ udp, inet, dgram, ?MODULE).
%% Remove all duplicate options from an option list.
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index 54a63833e6..bded2408a7 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -1 +1,27 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+{"%VSN%",
+ %% Up from - max two major revisions back
+ [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ %% Down to - max two major revisions back
+ [{<<"2\\.15(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"2\\.14(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"2\\.13(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+}.
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 2c5b8ccb66..f469a0af98 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -33,7 +33,7 @@
-export([config_change/1,
distr_changed_tc1/1, distr_changed_tc2/1,
- shutdown_func/1, do_shutdown/1]).
+ shutdown_func/1, do_shutdown/1, shutdown_timeout/1]).
-define(TESTCASE, testcase_name).
-define(testcase, ?config(?TESTCASE, Config)).
@@ -50,7 +50,7 @@ all() ->
load_use_cache, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
permit_false_start_dist, get_key,
- {group, distr_changed}, config_change, shutdown_func].
+ {group, distr_changed}, config_change, shutdown_func, shutdown_timeout].
groups() ->
[{reported_bugs, [],
@@ -1915,6 +1915,32 @@ do_shutdown(Reason) ->
+%%%-----------------------------------------------------------------
+%%% Tests the 'shutdown_timeout' kernel config parameter
+%%%-----------------------------------------------------------------
+shutdown_timeout(Config) when is_list(Config) ->
+ DataDir = ?config(data_dir,Config),
+ {ok,Cp1} = start_node(?MODULE_STRING++"_shutdown_timeout"),
+ wait_for_ready_net(),
+ ok = rpc:call(Cp1, application, set_env, [kernel, shutdown_timeout, 1000]),
+ rpc:call(Cp1, code, add_path, [filename:join([DataDir,deadlock])]),
+ ok = rpc:call(Cp1, application, start, [sasl]),
+ ok = rpc:call(Cp1, application, start, [deadlock]),
+ rpc:call(Cp1, deadlock, restart_and_fail, []),
+
+ ok = net_kernel:monitor_nodes(true),
+ _ = rpc:call(Cp1, init, stop, []),
+ receive
+ {nodedown,Cp1} ->
+ ok
+ after 10000 ->
+ ct:fail("timeout 10 sec: node termination hangs")
+ end,
+ ok.
+
+
+
+
%%-----------------------------------------------------------------
%% Utility functions
%%-----------------------------------------------------------------
diff --git a/lib/kernel/test/application_SUITE_data/Makefile.src b/lib/kernel/test/application_SUITE_data/Makefile.src
index a237f6badb..abc3c82907 100644
--- a/lib/kernel/test/application_SUITE_data/Makefile.src
+++ b/lib/kernel/test/application_SUITE_data/Makefile.src
@@ -2,7 +2,8 @@ EFLAGS=+debug_info
all: app_start_error.@EMULATOR@ trans_abnormal_sup.@EMULATOR@ \
trans_normal_sup.@EMULATOR@ transient.@EMULATOR@ \
- group_leader_sup.@EMULATOR@ group_leader.@EMULATOR@
+ group_leader_sup.@EMULATOR@ group_leader.@EMULATOR@ \
+ deadlock/deadlock.@EMULATOR@
app_start_error.@EMULATOR@: app_start_error.erl
erlc $(EFLAGS) app_start_error.erl
@@ -22,3 +23,5 @@ group_leader.@EMULATOR@: group_leader.erl
group_leader_sup.@EMULATOR@: group_leader_sup.erl
erlc $(EFLAGS) group_leader_sup.erl
+deadlock/deadlock.@EMULATOR@: deadlock/deadlock.erl
+ erlc $(EFLAGS) -o deadlock deadlock/deadlock.erl \ No newline at end of file
diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app
new file mode 100644
index 0000000000..0c1001bed6
--- /dev/null
+++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.app
@@ -0,0 +1,8 @@
+{application, deadlock, [
+ {vsn, "1"},
+ {registered, []},
+ {applications, [kernel, stdlib, sasl]},
+ {modules, [deadlock]},
+ {mod, {deadlock, []}},
+ {env, [{fail_start, false}]}
+]}.
diff --git a/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl
new file mode 100644
index 0000000000..5f68bf9078
--- /dev/null
+++ b/lib/kernel/test/application_SUITE_data/deadlock/deadlock.erl
@@ -0,0 +1,69 @@
+-module(deadlock).
+-behaviour(application).
+-compile(export_all).
+-define(SUP,deadlock_sup).
+-define(CHILD,deadlock_child).
+
+
+%%%-----------------------------------------------------------------
+%%% application callbacks
+start(_StartType, _StartArgs) ->
+ supervisor:start_link({local, ?SUP}, ?MODULE, [sup]).
+
+stop(_State) ->
+ ok.
+
+
+
+%%%-----------------------------------------------------------------
+%%% supervisor callbacks
+init([sup]) ->
+ {ok, {{one_for_one, 5, 10}, [
+ {
+ sasl_syslog_dm, {?MODULE, start_link, []},
+ permanent, brutal_kill, worker,
+ [deadlock]
+ }
+ ]}};
+
+
+%%%-----------------------------------------------------------------
+%%% gen_server callbacks
+init([child]) ->
+ case application:get_env(deadlock, fail_start) of
+ {ok, false} ->
+ %% we must not fail on the first init, otherwise supervisor
+ %% terminates immediately
+ {ok, []};
+ {ok, true} ->
+ timer:sleep(infinity), % init hangs!!!!
+ {ok, []}
+ end.
+
+handle_call(_Req, _From, State) ->
+ {reply, ok, State}.
+
+handle_cast(restart, State) ->
+ {stop, error, State}.
+
+handle_info(_Msg, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+
+%%%-----------------------------------------------------------------
+%%% Start child
+start_link() ->
+ gen_server:start_link({local, ?CHILD}, ?MODULE, [child], []).
+
+
+%%%-----------------------------------------------------------------
+%%% Provoke hanging
+restart_and_fail() ->
+ application:set_env(deadlock, fail_start, true), % next init will hang
+ gen_server:cast(?CHILD, restart).
diff --git a/lib/kernel/test/erl_boot_server_SUITE.erl b/lib/kernel/test/erl_boot_server_SUITE.erl
index cea3715ce4..bb64c01058 100644
--- a/lib/kernel/test/erl_boot_server_SUITE.erl
+++ b/lib/kernel/test/erl_boot_server_SUITE.erl
@@ -346,7 +346,7 @@ good_hosts(_Config) ->
[GoodHost1, GoodHost2, GoodHost3].
open_udp() ->
- ?line {ok, S} = prim_inet:open(udp, inet),
+ ?line {ok, S} = prim_inet:open(udp, inet, dgram),
?line ok = prim_inet:setopts(S, [{mode,list},{active,true},
{deliver,term},{broadcast,true}]),
?line {ok,_} = prim_inet:bind(S, {0,0,0,0}, 0),
diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl
index 1b534a5fc4..300152ddce 100644
--- a/lib/kernel/test/gen_sctp_SUITE.erl
+++ b/lib/kernel/test/gen_sctp_SUITE.erl
@@ -30,33 +30,29 @@
-export(
[basic/1,
api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1,
- xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1]).
+ xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1,
+ basic_stream/1, xfer_stream_min/1, peeloff/1, buffers/1]).
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[basic, api_open_close, api_listen, api_connect_init,
- api_opts, xfer_min, xfer_active, def_sndrcvinfo,
- implicit_inet6].
+ api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6,
+ basic_stream, xfer_stream_min, peeloff, buffers].
groups() ->
[].
-init_per_suite(Config) ->
- try gen_sctp:open() of
+init_per_suite(_Config) ->
+ case gen_sctp:open() of
{ok,Socket} ->
gen_sctp:close(Socket),
[];
- _ ->
- []
- catch
- error:badarg ->
- {skip,"SCTP not supported on this machine"};
- _:_ ->
- Config
+ {error,eprotonosupport} ->
+ {skip,"SCTP not supported on this machine"}
end.
-end_per_suite(_Conifig) ->
+end_per_suite(_Config) ->
ok.
init_per_group(_GroupName, Config) ->
@@ -96,7 +92,7 @@ xfer_min(Config) when is_list(Config) ->
?line Stream = 0,
?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
?line Loopback = {127,0,0,1},
- ?line {ok,Sb} = gen_sctp:open(),
+ ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
?line {ok,Pb} = inet:port(Sb),
?line ok = gen_sctp:listen(Sb, true),
@@ -108,29 +104,44 @@ xfer_min(Config) when is_list(Config) ->
inbound_streams=SaInboundStreams,
assoc_id=SaAssocId}=SaAssocChange} =
gen_sctp:connect(Sa, Loopback, Pb, []),
- ?line {ok,{Loopback,
- Pa,[],
+ ?line {SbAssocId,SaOutboundStreams,SaInboundStreams} =
+ case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
+ {Loopback,Pa,
#sctp_assoc_change{state=comm_up,
error=0,
outbound_streams=SbOutboundStreams,
inbound_streams=SbInboundStreams,
- assoc_id=SbAssocId}}} =
- gen_sctp:recv(Sb, infinity),
- ?line SaOutboundStreams = SbInboundStreams,
- ?line SbOutboundStreams = SaInboundStreams,
+ assoc_id=AssocId}} ->
+ {AssocId,SbInboundStreams,SbOutboundStreams};
+ {Loopback,Pa,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={Loopback,Pa},
+ error=0,
+ assoc_id=AssocId}} ->
+ {Loopback,Pa,
+ #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SbOutboundStreams,
+ inbound_streams=SbInboundStreams,
+ assoc_id=AssocId}} =
+ ?line recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ {AssocId,SbInboundStreams,SbOutboundStreams}
+ end,
+
?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
- ?line case gen_sctp:recv(Sb, infinity) of
- {ok,{Loopback,
- Pa,
- [#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} -> ok;
- {ok,{Loopback,
- Pa,[],
+ ?line case log_ok(gen_sctp:recv(Sb, infinity)) of
+ {Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data} -> ok;
+ Event1 ->
+ {Loopback,Pa,
#sctp_paddr_change{addr = {Loopback,_},
state = addr_available,
error = 0,
- assoc_id = SbAssocId}}} ->
+ assoc_id = SbAssocId}} =
+ recv_event(Event1),
{ok,{Loopback,
Pa,
[#sctp_sndrcvinfo{stream=Stream,
@@ -138,30 +149,40 @@ xfer_min(Config) when is_list(Config) ->
Data}} = gen_sctp:recv(Sb, infinity)
end,
?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
- ?line {ok,{Loopback,
- Pb,
+ ?line case log_ok(gen_sctp:recv(Sa, infinity)) of
+ {Loopback,Pb,
[#sctp_sndrcvinfo{stream=Stream,
assoc_id=SaAssocId}],
- Data}} =
- gen_sctp:recv(Sa, infinity),
+ Data} ->
+ ok;
+ Event2 ->
+ {Loopback,Pb,
+ #sctp_paddr_change{addr={_,Pb},
+ state=addr_confirmed,
+ error=0,
+ assoc_id=SaAssocId}} =
+ ?line recv_event(Event2),
+ ?line {Loopback,
+ Pb,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SaAssocId}],
+ Data} =
+ log_ok(gen_sctp:recv(Sa, infinity))
+ end,
%%
?line ok = gen_sctp:eof(Sa, SaAssocChange),
- ?line {ok,{Loopback,
- Pa,[],
- #sctp_shutdown_event{assoc_id=SbAssocId}}} =
- gen_sctp:recv(Sb, infinity),
- ?line {ok,{Loopback,
- Pb,[],
- #sctp_assoc_change{state=shutdown_comp,
- error=0,
- assoc_id=SaAssocId}}} =
- gen_sctp:recv(Sa, infinity),
- ?line {ok,{Loopback,
- Pa,[],
- #sctp_assoc_change{state=shutdown_comp,
- error=0,
- assoc_id=SbAssocId}}} =
- gen_sctp:recv(Sb, infinity),
+ ?line {Loopback,Pa,#sctp_shutdown_event{assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ ?line {Loopback,Pb,
+ #sctp_assoc_change{state=shutdown_comp,
+ error=0,
+ assoc_id=SaAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity))),
+ ?line {Loopback,Pa,
+ #sctp_assoc_change{state=shutdown_comp,
+ error=0,
+ assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
@@ -186,32 +207,52 @@ xfer_active(Config) when is_list(Config) ->
?line {ok,Sa} = gen_sctp:open([{active,true}]),
?line {ok,Pa} = inet:port(Sa),
- ?line {ok,#sctp_assoc_change{state=comm_up,
- error=0,
- outbound_streams=SaOutboundStreams,
- inbound_streams=SaInboundStreams,
- assoc_id=SaAssocId}=SaAssocChange} =
- gen_sctp:connect(Sa, Loopback, Pb, []),
+ ?line ok = gen_sctp:connect_init(Sa, Loopback, Pb, []),
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SaOutboundStreams,
+ inbound_streams=SaInboundStreams,
+ assoc_id=SaAssocId} = SaAssocChange =
+ recv_assoc_change(Sa, Loopback, Pb, Timeout),
?line io:format("Sa=~p, Pa=~p, Sb=~p, Pb=~p, SaAssocId=~p, "
"SaOutboundStreams=~p, SaInboundStreams=~p~n",
[Sa,Pa,Sb,Pb,SaAssocId,
SaOutboundStreams,SaInboundStreams]),
- ?line SbAssocId =
- receive
- {sctp,Sb,Loopback,Pa,
- {[],
- #sctp_assoc_change{state=comm_up,
- error=0,
- outbound_streams=SbOutboundStreams,
- inbound_streams=SbInboundStreams,
- assoc_id=SBAI}}} ->
- ?line SaOutboundStreams = SbInboundStreams,
- ?line SaInboundStreams = SbOutboundStreams,
- SBAI
- after Timeout ->
- ?line test_server:fail({unexpected,flush()})
- end,
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SbOutboundStreams,
+ inbound_streams=SbInboundStreams,
+ assoc_id=SbAssocId} =
+ recv_assoc_change(Sb, Loopback, Pa, Timeout),
+ ?line SbOutboundStreams = SaInboundStreams,
+ ?line SbInboundStreams = SaOutboundStreams,
?line io:format("SbAssocId=~p~n", [SbAssocId]),
+
+ ?line case recv_paddr_change(Sa, Loopback, Pb, 314) of
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={_,Pb},
+ error=0,
+ assoc_id=SaAssocId} -> ok;
+ #sctp_paddr_change{state=addr_available,
+ addr={_,Pb},
+ error=0,
+ assoc_id=SaAssocId} -> ok;
+ timeout -> ok
+ end,
+ ?line case recv_paddr_change(Sb, Loopback, Pa, 314) of
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={Loopback,Pa},
+ error=0,
+ assoc_id=SbAssocId} -> ok;
+ #sctp_paddr_change{state=addr_available,
+ addr={Loopback,P},
+ error=0,
+ assoc_id=SbAssocId} ->
+ ?line match_unless_solaris(Pa, P);
+ timeout -> ok
+ end,
+ ?line [] = flush(),
+
?line ok =
do_from_other_process(
fun () -> gen_sctp:send(Sa, SaAssocId, 0, Data) end),
@@ -219,21 +260,9 @@ xfer_active(Config) when is_list(Config) ->
{sctp,Sb,Loopback,Pa,
{[#sctp_sndrcvinfo{stream=Stream,
assoc_id=SbAssocId}],
- Data}} -> ok;
- {sctp,Sb,Loopback,Pa,
- {[],
- #sctp_paddr_change{addr = {Loopback,_},
- state = addr_available,
- error = 0,
- assoc_id = SbAssocId}}} ->
- ?line receive
- {sctp,Sb,Loopback,Pa,
- {[#sctp_sndrcvinfo{stream=Stream,
- assoc_id=SbAssocId}],
- Data}} -> ok
- end
+ Data}} -> ok
after Timeout ->
- ?line test_server:fail({unexpected,flush()})
+ ?line test_server:fail({timeout,flush()})
end,
?line ok = gen_sctp:send(Sb, SbAssocId, 0, Data),
?line receive
@@ -242,31 +271,28 @@ xfer_active(Config) when is_list(Config) ->
assoc_id=SaAssocId}],
Data}} -> ok
after Timeout ->
- ?line test_server:fail({unexpected,flush()})
+ ?line test_server:fail({timeout,flush()})
end,
%%
?line ok = gen_sctp:abort(Sa, SaAssocChange),
- ?line receive
- {sctp,Sb,Loopback,Pa,
- {[],
- #sctp_assoc_change{state=comm_lost,
- assoc_id=SbAssocId}}} -> ok
- after Timeout ->
- ?line test_server:fail({unexpected,flush()})
+ ?line case recv_assoc_change(Sb, Loopback, Pa, Timeout) of
+ #sctp_assoc_change{state=comm_lost,
+ assoc_id=SbAssocId} -> ok;
+ timeout ->
+ ?line test_server:fail({timeout,flush()})
end,
?line ok = gen_sctp:close(Sb),
+ ?line case recv_assoc_change(Sa, Loopback, Pb, Timeout) of
+ #sctp_assoc_change{state=comm_lost,
+ assoc_id=SaAssocId} -> ok;
+ timeout ->
+ ?line io:format("timeout waiting for comm_lost on Sa~n"),
+ ?line match_unless_solaris(ok, {timeout,flush()})
+ end,
?line receive
- {sctp,Sa,Loopback,Pb,
- {[],
- #sctp_assoc_change{state=comm_lost,
- assoc_id=SaAssocId}}} -> ok
- after Timeout ->
- ?line test_server:fail({unexpected,flush()})
- end,
- ?line receive
- {sctp_error,Sa,enotconn} -> ok % Solaris
- after 17 -> ok %% Only happens on Solaris
- end,
+ {sctp_error,Sa,enotconn} -> ok % Solaris
+ after 17 -> ok
+ end,
?line ok = gen_sctp:close(Sa),
%%
?line receive
@@ -275,6 +301,30 @@ xfer_active(Config) when is_list(Config) ->
end,
ok.
+recv_assoc_change(S, Addr, Port, Timeout) ->
+ receive
+ {sctp,S,Addr,Port,{[], #sctp_assoc_change{}=AssocChange}} ->
+ AssocChange;
+ {sctp,S,Addr,Port,
+ {[#sctp_sndrcvinfo{assoc_id=AssocId}],
+ #sctp_assoc_change{assoc_id=AssocId}=AssocChange}} ->
+ AssocChange
+ after Timeout ->
+ timeout
+ end.
+
+recv_paddr_change(S, Addr, Port, Timeout) ->
+ receive
+ {sctp,S,Addr,Port,{[], #sctp_paddr_change{}=PaddrChange}} ->
+ PaddrChange;
+ {sctp,S,Addr,Port,
+ {[#sctp_sndrcvinfo{assoc_id=AssocId}],
+ #sctp_paddr_change{assoc_id=AssocId}=PaddrChange}} ->
+ PaddrChange
+ after Timeout ->
+ timeout
+ end.
+
def_sndrcvinfo(doc) ->
"Test that #sctp_sndrcvinfo{} parameters set on a socket "
"are used by gen_sctp:send/4";
@@ -285,11 +335,11 @@ def_sndrcvinfo(Config) when is_list(Config) ->
?line Data = <<"What goes up, must come down.">>,
%%
?line S1 =
- ok(gen_sctp:open(
+ log_ok(gen_sctp:open(
0, [{sctp_default_send_param,#sctp_sndrcvinfo{ppid=17}}])),
?LOGVAR(S1),
?line P1 =
- ok(inet:port(S1)),
+ log_ok(inet:port(S1)),
?LOGVAR(P1),
?line #sctp_sndrcvinfo{ppid=17, context=0, timetolive=0, assoc_id=0} =
getopt(S1, sctp_default_send_param),
@@ -297,10 +347,10 @@ def_sndrcvinfo(Config) when is_list(Config) ->
gen_sctp:listen(S1, true),
%%
?line S2 =
- ok(gen_sctp:open()),
+ log_ok(gen_sctp:open()),
?LOGVAR(S2),
?line P2 =
- ok(inet:port(S2)),
+ log_ok(inet:port(S2)),
?LOGVAR(P2),
?line #sctp_sndrcvinfo{ppid=0, context=0, timetolive=0, assoc_id=0} =
getopt(S2, sctp_default_send_param),
@@ -309,32 +359,57 @@ def_sndrcvinfo(Config) when is_list(Config) ->
state=comm_up,
error=0,
assoc_id=S2AssocId} = S2AssocChange =
- ok(gen_sctp:connect(S2, Loopback, P1, [])),
+ log_ok(gen_sctp:connect(S2, Loopback, P1, [])),
?LOGVAR(S2AssocChange),
- ?line case ok(gen_sctp:recv(S1)) of
- {Loopback, P2,[],
+ ?line 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),
+ {Loopback,P2,
+ #sctp_assoc_change{
state=comm_up,
error=0,
- assoc_id=S1AssocId}} ->
- ?LOGVAR(S1AssocId)
+ assoc_id=S1AssocId}} =
+ recv_event(log_ok(gen_sctp:recv(S1)))
end,
+
?line #sctp_sndrcvinfo{
- ppid=17, context=0, timetolive=0, assoc_id=S1AssocId} =
+ ppid=17, context=0, timetolive=0} = %, assoc_id=S1AssocId} =
getopt(
S1, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S1AssocId}),
?line #sctp_sndrcvinfo{
- ppid=0, context=0, timetolive=0, assoc_id=S2AssocId} =
+ ppid=0, context=0, timetolive=0} = %, assoc_id=S2AssocId} =
getopt(
S2, sctp_default_send_param, #sctp_sndrcvinfo{assoc_id=S2AssocId}),
%%
?line ok =
gen_sctp:send(S1, S1AssocId, 1, <<"1: ",Data/binary>>),
- ?line case ok(gen_sctp:recv(S2)) of
+ ?line case log_ok(gen_sctp:recv(S2)) of
{Loopback,P1,
[#sctp_sndrcvinfo{
stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
- <<"1: ",Data/binary>>} -> ok
+ <<"1: ",Data/binary>>} -> ok;
+ Event1 ->
+ ?line {Loopback,P1,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={_,P1},
+ error=0,
+ assoc_id=S2AssocId}} =
+ recv_event(Event1),
+ ?line {Loopback,P1,
+ [#sctp_sndrcvinfo{
+ stream=1, ppid=17, context=0, assoc_id=S2AssocId}],
+ <<"1: ",Data/binary>>} =
+ log_ok(gen_sctp:recv(S2))
end,
%%
?line ok =
@@ -354,7 +429,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
%%
?line ok =
gen_sctp:send(S1, S1AssocId, 0, <<"2: ",Data/binary>>),
- ?line case ok(gen_sctp:recv(S2)) of
+ ?line case log_ok(gen_sctp:recv(S2)) of
{Loopback,P1,
[#sctp_sndrcvinfo{
stream=0, ppid=19, context=0, assoc_id=S2AssocId}],
@@ -362,16 +437,18 @@ def_sndrcvinfo(Config) when is_list(Config) ->
end,
?line ok =
gen_sctp:send(S2, S2AssocChange, 1, <<"3: ",Data/binary>>),
- ?line case ok(gen_sctp:recv(S1)) of
+ ?line case log_ok(gen_sctp:recv(S1)) of
{Loopback,P2,
[#sctp_sndrcvinfo{
stream=1, ppid=0, context=0, assoc_id=S1AssocId}],
<<"3: ",Data/binary>>} -> ok;
- {Loopback,P2,[],
- #sctp_paddr_change{
- addr={Loopback,_}, state=addr_available,
- error=0, assoc_id=S1AssocId}} ->
- ?line case ok(gen_sctp:recv(S1)) of
+ Event2 ->
+ {Loopback,P2,
+ #sctp_paddr_change{
+ addr={Loopback,_}, state=addr_available,
+ error=0, assoc_id=S1AssocId}} =
+ recv_event(Event2),
+ ?line case log_ok(gen_sctp:recv(S1)) of
{Loopback,P2,
[#sctp_sndrcvinfo{
stream=1, ppid=0, context=0,
@@ -387,7 +464,7 @@ def_sndrcvinfo(Config) when is_list(Config) ->
#sctp_sndrcvinfo{stream=0, ppid=20, assoc_id=S2AssocId},
<<"4: ",Data/binary>>)
end),
- ?line case ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
+ ?line case log_ok(do_from_other_process(fun() -> gen_sctp:recv(S1) end)) of
{Loopback,P2,
[#sctp_sndrcvinfo{
stream=0, ppid=20, context=0, assoc_id=S1AssocId}],
@@ -416,8 +493,12 @@ getopt(S, Opt, Param) ->
setopt(S, Opt, Val) ->
inet:setopts(S, [{Opt,Val}]).
-ok({ok,X}) ->
- io:format("OK: ~p~n", [X]),
+log_ok(X) -> log(ok(X)).
+
+ok({ok,X}) -> X.
+
+log(X) ->
+ io:format("LOG[~w]: ~p~n", [self(),X]),
X.
flush() ->
@@ -520,7 +601,10 @@ api_listen(Config) when is_list(Config) ->
#sctp_assoc_change{
state=comm_lost}}} =
gen_sctp:recv(Sa, infinity);
- {error,#sctp_assoc_change{state=cant_assoc}} -> ok
+ {error,#sctp_assoc_change{state=cant_assoc}} ->
+ ok%;
+ %% {error,{Localhost,Pb,_,#sctp_assoc_change{state=cant_assoc}}} ->
+ %% ok
end,
?line ok = gen_sctp:listen(Sb, true),
?line {ok,#sctp_assoc_change{state=comm_up,
@@ -552,29 +636,41 @@ api_connect_init(Config) when is_list(Config) ->
?line {ok,Sa} = gen_sctp:open(),
?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
{error,econnrefused} ->
- ?line {ok,{Localhost,
- Pb,[],
- #sctp_assoc_change{state=comm_lost}}} =
- gen_sctp:recv(Sa, infinity);
+ ?line {Localhost,Pb,#sctp_assoc_change{state=comm_lost}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity)));
ok ->
- ?line {ok,{Localhost,
- Pb,[],
- #sctp_assoc_change{state=cant_assoc}}} =
- gen_sctp:recv(Sa, infinity)
+ ?line {Localhost,Pb,#sctp_assoc_change{state=cant_assoc}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
end,
?line ok = gen_sctp:listen(Sb, true),
?line case gen_sctp:connect_init(Sa, localhost, Pb, []) of
ok ->
- ?line {ok,{Localhost,
- Pb,[],
- #sctp_assoc_change{
- state = comm_up}}} =
- gen_sctp:recv(Sa, infinity)
+ ?line {Localhost,Pb,#sctp_assoc_change{state=comm_up}} =
+ recv_event(log_ok(gen_sctp:recv(Sa, infinity)))
end,
?line ok = gen_sctp:close(Sa),
?line ok = gen_sctp:close(Sb),
ok.
+recv_event({Addr,Port,[],#sctp_assoc_change{}=AssocChange}) ->
+ {Addr,Port,AssocChange};
+recv_event({Addr,Port,
+ [#sctp_sndrcvinfo{assoc_id=Assoc}],
+ #sctp_assoc_change{assoc_id=Assoc}=AssocChange}) ->
+ {Addr,Port,AssocChange};
+recv_event({Addr,Port,[],#sctp_paddr_change{}=PaddrChange}) ->
+ {Addr,Port,PaddrChange};
+recv_event({Addr,Port,
+ [#sctp_sndrcvinfo{assoc_id=Assoc}],
+ #sctp_paddr_change{assoc_id=Assoc}=PaddrChange}) ->
+ {Addr,Port,PaddrChange};
+recv_event({Addr,Port,[],#sctp_shutdown_event{}=ShutdownEvent}) ->
+ {Addr,Port,ShutdownEvent};
+recv_event({Addr,Port,
+ [#sctp_sndrcvinfo{assoc_id=Assoc}],
+ #sctp_shutdown_event{assoc_id=Assoc}=ShutdownEvent}) ->
+ {Addr,Port,ShutdownEvent}.
+
api_opts(doc) ->
"Test socket options";
api_opts(suite) ->
@@ -600,7 +696,7 @@ api_opts(Config) when is_list(Config) ->
end.
implicit_inet6(Config) when is_list(Config) ->
- ?line Hostname = ok(inet:gethostname()),
+ ?line Hostname = log_ok(inet:gethostname()),
?line
case gen_sctp:open(0, [inet6]) of
{ok,S1} ->
@@ -613,16 +709,16 @@ implicit_inet6(Config) when is_list(Config) ->
?line ok = gen_sctp:close(S1),
%%
?line Localhost =
- ok(inet:getaddr("localhost", inet6)),
+ log_ok(inet:getaddr("localhost", inet6)),
?line io:format("~s ~p~n", ["localhost",Localhost]),
?line S2 =
- ok(gen_sctp:open(0, [{ip,Localhost}])),
+ log_ok(gen_sctp:open(0, [{ip,Localhost}])),
?line implicit_inet6(S2, Localhost),
?line ok = gen_sctp:close(S2),
%%
?line io:format("~s ~p~n", [Hostname,Host]),
?line S3 =
- ok(gen_sctp:open(0, [{ifaddr,Host}])),
+ log_ok(gen_sctp:open(0, [{ifaddr,Host}])),
?line implicit_inet6(S3, Host),
?line ok = gen_sctp:close(S1);
{error,eafnosupport} ->
@@ -635,25 +731,159 @@ implicit_inet6(Config) when is_list(Config) ->
implicit_inet6(S1, Addr) ->
?line ok = gen_sctp:listen(S1, true),
- ?line P1 = ok(inet:port(S1)),
- ?line S2 = ok(gen_sctp:open(0, [inet6])),
- ?line P2 = ok(inet:port(S2)),
+ ?line P1 = log_ok(inet:port(S1)),
+ ?line S2 = log_ok(gen_sctp:open(0, [inet6])),
+ ?line P2 = log_ok(inet:port(S2)),
?line #sctp_assoc_change{state=comm_up} =
- ok(gen_sctp:connect(S2, Addr, P1, [])),
- ?line case ok(gen_sctp:recv(S1)) of
- {Addr,P2,[],#sctp_assoc_change{state=comm_up}} ->
- ok
+ log_ok(gen_sctp:connect(S2, Addr, P1, [])),
+ ?line case recv_event(log_ok(gen_sctp:recv(S1))) of
+ {Addr,P2,#sctp_assoc_change{state=comm_up}} ->
+ ok;
+ {Addr,P2,#sctp_paddr_change{state=addr_confirmed,
+ addr={Addr,P2},
+ error=0}} ->
+ {Addr,P2,#sctp_assoc_change{state=comm_up}} =
+ recv_event(log_ok(gen_sctp:recv(S1)))
end,
- ?line case ok(inet:sockname(S1)) of
+ ?line case log_ok(inet:sockname(S1)) of
{Addr,P1} -> ok;
{{0,0,0,0,0,0,0,0},P1} -> ok
end,
- ?line case ok(inet:sockname(S2)) of
+ ?line case log_ok(inet:sockname(S2)) of
{Addr,P2} -> ok;
{{0,0,0,0,0,0,0,0},P2} -> ok
end,
?line ok = gen_sctp:close(S2).
+basic_stream(doc) ->
+ "Hello world stream socket";
+basic_stream(suite) ->
+ [];
+basic_stream(Config) when is_list(Config) ->
+ ?line {ok,S} = gen_sctp:open([{type,stream}]),
+ ?line ok = gen_sctp:listen(S, true),
+ ?line ok =
+ do_from_other_process(
+ fun () -> gen_sctp:listen(S, 10) end),
+ ?line ok = gen_sctp:close(S),
+ ok.
+
+xfer_stream_min(doc) ->
+ "Minimal data transfer";
+xfer_stream_min(suite) ->
+ [];
+xfer_stream_min(Config) when is_list(Config) ->
+ ?line Stream = 0,
+ ?line Data = <<"The quick brown fox jumps over a lazy dog 0123456789">>,
+ ?line Loopback = {127,0,0,1},
+ ?line {ok,Sb} = gen_sctp:open([{type,seqpacket}]),
+ ?line ?LOGVAR(Sb),
+ ?line {ok,Pb} = inet:port(Sb),
+ ?line ?LOGVAR(Pb),
+ ?line ok = gen_sctp:listen(Sb, true),
+
+ ?line {ok,Sa} = gen_sctp:open([{type,stream}]),
+ ?line ?LOGVAR(Sa),
+ ?line {ok,Pa} = inet:port(Sa),
+ ?line ?LOGVAR(Pa),
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=SaOutboundStreams,
+ inbound_streams=SaInboundStreams,
+ assoc_id=SaAssocId_X} =
+ log_ok(gen_sctp:connect(Sa, Loopback, Pb, [])),
+ ?line ?LOGVAR(SaAssocId_X),
+ ?line [{_,#sctp_paddrinfo{assoc_id=SaAssocId,state=active}}] =
+ log_ok(inet:getopts(Sa, [{sctp_get_peer_addr_info,
+ #sctp_paddrinfo{address={Loopback,Pb}}}])),
+ ?line ?LOGVAR(SaAssocId),
+ ?line match_unless_solaris(SaAssocId_X, SaAssocId),
+
+ ?line {SbOutboundStreams,SbInboundStreams,SbAssocId} =
+ case recv_event(log_ok(gen_sctp:recv(Sb, infinity))) of
+ {Loopback,Pa,
+ #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=OS,
+ inbound_streams=IS,
+ assoc_id=AI}} ->
+ {OS,IS,AI};
+ {Loopback,Pa,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={Loopback,Pa},
+ error=0,
+ assoc_id=AI}} ->
+ {Loopback,Pa,
+ ?line #sctp_assoc_change{state=comm_up,
+ error=0,
+ outbound_streams=OS,
+ inbound_streams=IS,
+ assoc_id=AI}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ {OS,IS,AI}
+ end,
+ ?line ?LOGVAR(SbAssocId),
+ ?line SaOutboundStreams = SbInboundStreams,
+ ?line ?LOGVAR(SaOutboundStreams),
+ ?line SbOutboundStreams = SaInboundStreams,
+ ?line ?LOGVAR(SbOutboundStreams),
+ ?line ok = gen_sctp:send(Sa, SaAssocId, 0, Data),
+ ?line case gen_sctp:recv(Sb, infinity) of
+ {ok,{Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data}} -> ok;
+ {ok,{Loopback,
+ Pa,[],
+ #sctp_paddr_change{addr = {Loopback,_},
+ state = addr_available,
+ error = 0,
+ assoc_id = SbAssocId}}} ->
+ {ok,{Loopback,
+ Pa,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SbAssocId}],
+ Data}} = gen_sctp:recv(Sb, infinity)
+ end,
+ ?line ok =
+ do_from_other_process(
+ fun () -> gen_sctp:send(Sb, SbAssocId, 0, Data) end),
+ ?line case log_ok(gen_sctp:recv(Sa, infinity)) of
+ {Loopback,Pb,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SaAssocId}],
+ Data} -> ok;
+ Event1 ->
+ ?line {Loopback,Pb,
+ #sctp_paddr_change{state=addr_confirmed,
+ addr={_,Pb},
+ error=0,
+ assoc_id=SaAssocId}} =
+ recv_event(Event1),
+ ?line {Loopback,Pb,
+ [#sctp_sndrcvinfo{stream=Stream,
+ assoc_id=SaAssocId}],
+ Data} =
+ log_ok(gen_sctp:recv(Sa, infinity))
+ end,
+ ?line ok = gen_sctp:close(Sa),
+ ?line {Loopback,Pa,
+ #sctp_shutdown_event{assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ ?line {Loopback,Pa,
+ #sctp_assoc_change{state=shutdown_comp,
+ error=0,
+ assoc_id=SbAssocId}} =
+ recv_event(log_ok(gen_sctp:recv(Sb, infinity))),
+ ?line ok = gen_sctp:close(Sb),
+
+ ?line receive
+ Msg -> test_server:fail({received,Msg})
+ after 17 -> ok
+ end,
+ ok.
+
do_from_other_process(Fun) ->
@@ -681,3 +911,419 @@ do_from_other_process(Fun) ->
{'DOWN',Mref,_,_,Reason} ->
erlang:exit(Reason)
end.
+
+
+
+peeloff(doc) ->
+ "Peel off an SCTP stream socket";
+peeloff(suite) ->
+ [];
+peeloff(Config) when is_list(Config) ->
+ ?line Addr = {127,0,0,1},
+ ?line Stream = 0,
+ ?line Timeout = 333,
+ ?line S1 = socket_open([{ifaddr,Addr}], Timeout),
+ ?line ?LOGVAR(S1),
+ ?line P1 = socket_call(S1, get_port),
+ ?line ?LOGVAR(P1),
+ ?line Socket1 = socket_call(S1, get_socket),
+ ?line ?LOGVAR(Socket1),
+ ?line socket_call(S1, {listen,true}),
+ ?line S2 = socket_open([{ifaddr,Addr}], Timeout),
+ ?line ?LOGVAR(S2),
+ ?line P2 = socket_call(S2, get_port),
+ ?line ?LOGVAR(P2),
+ ?line Socket2 = socket_call(S2, get_socket),
+ ?line ?LOGVAR(Socket2),
+ %%
+ ?line socket_call(S2, {connect_init,Addr,P1,[]}),
+ ?line S2Ai =
+ receive
+ {S2,{Addr,P1,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId2}}} -> AssocId2
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line ?LOGVAR(S2Ai),
+ ?line S1Ai =
+ receive
+ {S1,{Addr,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId1}}} -> AssocId1
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line ?LOGVAR(S1Ai),
+ %%
+ ?line socket_call(S2, {send,S2Ai,Stream,<<"Number one">>}),
+ ?line
+ receive
+ {S1,{Addr,P2,S1Ai,Stream,<<"Number one">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line socket_call(S2, {send,Socket1,S1Ai,Stream,<<"Number two">>}),
+ ?line
+ receive
+ {S2,{Addr,P1,S2Ai,Stream,<<"Number two">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ %%
+ ?line S3 = socket_peeloff(Socket1, S1Ai, Timeout),
+ ?line ?LOGVAR(S3),
+ ?line P3_X = socket_call(S3, get_port),
+ ?line ?LOGVAR(P3_X),
+ ?line P3 = case P3_X of 0 -> P1; _ -> P3_X end,
+ ?line [{_,#sctp_paddrinfo{assoc_id=S3Ai,state=active}}] =
+ socket_call(S3,
+ {getopts,[{sctp_get_peer_addr_info,
+ #sctp_paddrinfo{address={Addr,P2}}}]}),
+ %%?line S3Ai = S1Ai,
+ ?line ?LOGVAR(S3Ai),
+ %%
+ ?line socket_call(S3, {send,S3Ai,Stream,<<"Number three">>}),
+ ?line
+ receive
+ {S2,{Addr,P3,S2Ai,Stream,<<"Number three">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2,S3])
+ end,
+ ?line socket_call(S3, {send,Socket2,S2Ai,Stream,<<"Number four">>}),
+ ?line
+ receive
+ {S3,{Addr,P2,S3Ai,Stream,<<"Number four">>}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2,S3])
+ end,
+ %%
+ ?line inet:i(sctp),
+ ?line socket_close_verbose(S1),
+ ?line socket_close_verbose(S2),
+ ?line
+ receive
+ {S3,{Addr,P2,#sctp_shutdown_event{assoc_id=S3Ai_X}}} ->
+ ?line match_unless_solaris(S3Ai, S3Ai_X)
+ after Timeout ->
+ socket_bailout([S3])
+ end,
+ ?line
+ receive
+ {S3,{Addr,P2,#sctp_assoc_change{state=shutdown_comp,
+ assoc_id=S3Ai}}} -> ok
+ after Timeout ->
+ socket_bailout([S3])
+ end,
+ ?line socket_close_verbose(S3),
+ ?line [] = flush(),
+ ok.
+
+
+
+buffers(doc) ->
+ ["Check sndbuf and recbuf behaviour"];
+buffers(suite) ->
+ [];
+buffers(Config) when is_list(Config) ->
+ ?line Limit = 4096,
+ ?line Addr = {127,0,0,1},
+ ?line Stream = 1,
+ ?line Timeout = 3333,
+ ?line S1 = socket_open([{ip,Addr}], Timeout),
+ ?line ?LOGVAR(S1),
+ ?line P1 = socket_call(S1, get_port),
+ ?line ?LOGVAR(P1),
+ ?line ok = socket_call(S1, {listen,true}),
+ ?line S2 = socket_open([{ip,Addr}], Timeout),
+ ?line ?LOGVAR(S2),
+ ?line P2 = socket_call(S2, get_port),
+ ?line ?LOGVAR(P2),
+ %%
+ ?line socket_call(S2, {connect_init,Addr,P1,[]}),
+ ?line S2Ai =
+ receive
+ {S2,{Addr,P1,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId2}}} -> AssocId2
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ ?line S1Ai =
+ receive
+ {S1,{Addr,P2,
+ #sctp_assoc_change{
+ state=comm_up,
+ assoc_id=AssocId1}}} -> AssocId1
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ %%
+ ?line socket_call(S1, {setopts,[{recbuf,Limit}]}),
+ ?line Recbuf =
+ case socket_call(S1, {getopts,[recbuf]}) of
+ [{recbuf,RB1}] when RB1 >= Limit -> RB1
+ end,
+ ?line Data = mk_data(Recbuf+Limit),
+ ?line socket_call(S2, {setopts,[{sndbuf,Recbuf+Limit}]}),
+ ?line socket_call(S2, {send,S2Ai,Stream,Data}),
+ ?line
+ receive
+ {S1,{Addr,P2,S1Ai,Stream,Data}} -> ok
+ after Timeout ->
+ socket_bailout([S1,S2])
+ end,
+ %%
+ ?line socket_close_verbose(S1),
+ ?line
+ receive
+ {S2,{Addr,P1,#sctp_shutdown_event{assoc_id=S2Ai}}} -> ok
+ after Timeout ->
+ socket_bailout([S2])
+ end,
+ ?line
+ receive
+ {S2,{Addr,P1,#sctp_assoc_change{state=shutdown_comp,
+ assoc_id=S2Ai}}} -> ok
+ after Timeout ->
+ socket_bailout([S2])
+ end,
+ ?line socket_close_verbose(S2),
+ ?line [] = flush(),
+ ok.
+
+mk_data(Bytes) ->
+ mk_data(0, Bytes, <<>>).
+%%
+mk_data(N, Bytes, Bin) when N < Bytes ->
+ mk_data(N+4, Bytes, <<Bin/binary,N:32>>);
+mk_data(_, _, Bin) ->
+ Bin.
+
+%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%% socket gen_server ultra light
+
+socket_open(SocketOpts, Timeout) ->
+ Opts = [{type,seqpacket},{active,once},binary|SocketOpts],
+ Starter =
+ fun () ->
+ {ok,Socket} =
+ gen_sctp:open(Opts),
+ Socket
+ end,
+ s_start(Starter, Timeout).
+
+socket_peeloff(Socket, AssocId, Timeout) ->
+ Opts = [{active,once},binary],
+ Starter =
+ fun () ->
+ {ok,NewSocket} =
+ gen_sctp:peeloff(Socket, AssocId),
+ ok = inet:setopts(NewSocket, Opts),
+ NewSocket
+ end,
+ s_start(Starter, Timeout).
+
+socket_close_verbose(S) ->
+ History = socket_history(socket_close(S)),
+ io:format("socket_close ~p:~n ~p.~n", [S,History]),
+ History.
+
+socket_close(S) ->
+ s_req(S, close).
+
+socket_call(S, Request) ->
+ s_req(S, {Request}).
+
+%% socket_get(S, Key) ->
+%% s_req(S, {get,Key}).
+
+socket_bailout([S|Ss]) ->
+ History = socket_history(socket_close(S)),
+ io:format("bailout ~p:~n ~p.~n", [S,History]),
+ socket_bailout(Ss);
+socket_bailout([]) ->
+ io:format("flush: ~p.~n", [flush()]),
+ test_server:fail(socket_bailout).
+
+socket_history({State,Flush}) ->
+ {lists:keysort(
+ 2,
+ lists:flatten(
+ [[{Key,Val} || Val <- Vals]
+ || {Key,Vals} <- gb_trees:to_list(State)])),
+ Flush}.
+
+s_handler(Socket) ->
+ fun ({listen,Listen}) ->
+ ok = gen_sctp:listen(Socket, Listen);
+ (get_port) ->
+ ok(inet:port(Socket));
+ (get_socket) ->
+ Socket;
+ ({connect_init,ConAddr,ConPort,ConOpts}) ->
+ ok = gen_sctp:connect_init(Socket, ConAddr, ConPort, ConOpts);
+ ({send,AssocId,Stream,Data}) ->
+ ok = gen_sctp:send(Socket, AssocId, Stream, Data);
+ ({send,OtherSocket,AssocId,Stream,Data}) ->
+ ok = gen_sctp:send(OtherSocket, AssocId, Stream, Data);
+ ({setopts,Opts}) ->
+ ok = inet:setopts(Socket, Opts);
+ ({getopts,Optnames}) ->
+ ok(inet:getopts(Socket, Optnames))
+ end.
+
+s_req(S, Req) ->
+ Mref = erlang:monitor(process, S),
+ S ! {self(),Mref,Req},
+ receive
+ {'DOWN',Mref,_,_,Error} ->
+ exit(Error);
+ {S,Mref,Reply} ->
+ erlang:demonitor(Mref),
+ receive {'DOWN',Mref,_,_,_} -> ok after 0 -> ok end,
+ Reply
+ end.
+
+s_start(Starter, Timeout) ->
+ Parent = self(),
+ Owner =
+ spawn_link(
+ fun () ->
+ s_start(Starter(), Timeout, Parent)
+ end),
+ Owner.
+
+s_start(Socket, Timeout, Parent) ->
+ Handler = s_handler(Socket),
+ try
+ s_loop(Socket, Timeout, Parent, Handler, gb_trees:empty())
+ catch
+ Class:Reason ->
+ Stacktrace = erlang:get_stacktrace(),
+ io:format(?MODULE_STRING":socket exception ~w:~w at~n"
+ "~p.~n", [Class,Reason,Stacktrace]),
+ erlang:raise(Class, Reason, Stacktrace)
+ end.
+
+s_loop(Socket, Timeout, Parent, Handler, State) ->
+ receive
+ {Parent,Ref,close} -> % socket_close()
+ erlang:send_after(Timeout, self(), {Parent,Ref,exit}),
+ 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),
+ Parent ! {self(),Ref,{NewState,flush()}};
+ {Parent,Ref,{Msg}} ->
+ Result = Handler(Msg),
+ Key = req,
+ Val = {now(),{Msg,Result}},
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(),Ref,Result},
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ %% {Parent,Ref,{get,Key}} ->
+ %% Parent ! {self(),Ref,gb_get(Key, State)},
+ %% s_loop(Socket, Timeout, Parent, Handler, State);
+ {sctp,Socket,Addr,Port,
+ {[#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}}}|_]
+ when 0 =< Stream, Stream < Is-> ok;
+ [] -> ok
+ end,
+ Key = {msg,AssocId,Stream},
+ Val = {now(),{Addr,Port,SRI,Data}},
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(),{Addr,Port,AssocId,Stream,Data}},
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ {sctp,Socket,Addr,Port,
+ {SRI,#sctp_assoc_change{assoc_id=AssocId,state=St}=SAC}} ->
+ case SRI of
+ [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+ [] -> 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}}}|_],_}
+ when St =:= comm_lost; St =:= shutdown_comp -> ok
+ end,
+ NewState = gb_push(Key, Val, State),
+ Parent ! {self(),{Addr,Port,SAC}},
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ {sctp,Socket,Addr,Port,
+ {SRI,#sctp_paddr_change{assoc_id=AssocId,
+ addr={_,P},
+ state=St}=SPC}} ->
+ match_unless_solaris(Port, P),
+ case SRI of
+ [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+ [] -> ok
+ end,
+ case {gb_get({assoc_change,AssocId}, State),St} of
+ {[{_,{Addr,Port,#sctp_assoc_change{state=comm_up}}}|_],
+ addr_available} -> ok;
+ {[],addr_confirmed} -> ok
+ end,
+ Key = {paddr_change,AssocId},
+ Val = {now(),{Addr,Port,SPC}},
+ NewState = gb_push(Key, Val, State),
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ {sctp,Socket,Addr,Port,
+ {SRI,#sctp_shutdown_event{assoc_id=AssocId}=SSE}} ->
+ case SRI of
+ [#sctp_sndrcvinfo{assoc_id=AssocId,stream=0}] -> ok;
+ [] -> ok
+ end,
+ case gb_get({assoc_change,AssocId}, State) of
+ [{_,{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),
+ Parent ! {self(), {Addr,Port,SSE}},
+ again(Socket),
+ s_loop(Socket, Timeout, Parent, Handler, NewState);
+ Unexpected ->
+ erlang:error({unexpected,Unexpected})
+ end.
+
+again(Socket) ->
+ inet:setopts(Socket, [{active,once}]).
+
+gb_push(Key, Val, GBT) ->
+ case gb_trees:lookup(Key, GBT) of
+ none ->
+ gb_trees:insert(Key, [Val], GBT);
+ {value,V} ->
+ gb_trees:update(Key, [Val|V], GBT)
+ end.
+
+gb_get(Key, GBT) ->
+ case gb_trees:lookup(Key, GBT) of
+ none ->
+ [];
+ {value,V} ->
+ V
+ end.
+
+match_unless_solaris(A, B) ->
+ case os:type() of
+ {unix,sunos} -> B;
+ _ -> A = B
+ end.
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 514deaf065..2354f8accd 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -99,9 +99,9 @@ buffer_size(Config) when is_list(Config) ->
?line Bin = list_to_binary(lists:seq(0, Len-1)),
?line M = 8192 div Len,
?line Spec0 =
- [{opt,M},{safe,M-1},{long,M+1},
- {opt,2*M},{safe,2*M-1},{long,2*M+1},
- {opt,4*M},{safe,4*M-1},{long,4*M+1}],
+ [{opt,M},{safe,M-3},{long,M+1},
+ {opt,2*M},{safe,2*M-3},{long,2*M+1},
+ {opt,4*M},{safe,4*M-3},{long,4*M+1}],
?line Spec =
[case Tag of
opt ->
@@ -145,16 +145,27 @@ buffer_size_client(_, _, _, _, _, []) ->
?line ok;
buffer_size_client(Server, IP, Port,
Socket, Cnt, [Opts|T]) when is_list(Opts) ->
+ ?line io:format("buffer_size_client Cnt=~w setopts ~p.~n", [Cnt,Opts]),
?line ok = inet:setopts(Socket, Opts),
?line Server ! {self(),setopts,Cnt},
?line receive {Server,setopts,Cnt} -> ok end,
?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T);
buffer_size_client(Server, IP, Port,
- Socket, Cnt, [{B,Replies}|T]) when is_binary(B) ->
- ?line ok = gen_udp:send(Socket, IP, Port, B),
+ Socket, Cnt, [{B,Replies}|T]=Opts) when is_binary(B) ->
+ ?line io:format(
+ "buffer_size_client Cnt=~w send size ~w expecting ~p.~n",
+ [Cnt,size(B),Replies]),
+ ?line ok = gen_udp:send(Socket, IP, Port, <<Cnt,B/binary>>),
?line receive
{Server,Cnt,Reply} ->
- ?line case lists:member(Reply, Replies) of
+ ?line Tag =
+ if
+ is_tuple(Reply) ->
+ element(1, Reply);
+ is_atom(Reply) ->
+ Reply
+ end,
+ ?line case lists:member(Tag, Replies) of
true -> ok;
false ->
?line
@@ -162,34 +173,62 @@ buffer_size_client(Server, IP, Port,
byte_size(B),
inet:getopts(Socket,
[sndbuf,recbuf])})
- end
- end,
- ?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T).
+ end,
+ ?line buffer_size_client(Server, IP, Port, Socket, Cnt+1, T)
+ after 1313 ->
+ ?line buffer_size_client(Server, IP, Port, Socket, Cnt, Opts)
+ end.
buffer_size_server(_, _, _, _, _, []) ->
ok;
buffer_size_server(Client, IP, Port,
Socket, Cnt, [Opts|T]) when is_list(Opts) ->
receive {Client,setopts,Cnt} -> ok end,
+ ?line io:format("buffer_size_server Cnt=~w setopts ~p.~n", [Cnt,Opts]),
ok = inet:setopts(Socket, Opts),
Client ! {self(),setopts,Cnt},
buffer_size_server(Client, IP, Port, Socket, Cnt+1, T);
buffer_size_server(Client, IP, Port,
Socket, Cnt, [{B,_}|T]) when is_binary(B) ->
+ ?line io:format(
+ "buffer_size_server Cnt=~w expecting size ~w.~n",
+ [Cnt,size(B)]),
Client !
{self(),Cnt,
- receive
- {udp,Socket,IP,Port,D} when is_binary(D) ->
+ case buffer_size_server_recv(Socket, IP, Port, Cnt) of
+ D when is_binary(D) ->
SizeD = byte_size(D),
+ ?line io:format(
+ "buffer_size_server Cnt=~w received size ~w.~n",
+ [Cnt,SizeD]),
case B of
- D -> correct;
- <<D:SizeD/binary,_/binary>> -> truncated
+ D ->
+ correct;
+ <<D:SizeD/binary,_/binary>> ->
+ truncated;
+ _ ->
+ {unexpected,D}
end;
- {udp_error,Socket,Error} -> Error
- after 5000 -> timeout
+ Error ->
+ ?line io:format(
+ "buffer_size_server Cnt=~w received error ~w.~n",
+ [Cnt,Error]),
+ Error
end},
buffer_size_server(Client, IP, Port, Socket, Cnt+1, T).
+buffer_size_server_recv(Socket, IP, Port, Cnt) ->
+ receive
+ {udp,Socket,IP,Port,<<Cnt,B/binary>>} ->
+ B;
+ {udp,Socket,IP,Port,<<_/binary>>} ->
+ buffer_size_server_recv(Socket, IP, Port, Cnt);
+ {udp_error,Socket,Error} ->
+ Error
+ after 5000 ->
+ {timeout,flush()}
+ end.
+
%%-------------------------------------------------------------
diff --git a/lib/kernel/test/kernel_SUITE.erl b/lib/kernel/test/kernel_SUITE.erl
index 16b6c54939..0f29d895e5 100644
--- a/lib/kernel/test/kernel_SUITE.erl
+++ b/lib/kernel/test/kernel_SUITE.erl
@@ -32,7 +32,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
--export([app_test/1]).
+-export([app_test/1, appup_test/1]).
%%
%% all/1
@@ -40,7 +40,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test].
+ [app_test, appup_test].
groups() ->
[].
@@ -76,3 +76,63 @@ app_test(suite) ->
app_test(Config) when is_list(Config) ->
?line ok=?t:app_test(kernel),
ok.
+
+
+%% Test that appup allows upgrade from/downgrade to a maximum of two
+%% major releases back.
+appup_test(_Config) ->
+ application:load(kernel),
+ {_,_,Vsn} = lists:keyfind(kernel,1,application:loaded_applications()),
+ AppupFile = filename:join([code:lib_dir(kernel),ebin,"kernel.appup"]),
+ {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
+ ct:log("~p~n",[AppupScript]),
+ {OkVsns,NokVsns} = create_test_vsns(Vsn),
+ check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
+ check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
+ check_appup(NokVsns,UpFrom,error),
+ check_appup(NokVsns,DownTo,error),
+ ok.
+
+create_test_vsns(Current) ->
+ [XStr,YStr|Rest] = string:tokens(Current,"."),
+ X = list_to_integer(XStr),
+ Y = list_to_integer(YStr),
+ SecondMajor = vsn(X,Y-2),
+ SecondMinor = SecondMajor ++ ".1.3",
+ FirstMajor = vsn(X,Y-1),
+ FirstMinor = FirstMajor ++ ".57",
+ ThisMajor = vsn(X,Y),
+ This =
+ case Rest of
+ [] ->
+ [];
+ ["1"] ->
+ [ThisMajor];
+ _ ->
+ ThisMinor = ThisMajor ++ ".1",
+ [ThisMajor,ThisMinor]
+ end,
+ OkVsns = This ++ [FirstMajor, FirstMinor, SecondMajor, SecondMinor],
+
+ ThirdMajor = vsn(X,Y-3),
+ ThirdMinor = ThirdMajor ++ ".10.12",
+ Illegal = ThisMajor ++ ",1",
+ Newer1Major = vsn(X,Y+1),
+ Newer1Minor = Newer1Major ++ ".1",
+ Newer2Major = ThisMajor ++ "1",
+ NokVsns = [ThirdMajor,ThirdMinor,
+ Illegal,
+ Newer1Major,Newer1Minor,
+ Newer2Major],
+ {OkVsns,NokVsns}.
+
+vsn(X,Y) ->
+ integer_to_list(X) ++ "." ++ integer_to_list(Y).
+
+check_appup([Vsn|Vsns],Instrs,Expected) ->
+ case systools_relup:appup_search_for_version(Vsn, Instrs) of
+ Expected -> check_appup(Vsns,Instrs,Expected);
+ Other -> ct:fail({unexpected_result_for_vsn,Vsn,Other})
+ end;
+check_appup([],_,_) ->
+ ok.