aboutsummaryrefslogtreecommitdiffstats
path: root/lib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kernel')
-rw-r--r--lib/kernel/doc/src/application.xml27
-rw-r--r--lib/kernel/doc/src/inet.xml66
-rw-r--r--lib/kernel/doc/src/notes.xml60
-rw-r--r--lib/kernel/src/application.erl43
-rw-r--r--lib/kernel/src/hipe_unified_loader.erl47
-rw-r--r--lib/kernel/src/inet.erl114
-rw-r--r--lib/kernel/src/inet_int.hrl1
-rw-r--r--lib/kernel/src/inet_parse.erl4
-rw-r--r--lib/kernel/src/rpc.erl2
-rw-r--r--lib/kernel/test/Makefile2
-rw-r--r--lib/kernel/test/application_SUITE.erl107
-rw-r--r--lib/kernel/test/inet_SUITE.erl103
-rw-r--r--lib/kernel/test/interactive_shell_SUITE.erl51
-rw-r--r--lib/kernel/test/kernel_smoke.spec9
14 files changed, 584 insertions, 52 deletions
diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml
index da16964d61..29eaf348a9 100644
--- a/lib/kernel/doc/src/application.xml
+++ b/lib/kernel/doc/src/application.xml
@@ -253,15 +253,30 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code>
</warning>
</desc>
</func>
- <func>
+ <func>
<name name="ensure_started" arity="1"/>
<name name="ensure_started" arity="2"/>
<fsummary>Load and start an application</fsummary>
- <desc>
- <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except
- it returns <c>ok</c> for already started applications.</p>
- </desc>
- </func>
+ <desc>
+ <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except
+ it returns <c>ok</c> for already started applications.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="ensure_all_started" arity="1"/>
+ <name name="ensure_all_started" arity="2"/>
+ <fsummary>Load and start an application and its dependencies, recursively</fsummary>
+ <desc>
+ <p>Equivalent to calling <seealso marker="#start/2"><c>application:start/1,2</c></seealso>
+ repeatedly on all dependencies that have not yet been started for an application.
+ The function returns <c>{ok, AppNames}</c> for a successful start or for an already started
+ application (which are however omitted from the <c>AppNames</c> list), and reports
+ <c>{error, {AppName,Reason}}</c> for errors, where <c>Reason</c> is any possible reason
+ returned by <seealso marker="#start/2"><c>application:start/1,2</c></seealso> when starting a
+ specific dependency. In case of an error, the applications that were started by the
+ function are stopped to bring the set of running applications back to its initial state.</p>
+ </desc>
+ </func>
<func>
<name name="start" arity="1"/>
<name name="start" arity="2"/>
diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml
index 119d7d8df9..e1b0b34888 100644
--- a/lib/kernel/doc/src/inet.xml
+++ b/lib/kernel/doc/src/inet.xml
@@ -76,11 +76,11 @@ FFFF::192.168.42.2
{16#3ffe,16#b80,16#1f8d,16#2,16#204,16#acff,16#fe17,16#bf38}
fe80::204:acff:fe17:bf38
{16#fe80,0,0,0,0,16#204,16#acff,16#fe17,16#bf38}</code>
- <p>A function that may be useful is <c>inet_parse:address/1</c>:</p>
+ <p>A function that may be useful is <seealso marker="#parse_address/1">parse_address/1</seealso>:</p>
<pre>
-1> <input>inet_parse:address("192.168.42.2").</input>
+1> <input>inet:parse_address("192.168.42.2").</input>
{ok,{192,168,42,2}}
-2> <input>inet_parse:address("FFFF::192.168.42.2").</input>
+2> <input>inet:parse_address("FFFF::192.168.42.2").</input>
{ok,{65535,0,0,0,0,0,49320,10754}}</pre>
</description>
@@ -375,6 +375,13 @@ fe80::204:acff:fe17:bf38
</desc>
</func>
<func>
+ <name name="ntoa" arity="1" />
+ <fsummary>Convert IPv6 / IPV4 adress to ascii</fsummary>
+ <desc>
+ <p>Parses an <a href="#type-ip_address">ip_address()</a> and returns an IPv4 or IPv6 address string.</p>
+ </desc>
+ </func>
+ <func>
<name name="parse_ipv4_address" arity="1" />
<fsummary>Parse an IPv4 address</fsummary>
<desc>
@@ -715,6 +722,59 @@ fe80::204:acff:fe17:bf38
<p>Received <c>Packet</c> is delivered as defined by Mode.</p>
</item>
+ <tag><c>{netns, Namespace :: file:filename_all()}</c></tag>
+ <item>
+ <p>Set a network namespace for the socket. The <c>Namespace</c>
+ parameter is a filename defining the namespace for example
+ <c>"/var/run/netns/example"</c> typically created by the command
+ <c>ip netns add example</c>. This option must be used in a
+ function call that creates a socket i.e
+ <seealso marker="gen_tcp#connect/3">
+ gen_tcp:connect/3,4</seealso>,
+ <seealso marker="gen_tcp#listen/2">
+ gen_tcp:listen/2</seealso>,
+ <seealso marker="gen_udp#open/1">
+ gen_udp:open/1,2</seealso> or
+ <seealso marker="gen_sctp#open/0">
+ gen_sctp:open/0-2</seealso>.
+ </p>
+ <p>This option uses the Linux specific syscall
+ <c>setns()</c> such as in Linux kernel 3.0 or later
+ and therefore only exists when the runtime system
+ has been compiled for such an operating system.
+ </p>
+ <p>
+ The virtual machine also needs elevated privileges either
+ running as superuser or (for Linux) having the capability
+ <c>CAP_SYS_ADMIN</c> according to the documentation for setns(2).
+ However, during testing also <c>CAP_SYS_PTRACE</c>
+ and <c>CAP_DAC_READ_SEARCH</c> has proven to be necessary.
+ Example:<code>
+setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp
+</code>
+ Note also that the filesystem containing the virtual machine
+ executable (<c>beam.smp</c> in the example above) has to be local,
+ mounted without the <c>nosetuid</c> flag,
+ support extended attributes and that
+ the kernel has to support file capabilities.
+ All this runs out of the box on at least Ubuntu 12.04 LTS,
+ except that SCTP sockets appears to not support
+ network namespaces.
+ </p>
+ <p>The <c>Namespace</c> is a file name and is encoded
+ and decoded as discussed in
+ <seealso marker="file">file</seealso>
+ except that the emulator flag <c>+fnu</c> is ignored and
+ <seealso marker="#getopts/2">getopts/2</seealso>
+ for this option will return a binary for the filename
+ if the stored filename can not be decoded,
+ which should only happen if you set the option using a binary
+ that can not be decoded with the emulator's filename encoding:
+ <seealso marker="file#native_name_encoding/0">
+ file:native_name_encoding/0</seealso>.
+ </p>
+ </item>
+
<tag><c>list</c></tag>
<item>
<p>Received <c>Packet</c> is delivered as a list.</p>
diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml
index 6b703b7c2b..6cdc3a9d17 100644
--- a/lib/kernel/doc/src/notes.xml
+++ b/lib/kernel/doc/src/notes.xml
@@ -30,6 +30,66 @@
</header>
<p>This document describes the changes made to the Kernel application.</p>
+<section><title>Kernel 2.16.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix indentation of User switch command help in Erlang
+ shell. Thanks to Sylvain Benner.</p>
+ <p>
+ Own Id: OTP-11209</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The previous undocumented function ntoa/1 has been added
+ to inet docs and exported in the inet module.</p>
+ <p>
+ Own Id: OTP-10676 Aux Id: OTP-10314 </p>
+ </item>
+ <item>
+ <p>
+ Fix typo in abcast() function comment. Thanks to Johannes
+ Weissl.</p>
+ <p>
+ Own Id: OTP-11219</p>
+ </item>
+ <item>
+ <p>
+ Add application:ensure_all_started/1-2. Thanks to Fred
+ Hebert.</p>
+ <p>
+ Own Id: OTP-11250</p>
+ </item>
+ <item>
+ <p>
+ Make edlin understand a few important control keys.
+ Thanks to Stefan Zegenhagen.</p>
+ <p>
+ Own Id: OTP-11251</p>
+ </item>
+ <item>
+ <p>
+ Cleanup of hipe_unified_loader, eliminating uses of
+ is_subtype/2 in specs, change module-local void functions
+ to return 'ok' instead of [] and made sure there are no
+ dialyzer warnings with --Wunmatched_returns. Thanks to
+ Kostis Sagonas.</p>
+ <p>
+ Own Id: OTP-11301</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Kernel 2.16.2</title>
<section><title>Fixed Bugs and Malfunctions</title>
diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl
index 5dd6b73857..4e8ba1b78a 100644
--- a/lib/kernel/src/application.erl
+++ b/lib/kernel/src/application.erl
@@ -18,7 +18,8 @@
%%
-module(application).
--export([start/1, start/2, start_boot/1, start_boot/2, stop/1,
+-export([ensure_all_started/1, ensure_all_started/2, start/1, start/2,
+ start_boot/1, start_boot/2, stop/1,
load/1, load/2, unload/1, takeover/2,
which_applications/0, which_applications/1,
loaded_applications/0, permit/2]).
@@ -113,6 +114,46 @@ load1(Application, DistNodes) ->
unload(Application) ->
application_controller:unload_application(Application).
+
+-spec ensure_all_started(Application) -> {'ok', Started} | {'error', Reason} when
+ Application :: atom(),
+ Started :: [atom()],
+ Reason :: term().
+ensure_all_started(Application) ->
+ ensure_all_started(Application, temporary).
+
+-spec ensure_all_started(Application, Type) -> {'ok', Started} | {'error', Reason} when
+ Application :: atom(),
+ Type :: restart_type(),
+ Started :: [atom()],
+ Reason :: term().
+ensure_all_started(Application, Type) ->
+ case ensure_all_started(Application, Type, []) of
+ {ok, Started} ->
+ {ok, lists:reverse(Started)};
+ {error, Reason, Started} ->
+ [stop(App) || App <- Started],
+ {error, Reason}
+ end.
+
+ensure_all_started(Application, Type, Started) ->
+ case start(Application, Type) of
+ ok ->
+ {ok, [Application | Started]};
+ {error, {already_started, Application}} ->
+ {ok, Started};
+ {error, {not_started, Dependency}} ->
+ case ensure_all_started(Dependency, Type, Started) of
+ {ok, NewStarted} ->
+ ensure_all_started(Application, Type, NewStarted);
+ Error ->
+ Error
+ end;
+ {error, Reason} ->
+ {error, {Application, Reason}, Started}
+ end.
+
+
-spec start(Application) -> 'ok' | {'error', Reason} when
Application :: atom(),
Reason :: term().
diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl
index cc51b5ae07..e111cb800e 100644
--- a/lib/kernel/src/hipe_unified_loader.erl
+++ b/lib/kernel/src/hipe_unified_loader.erl
@@ -85,7 +85,7 @@ chunk_name(Architecture) ->
%%========================================================================
-spec load_native_code(Mod, binary()) -> 'no_native' | {'module', Mod}
- when is_subtype(Mod, atom()).
+ when Mod :: atom().
%% @doc
%% Loads the native code of a module Mod.
%% Returns {module,Mod} on success (for compatibility with
@@ -148,8 +148,8 @@ version_check(Version, Mod) when is_atom(Mod) ->
%%========================================================================
--spec load_module(Mod, binary(), _) -> 'bad_crc' | {'module',Mod}
- when is_subtype(Mod,atom()).
+-spec load_module(Mod, binary(), _) -> 'bad_crc' | {'module', Mod}
+ when Mod :: atom().
load_module(Mod, Bin, Beam) ->
erlang:system_flag(multi_scheduling, block),
try
@@ -169,8 +169,8 @@ load_module(Mod, Bin, Beam, OldReferencesToPatch) ->
%%========================================================================
--spec load(Mod, binary()) -> 'bad_crc' | {'module',Mod}
- when is_subtype(Mod,atom()).
+-spec load(Mod, binary()) -> 'bad_crc' | {'module', Mod} when Mod :: atom().
+
load(Mod, Bin) ->
erlang:system_flag(multi_scheduling, block),
try
@@ -204,15 +204,17 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
bad_crc;
true ->
%% Create data segment
- {ConstAddr,ConstMap2} = create_data_segment(ConstAlign, ConstSize, ConstMap),
+ {ConstAddr,ConstMap2} =
+ create_data_segment(ConstAlign, ConstSize, ConstMap),
%% Find callees for which we may need trampolines.
CalleeMFAs = find_callee_mfas(Refs),
%% Write the code to memory.
- {CodeAddress,Trampolines} = enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam),
+ {CodeAddress,Trampolines} =
+ enter_code(CodeSize, CodeBinary, CalleeMFAs, Mod, Beam),
%% Construct CalleeMFA-to-trampoline mapping.
TrampolineMap = mk_trampoline_map(CalleeMFAs, Trampolines),
%% Patch references to code labels in data seg.
- patch_consts(LabelMap, ConstAddr, CodeAddress),
+ ok = patch_consts(LabelMap, ConstAddr, CodeAddress),
%% Find out which functions are being loaded (and where).
%% Note: Addresses are sorted descending.
{MFAs,Addresses} = exports(ExportMap, CodeAddress),
@@ -221,7 +223,7 @@ load_common(Mod, Bin, Beam, OldReferencesToPatch) ->
ok = remove_refs_from(MFAs),
%% Patch all dynamic references in the code.
%% Function calls, Atoms, Constants, System calls
- patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap),
+ ok = patch(Refs, CodeAddress, ConstMap2, Addresses, TrampolineMap),
%% Tell the system where the loaded funs are.
%% (patches the BEAM code to redirect to native.)
case Beam of
@@ -324,7 +326,7 @@ trampoline_map_get(MFA, Map) -> gb_trees:get(MFA, Map).
trampoline_map_lookup(_, []) -> []; % archs not using trampolines
trampoline_map_lookup(Primop, Map) ->
case gb_trees:lookup(Primop, Map) of
- {value,X} -> X;
+ {value, X} -> X;
_ -> []
end.
@@ -371,7 +373,7 @@ offsets_to_addresses(Os, Base) ->
find_closure_patches([{Type,Refs} | Rest]) ->
case ?EXT2PATCH_TYPE(Type) of
load_address ->
- find_closure_refs(Refs,Rest);
+ find_closure_refs(Refs, Rest);
_ ->
find_closure_patches(Rest)
end;
@@ -406,16 +408,17 @@ export_funs([FunDef | Addresses]) ->
hipe_bifs:set_native_address(MFA, Address, IsClosure),
export_funs(Addresses);
export_funs([]) ->
- true.
+ ok.
export_funs(Mod, Beam, Addresses, ClosuresToPatch) ->
- Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- Addresses],
- code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch}).
+ Fs = [{F,A,Address} || #fundef{address=Address, mfa={_M,F,A}} <- Addresses],
+ Mod = code:make_stub_module(Mod, Beam, {Fs,ClosuresToPatch}),
+ ok.
%%========================================================================
%% Patching
%% @spec patch(refs(), BaseAddress::integer(), ConstAndZone::term(),
-%% Addresses::term(), TrampolineMap::term()) -> term()
+%% Addresses::term(), TrampolineMap::term()) -> 'ok'.
%% @type refs()=[{RefType::integer(), Reflist::reflist()} | refs()]
%%
%% @type reflist()= [{Data::term(), Offsets::offests()}|reflist()]
@@ -428,7 +431,7 @@ export_funs(Mod, Beam, Addresses, ClosuresToPatch) ->
%%
patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, Addresses, TrampolineMap) ->
- ?debug_msg("Patching ~w at [~w+offset] with ~w\n",
+ ?debug_msg("Patching ~w at [~w+offset] with ~w\n",
[Type,CodeAddress,SortedRefs]),
case ?EXT2PATCH_TYPE(Type) of
call_local ->
@@ -439,7 +442,7 @@ patch([{Type,SortedRefs}|Rest], CodeAddress, ConstMap2, Addresses, TrampolineMap
patch_all(Other, SortedRefs, CodeAddress, {ConstMap2,CodeAddress}, Addresses)
end,
patch(Rest, CodeAddress, ConstMap2, Addresses, TrampolineMap);
-patch([], _, _, _, _) -> true.
+patch([], _, _, _, _) -> ok.
%%----------------------------------------------------------------
%% Handle a 'call_local' or 'call_remote' patch.
@@ -461,14 +464,14 @@ patch_call([{DestMFA,Offsets}|SortedRefs], BaseAddress, Addresses, RemoteOrLocal
end,
patch_call(SortedRefs, BaseAddress, Addresses, RemoteOrLocal, TrampolineMap);
patch_call([], _, _, _, _) ->
- true.
+ ok.
patch_bif_call_list([Offset|Offsets], BaseAddress, BifAddress, Trampoline) ->
CallAddress = BaseAddress+Offset,
?ASSERT(assert_local_patch(CallAddress)),
patch_call_insn(CallAddress, BifAddress, Trampoline),
patch_bif_call_list(Offsets, BaseAddress, BifAddress, Trampoline);
-patch_bif_call_list([], _, _, _) -> [].
+patch_bif_call_list([], _, _, _) -> ok.
patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline) ->
CallAddress = BaseAddress+Offset,
@@ -476,7 +479,7 @@ patch_mfa_call_list([Offset|Offsets], BaseAddress, DestMFA, DestAddress, Address
?ASSERT(assert_local_patch(CallAddress)),
patch_call_insn(CallAddress, DestAddress, Trampoline),
patch_mfa_call_list(Offsets, BaseAddress, DestMFA, DestAddress, Addresses, RemoteOrLocal, Trampoline);
-patch_mfa_call_list([], _, _, _, _, _, _) -> [].
+patch_mfa_call_list([], _, _, _, _, _, _) -> ok.
patch_call_insn(CallAddress, DestAddress, Trampoline) ->
%% This assertion is false when we're called from redirect/2.
@@ -489,7 +492,7 @@ patch_call_insn(CallAddress, DestAddress, Trampoline) ->
patch_all(Type, [{Dest,Offsets}|Rest], BaseAddress, ConstAndZone, Addresses)->
patch_all_offsets(Type, Dest, Offsets, BaseAddress, ConstAndZone, Addresses),
patch_all(Type, Rest, BaseAddress, ConstAndZone, Addresses);
-patch_all(_, [], _, _, _) -> true.
+patch_all(_, [], _, _, _) -> ok.
patch_all_offsets(Type, Data, [Offset|Offsets], BaseAddress,
ConstAndZone, Addresses) ->
@@ -499,7 +502,7 @@ patch_all_offsets(Type, Data, [Offset|Offsets], BaseAddress,
patch_offset(Type, Data, Address, ConstAndZone, Addresses),
?debug_msg("Patching done\n",[]),
patch_all_offsets(Type, Data, Offsets, BaseAddress, ConstAndZone, Addresses);
-patch_all_offsets(_, _, [], _, _, _) -> true.
+patch_all_offsets(_, _, [], _, _, _) -> ok.
%%----------------------------------------------------------------
%% Handle any patch type except 'call_local' or 'call_remote'.
diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl
index 6f65968fc2..9cfd3819f0 100644
--- a/lib/kernel/src/inet.erl
+++ b/lib/kernel/src/inet.erl
@@ -32,7 +32,7 @@
ip/1, stats/0, options/0,
pushf/3, popf/1, close/1, gethostname/0, gethostname/1,
parse_ipv4_address/1, parse_ipv6_address/1, parse_ipv4strict_address/1,
- parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1]).
+ parse_ipv6strict_address/1, parse_address/1, parse_strict_address/1, ntoa/1]).
-export([connect_options/2, listen_options/2, udp_options/2, sctp_options/2]).
@@ -200,7 +200,14 @@ send(Socket, Packet) ->
Options :: [socket_setopt()].
setopts(Socket, Opts) ->
- prim_inet:setopts(Socket, Opts).
+ SocketOpts =
+ [case Opt of
+ {netns,NS} ->
+ {netns,filename2binary(NS)};
+ _ ->
+ Opt
+ end || Opt <- Opts],
+ prim_inet:setopts(Socket, SocketOpts).
-spec getopts(Socket, Options) ->
{'ok', OptionValues} | {'error', posix()} when
@@ -209,7 +216,18 @@ setopts(Socket, Opts) ->
OptionValues :: [socket_setopt()].
getopts(Socket, Opts) ->
- prim_inet:getopts(Socket, Opts).
+ case prim_inet:getopts(Socket, Opts) of
+ {ok,OptionValues} ->
+ {ok,
+ [case OptionValue of
+ {netns,Bin} ->
+ {netns,binary2filename(Bin)};
+ _ ->
+ OptionValue
+ end || OptionValue <- OptionValues]};
+ Other ->
+ Other
+ end.
-spec getifaddrs(Socket :: socket()) ->
{'ok', [string()]} | {'error', posix()}.
@@ -529,6 +547,13 @@ getservbyname(Name, Protocol) when is_atom(Name) ->
Error -> Error
end.
+-spec ntoa(IpAddress) ->
+ {ok, Address} | {error, einval} when
+ Address :: string(),
+ IpAddress :: ip_address().
+ntoa(Addr) ->
+ inet_parse:ntoa(Addr).
+
-spec parse_ipv4_address(Address) ->
{ok, IPv4Address} | {error, einval} when
Address :: string(),
@@ -634,6 +659,14 @@ con_opt([Opt | Opts], R, As) ->
{tcp_module,_} -> con_opt(Opts, R, As);
inet -> con_opt(Opts, R, As);
inet6 -> con_opt(Opts, R, As);
+ {netns,NS} ->
+ BinNS = filename2binary(NS),
+ case prim_inet:is_sockopt_val(netns, BinNS) of
+ true ->
+ con_opt(Opts, R#connect_opts { fd = [{netns,BinNS}] }, As);
+ false ->
+ {error, badarg}
+ end;
{Name,Val} when is_atom(Name) -> con_add(Name, Val, R, Opts, As);
_ -> {error, badarg}
end;
@@ -692,6 +725,14 @@ list_opt([Opt | Opts], R, As) ->
{tcp_module,_} -> list_opt(Opts, R, As);
inet -> list_opt(Opts, R, As);
inet6 -> list_opt(Opts, R, As);
+ {netns,NS} ->
+ BinNS = filename2binary(NS),
+ case prim_inet:is_sockopt_val(netns, BinNS) of
+ true ->
+ list_opt(Opts, R#listen_opts { fd = [{netns,BinNS}] }, As);
+ false ->
+ {error, badarg}
+ end;
{Name,Val} when is_atom(Name) -> list_add(Name, Val, R, Opts, As);
_ -> {error, badarg}
end;
@@ -738,6 +779,14 @@ udp_opt([Opt | Opts], R, As) ->
{udp_module,_} -> udp_opt(Opts, R, As);
inet -> udp_opt(Opts, R, As);
inet6 -> udp_opt(Opts, R, As);
+ {netns,NS} ->
+ BinNS = filename2binary(NS),
+ case prim_inet:is_sockopt_val(netns, BinNS) of
+ true ->
+ list_opt(Opts, R#udp_opts { fd = [{netns,BinNS}] }, As);
+ false ->
+ {error, badarg}
+ end;
{Name,Val} when is_atom(Name) -> udp_add(Name, Val, R, Opts, As);
_ -> {error, badarg}
end;
@@ -807,6 +856,17 @@ sctp_opt([Opt|Opts], Mod, R, As) ->
{sctp_module,_} -> sctp_opt (Opts, Mod, R, As); % Done with
inet -> sctp_opt (Opts, Mod, R, As); % Done with
inet6 -> sctp_opt (Opts, Mod, R, As); % Done with
+ {netns,NS} ->
+ BinNS = filename2binary(NS),
+ case prim_inet:is_sockopt_val(netns, BinNS) of
+ true ->
+ sctp_opt(
+ Opts, Mod,
+ R#sctp_opts { fd = [{netns,BinNS}] },
+ As);
+ false ->
+ {error, badarg}
+ end;
{Name,Val} -> sctp_opt (Opts, Mod, R, As, Name, Val);
_ -> {error,badarg}
end;
@@ -851,6 +911,39 @@ add_opt(Name, Val, Opts, As) ->
end.
+%% Passthrough all unknown - catch type errors later
+filename2binary(List) when is_list(List) ->
+ OutEncoding = file:native_name_encoding(),
+ try unicode:characters_to_binary(List, unicode, OutEncoding) of
+ Bin when is_binary(Bin) ->
+ Bin;
+ _ ->
+ List
+ catch
+ error:badarg ->
+ List
+ end;
+filename2binary(Bin) ->
+ Bin.
+
+binary2filename(Bin) ->
+ InEncoding = file:native_name_encoding(),
+ case unicode:characters_to_list(Bin, InEncoding) of
+ Filename when is_list(Filename) ->
+ Filename;
+ _ ->
+ %% For getopt/setopt of netns this should only happen if
+ %% a binary with wrong encoding was used when setting the
+ %% option, hence the user shall eat his/her own medicine.
+ %%
+ %% I.e passthrough here too for now.
+ %% Future usecases will most probably not want this,
+ %% rather Unicode error or warning
+ %% depending on emulator flag instead.
+ Bin
+ end.
+
+
translate_ip(any, inet) -> {0,0,0,0};
translate_ip(loopback, inet) -> {127,0,0,1};
translate_ip(any, inet6) -> {0,0,0,0,0,0,0,0};
@@ -1063,7 +1156,7 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) ->
Result -> Result
end.
--spec open(Fd :: integer(),
+-spec open(Fd_or_OpenOpts :: integer() | list(),
Addr :: ip_address(),
Port :: port_number(),
Opts :: [socket_setopt()],
@@ -1073,8 +1166,14 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) ->
Module :: atom()) ->
{'ok', socket()} | {'error', posix()}.
-open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
- case prim_inet:open(Protocol, Family, Type) of
+open(FdO, Addr, Port, Opts, Protocol, Family, Type, Module)
+ when is_integer(FdO), FdO < 0;
+ is_list(FdO) ->
+ OpenOpts =
+ if is_list(FdO) -> FdO;
+ true -> []
+ end,
+ case prim_inet:open(Protocol, Family, Type, OpenOpts) of
{ok,S} ->
case prim_inet:setopts(S, Opts) of
ok ->
@@ -1097,7 +1196,8 @@ open(Fd, Addr, Port, Opts, Protocol, Family, Type, Module) when Fd < 0 ->
Error ->
Error
end;
-open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module) ->
+open(Fd, _Addr, _Port, Opts, Protocol, Family, Type, Module)
+ when is_integer(Fd) ->
fdopen(Fd, Opts, Protocol, Family, Type, Module).
bindx(S, [Addr], Port0) ->
diff --git a/lib/kernel/src/inet_int.hrl b/lib/kernel/src/inet_int.hrl
index 67a99913a1..18a4a61b2f 100644
--- a/lib/kernel/src/inet_int.hrl
+++ b/lib/kernel/src/inet_int.hrl
@@ -143,6 +143,7 @@
-define(INET_LOPT_TCP_SEND_TIMEOUT_CLOSE, 35).
-define(INET_LOPT_MSGQ_HIWTRMRK, 36).
-define(INET_LOPT_MSGQ_LOWTRMRK, 37).
+-define(INET_LOPT_NETNS, 38).
% Specific SCTP options: separate range:
-define(SCTP_OPT_RTOINFO, 100).
-define(SCTP_OPT_ASSOCINFO, 101).
diff --git a/lib/kernel/src/inet_parse.erl b/lib/kernel/src/inet_parse.erl
index d24bda0c3d..a88c94a453 100644
--- a/lib/kernel/src/inet_parse.erl
+++ b/lib/kernel/src/inet_parse.erl
@@ -722,7 +722,9 @@ ntoa({0,0,0,0,0,16#ffff,A,B}) ->
"::FFFF:" ++ dig_to_dec(A) ++ "." ++ dig_to_dec(B);
ntoa({_,_,_,_,_,_,_,_}=T) ->
%% Find longest sequence of zeros, at least 2, to replace with "::"
- ntoa(tuple_to_list(T), []).
+ ntoa(tuple_to_list(T), []);
+ntoa(_) ->
+ {error, einval}.
%% Find first double zero
ntoa([], R) ->
diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl
index 78fec2aa2b..7dc51495f7 100644
--- a/lib/kernel/src/rpc.erl
+++ b/lib/kernel/src/rpc.erl
@@ -407,7 +407,7 @@ cast(Node, Mod, Fun, Args) ->
true.
-%% Asynchronous broadcast, returns nothing, it's just send'n prey
+%% Asynchronous broadcast, returns nothing, it's just send 'n' pray
-spec abcast(Name, Msg) -> abcast when
Name :: atom(),
Msg :: term().
diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile
index cb11d4e899..f1b8a105ed 100644
--- a/lib/kernel/test/Makefile
+++ b/lib/kernel/test/Makefile
@@ -145,7 +145,7 @@ release_tests_spec: make_emakefile
$(INSTALL_DIR) "$(RELSYSDIR)"
$(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)"
$(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)"
- $(INSTALL_DATA) kernel.spec $(EMAKEFILE)\
+ $(INSTALL_DATA) kernel.spec kernel_smoke.spec $(EMAKEFILE)\
$(COVERFILE) "$(RELSYSDIR)"
chmod -R u+w "$(RELSYSDIR)"
@tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -)
diff --git a/lib/kernel/test/application_SUITE.erl b/lib/kernel/test/application_SUITE.erl
index 1ff291be54..9ec8a15861 100644
--- a/lib/kernel/test/application_SUITE.erl
+++ b/lib/kernel/test/application_SUITE.erl
@@ -35,7 +35,7 @@
-export([config_change/1,
distr_changed_tc1/1, distr_changed_tc2/1,
- ensure_started/1,
+ ensure_started/1, ensure_all_started/1,
shutdown_func/1, do_shutdown/1, shutdown_timeout/1]).
-define(TESTCASE, testcase_name).
@@ -52,7 +52,7 @@ all() ->
[failover, failover_comp, permissions, load,
load_use_cache, ensure_started, {group, reported_bugs}, start_phases,
script_start, nodedown_start, permit_false_start_local,
- permit_false_start_dist, get_key, get_env,
+ permit_false_start_dist, get_key, get_env, ensure_all_started,
{group, distr_changed}, config_change, shutdown_func, shutdown_timeout].
groups() ->
@@ -978,6 +978,85 @@ ensure_started(Conf) ->
ok = application:unload(app1),
ok.
+ensure_all_started(suite) -> [];
+ensure_all_started(doc) -> ["Test application:ensure_all_started/1-2."];
+ensure_all_started(Conf) ->
+
+ {ok, Fd1} = file:open("app1.app", [write]),
+ w_app1(Fd1),
+ file:close(Fd1),
+ {ok, Fd9} = file:open("app9.app", [write]),
+ w_app9(Fd9),
+ file:close(Fd9),
+ {ok, Fd10} = file:open("app10.app", [write]),
+ w_app10_dep9(Fd10),
+ file:close(Fd10),
+ {ok, FdErr} = file:open("app_chain_error.app", [write]),
+ w_app(FdErr, app_chain_error()),
+ file:close(FdErr),
+ {ok, FdErr2} = file:open("app_chain_error2.app", [write]),
+ w_app(FdErr2, app_chain_error2()),
+ file:close(FdErr2),
+
+ %% Single app start/stop
+ false = lists:keyfind(app1, 1, application:which_applications()),
+ {ok, [app1]} = application:ensure_all_started(app1), % app1 started
+ {app1, _, _} = lists:keyfind(app1, 1, application:which_applications()),
+ {ok, []} = application:ensure_all_started(app1), % no start needed
+ ok = application:stop(app1),
+ false = lists:keyfind(app1, 1, application:which_applications()),
+ ok = application:unload(app1),
+
+ %% App or dependency not found.
+ Name = hopefully_not_an_existing_app_file,
+ {error,{Name, {"no such file or directory", _ }}} =
+ application:ensure_all_started(Name),
+
+ %% Start dependencies.
+ {error, {not_started, app9}} = application:start(app10),
+ {ok, [app9,app10]} = application:ensure_all_started(app10, temporary),
+ {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()),
+ {app10, _, _} = lists:keyfind(app10, 1, application:which_applications()),
+ %% Only report apps/dependencies that actually needed to start
+ ok = application:stop(app10),
+ ok = application:unload(app10),
+ {ok, [app10]} = application:ensure_all_started(app10, temporary),
+ ok = application:stop(app9),
+ ok = application:unload(app9),
+ ok = application:stop(app10),
+ ok = application:unload(app10),
+
+ %% Deeper failure chain. We have the following dependencies:
+ %% app_chain_error -> app_chain_error2
+ %% app_chain_error2 -> app10
+ %% app_chain_error2 -> hopefully_not_an_existing_app
+ %% app10 -> app 9
+ %% First we have none running and we expect to have neither app9
+ %% nor app10 running after failing to start
+ %% hopefully_not_an_existing_app
+ {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}=
+ application:ensure_all_started(app_chain_error),
+ false = lists:keyfind(app9, 1, application:which_applications()),
+ false = lists:keyfind(app10, 1, application:which_applications()),
+ false = lists:keyfind(app_chain_error2,1,application:which_applications()),
+ false = lists:keyfind(app_chain_error, 1, application:which_applications()),
+ %% Here we will have app9 already running, and app10 should be
+ %% able to boot fine.
+ %% In this dependency failing, we expect app9 to still be running, but
+ %% not app10 after failing to start hopefully_not_an_existing_app
+ {ok, [app9]} = application:ensure_all_started(app9, temporary),
+ {error, {hopefully_not_an_existing_app, {"no such file or directory", _}}}=
+ application:ensure_all_started(app_chain_error),
+ {app9, _, _} = lists:keyfind(app9, 1, application:which_applications()),
+ false = lists:keyfind(app10, 1, application:which_applications()),
+ false = lists:keyfind(app_chain_error2,1,application:which_applications()),
+ false = lists:keyfind(app_chain_error, 1, application:which_applications()),
+ ok = application:stop(app9),
+ ok = application:unload(app9),
+ ok = application:unload(app10),
+ ok = application:unload(app_chain_error2),
+ ok = application:unload(app_chain_error),
+ ok.
%%%-----------------------------------------------------------------
%%% Testing of reported bugs and other tickets.
@@ -2125,6 +2204,24 @@ app_start_error() ->
{applications, [kernel]},
{mod, {app_start_error, []}}]}.
+app_chain_error() ->
+ {application, app_chain_error,
+ [{description, "ERTS CXC 138 ce"},
+ {vsn, "2.0"},
+ {modules, []},
+ {registered, []},
+ {applications, [kernel, app_chain_error2]},
+ {mod, {ch_sup, {app_chain_error, 20,20}}}]}.
+
+app_chain_error2() ->
+ {application, app_chain_error2,
+ [{description, "ERTS CXC 138 ce2"},
+ {vsn, "2.0"},
+ {modules, []},
+ {registered, []},
+ {applications, [kernel, app10, hopefully_not_an_existing_app]},
+ {mod, {ch_sup, {app_chain_error2, 21,21}}}]}.
+
app_group_leader() ->
{application, group_leader,
[{description, "GROUP_LEADER CXC 138 11"},
@@ -2374,6 +2471,12 @@ w_app7(Fd) ->
w_app8(Fd) ->
io:format(Fd, "~p.\n", [app8()]).
+w_app9(Fd) ->
+ io:format(Fd, "~p.\n", [app9()]).
+
+w_app10_dep9(Fd) ->
+ io:format(Fd, "~p.\n", [app10_dep9()]).
+
w_app_start_error(Fd) ->
io:format(Fd, "~p.\n", [app_start_error()]).
diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl
index 62ba95e1a3..ed43749cc0 100644
--- a/lib/kernel/test/inet_SUITE.erl
+++ b/lib/kernel/test/inet_SUITE.erl
@@ -38,10 +38,10 @@
gethostnative_debug_level/0, gethostnative_debug_level/1,
getif/1,
getif_ifr_name_overflow/1,getservbyname_overflow/1, getifaddrs/1,
- parse_strict_address/1]).
+ parse_strict_address/1, simple_netns/1]).
-export([get_hosts/1, get_ipv6_hosts/1, parse_hosts/1, parse_address/1,
- kill_gethost/0, parallell_gethost/0]).
+ kill_gethost/0, parallell_gethost/0, test_netns/0]).
-export([init_per_testcase/2, end_per_testcase/2]).
suite() -> [{ct_hooks,[ts_install_cth]}].
@@ -53,7 +53,7 @@ all() ->
t_gethostnative, gethostnative_parallell, cname_loop,
gethostnative_debug_level, gethostnative_soft_restart,
getif, getif_ifr_name_overflow, getservbyname_overflow,
- getifaddrs, parse_strict_address].
+ getifaddrs, parse_strict_address, simple_netns].
groups() ->
[{parse, [], [parse_hosts, parse_address]}].
@@ -226,7 +226,7 @@ t_gethostbyname_v6(Config) when is_list(Config) ->
h_addr_list = [IP4]} = HEnt4,
{ok,IP46} =
inet_parse:ipv6_address(
- "::ffff:" ++ inet_parse:ntoa(IP4)),
+ "::ffff:" ++ inet:ntoa(IP4)),
check_elems(
[{HEnt#hostent.h_name,[Name,FullName]}])
end,
@@ -246,7 +246,7 @@ t_gethostbyname_v6(Config) when is_list(Config) ->
h_addr_list = [IP4F]} = HEnt4F,
{ok,IP46F} =
inet_parse:ipv6_address(
- "::ffff:" ++ inet_parse:ntoa(IP4F)),
+ "::ffff:" ++ inet:ntoa(IP4F)),
check_elems(
[{HEntF#hostent.h_name,[Name,FullName]}])
end;
@@ -1099,3 +1099,96 @@ toupper([C|Cs]) when is_integer(C) ->
end;
toupper([]) ->
[].
+
+
+simple_netns(Config) when is_list(Config) ->
+ {ok,U} = gen_udp:open(0),
+ case inet:setopts(U, [{netns,""}]) of
+ ok ->
+ jog_netns_opt(U),
+ ok = gen_udp:close(U),
+ %%
+ {ok,L} = gen_tcp:listen(0, []),
+ jog_netns_opt(L),
+ ok = gen_tcp:close(L),
+ %%
+ {ok,S} = gen_sctp:open(),
+ jog_netns_opt(S),
+ ok = gen_sctp:close(S);
+ {error,einval} ->
+ {skip,"setns() not supported"}
+ end.
+
+jog_netns_opt(S) ->
+ %% This is just jogging the option mechanics
+ ok = inet:setopts(S, [{netns,""}]),
+ {ok,[{netns,""}]} = inet:getopts(S, [netns]),
+ ok = inet:setopts(S, [{netns,"/proc/self/ns/net"}]),
+ {ok,[{netns,"/proc/self/ns/net"}]} = inet:getopts(S, [netns]),
+ ok.
+
+
+%% Manual test to be run outside test_server in an emulator
+%% started by root, in a machine with setns() support...
+test_netns() ->
+ DefaultIF = v1,
+ DefaultIP = {192,168,1,17},
+ Namespace = "test",
+ NamespaceIF = v2,
+ NamespaceIP = {192,168,1,18},
+ %%
+ DefaultIPString = inet_parse:ntoa(DefaultIP),
+ NamespaceIPString = inet_parse:ntoa(NamespaceIP),
+ cmd("ip netns add ~s",
+ [Namespace]),
+ cmd("ip link add name ~w type veth peer name ~w netns ~s",
+ [DefaultIF,NamespaceIF,Namespace]),
+ cmd("ip netns exec ~s ip addr add ~s/30 dev ~w",
+ [Namespace,NamespaceIPString,NamespaceIF]),
+ cmd("ip netns exec ~s ip link set ~w up",
+ [Namespace,NamespaceIF]),
+ cmd("ip addr add ~s/30 dev ~w",
+ [DefaultIPString,DefaultIF]),
+ cmd("ip link set ~w up",
+ [DefaultIF]),
+ try test_netns(
+ {DefaultIF,DefaultIP},
+ filename:join("/var/run/netns/", Namespace),
+ {NamespaceIF,NamespaceIP}) of
+ Result ->
+ io:put_chars(["#### Test done",io_lib:nl()]),
+ Result
+ after
+ cmd("ip link delete ~w type veth",
+ [DefaultIF]),
+ cmd("ip netns delete ~s",
+ [Namespace])
+ end.
+
+test_netns({DefaultIF,DefaultIP}, Namespace, {NamespaceIF,NamespaceIP}) ->
+ {ok,ListenSocket} = gen_tcp:listen(0, [{active,false}]),
+ {ok,[{addr,DefaultIP}]} = inet:ifget(ListenSocket, DefaultIF, [addr]),
+ {ok,ListenPort} = inet:port(ListenSocket),
+ {ok,ConnectSocket} =
+ gen_tcp:connect(
+ DefaultIP, ListenPort, [{active,false},{netns,Namespace}], 3000),
+ {ok,[{addr,NamespaceIP}]} = inet:ifget(ConnectSocket, NamespaceIF, [addr]),
+ {ok,ConnectPort} = inet:port(ConnectSocket),
+ {ok,AcceptSocket} = gen_tcp:accept(ListenSocket, 0),
+ {ok,AcceptPort} = inet:port(AcceptSocket),
+ {ok,{NamespaceIP,ConnectPort}} = inet:peername(AcceptSocket),
+ {ok,{DefaultIP,AcceptPort}} = inet:peername(ConnectSocket),
+ ok = gen_tcp:send(ConnectSocket, "data"),
+ ok = gen_tcp:close(ConnectSocket),
+ {ok,"data"} = gen_tcp:recv(AcceptSocket, 4, 1000),
+ {error,closed} = gen_tcp:recv(AcceptSocket, 1, 1000),
+ ok = gen_tcp:close(AcceptSocket),
+ ok = gen_tcp:close(ListenSocket).
+
+cmd(Cmd, Args) ->
+ cmd(io_lib:format(Cmd, Args)).
+%%
+cmd(CmdString) ->
+ io:put_chars(["# ",CmdString,io_lib:nl()]),
+ io:put_chars([os:cmd(CmdString++" ; echo ' =>' $?")]),
+ ok.
diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl
index d7d9434b1f..a375adceea 100644
--- a/lib/kernel/test/interactive_shell_SUITE.erl
+++ b/lib/kernel/test/interactive_shell_SUITE.erl
@@ -22,7 +22,7 @@
init_per_group/2,end_per_group/2,
get_columns_and_rows/1, exit_initial/1, job_control_local/1,
job_control_remote/1,
- job_control_remote_noshell/1]).
+ job_control_remote_noshell/1,ctrl_keys/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
%% For spawn
@@ -41,7 +41,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[get_columns_and_rows, exit_initial, job_control_local,
- job_control_remote, job_control_remote_noshell].
+ job_control_remote, job_control_remote_noshell,
+ ctrl_keys].
groups() ->
[].
@@ -289,7 +290,51 @@ job_control_remote_noshell(Config) when is_list(Config) ->
?line stop_noshell_node(NSNode),
?line Res
end.
-
+
+ctrl_keys(suite) -> [];
+ctrl_keys(doc) -> ["Tests various control keys"];
+ctrl_keys(_Conf) when is_list(_Conf) ->
+ Cu=[$\^u],
+ Cw=[$\^w],
+ Home=[27,$O,$H],
+ End=[27,$O,$F],
+ rtnode([{putline,""},
+ {putline,"2."},
+ {getline,"2"},
+ {putline,"\"hello "++Cw++"world\"."}, % test <CTRL>+W
+ {getline,"\"world\""},
+ {putline,"\"hello "++Cu++"\"world\"."}, % test <CTRL>+U
+ {getline,"\"world\""},
+ {putline,"world\"."++Home++"\"hello "}, % test <HOME>
+ {getline,"\"hello world\""},
+ {putline,"world"++Home++"\"hello "++End++"\"."}, % test <END>
+ {getline,"\"hello world\""}]
+ ++wordLeft()++wordRight(),[]).
+
+
+wordLeft() ->
+ L1=[27,27,$[,$D],
+ L2=[27]++"[5D",
+ L3=[27]++"[1;5D",
+ wordLeft(L1)++wordLeft(L2)++wordLeft(L3).
+
+wordLeft(Chars) ->
+ End=[27,$O,$F],
+ [{putline,"\"world\""++Chars++"hello "++End++"."},
+ {getline,"\"hello world\""}].
+
+wordRight() ->
+ R1=[27,27,$[,$C],
+ R2=[27]++"[5C",
+ R3=[27]++"[1;5C",
+ wordRight(R1)++wordRight(R2)++wordRight(R3).
+
+wordRight(Chars) ->
+ Home=[27,$O,$H],
+ [{putline,"world"++Home++"\"hello "++Chars++"\"."},
+ {getline,"\"hello world\""}].
+
+
rtnode(C,N) ->
rtnode(C,N,[]).
rtnode(Commands,Nodename,ErlPrefix) ->
diff --git a/lib/kernel/test/kernel_smoke.spec b/lib/kernel/test/kernel_smoke.spec
new file mode 100644
index 0000000000..e5d8273c56
--- /dev/null
+++ b/lib/kernel/test/kernel_smoke.spec
@@ -0,0 +1,9 @@
+{config, "../test_server/ts.config"}.
+{config, "../test_server/ts.unix.config"}.
+
+{cases,"../kernel_test", inet_SUITE,[t_gethostbyaddr,t_gethostbyname,
+ t_gethostbyaddr_v6,t_gethostbyname_v6,t_gethostnative,getifaddrs]}.
+{cases,"../kernel_test", inet_res_SUITE,[gethostbyaddr,gethostbyname,
+ gethostbyaddr_v6,gethostbyname_v6,basic]}.
+{cases,"../kernel_test", gen_tcp_echo_SUITE,[active_echo]}.
+{cases,"../kernel_test", heart_SUITE,[reboot]}.