aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/notes.xml215
-rw-r--r--lib/kernel/src/code_server.erl20
-rw-r--r--lib/kernel/src/erl_epmd.erl6
-rw-r--r--lib/kernel/src/inet.erl67
-rw-r--r--lib/kernel/src/inet_tcp_dist.erl2
-rw-r--r--lib/kernel/src/local_tcp.erl2
-rw-r--r--lib/kernel/src/os.erl48
-rw-r--r--lib/kernel/test/gen_sctp_SUITE.erl148
-rw-r--r--lib/kernel/test/gen_tcp_api_SUITE.erl265
-rw-r--r--lib/kernel/test/gen_udp_SUITE.erl177
-rw-r--r--lib/kernel/test/init_SUITE.erl2
-rw-r--r--lib/kernel/test/loose_node.erl11
-rw-r--r--lib/kernel/test/os_SUITE.erl22
-rw-r--r--lib/kernel/vsn.mk2
14 files changed, 841 insertions, 146 deletions
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index d0540768de..0e3914b083 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -31,6 +31,221 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 5.0.1</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix a os:cmd bug where creating a background job using
+ &amp; would cause os:cmd to hang until the background job
+ terminated or closed its stdout and stderr file
+ descriptors. This bug has existed from kernel 5.0.</p>
+ <p>
+ Own Id: OTP-13741</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Kernel 5.0</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>The handling of <c>on_load</c> functions has been
+ improved. The major improvement is that if a code upgrade
+ fails because the <c>on_load</c> function fails, the
+ previous version of the module will now be retained.</p>
+ <p>
+ Own Id: OTP-12593</p>
+ </item>
+ <item>
+ <p><c>rpc:call()</c> and <c>rpc:block_call()</c> would
+ sometimes cause an exception (which was not mentioned in
+ the documentation). This has been corrected so that
+ <c>{badrpc,Reason}</c> will be returned instead.</p>
+ <p>
+ Own Id: OTP-13409</p>
+ </item>
+ <item>
+ <p>On Windows, for modules that were loaded early (such
+ as the <c>lists</c> module), <c>code:which/1</c> would
+ return the path with mixed slashes and backslashes, for
+ example: <c>"C:\\Program
+ Files\\erl8.0/lib/stdlib-2.7/ebin/lists.beam"</c>. This
+ has been corrected.</p>
+ <p>
+ Own Id: OTP-13410</p>
+ </item>
+ <item>
+ <p>
+ Make file:datasync use fsync instead of fdatasync on Mac
+ OSX.</p>
+ <p>
+ Own Id: OTP-13411</p>
+ </item>
+ <item>
+ <p>
+ The default chunk size for the fallback sendfile
+ implementation, used on platforms that do not have a
+ native sendfile, has been decreased in order to reduce
+ connectivity issues.</p>
+ <p>
+ Own Id: OTP-13444</p>
+ </item>
+ <item>
+ <p>
+ Large file writes (2Gb or more) could fail on some Unix
+ platforms (for example, OS X and FreeBSD).</p>
+ <p>
+ Own Id: OTP-13461</p>
+ </item>
+ <item>
+ <p>
+ A bug has been fixed where the DNS resolver inet_res did
+ not refresh its view of the contents of for example
+ resolv.conf immediately after start and hence then failed
+ name resolution. Reported and fix suggested by Michal
+ Ptaszek in GitHUB pull req #949.</p>
+ <p>
+ Own Id: OTP-13470 Aux Id: Pull #969 </p>
+ </item>
+ <item>
+ <p>
+ Fix process leak from global_group.</p>
+ <p>
+ Own Id: OTP-13516 Aux Id: PR-1008 </p>
+ </item>
+ <item>
+ <p>
+ The function <c>inet:gethostbyname/1</c> now honors the
+ resolver option <c>inet6</c> instead of always looking up
+ IPv4 addresses.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13622 Aux Id: PR-1065 </p>
+ </item>
+ <item>
+ <p>
+ The <c>Status</c> argument to <c>init:stop/1</c> is now
+ sanity checked to make sure <c>erlang:halt</c> does not
+ fail.</p>
+ <p>
+ Own Id: OTP-13631 Aux Id: PR-911 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Add {line_delim, byte()} option to inet:setopts/2 and
+ decode_packet/3</p>
+ <p>
+ Own Id: OTP-12837</p>
+ </item>
+ <item>
+ <p>
+ Added <seealso
+ marker="kernel:os#perf_counter/1">os:perf_counter/1</seealso>.</p>
+ <p>
+ The perf_counter is a very very cheap and high resolution
+ timer that can be used to timestamp system events. It
+ does not have monoticity guarantees, but should on most
+ OS's expose a monotonous time.</p>
+ <p>
+ Own Id: OTP-12908</p>
+ </item>
+ <item>
+ <p>
+ The os:cmd call has been optimized on unix platforms to
+ be scale better with the number of schedulers.</p>
+ <p>
+ Own Id: OTP-13089</p>
+ </item>
+ <item>
+ <p>New functions that can load multiple modules at once
+ have been added to the '<c>code</c>' module. The
+ functions are <c>code:atomic_load/1</c>,
+ <c>code:prepare_loading/1</c>,
+ <c>code:finish_loading/1</c>, and
+ <c>code:ensure_modules_loaded/1</c>.</p>
+ <p>
+ Own Id: OTP-13111</p>
+ </item>
+ <item>
+ <p>
+ The code path cache feature turned out not to be very
+ useful in practice and has been removed. If an attempt is
+ made to enable the code path cache, there will be a
+ warning report informing the user that the feature has
+ been removed.</p>
+ <p>
+ Own Id: OTP-13191</p>
+ </item>
+ <item>
+ <p>When an attempt is made to start a distributed Erlang
+ node with the same name as an existing node, the error
+ message will be much shorter and easier to read than
+ before. Example:</p>
+ <p><c>Protocol 'inet_tcp': the name somename@somehost
+ seems to be in use by another Erlang node</c></p>
+ <p>
+ Own Id: OTP-13294</p>
+ </item>
+ <item>
+ <p>
+ The output of the default error logger is somewhat
+ prettier and easier to read. The default error logger is
+ used during start-up of the OTP system. If the start-up
+ fails, the output will be easier to read.</p>
+ <p>
+ Own Id: OTP-13325</p>
+ </item>
+ <item>
+ <p>The functions <c>rpc:safe_multi_server_call/2,3</c>
+ that were deprecated in R12B have been removed.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-13449</p>
+ </item>
+ <item>
+ <p>
+ Update the error reasons in dist_util, and show them in
+ the logs if net_kernel:verbose(1) has been called.</p>
+ <p>
+ Own Id: OTP-13458</p>
+ </item>
+ <item>
+ <p>
+ Experimental support for Unix Domain Sockets has been
+ implemented. Read the sources if you want to try it out.
+ Example: <c>gen_udp:open(0,
+ [{ifaddr,{local,"/tmp/socket"}}])</c>. Documentation will
+ be written after user feedback on the experimental API.</p>
+ <p>
+ Own Id: OTP-13572 Aux Id: PR-612 </p>
+ </item>
+ <item>
+ <p>
+ Allow heart to be configured to not kill the previous
+ emulator before calling the HEART_COMMAND. This is done
+ by setting the environment variable HEART_NO_KILL to
+ TRUE.</p>
+ <p>
+ Own Id: OTP-13650</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 4.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl
index 6174136507..1792f9e908 100644
--- a/lib/kernel/src/code_server.erl
+++ b/lib/kernel/src/code_server.erl
@@ -933,14 +933,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/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 9fc685e728..a91a6ed517 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -1344,18 +1344,12 @@ open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module)
case prim_inet:open(Protocol, Family, Type, OpenOpts) of
{ok,S} ->
case prim_inet:setopts(S, Opts) of
+ ok when Addr =:= undefined ->
+ inet_db:register_socket(S, Module),
+ {ok,S};
ok ->
- case
- case Addr of
- undefined ->
- {ok, undefined};
- _ when is_list(Addr) ->
- bindx(S, Addr, Port);
- _ ->
- prim_inet:bind(S, Addr, Port)
- end
- of
- {ok, _} ->
+ case bind(S, Addr, Port) of
+ {ok, _} ->
inet_db:register_socket(S, Module),
{ok,S};
Error ->
@@ -1373,6 +1367,11 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module)
when is_integer(Fd) ->
fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module).
+bind(S, Addr, Port) when is_list(Addr) ->
+ bindx(S, Addr, Port);
+bind(S, Addr, Port) ->
+ prim_inet:bind(S, Addr, Port).
+
bindx(S, [Addr], Port0) ->
{IP, Port} = set_bindx_port(Addr, Port0),
prim_inet:bind(S, IP, Port);
@@ -1413,34 +1412,36 @@ fdopen(Fd, Opts, Protocol, Family, Type, Module) ->
fdopen(Fd, any, 0, Opts, Protocol, Family, Type, Module).
fdopen(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) ->
- IsAnyAddr = (Addr == {0,0,0,0} orelse Addr == {0,0,0,0,0,0,0,0}
- orelse Addr == any),
- Bound = Port == 0 andalso IsAnyAddr,
+ Bound =
+ %% We do not do any binding if default port+addr options
+ %% were given, in order to keep backwards compatability
+ %% with pre Erlang/OTP 17
+ case Addr of
+ {0,0,0,0} when Port =:= 0 -> true;
+ {0,0,0,0,0,0,0,0} when Port =:= 0 -> true;
+ any when Port =:= 0 -> true;
+ _ -> false
+ end,
case prim_inet:fdopen(Protocol, Family, Type, Fd, Bound) of
{ok, S} ->
case prim_inet:setopts(S, Opts) of
+ ok
+ when Addr =:= undefined;
+ Bound ->
+ inet_db:register_socket(S, Module),
+ {ok, S};
ok ->
- case if
- Bound ->
- %% We do not do any binding if default
- %% port+addr options where given in order
- %% to keep backwards compatability with
- %% pre Erlang/TOP 17
- {ok, ok};
- is_list(Addr) ->
- bindx(S, Addr, Port);
- true ->
- prim_inet:bind(S, Addr, Port)
- end of
- {ok, _} ->
- inet_db:register_socket(S, Module),
- {ok, S};
- Error ->
- prim_inet:close(S),
- Error
+ case bind(S, Addr, Port) of
+ {ok, _} ->
+ inet_db:register_socket(S, Module),
+ {ok, S};
+ Error ->
+ prim_inet:close(S),
+ Error
end;
Error ->
- prim_inet:close(S), Error
+ prim_inet:close(S),
+ Error
end;
Error -> Error
end.
diff --git a/lib/kernel/src/inet_tcp_dist.erl b/lib/kernel/src/inet_tcp_dist.erl
index f91d7ef7c3..94bde55133 100644
--- a/lib/kernel/src/inet_tcp_dist.erl
+++ b/lib/kernel/src/inet_tcp_dist.erl
@@ -74,7 +74,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 ->
diff --git a/lib/kernel/src/local_tcp.erl b/lib/kernel/src/local_tcp.erl
index e3c67dfbb7..90e0fa2162 100644
--- a/lib/kernel/src/local_tcp.erl
+++ b/lib/kernel/src/local_tcp.erl
@@ -175,4 +175,4 @@ accept(L, Timeout) ->
%% Create a port/socket from a file descriptor
%%
fdopen(Fd, Opts) ->
- inet:fdopen(Fd, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE).
+ inet:open(Fd, undefined, 0, Opts, ?PROTO, ?FAMILY, ?TYPE, ?MODULE).
diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl
index f0ad26b1f2..81b70a7fee 100644
--- a/lib/kernel/src/os.erl
+++ b/lib/kernel/src/os.erl
@@ -226,11 +226,13 @@ extensions() ->
Command :: atom() | io_lib:chars().
cmd(Cmd) ->
validate(Cmd),
- {SpawnCmd, SpawnOpts, SpawnInput} = mk_cmd(os:type(), Cmd),
+ {SpawnCmd, SpawnOpts, SpawnInput, Eot} = mk_cmd(os:type(), Cmd),
Port = open_port({spawn, SpawnCmd}, [binary, stderr_to_stdout,
- stream, in, eof, hide | SpawnOpts]),
+ stream, in, hide | SpawnOpts]),
+ MonRef = erlang:monitor(port, Port),
true = port_command(Port, SpawnInput),
- Bytes = get_data(Port, []),
+ Bytes = get_data(Port, MonRef, Eot, []),
+ demonitor(MonRef, [flush]),
String = unicode:characters_to_list(Bytes),
if %% Convert to unicode list if possible otherwise return bytes
is_list(String) -> String;
@@ -243,7 +245,7 @@ mk_cmd({win32,Wtype}, Cmd) ->
{false,_} -> lists:concat(["cmd /c", Cmd]);
{Cspec,_} -> lists:concat([Cspec," /c",Cmd])
end,
- {Command, [], []};
+ {Command, [], [], <<>>};
mk_cmd(OsType,Cmd) when is_atom(Cmd) ->
mk_cmd(OsType, atom_to_list(Cmd));
mk_cmd(_,Cmd) ->
@@ -252,7 +254,8 @@ mk_cmd(_,Cmd) ->
{"/bin/sh -s unix:cmd", [out],
%% We insert a new line after the command, in case the command
%% contains a comment character.
- ["(", unicode:characters_to_binary(Cmd), "\n); exit\n"]}.
+ ["(", unicode:characters_to_binary(Cmd), "\n); echo \"\^D\"\n"],
+ <<$\^D>>}.
validate(Atom) when is_atom(Atom) ->
ok;
@@ -267,16 +270,18 @@ validate1([List|Rest]) when is_list(List) ->
validate1([]) ->
ok.
-get_data(Port, Sofar) ->
+get_data(Port, MonRef, Eot, Sofar) ->
receive
{Port, {data, Bytes}} ->
- get_data(Port, [Sofar,Bytes]);
- {Port, eof} ->
- Port ! {self(), close},
- receive
- {Port, closed} ->
- true
- end,
+ case eot(Bytes, Eot) of
+ more ->
+ get_data(Port, MonRef, Eot, [Sofar,Bytes]);
+ Last ->
+ Port ! {self(), close},
+ flush_until_closed(Port),
+ iolist_to_binary([Sofar, Last])
+ end;
+ {'DOWN', MonRef, _, _ , _} ->
receive
{'EXIT', Port, _} ->
ok
@@ -285,3 +290,20 @@ get_data(Port, Sofar) ->
end,
iolist_to_binary(Sofar)
end.
+
+eot(_Bs, <<>>) ->
+ more;
+eot(Bs, Eot) ->
+ case binary:match(Bs, Eot) of
+ nomatch -> more;
+ {Pos, _} ->
+ binary:part(Bs,{0, Pos})
+ end.
+
+flush_until_closed(Port) ->
+ receive
+ {Port, {data, _Bytes}} ->
+ flush_until_closed(Port);
+ {Port, closed} ->
+ true
+ end.
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/gen_tcp_api_SUITE.erl b/lib/kernel/test/gen_tcp_api_SUITE.erl
index 54298e6309..77ec89b4f4 100644
--- a/lib/kernel/test/gen_tcp_api_SUITE.erl
+++ b/lib/kernel/test/gen_tcp_api_SUITE.erl
@@ -34,7 +34,11 @@
t_recv_timeout/1, t_recv_eof/1, t_recv_delim/1,
t_shutdown_write/1, t_shutdown_both/1, t_shutdown_error/1,
t_shutdown_async/1,
- t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1]).
+ t_fdopen/1, t_fdconnect/1, t_implicit_inet6/1,
+ t_local_basic/1, t_local_unbound/1, t_local_fdopen/1,
+ t_local_fdopen_listen/1, t_local_fdopen_listen_unbound/1,
+ t_local_fdopen_connect/1, t_local_fdopen_connect_unbound/1,
+ t_local_abstract/1]).
-export([getsockfd/0,closesockfd/1]).
@@ -45,12 +49,18 @@ suite() ->
all() ->
[{group, t_accept}, {group, t_connect}, {group, t_recv},
t_shutdown_write, t_shutdown_both, t_shutdown_error,
- t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6].
+ t_shutdown_async, t_fdopen, t_fdconnect, t_implicit_inet6,
+ {group, t_local}].
groups() ->
[{t_accept, [], [t_accept_timeout]},
{t_connect, [], [t_connect_timeout, t_connect_bad]},
- {t_recv, [], [t_recv_timeout, t_recv_eof, t_recv_delim]}].
+ {t_recv, [], [t_recv_timeout, t_recv_eof, t_recv_delim]},
+ {t_local, [],
+ [t_local_basic, t_local_unbound, t_local_fdopen,
+ t_local_fdopen_listen, t_local_fdopen_listen_unbound,
+ t_local_fdopen_connect, t_local_fdopen_connect_unbound,
+ t_local_abstract]}].
@@ -60,17 +70,36 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
+init_per_group(t_local, Config) ->
+ case gen_tcp:connect({local,<<"/">>}, 0, []) of
+ {error,eafnosupport} ->
+ {skip, "AF_LOCAL not supported"};
+ {error,_} ->
+ Config
+ end;
init_per_group(_GroupName, Config) ->
Config.
-end_per_group(_,_Config) ->
+end_per_group(t_local, _Config) ->
+ delete_local_filenames();
+end_per_group(_, _Config) ->
ok.
+
+init_per_testcase(Func, Config)
+ when Func =:= undefined -> % Insert your testcase name here
+ dbg:tracer(),
+ dbg:p(self(), c),
+ dbg:tpl(prim_inet, cx),
+ dbg:tpl(local_tcp, cx),
+ dbg:tpl(inet, cx),
+ dbg:tpl(gen_tcp, cx),
+ Config;
init_per_testcase(_Func, Config) ->
Config.
end_per_testcase(_Func, _Config) ->
- ok.
+ dbg:stop().
%%% gen_tcp:accept/1,2
@@ -279,9 +308,7 @@ t_implicit_inet6(Host, Addr) ->
implicit_inet6(S1, Loopback),
ok = gen_tcp:close(S1),
%%
- Localhost = "localhost",
- Localaddr = ok(inet:getaddr(Localhost, inet6)),
- io:format("~s ~p~n", [Localhost,Localaddr]),
+ Localaddr = ok(get_localaddr()),
S2 = ok(gen_tcp:listen(0, [{ip,Localaddr}])),
implicit_inet6(S2, Localaddr),
ok = gen_tcp:close(S2),
@@ -308,6 +335,192 @@ implicit_inet6(S, Addr) ->
ok = gen_tcp:close(S1).
+
+t_local_basic(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ CFile = local_filename(client),
+ CAddr = {local,bin_filename(CFile)},
+ _ = file:delete(SFile),
+ _ = file:delete(CFile),
+ %%
+ L =
+ ok(
+ gen_tcp:listen(0, [{ifaddr,{local,SFile}},{active,false}])),
+ C =
+ ok(
+ gen_tcp:connect(
+ {local,SFile}, 0, [{ifaddr,{local,CFile}},{active,false}])),
+ S = ok(gen_tcp:accept(L)),
+ SAddr = ok(inet:sockname(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, CAddr),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ %%
+ ok = file:delete(SFile),
+ ok = file:delete(CFile),
+ ok.
+
+t_local_unbound(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ _ = file:delete(SFile),
+ %%
+ L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])),
+ C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])),
+ S = ok(gen_tcp:accept(L)),
+ SAddr = ok(inet:sockname(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, {local,<<>>}),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ ok = file:delete(SFile),
+ ok.
+
+t_local_fdopen(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ _ = file:delete(SFile),
+ %%
+ L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])),
+ C0 = ok(gen_tcp:connect(SAddr, 0, [{active,false}])),
+ Fd = ok(prim_inet:getfd(C0)),
+ ok = prim_inet:ignorefd(C0, true),
+ C = ok(gen_tcp:fdopen(Fd, [local])),
+ S = ok(gen_tcp:accept(L)),
+ SAddr = ok(inet:sockname(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, {local,<<>>}),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ ok = gen_tcp:close(C0),
+ ok = file:delete(SFile),
+ ok.
+
+t_local_fdopen_listen(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ _ = file:delete(SFile),
+ L0 = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])),
+ Fd = ok(prim_inet:getfd(L0)),
+ L = ok(gen_tcp:listen(0, [{fd,Fd},local,{active,false}])),
+ C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])),
+ S = ok(gen_tcp:accept(L)),
+ SAddr = ok(inet:sockname(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, {local,<<>>}),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(L0),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ ok = file:delete(SFile),
+ ok.
+
+t_local_fdopen_listen_unbound(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ _ = file:delete(SFile),
+ P = ok(prim_inet:open(tcp, local, stream)),
+ Fd = ok(prim_inet:getfd(P)),
+ L =
+ ok(gen_tcp:listen(
+ 0, [{fd,Fd},{ifaddr,SAddr},{active,false}])),
+ C = ok(gen_tcp:connect(SAddr, 0, [{active,false}])),
+ S = ok(gen_tcp:accept(L)),
+ SAddr = ok(inet:sockname(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, {local,<<>>}),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(P),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ ok = file:delete(SFile),
+ ok.
+
+t_local_fdopen_connect(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ CFile = local_filename(client),
+ CAddr = {local,bin_filename(CFile)},
+ _ = file:delete(SFile),
+ _ = file:delete(CFile),
+ L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])),
+ P = ok(prim_inet:open(tcp, local, stream)),
+ Fd = ok(prim_inet:getfd(P)),
+ C =
+ ok(gen_tcp:connect(
+ SAddr, 0, [{fd,Fd},{ifaddr,CAddr},{active,false}])),
+ S = ok(gen_tcp:accept(L)),
+ SAddr = ok(inet:sockname(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, CAddr),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ ok = gen_tcp:close(P),
+ ok = file:delete(SFile),
+ ok.
+
+t_local_fdopen_connect_unbound(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ _ = file:delete(SFile),
+ L = ok(gen_tcp:listen(0, [{ifaddr,SAddr},{active,false}])),
+ P = ok(prim_inet:open(tcp, local, stream)),
+ Fd = ok(prim_inet:getfd(P)),
+ C = ok(gen_tcp:connect(SAddr, 0, [{fd,Fd},{active,false}])),
+ S = ok(gen_tcp:accept(L)),
+ SAddr = ok(inet:sockname(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, {local,<<>>}),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ ok = gen_tcp:close(P),
+ ok = file:delete(SFile),
+ ok.
+
+t_local_abstract(_Config) ->
+ case os:type() of
+ {unix,linux} ->
+ AbstAddr = {local,<<>>},
+ L =
+ ok(gen_tcp:listen(
+ 0, [{ifaddr,AbstAddr},{active,false}])),
+ {local,_} = SAddr = ok(inet:sockname(L)),
+ C =
+ ok(gen_tcp:connect(
+ SAddr, 0, [{ifaddr,AbstAddr},{active,false}])),
+ {local,_} = CAddr = ok(inet:sockname(C)),
+ S = ok(gen_tcp:accept(L)),
+ {error,enotconn} = inet:peername(L),
+ local_handshake(S, SAddr, C, CAddr),
+ ok = gen_tcp:close(L),
+ ok = gen_tcp:close(S),
+ ok = gen_tcp:close(C),
+ ok;
+ _ ->
+ {skip,"AF_LOCAL Abstract Addresses only supported on Linux"}
+ end.
+
+
+local_handshake(S, SAddr, C, CAddr) ->
+ SData = "9876543210",
+ CData = "0123456789",
+ SAddr = ok(inet:sockname(S)),
+ CAddr = ok(inet:sockname(C)),
+ CAddr = ok(inet:peername(S)),
+ SAddr = ok(inet:peername(C)),
+ ok = gen_tcp:send(C, CData),
+ ok = gen_tcp:send(S, SData),
+ CData = ok(gen_tcp:recv(S, length(CData))),
+ SData = ok(gen_tcp:recv(C, length(SData))),
+ ok.
+
%%% Utilities
%% Calls M:F/length(A), which should return a timeout error, and complete
@@ -369,8 +582,42 @@ unused_ip(A, B, C, D) ->
{error, _} -> {ok, {A, B, C, D}}
end.
-ok({ok,V}) -> V.
+ok({ok,V}) -> V;
+ok(NotOk) ->
+ try throw(not_ok)
+ catch
+ Thrown ->
+ erlang:raise(
+ error, {Thrown, NotOk}, tl(erlang:get_stacktrace()))
+ end.
+get_localaddr() ->
+ get_localaddr(["localhost", "localhost6", "ip6-localhost"]).
+
+get_localaddr([]) ->
+ {error, localaddr_not_found};
+get_localaddr([Localhost|Ls]) ->
+ case inet:getaddr(Localhost, inet6) of
+ {ok, LocalAddr} ->
+ io:format("~s ~p~n", [Localhost, LocalAddr]),
+ {ok, LocalAddr};
+ _ ->
+ get_localaddr(Ls)
+ end.
getsockfd() -> undefined.
closesockfd(_FD) -> undefined.
+
+local_filename(Tag) ->
+ "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag).
+
+bin_filename(String) ->
+ unicode:characters_to_binary(String, file:native_name_encoding()).
+
+delete_local_filenames() ->
+ _ =
+ [file:delete(F) ||
+ F <-
+ filelib:wildcard(
+ "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")],
+ ok.
diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl
index 85dc6312ea..1029d7ef0a 100644
--- a/lib/kernel/test/gen_udp_SUITE.erl
+++ b/lib/kernel/test/gen_udp_SUITE.erl
@@ -35,7 +35,9 @@
-export([send_to_closed/1, active_n/1,
buffer_size/1, binary_passive_recv/1, bad_address/1,
- read_packets/1, open_fd/1, connect/1, implicit_inet6/1]).
+ read_packets/1, open_fd/1, connect/1, implicit_inet6/1,
+ local_basic/1, local_unbound/1,
+ local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]).
suite() ->
[{ct_hooks,[ts_install_cth]},
@@ -44,10 +46,13 @@ suite() ->
all() ->
[send_to_closed, buffer_size, binary_passive_recv,
bad_address, read_packets, open_fd, connect,
- implicit_inet6, active_n].
+ implicit_inet6, active_n,
+ {group, local}].
groups() ->
- [].
+ [{local, [],
+ [local_basic, local_unbound,
+ local_fdopen, local_fdopen_unbound, local_abstract]}].
init_per_suite(Config) ->
Config.
@@ -55,9 +60,19 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
+init_per_group(local, Config) ->
+ case gen_udp:open(0, [local]) of
+ {ok,S} ->
+ ok = gen_udp:close(S),
+ Config;
+ {error,eafnosupport} ->
+ {skip, "AF_LOCAL not supported"}
+ end;
init_per_group(_GroupName, Config) ->
Config.
+end_per_group(local, _Config) ->
+ delete_local_filenames();
end_per_group(_GroupName, Config) ->
Config.
@@ -65,7 +80,7 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(_Case, Config) ->
Config.
-end_per_testcase(_Case, Config) ->
+end_per_testcase(_Case, _Config) ->
ok.
%%-------------------------------------------------------------
@@ -550,6 +565,116 @@ active_n(Config) when is_list(Config) ->
ok = gen_udp:close(S1),
ok.
+
+local_basic(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ CFile = local_filename(client),
+ CAddr = {local,bin_filename(CFile)},
+ _ = file:delete(SFile),
+ _ = file:delete(CFile),
+ %%
+ S = ok(gen_udp:open(0, [{ifaddr,{local,SFile}},{active,false}])),
+ C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])),
+ SAddr = ok(inet:sockname(S)),
+ CAddr = ok(inet:sockname(C)),
+ local_handshake(S, SAddr, C, CAddr),
+ ok = gen_udp:close(S),
+ ok = gen_udp:close(C),
+ %%
+ ok = file:delete(SFile),
+ ok = file:delete(CFile),
+ ok.
+
+local_unbound(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ _ = file:delete(SFile),
+ %%
+ S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])),
+ C = ok(gen_udp:open(0, [local,{active,false}])),
+ SAddr = ok(inet:sockname(S)),
+ local_handshake(S, SAddr, C, undefined),
+ ok = gen_udp:close(S),
+ ok = gen_udp:close(C),
+ %%
+ ok = file:delete(SFile),
+ ok.
+
+local_fdopen(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ CFile = local_filename(client),
+ CAddr = {local,bin_filename(CFile)},
+ _ = file:delete(SFile),
+ _ = file:delete(CFile),
+ %%
+ S0 = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])),
+ C = ok(gen_udp:open(0, [{ifaddr,{local,CFile}},{active,false}])),
+ SAddr = ok(inet:sockname(S0)),
+ CAddr = ok(inet:sockname(C)),
+ Fd = ok(prim_inet:getfd(S0)),
+ S = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])),
+ SAddr = ok(inet:sockname(S)),
+ local_handshake(S, SAddr, C, CAddr),
+ ok = gen_udp:close(S),
+ ok = gen_udp:close(S0),
+ ok = gen_udp:close(C),
+ %%
+ ok = file:delete(SFile),
+ ok = file:delete(CFile),
+ ok.
+
+local_fdopen_unbound(_Config) ->
+ SFile = local_filename(server),
+ SAddr = {local,bin_filename(SFile)},
+ _ = file:delete(SFile),
+ %%
+ S = ok(gen_udp:open(0, [{ifaddr,SAddr},{active,false}])),
+ C0 = ok(gen_udp:open(0, [local,{active,false}])),
+ SAddr = ok(inet:sockname(S)),
+ Fd = ok(prim_inet:getfd(C0)),
+ C = ok(gen_udp:open(0, [{fd,Fd},local,{active,false}])),
+ local_handshake(S, SAddr, C, undefined),
+ ok = gen_udp:close(S),
+ ok = gen_udp:close(C),
+ ok = gen_udp:close(C0),
+ %%
+ ok = file:delete(SFile),
+ ok.
+
+local_abstract(_Config) ->
+ case os:type() of
+ {unix,linux} ->
+ S = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])),
+ C = ok(gen_udp:open(0, [{ifaddr,{local,<<>>}},{active,false}])),
+ {local,_} = SAddr = ok(inet:sockname(S)),
+ {local,_} = CAddr = ok(inet:sockname(C)),
+ local_handshake(S, SAddr, C, CAddr),
+ ok = gen_udp:close(S),
+ ok = gen_udp:close(C),
+ ok;
+ _ ->
+ {skip,"AF_LOCAL Abstract Addresses only supported on Linux"}
+ end.
+
+
+local_handshake(S, SAddr, C, CAddr) ->
+ SData = "9876543210",
+ CData = "0123456789",
+ ok = gen_udp:send(C, SAddr, 0, CData),
+ case ok(gen_tcp:recv(S, 112)) of
+ {{unspec,<<>>}, 0, CData} when CAddr =:= undefined ->
+ ok;
+ {{local,<<>>}, 0, CData} when CAddr =:= undefined ->
+ ok;
+ {CAddr, 0, CData} when CAddr =/= undefined ->
+ ok = gen_udp:send(S, CAddr, 0, SData),
+ {SAddr, 0, SData} = ok(gen_tcp:recv(C, 112)),
+ ok
+
+ end.
+
%%
%% Utils
%%
@@ -572,7 +697,7 @@ connect(Config) when is_list(Config) ->
ok = gen_udp:close(S1),
ok = gen_udp:connect(S2, Addr, P1),
ok = gen_udp:send(S2, <<16#deadbeef:32>>),
- ok = case gen_udp:recv(S2, 0, 5) of
+ ok = case gen_udp:recv(S2, 0, 500) of
{error,econnrefused} -> ok;
{error,econnreset} -> ok;
Other -> Other
@@ -599,9 +724,7 @@ implicit_inet6(Host, Addr) ->
implicit_inet6(S1, Active, Loopback),
ok = gen_udp:close(S1),
%%
- Localhost = "localhost",
- Localaddr = ok(inet:getaddr(Localhost, inet6)),
- io:format("~s ~p~n", [Localhost,Localaddr]),
+ Localaddr = ok(get_localaddr()),
S2 = ok(gen_udp:open(0, [{ip,Localaddr},Active])),
implicit_inet6(S2, Active, Localaddr),
ok = gen_udp:close(S2),
@@ -630,4 +753,40 @@ implicit_inet6(S1, Active, Addr) ->
{Addr,P2,"pong"} = ok(gen_udp:recv(S1, 1024)),
ok = gen_udp:close(S2).
-ok({ok,V}) -> V.
+ok({ok,V}) -> V;
+ok(NotOk) ->
+ try throw(not_ok)
+ catch
+ Thrown ->
+ erlang:raise(
+ error, {Thrown, NotOk}, tl(erlang:get_stacktrace()))
+ end.
+
+
+local_filename(Tag) ->
+ "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag).
+
+bin_filename(String) ->
+ unicode:characters_to_binary(String, file:native_name_encoding()).
+
+delete_local_filenames() ->
+ _ =
+ [file:delete(F) ||
+ F <-
+ filelib:wildcard(
+ "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_*")],
+ ok.
+
+get_localaddr() ->
+ get_localaddr(["localhost", "localhost6", "ip6-localhost"]).
+
+get_localaddr([]) ->
+ {error, localaddr_not_found};
+get_localaddr([Localhost|Ls]) ->
+ case inet:getaddr(Localhost, inet6) of
+ {ok, LocalAddr} ->
+ io:format("~s ~p~n", [Localhost, LocalAddr]),
+ {ok, LocalAddr};
+ _ ->
+ get_localaddr(Ls)
+ end.
diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl
index a332e7966b..1370e23195 100644
--- a/lib/kernel/test/init_SUITE.erl
+++ b/lib/kernel/test/init_SUITE.erl
@@ -298,7 +298,7 @@ many_restarts() ->
many_restarts(Config) when is_list(Config) ->
{ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC),
- loop_restart(30,Node,rpc:call(Node,erlang,whereis,[error_logger])),
+ loop_restart(50,Node,rpc:call(Node,erlang,whereis,[error_logger])),
loose_node:stop(Node),
ok.
diff --git a/lib/kernel/test/loose_node.erl b/lib/kernel/test/loose_node.erl
index 93530c2735..cc3f9bbea0 100644
--- a/lib/kernel/test/loose_node.erl
+++ b/lib/kernel/test/loose_node.erl
@@ -57,9 +57,16 @@
%%
stop(Node) when is_atom(Node) ->
+ erlang:monitor_node(Node, true),
rpc:cast(Node, erlang, halt, []),
- io:format("Stopped loose node ~p~n", [Node]),
- ok.
+ receive
+ {nodedown, Node} ->
+ io:format("Stopped loose node ~p~n", [Node]),
+ ok
+ after 10000 ->
+ io:format("Failed to stop loose node: ~p~n", [Node]),
+ {error, node_not_stopped}
+ end.
start(Name, Args) ->
start(Name, Args, -1).
diff --git a/lib/kernel/test/os_SUITE.erl b/lib/kernel/test/os_SUITE.erl
index 2a1e5016ec..8f3e511941 100644
--- a/lib/kernel/test/os_SUITE.erl
+++ b/lib/kernel/test/os_SUITE.erl
@@ -24,7 +24,8 @@
init_per_testcase/2,end_per_testcase/2]).
-export([space_in_cwd/1, quoting/1, cmd_unicode/1, space_in_name/1, bad_command/1,
find_executable/1, unix_comment_in_command/1, deep_list_command/1,
- large_output_command/1, perf_counter_api/1]).
+ large_output_command/1, background_command/0, background_command/1,
+ perf_counter_api/1]).
-include_lib("common_test/include/ct.hrl").
@@ -35,7 +36,7 @@ suite() ->
all() ->
[space_in_cwd, quoting, cmd_unicode, space_in_name, bad_command,
find_executable, unix_comment_in_command, deep_list_command,
- large_output_command, perf_counter_api].
+ large_output_command, background_command, perf_counter_api].
groups() ->
[].
@@ -52,6 +53,13 @@ init_per_group(_GroupName, Config) ->
end_per_group(_GroupName, Config) ->
Config.
+init_per_testcase(background_command, Config) ->
+ case os:type() of
+ {win32, _} ->
+ {skip,"Should not work on windows"};
+ _ ->
+ Config
+ end;
init_per_testcase(_TC,Config) ->
Config.
@@ -261,13 +269,21 @@ deep_list_command(Config) when is_list(Config) ->
%% FYI: [$e, $c, "ho"] =:= io_lib:format("ec~s", ["ho"])
ok.
-%% Test to take sure that the correct data is
+%% Test to make sure that the correct data is
%% received when doing large commands.
large_output_command(Config) when is_list(Config) ->
%% Maximum allowed on windows is 8192, so we test well below that
AAA = lists:duplicate(7000, $a),
comp(AAA,os:cmd("echo " ++ AAA)).
+%% Test that it is possible on unix to start a background task using os:cmd.
+background_command() ->
+ [{timetrap, {seconds, 5}}].
+background_command(_Config) ->
+ %% This testcase fails when the os:cmd takes
+ %% longer then the 5 second timeout
+ os:cmd("sleep 10&").
+
%% Test that the os:perf_counter api works as expected
perf_counter_api(_Config) ->
diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk
index e7d422d03c..e88e4f9170 100644
--- a/lib/kernel/vsn.mk
+++ b/lib/kernel/vsn.mk
@@ -1 +1 @@
-KERNEL_VSN = 5.0
+KERNEL_VSN = 5.0.1