aboutsummaryrefslogtreecommitdiffstats
path: root/erts/preloaded
diff options
context:
space:
mode:
Diffstat (limited to 'erts/preloaded')
-rw-r--r--erts/preloaded/ebin/atomics.beambin0 -> 3292 bytes
-rw-r--r--erts/preloaded/ebin/counters.beambin0 -> 3092 bytes
-rw-r--r--erts/preloaded/ebin/erl_init.beambin0 -> 2312 bytes
-rw-r--r--erts/preloaded/ebin/erl_prim_loader.beambin54452 -> 52636 bytes
-rw-r--r--erts/preloaded/ebin/erl_tracer.beambin2188 -> 2200 bytes
-rw-r--r--erts/preloaded/ebin/erlang.beambin103300 -> 100196 bytes
-rw-r--r--erts/preloaded/ebin/erts_code_purger.beambin11372 -> 11204 bytes
-rw-r--r--erts/preloaded/ebin/erts_dirty_process_signal_handler.beambin2760 -> 2764 bytes
-rw-r--r--erts/preloaded/ebin/erts_internal.beambin16676 -> 17788 bytes
-rw-r--r--erts/preloaded/ebin/erts_literal_area_collector.beambin3288 -> 3268 bytes
-rw-r--r--erts/preloaded/ebin/init.beambin51428 -> 50304 bytes
-rw-r--r--erts/preloaded/ebin/net.beambin0 -> 6140 bytes
-rw-r--r--erts/preloaded/ebin/persistent_term.beambin0 -> 1836 bytes
-rw-r--r--erts/preloaded/ebin/prim_buffer.beambin3588 -> 3596 bytes
-rw-r--r--erts/preloaded/ebin/prim_eval.beambin1496 -> 1496 bytes
-rw-r--r--erts/preloaded/ebin/prim_file.beambin27740 -> 28084 bytes
-rw-r--r--erts/preloaded/ebin/prim_inet.beambin77932 -> 81012 bytes
-rw-r--r--erts/preloaded/ebin/prim_zip.beambin22892 -> 22644 bytes
-rw-r--r--erts/preloaded/ebin/socket.beambin0 -> 70300 bytes
-rw-r--r--erts/preloaded/ebin/zlib.beambin19760 -> 19716 bytes
-rw-r--r--erts/preloaded/src/Makefile55
-rw-r--r--erts/preloaded/src/atomics.erl119
-rw-r--r--erts/preloaded/src/counters.erl104
-rw-r--r--erts/preloaded/src/erl_init.erl70
-rw-r--r--erts/preloaded/src/erl_prim_loader.erl7
-rw-r--r--erts/preloaded/src/erlang.erl337
-rw-r--r--erts/preloaded/src/erts.app.src11
-rw-r--r--erts/preloaded/src/erts_internal.erl44
-rw-r--r--erts/preloaded/src/init.erl14
-rw-r--r--erts/preloaded/src/net.erl353
-rw-r--r--erts/preloaded/src/otp_ring0.erl37
-rw-r--r--erts/preloaded/src/persistent_term.erl62
-rw-r--r--erts/preloaded/src/prim_eval.S4
-rw-r--r--erts/preloaded/src/prim_file.erl55
-rw-r--r--erts/preloaded/src/prim_inet.erl399
-rw-r--r--erts/preloaded/src/socket.erl3611
36 files changed, 4896 insertions, 386 deletions
diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam
new file mode 100644
index 0000000000..ef402b5fee
--- /dev/null
+++ b/erts/preloaded/ebin/atomics.beam
Binary files differ
diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam
new file mode 100644
index 0000000000..674d0d27fa
--- /dev/null
+++ b/erts/preloaded/ebin/counters.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam
new file mode 100644
index 0000000000..0313988e3e
--- /dev/null
+++ b/erts/preloaded/ebin/erl_init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam
index 0f5f5036f0..661bcd8413 100644
--- a/erts/preloaded/ebin/erl_prim_loader.beam
+++ b/erts/preloaded/ebin/erl_prim_loader.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam
index 6017112dac..ec4d6153d1 100644
--- a/erts/preloaded/ebin/erl_tracer.beam
+++ b/erts/preloaded/ebin/erl_tracer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index bd8cc7d7e0..62dc8702e7 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam
index c899b69a2c..669149df82 100644
--- a/erts/preloaded/ebin/erts_code_purger.beam
+++ b/erts/preloaded/ebin/erts_code_purger.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
index 9490a56758..6d3528c2dc 100644
--- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
+++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam
index 15c59de80a..b3af713809 100644
--- a/erts/preloaded/ebin/erts_internal.beam
+++ b/erts/preloaded/ebin/erts_internal.beam
Binary files differ
diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam
index e650a6b5af..fc2bf6f6bd 100644
--- a/erts/preloaded/ebin/erts_literal_area_collector.beam
+++ b/erts/preloaded/ebin/erts_literal_area_collector.beam
Binary files differ
diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam
index 858a9dc63e..1d89174b25 100644
--- a/erts/preloaded/ebin/init.beam
+++ b/erts/preloaded/ebin/init.beam
Binary files differ
diff --git a/erts/preloaded/ebin/net.beam b/erts/preloaded/ebin/net.beam
new file mode 100644
index 0000000000..f61b2b4a69
--- /dev/null
+++ b/erts/preloaded/ebin/net.beam
Binary files differ
diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam
new file mode 100644
index 0000000000..c882e4fad4
--- /dev/null
+++ b/erts/preloaded/ebin/persistent_term.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam
index 4ad1380d0b..cf671bf8f4 100644
--- a/erts/preloaded/ebin/prim_buffer.beam
+++ b/erts/preloaded/ebin/prim_buffer.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam
index 2ae18846bf..24911123f9 100644
--- a/erts/preloaded/ebin/prim_eval.beam
+++ b/erts/preloaded/ebin/prim_eval.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam
index f375c05c99..0efd954e50 100644
--- a/erts/preloaded/ebin/prim_file.beam
+++ b/erts/preloaded/ebin/prim_file.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam
index c30c589d3a..ff9268ad38 100644
--- a/erts/preloaded/ebin/prim_inet.beam
+++ b/erts/preloaded/ebin/prim_inet.beam
Binary files differ
diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam
index 4923cadbdc..d319d7a343 100644
--- a/erts/preloaded/ebin/prim_zip.beam
+++ b/erts/preloaded/ebin/prim_zip.beam
Binary files differ
diff --git a/erts/preloaded/ebin/socket.beam b/erts/preloaded/ebin/socket.beam
new file mode 100644
index 0000000000..558a886565
--- /dev/null
+++ b/erts/preloaded/ebin/socket.beam
Binary files differ
diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam
index 07e7e97814..9610c94ac2 100644
--- a/erts/preloaded/ebin/zlib.beam
+++ b/erts/preloaded/ebin/zlib.beam
Binary files differ
diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile
index 4333f6643a..27d450c873 100644
--- a/erts/preloaded/src/Makefile
+++ b/erts/preloaded/src/Makefile
@@ -1,7 +1,7 @@
#
# %CopyrightBegin%
#
-# Copyright Ericsson AB 2008-2018. All Rights Reserved.
+# Copyright Ericsson AB 2008-2019. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -33,21 +33,34 @@ STATIC_EBIN=../ebin
include $(ERL_TOP)/erts/vsn.mk
include $(ERL_TOP)/lib/kernel/vsn.mk
+ifeq ($(USE_ESOCK), yes)
+PRE_LOADED_ERL_ESOCK_MODULES = \
+ socket \
+ net
+else
+PRE_LOADED_ERL_ESOCK_MODULES = \
+ net
+endif
+
PRE_LOADED_ERL_MODULES = \
erl_prim_loader \
init \
prim_buffer \
prim_file \
prim_inet \
+ $(PRE_LOADED_ERL_ESOCK_MODULES) \
zlib \
prim_zip \
- otp_ring0 \
+ erl_init \
erts_code_purger \
erlang \
erts_internal \
erl_tracer \
erts_literal_area_collector \
- erts_dirty_process_signal_handler
+ erts_dirty_process_signal_handler \
+ atomics \
+ counters \
+ persistent_term
PRE_LOADED_BEAM_MODULES = \
prim_eval
@@ -68,6 +81,11 @@ STATIC_TARGET_FILES = $(PRE_LOADED_MODULES:%=$(STATIC_EBIN)/%.$(EMULATOR))
APP_FILE= erts.app
APP_SRC= $(APP_FILE).src
APP_TARGET= $(STATIC_EBIN)/$(APP_FILE)
+ifeq ($(USE_ESOCK), yes)
+APP_ESOCK_MODS= net, socket
+else
+APP_ESOCK_MODS= net
+endif
KERNEL_SRC=$(ERL_TOP)/lib/kernel/src
@@ -76,6 +94,10 @@ STDLIB_INCLUDE=$(ERL_TOP)/lib/stdlib/include
ERL_COMPILE_FLAGS += +debug_info -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE)
+DIA_PLT = erts-preloaded.plt
+DIA_ANALYSIS = $(basename $(DIA_PLT)).dialyzer_analysis
+
+
debug opt: $(TARGET_FILES)
clean:
@@ -85,8 +107,7 @@ copy:
cp *.beam $(STATIC_EBIN)
$(APP_TARGET): $(APP_SRC) $(ERL_TOP)/erts/vsn.mk
- $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' $< > $@
-
+ $(vsn_verbose)sed -e 's;%VSN%;$(VSN);' -e 's;%ESOCK_MODS%;$(APP_ESOCK_MODS);' $< > $@
include $(ERL_TOP)/make/otp_release_targets.mk
@@ -102,6 +123,30 @@ release_docs_spec:
list_preloaded:
@echo $(PRE_LOADED_MODULES)
+dclean:
+ rm -f $(DIA_PLT)
+ rm -f $(DIA_ANALYSIS)
+
+dialyzer_plt: $(DIA_PLT)
+
+$(DIA_PLT): $(ERL_FILES)
+ @echo "Building ($(basename $(DIA_PLT))) plt file"
+ @dialyzer --build_plt \
+ --output_plt $@ \
+ -r ../ebin \
+ ../../../lib/kernel/ebin \
+ ../../../lib/stdlib/ebin \
+ ../../../lib/crypto/ebin \
+ ../../../lib/compiler/ebin \
+ --output $(DIA_ANALYSIS) \
+ --verbose
+
+dialyzer: $(DIA_PLT)
+ @echo "Running dialyzer on $(basename $(DIA_PLT))"
+ @dialyzer --plt $< \
+ ../ebin \
+ --verbose
+
#
# Combine a BEAM assembly script file a stub Erlang file into a BEAM file.
# See add_abstract_chunk script.
diff --git a/erts/preloaded/src/atomics.erl b/erts/preloaded/src/atomics.erl
new file mode 100644
index 0000000000..d1fe5e65cf
--- /dev/null
+++ b/erts/preloaded/src/atomics.erl
@@ -0,0 +1,119 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Purpose : Main atomics API module.
+
+-module(atomics).
+
+-export([new/2,
+ put/3, get/2,
+ add/3, add_get/3,
+ sub/3, sub_get/3,
+ exchange/3, compare_exchange/4,
+ info/1]).
+
+-export_type([atomics_ref/0]).
+
+-opaque atomics_ref() :: reference().
+
+-define(OPT_SIGNED, (1 bsl 0)).
+-define(OPT_DEFAULT, ?OPT_SIGNED).
+
+-spec new(Arity, Opts) -> atomics_ref() when
+ Arity :: pos_integer(),
+ Opts :: [Opt],
+ Opt :: {signed, boolean()}.
+new(Arity, Opts) ->
+ erts_internal:atomics_new(Arity, encode_opts(Opts, ?OPT_DEFAULT)).
+
+encode_opts([{signed, true}|T], Acc) ->
+ encode_opts(T, Acc bor ?OPT_SIGNED);
+encode_opts([{signed, false}|T], Acc) ->
+ encode_opts(T, Acc band (bnot ?OPT_SIGNED));
+encode_opts([], Acc) ->
+ Acc;
+encode_opts(_, _) ->
+ erlang:error(badarg).
+
+-spec put(Ref, Ix, Value) -> ok when
+ Ref :: atomics_ref(),
+ Ix :: integer(),
+ Value :: integer().
+put(_Ref, _Ix, _Value) ->
+ erlang:nif_error(undef).
+
+-spec get(Ref, Ix) -> integer() when
+ Ref :: atomics_ref(),
+ Ix :: integer().
+get(_Ref, _Ix) ->
+ erlang:nif_error(undef).
+
+-spec add(Ref, Ix, Incr) -> ok when
+ Ref :: atomics_ref(),
+ Ix :: integer(),
+ Incr :: integer().
+add(_Ref, _Ix, _Incr) ->
+ erlang:nif_error(undef).
+
+-spec add_get(Ref, Ix, Incr) -> integer() when
+ Ref :: atomics_ref(),
+ Ix :: integer(),
+ Incr :: integer().
+add_get(_Ref, _Ix, _Incr) ->
+ erlang:nif_error(undef).
+
+-spec sub(Ref, Ix, Decr) -> ok when
+ Ref :: atomics_ref(),
+ Ix :: integer(),
+ Decr :: integer().
+sub(Ref, Ix, Decr) ->
+ ?MODULE:add(Ref, Ix, -Decr).
+
+-spec sub_get(Ref, Ix, Decr) -> integer() when
+ Ref :: atomics_ref(),
+ Ix :: integer(),
+ Decr :: integer().
+sub_get(Ref, Ix, Decr) ->
+ ?MODULE:add_get(Ref, Ix, -Decr).
+
+-spec exchange(Ref, Ix, Desired) -> integer() when
+ Ref :: atomics_ref(),
+ Ix :: integer(),
+ Desired :: integer().
+exchange(_Ref, _Ix, _Desired) ->
+ erlang:nif_error(undef).
+
+-spec compare_exchange(Ref, Ix, Expected, Desired) -> ok | integer() when
+ Ref :: atomics_ref(),
+ Ix :: integer(),
+ Expected :: integer(),
+ Desired :: integer().
+compare_exchange(_Ref, _Ix, _Expected, _Desired) ->
+ erlang:nif_error(undef).
+
+-spec info(Ref) -> Info when
+ Ref :: atomics_ref(),
+ Info :: #{'size':=Size,'max':=Max,'min':=Min,'memory':=Memory},
+ Size :: non_neg_integer(),
+ Max :: integer(),
+ Min :: integer(),
+ Memory :: non_neg_integer().
+info(_Ref) ->
+ erlang:nif_error(undef).
diff --git a/erts/preloaded/src/counters.erl b/erts/preloaded/src/counters.erl
new file mode 100644
index 0000000000..a0e3035e0f
--- /dev/null
+++ b/erts/preloaded/src/counters.erl
@@ -0,0 +1,104 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%% Purpose : Main atomics API module.
+
+-module(counters).
+
+-export([new/2,
+ get/2,
+ add/3,
+ sub/3,
+ put/3,
+ info/1]).
+
+-export_type([counters_ref/0]).
+
+-opaque counters_ref() :: {atomics, reference()} | {write_concurrency, reference()}.
+
+-spec new(Size, Opts) -> counters_ref() when
+ Size :: pos_integer(),
+ Opts :: [Opt],
+ Opt :: atomics | write_concurrency.
+new(Size, [atomics]) ->
+ {atomics, atomics:new(Size, [{signed, true}])};
+new(Size, [write_concurrency]) ->
+ {write_concurrency, erts_internal:counters_new(Size)};
+new(Size, []) ->
+ new(Size, [atomics]);
+new(_, _) ->
+ erlang:error(badarg).
+
+-spec get(Ref, Ix) -> integer() when
+ Ref :: counters_ref(),
+ Ix :: integer().
+get({atomics,Ref}, Ix) ->
+ atomics:get(Ref, Ix);
+get({write_concurrency, Ref}, Ix) ->
+ erts_internal:counters_get(Ref, Ix);
+get(_, _) ->
+ erlang:error(badarg).
+
+
+
+-spec add(Ref, Ix, Incr) -> ok when
+ Ref :: counters_ref(),
+ Ix :: integer(),
+ Incr :: integer().
+add({atomics, Ref}, Ix, Incr) ->
+ atomics:add(Ref, Ix, Incr);
+add({write_concurrency, Ref}, Ix, Incr) ->
+ erts_internal:counters_add(Ref, Ix, Incr);
+add(_, _, _) ->
+ erlang:error(badarg).
+
+
+-spec sub(Ref, Ix, Decr) -> ok when
+ Ref :: counters_ref(),
+ Ix :: integer(),
+ Decr :: integer().
+sub(Ref, Ix, Decr) ->
+ add(Ref, Ix, -Decr).
+
+
+-spec put(Ref, Ix, Value) -> ok when
+ Ref :: counters_ref(),
+ Ix :: integer(),
+ Value :: integer().
+put({atomics, Ref}, Ix, Value) ->
+ atomics:put(Ref, Ix, Value);
+put({write_concurrency, Ref}, Ix, Value) ->
+ erts_internal:counters_put(Ref, Ix, Value);
+put(_, _, _) ->
+ erlang:error(badarg).
+
+
+-spec info(Ref) -> Info when
+ Ref :: counters_ref(),
+ Info :: #{'size':=Size, 'memory':=Memory},
+ Size :: non_neg_integer(),
+ Memory :: non_neg_integer().
+info({atomics, Ref}) ->
+ atomics:info(Ref);
+info({write_concurrency, Ref}) ->
+ erts_internal:counters_info(Ref);
+info(_) ->
+ erlang:error(badarg).
+
diff --git a/erts/preloaded/src/erl_init.erl b/erts/preloaded/src/erl_init.erl
new file mode 100644
index 0000000000..d209c4033b
--- /dev/null
+++ b/erts/preloaded/src/erl_init.erl
@@ -0,0 +1,70 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erl_init).
+
+%% Initial process of an Erlang system.
+
+-export([start/2]).
+
+%% This gets the module name given by the +i option (default 'init')
+%% and the list of command line arguments
+
+-spec start(Mod, BootArgs) -> no_return() when
+ Mod :: module(),
+ BootArgs :: [binary()].
+start(Mod, BootArgs) ->
+ %% Load the static nifs
+ zlib:on_load(),
+ erl_tracer:on_load(),
+ prim_buffer:on_load(),
+ prim_file:on_load(),
+ conditional_load(socket, [socket, net]), % socket:on_load(), net:on_load(),
+ %% Proceed to the specified boot module
+ run(Mod, boot, BootArgs).
+
+run(M, F, A) ->
+ case erlang:function_exported(M, F, 1) of
+ false ->
+ erlang:display({fatal,error,module,M,"does not export",F,"/1"}),
+ halt(1);
+ true ->
+ M:F(A)
+ end.
+
+conditional_load(CondMod, Mods2Load) ->
+ conditional_load(CondMod, erlang:loaded(), Mods2Load).
+
+conditional_load(_CondMod, [], _Mods2LOad) ->
+ ok;
+conditional_load(CondMod, [CondMod|_], Mods2Load) ->
+ on_load(Mods2Load);
+conditional_load(CondMod, [_|T], Mods2Load) ->
+ conditional_load(CondMod, T, Mods2Load).
+
+on_load([]) ->
+ ok;
+on_load([Mod|Mods]) ->
+ Mod:on_load(),
+ on_load(Mods).
+
+
+
+
+
diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl
index ae5f86e017..1605c20f2c 100644
--- a/erts/preloaded/src/erl_prim_loader.erl
+++ b/erts/preloaded/src/erl_prim_loader.erl
@@ -297,12 +297,13 @@ check_file_result(Func, Target, {error,Reason}) ->
"Target: " ++ TargetStr ++ ". " ++
"Function: " ++ atom_to_list(Func) ++ ". " ++ Process
end,
- %% this is equal to calling error_logger:error_report/1 which
- %% we don't want to do from code_server during system boot
+ %% This is equal to calling logger:error/2 which
+ %% we don't want to do from code_server during system boot.
+ %% We don't want to call logger:timestamp() either.
logger ! {log,error,#{label=>{?MODULE,file_error},report=>Report},
#{pid=>self(),
gl=>group_leader(),
- time=>erlang:monotonic_time(microsecond),
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>error_report,
type=>std_error}}},
error
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index f4f31b1e4b..ac73946dc0 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -48,6 +48,8 @@
dist_ctrl_put_data/2,
dist_ctrl_get_data/1,
dist_ctrl_get_data_notification/1,
+ dist_ctrl_get_opt/2,
+ dist_ctrl_set_opt/3,
dist_get_stat/1]).
-deprecated([get_stacktrace/0,now/0]).
@@ -86,15 +88,15 @@
| 'nano_seconds'.
-opaque prepared_code() :: reference().
-
-export_type([prepared_code/0]).
--opaque dist_handle() :: atom().
+-opaque nif_resource() :: reference().
+-export_type([nif_resource/0]).
+-opaque dist_handle() :: atom().
-export_type([dist_handle/0]).
-type iovec() :: [binary()].
-
-export_type([iovec/0]).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -2295,7 +2297,7 @@ process_flag(_Flag, _Value) ->
non_neg_integer()}]} |
{catchlevel, CatchLevel :: non_neg_integer()} |
{current_function,
- {Module :: module(), Function :: atom(), Arity :: arity()}} |
+ {Module :: module(), Function :: atom(), Arity :: arity()} | undefined} |
{current_location,
{Module :: module(), Function :: atom(), Arity :: arity(),
Location :: [{file, Filename :: string()} | % not a stack_item()!
@@ -2316,7 +2318,7 @@ process_flag(_Flag, _Value) ->
{min_heap_size, MinHeapSize :: non_neg_integer()} |
{min_bin_vheap_size, MinBinVHeapSize :: non_neg_integer()} |
{max_heap_size, MaxHeapSize :: max_heap_size()} |
- {monitored_by, Pids :: [pid()]} |
+ {monitored_by, MonitoredBy :: [pid() | port() | nif_resource()]} |
{monitors,
Monitors :: [{process | port, Pid :: pid() | port() |
{RegName :: atom(), Node :: node()}}]} |
@@ -2526,6 +2528,9 @@ subtract(_,_) ->
OldSchedulersOnline when
SchedulersOnline :: pos_integer(),
OldSchedulersOnline :: pos_integer();
+ (system_logger, Logger) -> PrevLogger when
+ Logger :: logger | undefined | pid(),
+ PrevLogger :: logger | undefined | pid();
(trace_control_word, TCW) -> OldTCW when
TCW :: non_neg_integer(),
OldTCW :: non_neg_integer();
@@ -2534,7 +2539,7 @@ subtract(_,_) ->
%% These are deliberately not documented
(internal_cpu_topology, term()) -> term();
(sequential_tracer, pid() | port() | {module(), term()} | false) -> pid() | port() | false;
- (1,0) -> true.
+ (reset_seq_trace,true) -> true.
system_flag(_Flag, _Value) ->
erlang:nif_error(undefined).
@@ -2681,11 +2686,12 @@ tuple_to_list(_Tuple) ->
(dist_ctrl) -> {Node :: node(),
ControllingEntity :: port() | pid()};
(driver_version) -> string();
- (dynamic_trace) -> none | dtrace | systemtap;
+ (dynamic_trace) -> none | dtrace | systemtap;
(dynamic_trace_probes) -> boolean();
(end_time) -> non_neg_integer();
(elib_malloc) -> false;
(eager_check_io) -> boolean();
+ (ets_count) -> pos_integer();
(ets_limit) -> pos_integer();
(fullsweep_after) -> {fullsweep_after, non_neg_integer()};
(garbage_collection) -> [{atom(), integer()}];
@@ -2730,8 +2736,9 @@ tuple_to_list(_Tuple) ->
(schedulers | schedulers_online) -> pos_integer();
(smp_support) -> boolean();
(start_time) -> integer();
- (system_version) -> string();
(system_architecture) -> string();
+ (system_logger) -> logger | undefined | pid();
+ (system_version) -> string();
(threads) -> boolean();
(thread_pool_size) -> non_neg_integer();
(time_correction) -> true | false;
@@ -3321,7 +3328,8 @@ dist_ctrl_input_handler(_DHandle, _InputHandler) ->
dist_ctrl_put_data(_DHandle, _Data) ->
erlang:nif_error(undefined).
--spec erlang:dist_ctrl_get_data(DHandle) -> Data | 'none' when
+-spec erlang:dist_ctrl_get_data(DHandle) -> {Size, Data} | Data | 'none' when
+ Size :: non_neg_integer(),
DHandle :: dist_handle(),
Data :: iodata().
@@ -3334,6 +3342,21 @@ dist_ctrl_get_data(_DHandle) ->
dist_ctrl_get_data_notification(_DHandle) ->
erlang:nif_error(undefined).
+-spec erlang:dist_ctrl_set_opt(DHandle, 'get_size', Value) -> OldValue when
+ DHandle :: dist_handle(),
+ Value :: boolean(),
+ OldValue :: boolean().
+
+dist_ctrl_set_opt(_DHandle, _Opt, _Val) ->
+ erlang:nif_error(undefined).
+
+-spec erlang:dist_ctrl_get_opt(DHandle, 'get_size') -> Value when
+ DHandle :: dist_handle(),
+ Value :: boolean().
+
+dist_ctrl_get_opt(_DHandle, _Opt) ->
+ erlang:nif_error(undefined).
+
-spec erlang:dist_get_stat(DHandle) -> Res when
DHandle :: dist_handle(),
InputPackets :: non_neg_integer(),
@@ -3394,60 +3417,15 @@ get_cookie() ->
-spec integer_to_list(Integer, Base) -> string() when
Integer :: integer(),
Base :: 2..36.
-integer_to_list(I, 10) ->
- erlang:integer_to_list(I);
-integer_to_list(I, Base)
- when erlang:is_integer(I), erlang:is_integer(Base),
- Base >= 2, Base =< 1+$Z-$A+10 ->
- if I < 0 ->
- [$-|integer_to_list(-I, Base, [])];
- true ->
- integer_to_list(I, Base, [])
- end;
-integer_to_list(I, Base) ->
- erlang:error(badarg, [I, Base]).
-
-integer_to_list(I0, Base, R0) ->
- D = I0 rem Base,
- I1 = I0 div Base,
- R1 = if D >= 10 ->
- [D-10+$A|R0];
- true ->
- [D+$0|R0]
- end,
- if I1 =:= 0 ->
- R1;
- true ->
- integer_to_list(I1, Base, R1)
- end.
+integer_to_list(_I, _Base) ->
+ erlang:nif_error(undefined).
-spec integer_to_binary(Integer, Base) -> binary() when
Integer :: integer(),
Base :: 2..36.
-integer_to_binary(I, 10) ->
- erlang:integer_to_binary(I);
-integer_to_binary(I, Base)
- when erlang:is_integer(I), erlang:is_integer(Base),
- Base >= 2, Base =< 1+$Z-$A+10 ->
- if I < 0 ->
- <<$-,(integer_to_binary(-I, Base, <<>>))/binary>>;
- true ->
- integer_to_binary(I, Base, <<>>)
- end;
-integer_to_binary(I, Base) ->
- erlang:error(badarg, [I, Base]).
-
-integer_to_binary(I0, Base, R0) ->
- D = I0 rem Base,
- I1 = I0 div Base,
- R1 = if
- D >= 10 -> <<(D-10+$A),R0/binary>>;
- true -> <<(D+$0),R0/binary>>
- end,
- if
- I1 =:= 0 -> R1;
- true -> integer_to_binary(I1, Base, R1)
- end.
+integer_to_binary(_I, _Base) ->
+ erlang:nif_error(undefined).
+
-record(cpu, {node = -1,
processor = -1,
@@ -3653,90 +3631,28 @@ memory() ->
-spec erlang:memory(Type :: memory_type()) -> non_neg_integer();
(TypeList :: [memory_type()]) -> [{memory_type(), non_neg_integer()}].
memory(Type) when erlang:is_atom(Type) ->
- {AA, ALCU, ChkSup, BadArgZero} = need_mem_info(Type),
- case get_mem_data(ChkSup, ALCU, AA) of
- notsup ->
- erlang:error(notsup, [Type]);
- Mem ->
- Value = get_memval(Type, Mem),
- case {BadArgZero, Value} of
- {true, 0} -> erlang:error(badarg, [Type]);
- _ -> Value
- end
+ try
+ case aa_mem_data(au_mem_data(?ALL_NEEDED_ALLOCS)) of
+ notsup -> erlang:error(notsup);
+ Mem -> get_memval(Type, Mem)
+ end
+ catch
+ error:badarg -> erlang:error(badarg)
end;
memory(Types) when erlang:is_list(Types) ->
- {AA, ALCU, ChkSup, BadArgZeroList} = need_mem_info_list(Types),
- case get_mem_data(ChkSup, ALCU, AA) of
- notsup ->
- erlang:error(notsup, [Types]);
- Mem ->
- case memory_result_list(Types, BadArgZeroList, Mem) of
- badarg -> erlang:error(badarg, [Types]);
- Result -> Result
- end
- end.
-
-memory_result_list([], [], _Mem) ->
- [];
-memory_result_list([T|Ts], [BAZ|BAZs], Mem) ->
- case memory_result_list(Ts, BAZs, Mem) of
- badarg -> badarg;
- TVs ->
- V = get_memval(T, Mem),
- case {BAZ, V} of
- {true, 0} -> badarg;
- _ -> [{T, V}| TVs]
- end
- end.
-
-get_mem_data(true, AlcUAllocs, NeedAllocatedAreas) ->
- case memory_is_supported() of
- false -> notsup;
- true -> get_mem_data(false, AlcUAllocs, NeedAllocatedAreas)
- end;
-get_mem_data(false, AlcUAllocs, NeedAllocatedAreas) ->
- AlcUMem = case AlcUAllocs of
- [] -> #memory{};
- _ ->
- au_mem_data(AlcUAllocs)
- end,
- case NeedAllocatedAreas of
- true -> aa_mem_data(AlcUMem);
- false -> AlcUMem
+ try
+ case aa_mem_data(au_mem_data(?ALL_NEEDED_ALLOCS)) of
+ notsup -> erlang:error(notsup);
+ Mem -> memory_1(Types, Mem)
+ end
+ catch
+ error:badarg -> erlang:error(badarg)
end.
-need_mem_info_list([]) ->
- {false, [], false, []};
-need_mem_info_list([T|Ts]) ->
- {MAA, MALCU, MChkSup, MBadArgZero} = need_mem_info_list(Ts),
- {AA, ALCU, ChkSup, BadArgZero} = need_mem_info(T),
- {case AA of
- true -> true;
- _ -> MAA
- end,
- ALCU ++ (MALCU -- ALCU),
- case ChkSup of
- true -> true;
- _ -> MChkSup
- end,
- [BadArgZero|MBadArgZero]}.
-
-need_mem_info(Type) when Type == total;
- Type == system ->
- {true, ?ALL_NEEDED_ALLOCS, false, false};
-need_mem_info(Type) when Type == processes;
- Type == processes_used ->
- {true, [eheap_alloc, fix_alloc], true, false};
-need_mem_info(Type) when Type == atom;
- Type == atom_used;
- Type == code ->
- {true, [], true, false};
-need_mem_info(binary) ->
- {false, [binary_alloc], true, false};
-need_mem_info(ets) ->
- {true, [ets_alloc], true, false};
-need_mem_info(_) ->
- {false, [], false, true}.
+memory_1([Type | Types], Mem) ->
+ [{Type, get_memval(Type, Mem)} | memory_1(Types, Mem)];
+memory_1([], _Mem) ->
+ [].
get_memval(total, #memory{total = V}) -> V;
get_memval(processes, #memory{processes = V}) -> V;
@@ -3747,16 +3663,7 @@ get_memval(atom_used, #memory{atom_used = V}) -> V;
get_memval(binary, #memory{binary = V}) -> V;
get_memval(code, #memory{code = V}) -> V;
get_memval(ets, #memory{ets = V}) -> V;
-get_memval(_, #memory{}) -> 0.
-
-memory_is_supported() ->
- {_, _, FeatureList, _} = erlang:system_info(allocator),
- case ((erlang:system_info(alloc_util_allocators)
- -- ?CARRIER_ALLOCS)
- -- FeatureList) of
- [] -> true;
- _ -> false
- end.
+get_memval(_, #memory{}) -> erlang:error(badarg).
get_blocks_size([{blocks_size, Sz, _, _} | Rest], Acc) ->
get_blocks_size(Rest, Acc+Sz);
@@ -3767,16 +3674,6 @@ get_blocks_size([_ | Rest], Acc) ->
get_blocks_size([], Acc) ->
Acc.
-
-blocks_size([{Carriers, SizeList} | Rest], Acc) when Carriers == mbcs;
- Carriers == mbcs_pool;
- Carriers == sbcs ->
- blocks_size(Rest, get_blocks_size(SizeList, Acc));
-blocks_size([_ | Rest], Acc) ->
- blocks_size(Rest, Acc);
-blocks_size([], Acc) ->
- Acc.
-
get_fix_proc([{ProcType, A1, U1}| Rest], {A0, U0}) when ProcType == proc;
ProcType == monitor;
ProcType == link;
@@ -3801,64 +3698,78 @@ fix_proc([_ | Rest], Acc) ->
fix_proc([], Acc) ->
Acc.
+au_mem_fix(#memory{ processes = Proc,
+ processes_used = ProcU,
+ system = Sys } = Mem, Data) ->
+ case fix_proc(Data, {0, 0}) of
+ {A, U} ->
+ Mem#memory{ processes = Proc+A,
+ processes_used = ProcU+U,
+ system = Sys-A };
+ {Mask, A, U} ->
+ Mem#memory{ processes = Mask band (Proc+A),
+ processes_used = Mask band (ProcU+U),
+ system = Mask band (Sys-A) }
+ end.
+
+au_mem_acc(#memory{ total = Tot,
+ processes = Proc,
+ processes_used = ProcU } = Mem,
+ eheap_alloc, Data) ->
+ Sz = get_blocks_size(Data, 0),
+ Mem#memory{ total = Tot+Sz,
+ processes = Proc+Sz,
+ processes_used = ProcU+Sz};
+au_mem_acc(#memory{ total = Tot,
+ system = Sys,
+ ets = Ets } = Mem, ets_alloc, Data) ->
+ Sz = get_blocks_size(Data, 0),
+ Mem#memory{ total = Tot+Sz,
+ system = Sys+Sz,
+ ets = Ets+Sz };
+au_mem_acc(#memory{total = Tot,
+ system = Sys,
+ binary = Bin } = Mem,
+ binary_alloc, Data) ->
+ Sz = get_blocks_size(Data, 0),
+ Mem#memory{ total = Tot+Sz,
+ system = Sys+Sz,
+ binary = Bin+Sz};
+au_mem_acc(#memory{ total = Tot,
+ system = Sys } = Mem,
+ _Type, Data) ->
+ Sz = get_blocks_size(Data, 0),
+ Mem#memory{ total = Tot+Sz,
+ system = Sys+Sz }.
+
+au_mem_foreign(Mem, [{Type, SizeList} | Rest]) ->
+ au_mem_foreign(au_mem_acc(Mem, Type, SizeList), Rest);
+au_mem_foreign(Mem, []) ->
+ Mem.
+
+au_mem_current(Mem0, Type, [{mbcs_pool, MBCS} | Rest]) ->
+ [Foreign] = [Foreign || {foreign_blocks, Foreign} <- MBCS],
+ SizeList = MBCS -- [Foreign],
+ Mem = au_mem_foreign(Mem0, Foreign),
+ au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
+au_mem_current(Mem, Type, [{mbcs, SizeList} | Rest]) ->
+ au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
+au_mem_current(Mem, Type, [{sbcs, SizeList} | Rest]) ->
+ au_mem_current(au_mem_acc(Mem, Type, SizeList), Type, Rest);
+au_mem_current(Mem, Type, [_ | Rest]) ->
+ au_mem_current(Mem, Type, Rest);
+au_mem_current(Mem, _Type, []) ->
+ Mem.
+
au_mem_data(notsup, _) ->
notsup;
au_mem_data(_, [{_, false} | _]) ->
notsup;
-au_mem_data(#memory{total = Tot,
- processes = Proc,
- processes_used = ProcU} = Mem,
- [{eheap_alloc, _, Data} | Rest]) ->
- Sz = blocks_size(Data, 0),
- au_mem_data(Mem#memory{total = Tot+Sz,
- processes = Proc+Sz,
- processes_used = ProcU+Sz},
- Rest);
-au_mem_data(#memory{total = Tot,
- system = Sys,
- ets = Ets} = Mem,
- [{ets_alloc, _, Data} | Rest]) ->
- Sz = blocks_size(Data, 0),
- au_mem_data(Mem#memory{total = Tot+Sz,
- system = Sys+Sz,
- ets = Ets+Sz},
- Rest);
-au_mem_data(#memory{total = Tot,
- system = Sys,
- binary = Bin} = Mem,
- [{binary_alloc, _, Data} | Rest]) ->
- Sz = blocks_size(Data, 0),
- au_mem_data(Mem#memory{total = Tot+Sz,
- system = Sys+Sz,
- binary = Bin+Sz},
- Rest);
-au_mem_data(#memory{total = Tot,
- processes = Proc,
- processes_used = ProcU,
- system = Sys} = Mem,
- [{fix_alloc, _, Data} | Rest]) ->
- Sz = blocks_size(Data, 0),
- case fix_proc(Data, {0, 0}) of
- {A, U} ->
- au_mem_data(Mem#memory{total = Tot+Sz,
- processes = Proc+A,
- processes_used = ProcU+U,
- system = Sys+Sz-A},
- Rest);
- {Mask, A, U} ->
- au_mem_data(Mem#memory{total = Tot+Sz,
- processes = Mask band (Proc+A),
- processes_used = Mask band (ProcU+U),
- system = Mask band (Sys+Sz-A)},
- Rest)
- end;
-au_mem_data(#memory{total = Tot,
- system = Sys} = Mem,
- [{_, _, Data} | Rest]) ->
- Sz = blocks_size(Data, 0),
- au_mem_data(Mem#memory{total = Tot+Sz,
- system = Sys+Sz},
- Rest);
+au_mem_data(#memory{} = Mem0, [{fix_alloc, _, Data} | Rest]) ->
+ Mem = au_mem_fix(Mem0, Data),
+ au_mem_data(au_mem_current(Mem, fix_alloc, Data), Rest);
+au_mem_data(#memory{} = Mem, [{Type, _, Data} | Rest]) ->
+ au_mem_data(au_mem_current(Mem, Type, Data), Rest);
au_mem_data(EMD, []) ->
EMD.
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index 1ccc4988c2..132397b478 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2018. All Rights Reserved.
+%% Copyright Ericsson AB 2013-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -26,19 +26,22 @@
erl_prim_loader,
erts_internal,
init,
- otp_ring0,
+ erl_init,
erts_code_purger,
prim_buffer,
prim_eval,
prim_file,
prim_inet,
prim_zip,
- zlib
+ atomics,
+ counters,
+ zlib,
+ %ESOCK_MODS%
]},
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-3.5", "kernel-6.0", "sasl-3.0.1"]}
+ {runtime_dependencies, ["stdlib-3.5", "kernel-6.1", "sasl-3.3"]}
]}.
%% vim: ft=erlang
diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl
index 88f47e917b..305b524438 100644
--- a/erts/preloaded/src/erts_internal.erl
+++ b/erts/preloaded/src/erts_internal.erl
@@ -90,6 +90,15 @@
-export([create_dist_channel/4]).
+-export([erase_persistent_terms/0]).
+
+-export([atomics_new/2]).
+
+-export([counters_new/1, counters_get/2, counters_add/3,
+ counters_put/3, counters_info/1]).
+
+-export([spawn_system_process/3]).
+
%%
%% Await result of send to port
%%
@@ -691,3 +700,38 @@ process_flag(_Pid, _Flag, _Value) ->
create_dist_channel(_Node, _DistCtrlr, _Flags, _Ver) ->
erlang:nif_error(undefined).
+
+-spec erase_persistent_terms() -> 'ok'.
+erase_persistent_terms() ->
+ erlang:nif_error(undefined).
+
+-spec atomics_new(pos_integer(), pos_integer()) -> reference().
+atomics_new(_Arity, _EncOpts) ->
+ erlang:nif_error(undef).
+
+-spec counters_new(pos_integer()) -> reference().
+counters_new(_Size) ->
+ erlang:nif_error(undef).
+
+-spec counters_get(reference(), pos_integer()) -> integer().
+counters_get(_Ref, _Ix) ->
+ erlang:nif_error(undef).
+
+-spec counters_add(reference(), pos_integer(), integer()) -> ok.
+counters_add(_Ref, _Ix, _Incr) ->
+ erlang:nif_error(undef).
+
+-spec counters_put(reference(), pos_integer(), integer()) -> ok.
+counters_put(_Ref, _Ix, _Value) ->
+ erlang:nif_error(undef).
+
+-spec counters_info(reference()) -> #{}.
+counters_info(_Ref) ->
+ erlang:nif_error(undef).
+
+-spec spawn_system_process(Mod, Func, Args) -> pid() when
+ Mod :: atom(),
+ Func :: atom(),
+ Args :: list().
+spawn_system_process(_Mod, _Func, _Args) ->
+ erlang:nif_error(undefined).
diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl
index 253fcf7a1f..5ea67347ec 100644
--- a/erts/preloaded/src/init.erl
+++ b/erts/preloaded/src/init.erl
@@ -200,12 +200,6 @@ boot(BootArgs) ->
register(init, self()),
process_flag(trap_exit, true),
- %% Load the static nifs
- zlib:on_load(),
- erl_tracer:on_load(),
- prim_buffer:on_load(),
- prim_file:on_load(),
-
{Start0,Flags,Args} = parse_boot_args(BootArgs),
%% We don't get to profile parsing of BootArgs
case b2a(get_flag(profile_boot, Flags, false)) of
@@ -483,13 +477,16 @@ do_handle_msg(Msg,State) ->
{From, {ensure_loaded, _}} ->
From ! {init, not_allowed};
X ->
+ %% This is equal to calling logger:info/3 which we don't
+ %% want to do from the init process, at least not during
+ %% system boot. We don't want to call logger:timestamp()
+ %% either.
case whereis(user) of
undefined ->
- Time = erlang:monotonic_time(microsecond),
catch logger ! {log, info, "init got unexpected: ~p", [X],
#{pid=>self(),
gl=>self(),
- time=>Time,
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>info_msg}}};
User ->
User ! X,
@@ -552,6 +549,7 @@ stop(Reason,State) ->
do_stop(restart,#state{start = Start, flags = Flags, args = Args}) ->
%% Make sure we don't have any outstanding messages before doing the restart.
flush(),
+ erts_internal:erase_persistent_terms(),
boot(Start,Flags,Args);
do_stop(reboot,_) ->
halt();
diff --git a/erts/preloaded/src/net.erl b/erts/preloaded/src/net.erl
new file mode 100644
index 0000000000..13d2e3a117
--- /dev/null
+++ b/erts/preloaded/src/net.erl
@@ -0,0 +1,353 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(net).
+
+-compile(no_native).
+
+%% Administrative and "global" utility functions
+-export([
+ on_load/0, on_load/1,
+ info/0,
+ command/1
+ ]).
+
+-export([
+ gethostname/0,
+ getnameinfo/1, getnameinfo/2,
+ getaddrinfo/1, getaddrinfo/2,
+
+ if_name2index/1,
+ if_index2name/1,
+ if_names/0
+ ]).
+
+%% Deprecated functions from the "old" net module
+-export([call/4,
+ cast/4,
+ broadcast/3,
+ ping/1,
+ relay/1,
+ sleep/1]).
+
+-export_type([
+ address_info/0,
+ name_info/0,
+
+ name_info_flags/0,
+ name_info_flag/0,
+ name_info_flag_ext/0,
+
+ network_interface_name/0,
+ network_interface_index/0
+ ]).
+
+-deprecated({call, 4, eventually}).
+-deprecated({cast, 4, eventually}).
+-deprecated({broadcast, 3, eventually}).
+-deprecated({ping, 1, eventually}).
+-deprecated({relay, 1, eventually}).
+-deprecated({sleep, 1, eventually}).
+
+
+-type name_info_flags() :: [name_info_flag()|name_info_flag_ext()].
+-type name_info_flag() :: namereqd |
+ dgram |
+ nofqdn |
+ numerichost |
+ nomericserv.
+-type name_info_flag_ext() :: idn |
+ idna_allow_unassigned |
+ idna_use_std3_ascii_rules.
+-type name_info() :: #{host := string(),
+ service := string()}.
+-type address_info() :: #{family := socket:domain(),
+ socktype := socket:type(),
+ protocol := socket:protocol(),
+ address := socket:sockaddr()}.
+-type network_interface_name() :: string().
+-type network_interface_index() :: non_neg_integer().
+
+
+%% ===========================================================================
+%%
+%% D E P R E C A T E D F U N C T I O N S
+%%
+%% ===========================================================================
+
+call(N,M,F,A) -> rpc:call(N,M,F,A).
+cast(N,M,F,A) -> rpc:cast(N,M,F,A).
+broadcast(M,F,A) -> rpc:eval_everywhere(M,F,A).
+ping(Node) -> net_adm:ping(Node).
+sleep(T) -> receive after T -> ok end.
+relay(X) -> slave:relay(X).
+
+
+
+%% ===========================================================================
+%%
+%% Administrative and utility API
+%%
+%% ===========================================================================
+
+-spec on_load() -> ok.
+
+%% Should we require that the Extra arg is a map?
+on_load() ->
+ on_load(#{}).
+
+-spec on_load(Extra) -> ok when
+ Extra :: map().
+
+on_load(Extra) ->
+ ok = erlang:load_nif(atom_to_list(?MODULE), Extra).
+
+
+-spec info() -> list().
+
+info() ->
+ nif_info().
+
+
+-spec command(Cmd :: term()) -> term().
+
+command(Cmd) ->
+ nif_command(Cmd).
+
+
+
+%% ===========================================================================
+%%
+%% The proper net API
+%%
+%% ===========================================================================
+
+%% ===========================================================================
+%%
+%% gethostname - Get the name of the current host.
+%%
+%%
+
+-spec gethostname() -> {ok, HostName} | {error, Reason} when
+ HostName :: string(),
+ Reason :: term().
+
+gethostname() ->
+ nif_gethostname().
+
+
+%% ===========================================================================
+%%
+%% getnameinfo - Address-to-name translation in protocol-independent manner.
+%%
+%%
+
+-spec getnameinfo(SockAddr) -> {ok, Info} | {error, Reason} when
+ SockAddr :: socket:sockaddr(),
+ Info :: name_info(),
+ Reason :: term().
+
+getnameinfo(SockAddr) ->
+ getnameinfo(SockAddr, undefined).
+
+-spec getnameinfo(SockAddr, Flags) -> {ok, Info} | {error, Reason} when
+ SockAddr :: socket:sockaddr(),
+ Flags :: name_info_flags() | undefined,
+ Info :: name_info(),
+ Reason :: term().
+
+getnameinfo(SockAddr, [] = _Flags) ->
+ getnameinfo(SockAddr, undefined);
+getnameinfo(#{family := Fam, addr := _Addr} = SockAddr, Flags)
+ when ((Fam =:= inet) orelse (Fam =:= inet6)) andalso
+ (is_list(Flags) orelse (Flags =:= undefined)) ->
+ nif_getnameinfo((catch ensure_sockaddr(SockAddr)), Flags);
+getnameinfo(#{family := Fam, path := _Path} = SockAddr, Flags)
+ when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) ->
+ nif_getnameinfo(SockAddr, Flags).
+
+
+%% This function is intended to "handle" the case when the user
+%% has built their (OTP) system with "--disable-esock".
+%% That means the socket module does not exist. This is not really
+%% a problem since the nif_getnameinfo won't work either (since
+%% the nif file is not part of the system). The result of calling
+%% getnameinfo will be a undef exception (erlang:nif_error(undef)).
+%%
+%% The only functions in this module that actually work in this case
+%% (--disable-esock) is the depricated stuff (call, cast, ...).
+%%
+ensure_sockaddr(SockAddr) ->
+ try socket:ensure_sockaddr(SockAddr)
+ catch
+ error:undef:_ ->
+ undefined
+ end.
+
+%% ===========================================================================
+%%
+%% getaddrinfo - Network address and service translation
+%%
+%% There is also a "hint" argument that we "at some point" should implement.
+
+-spec getaddrinfo(Host) -> {ok, Info} | {error, Reason} when
+ Host :: string(),
+ Info :: [address_info()],
+ Reason :: term().
+
+getaddrinfo(Host) when is_list(Host) ->
+ getaddrinfo(Host, undefined).
+
+
+-spec getaddrinfo(Host, undefined) -> {ok, Info} | {error, Reason} when
+ Host :: string(),
+ Info :: [address_info()],
+ Reason :: term()
+ ; (undefined, Service) -> {ok, Info} | {error, Reason} when
+ Service :: string(),
+ Info :: [address_info()],
+ Reason :: term()
+ ; (Host, Service) -> {ok, Info} | {error, Reason} when
+ Host :: string(),
+ Service :: string(),
+ Info :: [address_info()],
+ Reason :: term().
+
+getaddrinfo(Host, Service)
+ when (is_list(Host) orelse (Host =:= undefined)) andalso
+ (is_list(Service) orelse (Service =:= undefined)) andalso
+ (not ((Service =:= undefined) andalso (Host =:= undefined))) ->
+ nif_getaddrinfo(Host, Service, undefined).
+
+
+
+%% ===========================================================================
+%%
+%% if_name2index - Mappings between network interface names and indexes:
+%% name -> idx
+%%
+%%
+
+-spec if_name2index(Name) -> {ok, Idx} | {error, Reason} when
+ Name :: network_interface_name(),
+ Idx :: network_interface_index(),
+ Reason :: term().
+
+if_name2index(If) when is_list(If) ->
+ nif_if_name2index(If).
+
+
+
+%% ===========================================================================
+%%
+%% if_index2name - Mappings between network interface index and names:
+%% idx -> name
+%%
+%%
+
+-spec if_index2name(Idx) -> {ok, Name} | {error, Reason} when
+ Idx :: network_interface_index(),
+ Name :: network_interface_name(),
+ Reason :: term().
+
+if_index2name(Idx) when is_integer(Idx) ->
+ nif_if_index2name(Idx).
+
+
+
+%% ===========================================================================
+%%
+%% if_names - Get network interface names and indexes
+%%
+%%
+
+-spec if_names() -> Names | {error, Reason} when
+ Names :: [{Idx, If}],
+ Idx :: network_interface_index(),
+ If :: network_interface_name(),
+ Reason :: term().
+
+if_names() ->
+ nif_if_names().
+
+
+
+%% ===========================================================================
+%%
+%% Misc utility functions
+%%
+%% ===========================================================================
+
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
+
+%% format_timestamp(Now) ->
+%% N2T = fun(N) -> calendar:now_to_local_time(N) end,
+%% format_timestamp(Now, N2T, true).
+
+%% format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+%% FormatExtra = ".~.2.0w",
+%% ArgsExtra = [N3 div 10000],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+%% FormatExtra = "",
+%% ArgsExtra = [],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+%% {Date, Time} = N2T(N),
+%% {YYYY,MM,DD} = Date,
+%% {Hour,Min,Sec} = Time,
+%% FormatDate =
+%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+%% lists:flatten(FormatDate).
+
+
+%% ===========================================================================
+%%
+%% The actual NIF-functions.
+%%
+%% ===========================================================================
+
+nif_info() ->
+ erlang:nif_error(undef).
+
+nif_command(_Cmd) ->
+ erlang:nif_error(undef).
+
+nif_gethostname() ->
+ erlang:nif_error(undef).
+
+nif_getnameinfo(_Addr, _Flags) ->
+ erlang:nif_error(undef).
+
+nif_getaddrinfo(_Host, _Service, _Hints) ->
+ erlang:nif_error(undef).
+
+nif_if_name2index(_Name) ->
+ erlang:nif_error(undef).
+
+nif_if_index2name(_Id) ->
+ erlang:nif_error(undef).
+
+nif_if_names() ->
+ erlang:nif_error(undef).
+
diff --git a/erts/preloaded/src/otp_ring0.erl b/erts/preloaded/src/otp_ring0.erl
deleted file mode 100644
index 62a60fffe2..0000000000
--- a/erts/preloaded/src/otp_ring0.erl
+++ /dev/null
@@ -1,37 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2000-2016. All Rights Reserved.
-%%
-%% Licensed under the Apache License, Version 2.0 (the "License");
-%% you may not use this file except in compliance with the License.
-%% You may obtain a copy of the License at
-%%
-%% http://www.apache.org/licenses/LICENSE-2.0
-%%
-%% Unless required by applicable law or agreed to in writing, software
-%% distributed under the License is distributed on an "AS IS" BASIS,
-%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%% See the License for the specific language governing permissions and
-%% limitations under the License.
-%%
-%% %CopyrightEnd%
-%%
--module(otp_ring0).
-
-%% Purpose : Start up of erlang system.
-
--export([start/2]).
-
--spec start(_, term()) -> term().
-start(_Env, Argv) ->
- run(init, boot, Argv).
-
-run(M, F, A) ->
- case erlang:function_exported(M, F, 1) of
- false ->
- erlang:display({fatal,error,module,M,"does not export",F,"/1"}),
- halt(1);
- true ->
- M:F(A)
- end.
diff --git a/erts/preloaded/src/persistent_term.erl b/erts/preloaded/src/persistent_term.erl
new file mode 100644
index 0000000000..ee7e49b6cb
--- /dev/null
+++ b/erts/preloaded/src/persistent_term.erl
@@ -0,0 +1,62 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(persistent_term).
+
+-export([erase/1,get/0,get/1,get/2,info/0,put/2]).
+
+-type key() :: term().
+-type value() :: term().
+
+-spec erase(Key) -> Result when
+ Key :: key(),
+ Result :: boolean().
+erase(_Key) ->
+ erlang:nif_error(undef).
+
+-spec get() -> List when
+ List :: [{key(),value()}].
+get() ->
+ erlang:nif_error(undef).
+
+-spec get(Key) -> Value when
+ Key :: key(),
+ Value :: value().
+get(_Key) ->
+ erlang:nif_error(undef).
+
+-spec get(Key, Default) -> Value when
+ Key :: key(),
+ Default :: value(),
+ Value :: value().
+get(_Key, _Default) ->
+ erlang:nif_error(undef).
+
+-spec info() -> Info when
+ Info :: #{'count':=Count,'memory':=Memory},
+ Count :: non_neg_integer(),
+ Memory :: non_neg_integer().
+info() ->
+ erlang:nif_error(undef).
+
+-spec put(Key, Value) -> 'ok' when
+ Key :: key(),
+ Value :: value().
+put(_Key, _Value) ->
+ erlang:nif_error(undef).
diff --git a/erts/preloaded/src/prim_eval.S b/erts/preloaded/src/prim_eval.S
index e4b1560517..900fda5d89 100644
--- a/erts/preloaded/src/prim_eval.S
+++ b/erts/preloaded/src/prim_eval.S
@@ -42,6 +42,10 @@
{label,3}.
{loop_rec,{f,5},{x,0}}.
{move,{y,1},{x,1}}.
+ %% Tell the validator that it's safe to pass the message as an argument,
+ %% as the match fun is "known" not to build a term with it, and the
+ %% loop_rec instruction has disabled the GC.
+ {'%', {remove_fragility, {x,0}}}.
{call_fun,1}.
{test,is_ne_exact,{f,4},[{x,0},{atom,nomatch}]}.
remove_message.
diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl
index 41ff38359c..1aa5d85c64 100644
--- a/erts/preloaded/src/prim_file.erl
+++ b/erts/preloaded/src/prim_file.erl
@@ -46,7 +46,7 @@
-define(MIN_READLINE_SIZE, 256).
-define(LARGEFILESIZE, (1 bsl 63)).
--export([copy/3]).
+-export([copy/3, start/0]).
-include("file_int.hrl").
@@ -83,7 +83,23 @@ internal_normalize_utf8(_) ->
is_translatable(_) ->
erlang:nif_error(undefined).
-%%
+%% This is a janitor process used to close files whose controlling process has
+%% died. The emulator will be torn down if this is killed.
+start() ->
+ helper_loop().
+
+helper_loop() ->
+ receive
+ {close, FRef} when is_reference(FRef) -> delayed_close_nif(FRef);
+ _ -> ok
+ end,
+ helper_loop().
+
+on_load() ->
+ %% This is spawned as a system process to prevent init:restart/0 from
+ %% killing it.
+ Pid = erts_internal:spawn_system_process(?MODULE, start, []),
+ ok = erlang:load_nif(atom_to_list(?MODULE), Pid).
%% Returns {error, Reason} | {ok, BytesCopied}
copy(#file_descriptor{module = ?MODULE} = Source,
@@ -94,9 +110,6 @@ copy(#file_descriptor{module = ?MODULE} = Source,
%% XXX Should be moved down to the driver for optimization.
file:copy_opened(Source, Dest, Length).
-on_load() ->
- ok = erlang:load_nif(atom_to_list(?MODULE), 0).
-
open(Name, Modes) ->
%% The try/catch pattern seen here is used throughout the file to adhere to
%% the public file interface, which has leaked through for ages because of
@@ -482,6 +495,8 @@ truncate_nif(_FileRef) ->
erlang:nif_error(undef).
get_handle_nif(_FileRef) ->
erlang:nif_error(undef).
+delayed_close_nif(_FileRef) ->
+ erlang:nif_error(undef).
%%
%% Quality-of-life helpers
@@ -557,12 +572,13 @@ list_dir_convert([RawName | Rest], SkipInvalid, Result) ->
{error, ignore} ->
list_dir_convert(Rest, SkipInvalid, Result);
{error, warning} ->
- %% this is equal to calling error_logger:warning_msg/2 which
- %% we don't want to do from code_server during system boot
+ %% This is equal to calling logger:warning/3 which
+ %% we don't want to do from code_server during system boot.
+ %% We don't want to call logger:timestamp() either.
logger ! {log,warning,"Non-unicode filename ~p ignored\n", [RawName],
#{pid=>self(),
gl=>group_leader(),
- time=>erlang:system_time(microsecond),
+ time=>os:system_time(microsecond),
error_logger=>#{tag=>warning_msg}}},
list_dir_convert(Rest, SkipInvalid, Result);
{error, _} ->
@@ -580,15 +596,20 @@ read_link_info(Name, Opts) ->
read_info_1(Name, 0, proplist_get_value(time, Opts, local)).
read_info_1(Name, FollowLinks, TimeType) ->
- try read_info_nif(encode_path(Name), FollowLinks) of
- {error, Reason} -> {error, Reason};
- FileInfo ->
- CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
- MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
- ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
- {ok, FileInfo#file_info{ ctime = CTime, mtime = MTime, atime = ATime }}
+ try
+ case read_info_nif(encode_path(Name), FollowLinks) of
+ {error, Reason} ->
+ {error, Reason};
+ FileInfo ->
+ CTime = from_posix_seconds(FileInfo#file_info.ctime, TimeType),
+ MTime = from_posix_seconds(FileInfo#file_info.mtime, TimeType),
+ ATime = from_posix_seconds(FileInfo#file_info.atime, TimeType),
+ {ok, FileInfo#file_info{ ctime = CTime,
+ mtime = MTime,
+ atime = ATime }}
+ end
catch
- error:badarg -> {error, badarg}
+ error:_ -> {error, badarg}
end.
write_file_info(Filename, Info) ->
@@ -781,7 +802,7 @@ altname_nif(_Path) ->
%% We know for certain that lists:reverse/2 is a BIF, so it's safe to use it
%% even though this module is preloaded.
-reverse_list(List) -> lists:reverse(List).
+reverse_list(List) -> lists:reverse(List, []).
proplist_get_value(_Key, [], Default) ->
Default;
diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl
index 2a3605260d..d5abdd2483 100644
--- a/erts/preloaded/src/prim_inet.erl
+++ b/erts/preloaded/src/prim_inet.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2017. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2019. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
@@ -29,7 +29,7 @@
-export([open/3, open/4, fdopen/4, fdopen/5, close/1]).
-export([bind/3, listen/1, listen/2, peeloff/2]).
-export([connect/3, connect/4, async_connect/4]).
--export([accept/1, accept/2, async_accept/2]).
+-export([accept/1, accept/2, accept/3, async_accept/2]).
-export([shutdown/2]).
-export([send/2, send/3, sendto/4, sendmsg/3, sendfile/4]).
-export([recv/2, recv/3, async_recv/3]).
@@ -49,9 +49,15 @@
-include("inet_sctp.hrl").
-include("inet_int.hrl").
-%-define(DEBUG, 1).
+%%%-define(DEBUG, 1).
-ifdef(DEBUG).
--define(DBG_FORMAT(Format, Args), (io:format((Format), (Args)))).
+-define(
+ DBG_FORMAT(Format, Args),
+ begin
+ %% io:format((Format), (Args)),
+ erlang:display(lists:flatten(io_lib:format((Format), (Args)))),
+ ok
+ end).
-else.
-define(DBG_FORMAT(Format, Args), ok).
-endif.
@@ -150,39 +156,106 @@ shutdown_1(S, How) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
close(S) when is_port(S) ->
+ ?DBG_FORMAT("prim_inet:close(~p)~n", [S]),
case getopt(S, linger) of
{ok,{true,0}} ->
close_port(S);
- _ ->
- case subscribe(S, [subs_empty_out_q]) of
- {ok, [{subs_empty_out_q,N}]} when N > 0 ->
- close_pend_loop(S, N); %% wait for pending output to be sent
- _ ->
- close_port(S)
- end
+ {ok,{true,T}} ->
+ %% Wait for T seconds for pending output to be sent
+ %%
+ %% Note that this handling of Linger may look ok,
+ %% but sweeps some problems under the rug since
+ %% there are OS buffers that may have remaining data
+ %% after the inet driver has emptied its buffers.
+ %% But Linger for nonblocking sockets is broken
+ %% anyway on all OS:es, according to hearsay,
+ %% and is a contradiction in itself.
+ %% We have hereby done our best...
+ %%
+ case subscribe(S, [subs_empty_out_q]) of
+ {ok, [{subs_empty_out_q,0}]} ->
+ close_port(S);
+ {ok, [{subs_empty_out_q,N}]} when N > 0 ->
+ %% Wait for pending output to be sent
+ Tref = erlang:start_timer(T * 1000, self(), close_port),
+ close_pend_loop(S, Tref, N);
+ _ ->
+ %% Subscribe failed - wait full time
+ Tref = erlang:start_timer(T * 1000, self(), close_port),
+ close_pend_loop(S, Tref, undefined)
+ end;
+ _ -> % Regard this as {ok,{false,_}}
+ case subscribe(S, [subs_empty_out_q]) of
+ {ok, [{subs_empty_out_q,N}]} when N > 0 ->
+ %% Wait for pending output to be sent
+ DefaultT = 180000, % Arbitrary system timeout 3 min
+ Tref = erlang:start_timer(DefaultT, self(), close_port),
+ close_pend_loop(S, Tref, N);
+ _ ->
+ %% Subscribe failed or empty out q - give up or done
+ close_port(S)
+ end
end.
-close_pend_loop(S, N) ->
+close_pend_loop(S, Tref, N) ->
+ ?DBG_FORMAT("prim_inet:close_pend_loop(~p, _, ~p)~n", [S,N]),
receive
- {empty_out_q,S} ->
- close_port(S)
+ {timeout,Tref,_} -> % Linger timeout
+ ?DBG_FORMAT("prim_inet:close_pend_loop(~p, _, _) timeout~n", [S]),
+ close_port(S);
+ {empty_out_q,S} when N =/= undefined ->
+ ?DBG_FORMAT(
+ "prim_inet:close_pend_loop(~p, _, _) empty_out_q~n", [S]),
+ close_port(S, Tref)
after ?INET_CLOSE_TIMEOUT ->
case getstat(S, [send_pend]) of
{ok, [{send_pend,N1}]} ->
+ ?DBG_FORMAT(
+ "prim_inet:close_pend_loop(~p, _, _) send_pend ~p~n",
+ [S,N1]),
if
- N1 =:= N ->
- close_port(S);
- true ->
- close_pend_loop(S, N1)
+ N1 =:= 0 ->
+ %% Empty outq - done
+ close_port(S, Tref);
+ N =:= undefined ->
+ %% Within linger time - wait some more
+ close_pend_loop(S, Tref, N);
+ N1 =:= N ->
+ %% Inactivity - give up
+ close_port(S, Tref);
+ true ->
+ %% Still moving - wait some more
+ close_pend_loop(S, Tref, N)
end;
- _ ->
- close_port(S)
- end
+ _Stat ->
+ %% Failed getstat - give up
+ ?DBG_FORMAT(
+ "prim_inet:close_pend_loop(~p, _, _) getstat ~p~n",
+ [S,_Stat]),
+ close_port(S, Tref)
+ end
end.
+
+close_port(S, Tref) ->
+ ?DBG_FORMAT("prim_inet:close_port(~p, _)~n", [S]),
+ case erlang:cancel_timer(Tref) of
+ false ->
+ receive
+ {timeout,Tref,_} ->
+ ok
+ end;
+ _N ->
+ ok
+ end,
+ close_port(S).
+%%
close_port(S) ->
- catch erlang:port_close(S),
- receive {'EXIT',S,_} -> ok after 0 -> ok end.
+ ?DBG_FORMAT("prim_inet:close_port(~p)~n", [S]),
+ _Closed = (catch erlang:port_close(S)),
+ receive {'EXIT',S,_} -> ok after 0 -> ok end,
+ ?DBG_FORMAT("prim_inet:close_port(~p) ~p~n", [S,_Closed]),
+ ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
@@ -307,7 +380,7 @@ async_connect0(S, Addr, Time) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%
-%% ACCEPT(insock() [,Timeout] ) -> {ok,insock()} | {error, Reason}
+%% ACCEPT(insock() [,Timeout][,FamilyOpts] ) -> {ok,insock()} | {error, Reason}
%%
%% accept incoming connection on listen socket
%% if timeout is given:
@@ -315,6 +388,8 @@ async_connect0(S, Addr, Time) ->
%% 0 -> immediate accept (poll)
%% > 0 -> wait for timeout ms for accept if no accept then
%% return {error, timeout}
+%% FamilyOpts are address family specific options to copy from
+%% listen socket to accepted socket
%%
%% ASYNC_ACCEPT(insock(), Timeout)
%%
@@ -325,17 +400,22 @@ async_connect0(S, Addr, Time) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% For TCP sockets only.
%%
-accept(L) -> accept0(L, -1).
+accept(L) -> accept0(L, -1, []).
+
+accept(L, infinity) -> accept0(L, -1, []);
+accept(L, FamilyOpts) when is_list(FamilyOpts) -> accept0(L, -1, FamilyOpts);
+accept(L, Time) -> accept0(L, Time, []).
-accept(L, infinity) -> accept0(L, -1);
-accept(L, Time) -> accept0(L, Time).
+accept(L, infinity, FamilyOpts) -> accept0(L, -1, FamilyOpts);
+accept(L, Time, FamilyOpts) -> accept0(L, Time, FamilyOpts).
-accept0(L, Time) when is_port(L), is_integer(Time) ->
+accept0(L, Time, FamilyOpts)
+ when is_port(L), is_integer(Time), is_list(FamilyOpts) ->
case async_accept(L, Time) of
{ok, Ref} ->
receive
{inet_async, L, Ref, {ok,S}} ->
- accept_opts(L, S);
+ accept_opts(L, S, FamilyOpts);
{inet_async, L, Ref, Error} ->
Error
end;
@@ -343,25 +423,22 @@ accept0(L, Time) when is_port(L), is_integer(Time) ->
end.
%% setup options from listen socket on the connected socket
-accept_opts(L, S) ->
- case getopts(L, [active, nodelay, keepalive, delay_send, priority, tos]) of
+accept_opts(L, S, FamilyOpts) ->
+ case
+ getopts(
+ L,
+ [active, nodelay, keepalive, delay_send, priority]
+ ++ FamilyOpts)
+ of
{ok, Opts} ->
- case setopts(S, Opts) of
- ok ->
- case getopts(L, [tclass]) of
- {ok, []} ->
- {ok, S};
- {ok, TClassOpts} ->
- case setopts(S, TClassOpts) of
- ok ->
- {ok, S};
- Error -> close(S), Error
- end
- end;
- Error -> close(S), Error
- end;
- Error ->
- close(S), Error
+ case setopts(S, Opts) of
+ ok ->
+ {ok, S};
+ Error1 ->
+ close(S), Error1
+ end;
+ Error2 ->
+ close(S), Error2
end.
async_accept(L, Time) ->
@@ -420,23 +497,49 @@ peeloff(S, AssocId) ->
%% be called directly -- use "sendmsg" instead:
%%
send(S, Data, OptList) when is_port(S), is_list(OptList) ->
- ?DBG_FORMAT("prim_inet:send(~p, ~p)~n", [S,Data]),
+ ?DBG_FORMAT("prim_inet:send(~p, _, ~p)~n", [S,OptList]),
try erlang:port_command(S, Data, OptList) of
false -> % Port busy and nosuspend option passed
?DBG_FORMAT("prim_inet:send() -> {error,busy}~n", []),
{error,busy};
true ->
- receive
- {inet_reply,S,Status} ->
- ?DBG_FORMAT("prim_inet:send() -> ~p~n", [Status]),
- Status
- end
+ send_recv_reply(S, undefined)
catch
error:_Error ->
?DBG_FORMAT("prim_inet:send() -> {error,einval}~n", []),
{error,einval}
end.
+send_recv_reply(S, Mref) ->
+ ReplyTimeout =
+ case Mref of
+ undefined ->
+ ?INET_CLOSE_TIMEOUT;
+ _ ->
+ infinity
+ end,
+ receive
+ {inet_reply,S,Status} ->
+ ?DBG_FORMAT(
+ "prim_inet:send_recv_reply(~p, _): inet_reply ~p~n",
+ [S,Status]),
+ case Mref of
+ undefined -> ok;
+ _ ->
+ demonitor(Mref, [flush]),
+ ok
+ end,
+ Status;
+ {'DOWN',Mref,_,_,_Reason} when Mref =/= undefined ->
+ ?DBG_FORMAT(
+ "prim_inet:send_recv_reply(~p, _) 'DOWN' ~p~n",
+ [S,_Reason]),
+ {error,closed}
+ after ReplyTimeout ->
+ send_recv_reply(S, monitor(port, S))
+ end.
+
+
send(S, Data) ->
send(S, Data, []).
@@ -516,13 +619,35 @@ sendfile(S, FileHandle, Offset, Length)
sendfile(S, FileHandle, Offset, Length) ->
case erlang:port_info(S, connected) of
{connected, Pid} when Pid =:= self() ->
- sendfile_1(S, FileHandle, Offset, Length);
+ Uncork = sendfile_maybe_cork(S),
+ Result = sendfile_1(S, FileHandle, Offset, Length),
+ sendfile_maybe_uncork(S, Uncork),
+ Result;
{connected, Pid} when Pid =/= self() ->
{error, not_owner};
_Other ->
{error, einval}
end.
+sendfile_maybe_cork(S) ->
+ case getprotocol(S) of
+ tcp ->
+ case getopts(S, [nopush]) of
+ {ok, [{nopush,false}]} ->
+ _ = setopts(S, [{nopush,true}]),
+ true;
+ _ ->
+ false
+ end;
+ _ -> false
+ end.
+
+sendfile_maybe_uncork(S, true) ->
+ _ = setopts(S, [{nopush,false}]),
+ ok;
+sendfile_maybe_uncork(_, false) ->
+ ok.
+
sendfile_1(S, FileHandle, Offset, 0) ->
sendfile_1(S, FileHandle, Offset, (1 bsl 63) - 1);
sendfile_1(_S, _FileHandle, Offset, Length) when
@@ -616,7 +741,16 @@ recvfrom0(S, Length, Time)
Ref = ?u16(R1,R0),
receive
% Success, UDP:
+ {inet_async, S, Ref, {ok, {[F | AddrData], AncData}}} ->
+ %% With ancillary data
+ case get_addr(F, AddrData) of
+ {{Family, _} = Addr, Data} when is_atom(Family) ->
+ {ok, {Addr, 0, AncData, Data}};
+ {{IP, Port}, Data} ->
+ {ok, {IP, Port, AncData, Data}}
+ end;
{inet_async, S, Ref, {ok, [F | AddrData]}} ->
+ %% Without ancillary data
case get_addr(F, AddrData) of
{{Family, _} = Addr, Data} when is_atom(Family) ->
{ok, {Addr, 0, Data}};
@@ -857,9 +991,9 @@ chgopts(S, Opts) when is_port(S), is_list(Opts) ->
getifaddrs(S) when is_port(S) ->
case ctl_cmd(S, ?INET_REQ_GETIFADDRS, []) of
- {ok, Data} ->
- {ok, comp_ifaddrs(build_ifaddrs(Data), ktree_empty())};
- {error,enotsup} ->
+ {ok, Data} ->
+ {ok, comp_ifaddrs(build_ifaddrs(Data))};
+ {error,enotsup} ->
case getiflist(S) of
{ok, IFs} ->
{ok, getifaddrs_ifget(S, IFs)};
@@ -868,30 +1002,75 @@ getifaddrs(S) when is_port(S) ->
Err2 -> Err2
end.
-%% Restructure interface properties per interface and remove duplicates
-
-comp_ifaddrs([{If,Opts}|IfOpts], T) ->
- case ktree_is_defined(If, T) of
- true ->
- OptSet = comp_ifaddrs_add(ktree_get(If, T), Opts),
- comp_ifaddrs(IfOpts, ktree_update(If, OptSet, T));
- false ->
- OptSet = comp_ifaddrs_add(ktree_empty(), Opts),
- comp_ifaddrs(IfOpts, ktree_insert(If, OptSet, T))
- end;
-comp_ifaddrs([], T) ->
- [{If,ktree_keys(ktree_get(If, T))} || If <- ktree_keys(T)].
-
-comp_ifaddrs_add(OptSet, [Opt|Opts]) ->
- case ktree_is_defined(Opt, OptSet) of
- true
- when element(1, Opt) =:= flags;
- element(1, Opt) =:= hwaddr ->
- comp_ifaddrs_add(OptSet, Opts);
- _ ->
- comp_ifaddrs_add(ktree_insert(Opt, undefined, OptSet), Opts)
+%% Restructure interface properties per interface
+
+comp_ifaddrs(IfOpts) ->
+ comp_ifaddrs(IfOpts, ktree_empty()).
+%%
+comp_ifaddrs([{If,[{flags,Flags}|Opts]}|IfOpts], IfT) ->
+ case ktree_is_defined(If, IfT) of
+ true ->
+ comp_ifaddrs(
+ IfOpts,
+ ktree_update(
+ If,
+ comp_ifaddrs_flags(Flags, Opts, ktree_get(If, IfT)),
+ IfT));
+ false ->
+ comp_ifaddrs(
+ IfOpts,
+ ktree_insert(
+ If,
+ comp_ifaddrs_flags(Flags, Opts, ktree_empty()),
+ IfT))
end;
-comp_ifaddrs_add(OptSet, []) -> OptSet.
+comp_ifaddrs([], IfT) ->
+ comp_ifaddrs_2(ktree_keys(IfT), IfT).
+
+comp_ifaddrs_flags(Flags, Opts, FlagsT) ->
+ case ktree_is_defined(Flags, FlagsT) of
+ true ->
+ ktree_update(
+ Flags,
+ rev(Opts, ktree_get(Flags, FlagsT)),
+ FlagsT);
+ false ->
+ ktree_insert(Flags, rev(Opts), FlagsT)
+ end.
+
+comp_ifaddrs_2([If|Ifs], IfT) ->
+ FlagsT = ktree_get(If, IfT),
+ [{If,comp_ifaddrs_3(ktree_keys(FlagsT), FlagsT)}
+ | comp_ifaddrs_2(Ifs, IfT)];
+comp_ifaddrs_2([], _IfT) ->
+ [].
+%%
+comp_ifaddrs_3([Flags|FlagsL], FlagsT) ->
+ [{flags,Flags}|hwaddr_last(rev(ktree_get(Flags, FlagsT)))]
+ ++ hwaddr_last(comp_ifaddrs_3(FlagsL, FlagsT));
+comp_ifaddrs_3([], _FlagsT) ->
+ [].
+
+%% Place hwaddr last to look more like legacy emulation
+hwaddr_last(Opts) ->
+ hwaddr_last(Opts, Opts, []).
+%%
+hwaddr_last([{hwaddr,_} = Opt|Opts], L, R) ->
+ hwaddr_last(Opts, L, [Opt|R]);
+hwaddr_last([_|Opts], L, R) ->
+ hwaddr_last(Opts, L, R);
+hwaddr_last([], L, []) ->
+ L;
+hwaddr_last([], L, R) ->
+ rev(hwaddr_last(L, []), rev(R)).
+%%
+hwaddr_last([{hwaddr,_}|Opts], R) ->
+ hwaddr_last(Opts, R);
+hwaddr_last([Opt|Opts], R) ->
+ hwaddr_last(Opts, [Opt|R]);
+hwaddr_last([], R) ->
+ R.
+
%% Legacy emulation of getifaddrs
@@ -899,21 +1078,19 @@ getifaddrs_ifget(_, []) -> [];
getifaddrs_ifget(S, [IF|IFs]) ->
case ifget(S, IF, [flags]) of
{ok,[{flags,Flags}]=FlagsVals} ->
- BroadOpts =
- case member(broadcast, Flags) of
- true ->
- [broadaddr,hwaddr];
- false ->
- [hwaddr]
- end,
- P2POpts =
- case member(pointtopoint, Flags) of
- true ->
- [dstaddr|BroadOpts];
- false ->
- BroadOpts
- end,
- getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|P2POpts]);
+ GetOpts =
+ case member(pointtopoint, Flags) of
+ true ->
+ [dstaddr,hwaddr];
+ false ->
+ case member(broadcast, Flags) of
+ true ->
+ [broadaddr,hwaddr];
+ false ->
+ [hwaddr]
+ end
+ end,
+ getifaddrs_ifget(S, IFs, IF, FlagsVals, [addr,netmask|GetOpts]);
_ ->
getifaddrs_ifget(S, IFs, IF, [], [addr,netmask,hwaddr])
end.
@@ -1256,7 +1433,13 @@ enc_opt(recbuf) -> ?INET_OPT_RCVBUF;
enc_opt(priority) -> ?INET_OPT_PRIORITY;
enc_opt(tos) -> ?INET_OPT_TOS;
enc_opt(tclass) -> ?INET_OPT_TCLASS;
+enc_opt(recvtos) -> ?INET_OPT_RECVTOS;
+enc_opt(recvtclass) -> ?INET_OPT_RECVTCLASS;
+enc_opt(pktoptions) -> ?INET_OPT_PKTOPTIONS;
+enc_opt(ttl) -> ?INET_OPT_TTL;
+enc_opt(recvttl) -> ?INET_OPT_RECVTTL;
enc_opt(nodelay) -> ?TCP_OPT_NODELAY;
+enc_opt(nopush) -> ?TCP_OPT_NOPUSH;
enc_opt(multicast_if) -> ?UDP_OPT_MULTICAST_IF;
enc_opt(multicast_ttl) -> ?UDP_OPT_MULTICAST_TTL;
enc_opt(multicast_loop) -> ?UDP_OPT_MULTICAST_LOOP;
@@ -1318,6 +1501,12 @@ dec_opt(?INET_OPT_PRIORITY) -> priority;
dec_opt(?INET_OPT_TOS) -> tos;
dec_opt(?INET_OPT_TCLASS) -> tclass;
dec_opt(?TCP_OPT_NODELAY) -> nodelay;
+dec_opt(?TCP_OPT_NOPUSH) -> nopush;
+dec_opt(?INET_OPT_RECVTOS) -> recvtos;
+dec_opt(?INET_OPT_RECVTCLASS) -> recvtclass;
+dec_opt(?INET_OPT_PKTOPTIONS) -> pktoptions;
+dec_opt(?INET_OPT_TTL) -> ttl;
+dec_opt(?INET_OPT_RECVTTL) -> recvttl;
dec_opt(?UDP_OPT_MULTICAST_IF) -> multicast_if;
dec_opt(?UDP_OPT_MULTICAST_TTL) -> multicast_ttl;
dec_opt(?UDP_OPT_MULTICAST_LOOP) -> multicast_loop;
@@ -1393,7 +1582,13 @@ type_opt_1(recbuf) -> int;
type_opt_1(priority) -> int;
type_opt_1(tos) -> int;
type_opt_1(tclass) -> int;
+type_opt_1(recvtos) -> bool;
+type_opt_1(recvtclass) -> bool;
+type_opt_1(pktoptions) -> opts;
+type_opt_1(ttl) -> int;
+type_opt_1(recvttl) -> bool;
type_opt_1(nodelay) -> bool;
+type_opt_1(nopush) -> bool;
type_opt_1(ipv6_v6only) -> bool;
%% multicast
type_opt_1(multicast_ttl) -> int;
@@ -1557,7 +1752,7 @@ type_opt_1(O) when is_atom(O) -> undefined.
%% Get. No supplied value.
type_value(get, undefined) -> false; % Undefined type
-%% These two clauses can not happen since they are only used
+%% These two clauses cannot happen since they are only used
%% in record fields - from record fields they must have a
%% value though it might be 'undefined', so record fields
%% calls type_value/3, not type_value/2.
@@ -1723,7 +1918,7 @@ type_value_2(_, _) -> false.
%% Get. No supplied value.
%%
-%% These two clauses can not happen since they are only used
+%% These two clauses cannot happen since they are only used
%% in record fields - from record fields they must have a
%% value though it might be 'undefined', so record fields
%% calls enc_value/3, not enc_value/2.
@@ -1899,6 +2094,11 @@ dec_value(binary,[L0,L1,L2,L3|List]) ->
Len = ?i32(L0,L1,L2,L3),
{X,T}=split(Len,List),
{list_to_binary(X),T};
+dec_value(opts, [L0,L1,L2,L3|List]) ->
+ Len = ?u32(L0,L1,L2,L3),
+ {X,T} = split(Len, List),
+ Opts = dec_opt_val(X),
+ {Opts,T};
dec_value(Types, List) when is_tuple(Types) ->
{L,T} = dec_value_tuple(Types, List, 1, []),
{list_to_tuple(L),T};
@@ -2467,7 +2667,7 @@ get_addrs([F|Addrs]) ->
[Addr|get_addrs(Rest)].
get_addr(?INET_AF_LOCAL, [N|Addr]) ->
- {A,Rest} = lists:split(N, Addr),
+ {A,Rest} = split(N, Addr),
{{local,iolist_to_binary(A)},Rest};
get_addr(?INET_AF_UNSPEC, Rest) ->
{{unspec,<<>>},Rest};
@@ -2489,12 +2689,13 @@ get_ip6([X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15,X16 | T]) ->
?u16(X9,X10),?u16(X11,X12),?u16(X13,X14),?u16(X15,X16)},
T }.
+-define(ERTS_INET_DRV_CONTROL_MAGIC_NUMBER, 16#03f1a300).
%% Control command
ctl_cmd(Port, Cmd, Args) ->
?DBG_FORMAT("prim_inet:ctl_cmd(~p, ~p, ~p)~n", [Port,Cmd,Args]),
Result =
- try erlang:port_control(Port, Cmd, Args) of
+ try erlang:port_control(Port, Cmd+?ERTS_INET_DRV_CONTROL_MAGIC_NUMBER, Args) of
[?INET_REP_OK|Reply] -> {ok,Reply};
[?INET_REP] -> inet_reply;
[?INET_REP_ERROR|Err] -> {error,list_to_atom(Err)}
diff --git a/erts/preloaded/src/socket.erl b/erts/preloaded/src/socket.erl
new file mode 100644
index 0000000000..126db66cdd
--- /dev/null
+++ b/erts/preloaded/src/socket.erl
@@ -0,0 +1,3611 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2018-2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(socket).
+
+-compile(no_native).
+-compile({no_auto_import,[error/1]}).
+
+%% Administrative and "global" utility functions
+-export([
+ on_load/0, on_load/1,
+ info/0,
+ supports/0, supports/1, supports/2, supports/3,
+ ensure_sockaddr/1
+ ]).
+
+-export([
+ open/2, open/3, open/4,
+ bind/2, bind/3,
+ connect/2, connect/3,
+ listen/1, listen/2,
+ accept/1, accept/2,
+
+ send/2, send/3, send/4,
+ sendto/3, sendto/4, sendto/5,
+ sendmsg/2, sendmsg/3, sendmsg/4,
+
+ recv/1, recv/2, recv/3, recv/4,
+ recvfrom/1, recvfrom/2, recvfrom/3, recvfrom/4,
+ recvmsg/1, recvmsg/2, recvmsg/3, recvmsg/5,
+
+ close/1,
+ shutdown/2,
+
+ setopt/4,
+ getopt/3,
+
+ sockname/1,
+ peername/1
+ ]).
+
+-export_type([
+ domain/0,
+ type/0,
+ protocol/0,
+ socket/0,
+
+ port_number/0,
+ ip_address/0,
+ ip4_address/0,
+ ip6_address/0,
+ sockaddr/0,
+ sockaddr_in4/0,
+ sockaddr_in6/0,
+ sockaddr_un/0,
+
+ accept_flags/0,
+ accept_flag/0,
+
+ send_flags/0,
+ send_flag/0,
+
+ recv_flags/0,
+ recv_flag/0,
+
+ shutdown_how/0,
+
+ sockopt_level/0,
+ otp_socket_option/0,
+ socket_option/0,
+ ip_socket_option/0,
+ ipv6_socket_option/0,
+ tcp_socket_option/0,
+ udp_socket_option/0,
+ sctp_socket_option/0,
+ raw_socket_option/0,
+
+ timeval/0,
+ ip_tos/0,
+ ip_mreq/0,
+ ip_mreq_source/0,
+ ip_pmtudisc/0,
+ ip_msfilter_mode/0,
+ ip_msfilter/0,
+ ip_pktinfo/0,
+ ipv6_mreq/0,
+ ipv6_pmtudisc/0,
+ ipv6_pktinfo/0,
+ in6_flow_info/0,
+ in6_scope_id/0,
+ sctp_assoc_id/0,
+ sctp_sndrcvinfo/0,
+ sctp_event_subscribe/0,
+ sctp_assocparams/0,
+ sctp_initmsg/0,
+ sctp_rtoinfo/0,
+
+
+ msghdr_flag/0,
+ msghdr_flags/0,
+ msghdr/0,
+ cmsghdr_level/0,
+ cmsghdr_type/0,
+ %% cmsghdr_data/0,
+ cmsghdr_recv/0, cmsghdr_send/0,
+
+ uint8/0,
+ uint16/0,
+ uint20/0,
+ uint32/0,
+ int32/0
+ ]).
+
+
+-type uint8() :: 0..16#FF.
+-type uint16() :: 0..16#FFFF.
+-type uint20() :: 0..16#FFFFF.
+-type uint32() :: 0..16#FFFFFFFF.
+-type int32() :: -2147483648..2147483647.
+
+
+%% We support only a subset of all domains.
+-type domain() :: local | inet | inet6.
+
+%% We support only a subset of all types.
+%% RDM - Reliably Delivered Messages
+-type type() :: stream | dgram | raw | rdm | seqpacket.
+
+%% We support only a subset of all protocols:
+%% Note that the '{raw, integer()}' construct is intended
+%% to be used with type = raw.
+%% Note also that only the "superuser" can create a raw socket.
+-type protocol() :: ip | tcp | udp | sctp | icmp | igmp | {raw, integer()}.
+
+-type port_number() :: 0..65535.
+
+-type ip_address() :: ip4_address() | ip6_address().
+
+-type ip4_address() :: {0..255, 0..255, 0..255, 0..255}.
+
+-type in6_flow_info() :: uint20().
+-type in6_scope_id() :: uint32().
+
+-type ip6_address() ::
+ {0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535,
+ 0..65535}.
+
+-type timeval() :: #{sec := integer(),
+ usec := integer()}.
+
+-type ip_pktinfo() :: #{
+ ifindex := non_neg_integer(), % Interface Index
+ spec_dst := ip4_address(), % Local Address
+ addr := ip4_address() % Header Destination address
+ }.
+
+%% If the integer value is used, its up to the caller to ensure its valid!
+-type ip_tos() :: lowdeley |
+ throughput |
+ reliability |
+ mincost |
+ integer().
+
+%% This type is used when requesting to become member of a multicast
+%% group with a call to setopt. Example:
+%%
+%% socket:setopt(Socket, ip, add_membership, #{multiaddr => Addr,
+%% interface => any}).
+%%
+%% Its also used when removing from a multicast group. Example:
+%%
+%% socket:setopt(Socket, ip, drop_membership, #{multiaddr => Addr,
+%% interface => any}).
+%%
+
+-type ip_mreq() :: #{multiaddr := ip4_address(),
+ interface := any | ip4_address()}.
+%% -type ip_mreqn() :: #{multiaddr := ip4_address(),
+%% address := any | ip4_address(),
+%% ifindex := integer()}.
+
+-type ip_mreq_source() :: #{multiaddr := ip4_address(),
+ interface := ip4_address(),
+ sourceaddr := ip4_address()}.
+
+-type ip_pmtudisc() :: want | dont | do | probe.
+
+%% multiaddr: Multicast group address
+%% interface: Address of local interface
+%% mode: Filter mode
+%% slist: List of source addresses
+-type ip_msfilter_mode() :: include | exclude.
+
+-type ip_msfilter() :: #{multiaddr := ip4_address(),
+ interface := ip4_address(),
+ mode := ip_msfilter_mode(),
+ slist := [ip4_address()]}.
+
+-type ipv6_mreq() :: #{multiaddr := ip6_address(),
+ interface := non_neg_integer()}.
+
+-type ipv6_pmtudisc() :: ip_pmtudisc().
+
+-type ipv6_pktinfo() :: #{
+ addr := ip6_address(),
+ ifindex := integer()
+ }.
+
+
+-type sctp_assoc_id() :: int32().
+-type sctp_sndrcvinfo() :: #{
+ stream := uint16(),
+ ssn := uint16(),
+ flags := uint16(),
+ ppid := uint16(),
+ context := uint16(),
+ timetolive := uint16(),
+ tsn := uint16(),
+ cumtsn := uint16(),
+ assoc_id := sctp_assoc_id()
+ }.
+
+-type sctp_event_subscribe() :: #{data_in := boolean(),
+ association := boolean(),
+ address := boolean(),
+ send_failure := boolean(),
+ peer_error := boolean(),
+ shutdown := boolean(),
+ partial_delivery := boolean(),
+ adaptation_layer := boolean(),
+ authentication := boolean(),
+ sender_dry := boolean()}.
+
+-type sctp_assocparams() :: #{assoc_id := sctp_assoc_id(),
+ max_rxt := uint16(),
+ num_peer_dests := uint16(),
+ peer_rwnd := uint32(),
+ local_rwnd := uint32(),
+ cookie_life := uint32()}.
+
+-type sctp_initmsg() :: #{num_outstreams := uint16(),
+ max_instreams := uint16(),
+ max_attempts := uint16(),
+ max_init_timeo := uint16()
+ }.
+
+-type sctp_rtoinfo() :: #{assoc_id := sctp_assoc_id(),
+ initial := uint32(),
+ max := uint32(),
+ min := uint32()}.
+
+-type sockaddr_un() :: #{family := local,
+ path := binary() | string()}.
+-type sockaddr_in4() :: #{family := inet,
+ port := port_number(),
+ addr := any | loopback | ip4_address()}.
+-type sockaddr_in6() :: #{family := inet6,
+ port := port_number(),
+ addr := any | loopback | ip6_address(),
+ flowinfo := in6_flow_info(),
+ scope_id := in6_scope_id()}.
+-type sockaddr() :: sockaddr_in4() |
+ sockaddr_in6() |
+ sockaddr_un().
+
+-define(SOCKADDR_IN4_DEFAULTS(A), #{port => 0,
+ addr => A}).
+-define(SOCKADDR_IN4_DEFAULTS, ?SOCKADDR_IN4_DEFAULTS(any)).
+-define(SOCKADDR_IN4_DEFAULT(A), (?SOCKADDR_IN4_DEFAULTS(A))#{family => inet}).
+-define(SOCKADDR_IN6_DEFAULTS(A), #{port => 0,
+ addr => A,
+ flowinfo => 0,
+ scope_id => 0}).
+-define(SOCKADDR_IN6_DEFAULTS, ?SOCKADDR_IN6_DEFAULTS(any)).
+-define(SOCKADDR_IN6_DEFAULT(A), (?SOCKADDR_IN6_DEFAULTS(A))#{family => inet6}).
+
+%% otp - The option is internal to our (OTP) imeplementation.
+%% socket - The socket layer (SOL_SOCKET).
+%% ip - The IP layer (SOL_IP or is it IPPROTO_IP?).
+%% ipv6 - The IPv6 layer (SOL_IPV6).
+%% tcp - The TCP (Transport Control Protocol) layer (IPPROTO_TCP).
+%% udp - The UDP (User Datagram Protocol) layer (IPPROTO_UDP).
+%% sctp - The SCTP (Stream Control Transmission Protocol) layer (IPPROTO_SCTP).
+%% Int - Raw level, sent down and used "as is".
+-type sockopt_level() :: otp |
+ socket |
+ ip | ipv6 | tcp | udp | sctp |
+ non_neg_integer().
+
+%% There are some options that are 'read-only'.
+%% Should those be included here or in a special list?
+%% Should we just document it and leave it to the user?
+%% Or catch it in the encode functions?
+%% A setopt for a readonly option leads to einval?
+%% Do we really need a sndbuf?
+
+-type otp_socket_option() :: debug |
+ iow |
+ controlling_process |
+ rcvbuf | % sndbuf |
+ rcvctrlbuf |
+ sndctrlbuf |
+ fd.
+%% Shall we have special treatment of linger??
+%% read-only options:
+%% domain | protocol | type.
+%% FreeBSD (only?): acceptfilter
+-type socket_option() :: acceptconn |
+ acceptfilter |
+ bindtodevice |
+ broadcast |
+ busy_poll |
+ debug |
+ domain |
+ dontroute |
+ error |
+ keepalive |
+ linger |
+ mark |
+ oobinline |
+ passcred |
+ peek_off |
+ peekcred |
+ priority |
+ protocol |
+ rcvbuf |
+ rcvbufforce |
+ rcvlowat |
+ rcvtimeo |
+ reuseaddr |
+ reuseport |
+ rxq_ovfl |
+ setfib |
+ sndbuf |
+ sndbufforce |
+ sndlowat |
+ sndtimeo |
+ timestamp |
+ type.
+
+%% Read-only options:
+%% mtu
+%%
+%% Options only valid on FreeBSD?:
+%% dontfrag
+%% Options only valid for RAW sockets:
+%% nodefrag (linux only?)
+-type ip_socket_option() :: add_membership |
+ add_source_membership |
+ block_source |
+ dontfrag |
+ drop_membership |
+ drop_source_membership |
+ freebind |
+ hdrincl |
+ minttl |
+ msfilter |
+ mtu |
+ mtu_discover |
+ multicast_all |
+ multicast_if |
+ multicast_loop |
+ multicast_ttl |
+ nodefrag |
+ options |
+ pktinfo |
+ recverr |
+ recvif |
+ recvdstaddr |
+ recvopts |
+ recvorigdstaddr |
+ recvtos |
+ recvttl |
+ retopts |
+ router_alert |
+ sndsrcaddr |
+ tos |
+ transparent |
+ ttl |
+ unblock_source.
+-type ipv6_socket_option() ::
+ addrform |
+ add_membership |
+ authhdr |
+ auth_level |
+ checksum |
+ drop_membership |
+ dstopts |
+ esp_trans_level |
+ esp_network_level |
+ faith |
+ flowinfo |
+ hoplimit |
+ hopopts |
+ ipcomp_level |
+ join_group |
+ leave_group |
+ mtu |
+ mtu_discover |
+ multicast_hops |
+ multicast_if |
+ multicast_loop |
+ portrange |
+ pktoptions |
+ recverr |
+ recvpktinfo | pktinfo |
+ recvtclass |
+ router_alert |
+ rthdr |
+ tclass |
+ unicast_hops |
+ use_min_mtu |
+ v6only.
+
+-type tcp_socket_option() :: congestion |
+ cork |
+ info |
+ keepcnt |
+ keepidle |
+ keepintvl |
+ maxseg |
+ md5sig |
+ nodelay |
+ noopt |
+ nopush |
+ syncnt |
+ user_timeout.
+
+-type udp_socket_option() :: cork.
+
+-type sctp_socket_option() ::
+ adaption_layer |
+ associnfo |
+ auth_active_key |
+ auth_asconf |
+ auth_chunk |
+ auth_key |
+ auth_delete_key |
+ autoclose |
+ context |
+ default_send_params |
+ delayed_ack_time |
+ disable_fragments |
+ hmac_ident |
+ events |
+ explicit_eor |
+ fragment_interleave |
+ get_peer_addr_info |
+ initmsg |
+ i_want_mapped_v4_addr |
+ local_auth_chunks |
+ maxseg |
+ maxburst |
+ nodelay |
+ partial_delivery_point |
+ peer_addr_params |
+ peer_auth_chunks |
+ primary_addr |
+ reset_streams |
+ rtoinfo |
+ set_peer_primary_addr |
+ status |
+ use_ext_recvinfo.
+
+-type raw_socket_option() :: filter.
+
+%% -type plain_socket_option() :: integer().
+%% -type sockopt() :: otp_socket_option() |
+%% socket_option() |
+%% ip_socket_option() |
+%% ipv6_socket_option() |
+%% tcp_socket_option() |
+%% udp_socket_option() |
+%% sctp_socket_option() |
+%% raw_socket_option() |
+%% plain_socket_option().
+
+-record(socket, {ref :: reference()}).
+
+-opaque socket() :: #socket{}.
+
+-type accept_flags() :: [accept_flag()].
+-type accept_flag() :: nonblock | cloexec.
+
+-type send_flags() :: [send_flag()].
+-type send_flag() :: confirm |
+ dontroute |
+ eor |
+ more |
+ nosignal |
+ oob.
+
+%% Extend with OWN flags for other usage:
+%% - adapt-buffer-sz:
+%% This will have the effect that the nif recvfrom will use
+%% MSG_PEEK to ensure no part of the message is lost, but if
+%% necessary adapt (increase) the buffer size until all of
+%% it fits.
+%%
+%% Note that not all of these flags are useful for every recv function!
+%%
+-type recv_flags() :: [recv_flag()].
+-type recv_flag() :: cmsg_cloexec |
+ errqueue |
+ oob |
+ peek |
+ trunc.
+
+-type shutdown_how() :: read | write | read_write.
+
+-type msghdr_flag() :: ctrunc | eor | errqueue | oob | trunc.
+-type msghdr_flags() :: [msghdr_flag()].
+-type msghdr() :: #{
+ %% *Optional* target address
+ %% Used on an unconnected socket to specify the
+ %% target address for a datagram.
+ addr := sockaddr(),
+
+ iov := [binary()],
+
+ %% The maximum size of the control buffer is platform
+ %% specific. It is the users responsibility to ensure
+ %% that its not exceeded.
+ ctrl := [cmsghdr_recv()] | [cmsghdr_send()],
+
+ %% Only valid with recvmsg
+ flags := msghdr_flags()
+ }.
+%% We are able to (completely) decode *some* control message headers.
+%% Even if we are able to decode both level and type, we may not be
+%% able to decode the data, in which case it will be a binary.
+
+-type cmsghdr_level() :: socket | ip | ipv6 | integer().
+-type cmsghdr_type() :: timestamp |
+ pktinfo |
+ tos |
+ ttl |
+ rights |
+ credentials |
+ origdstaddr |
+ integer().
+-type cmsghdr_recv() ::
+ #{level := socket, type := timestamp, data := timeval()} |
+ #{level := socket, type := rights, data := binary()} |
+ #{level := socket, type := credentials, data := binary()} |
+ #{level := socket, type := integer(), data := binary()} |
+ #{level := ip, type := tos, data := ip_tos()} |
+ #{level := ip, type := ttl, data := integer()} |
+ #{level := ip, type := pktinfo, data := ip_pktinfo()} |
+ #{level := ip, type := origdstaddr, data := sockaddr_in4()} |
+ #{level := ip, type := integer(), data := binary()} |
+ #{level := ipv6, type := pktinfo, data := ipv6_pktinfo()} |
+ #{level := ipv6, type := integer(), data := binary()} |
+ #{level := integer(), type := integer(), data := binary()}.
+-type cmsghdr_send() ::
+ #{level := socket, type := integer(), data := binary()} |
+ #{level := ip, type := tos, data := ip_tos() | binary()} |
+ #{level := ip, type := ttl, data := integer() | binary()} |
+ #{level := ip, type := integer(), data := binary()} |
+ #{level := ipv6, type := integer(), data := binary()} |
+ #{level := udp, type := integer(), data := binary()} |
+ #{level := integer(), type := integer(), data := binary()}.
+
+
+%% This is used in messages sent from the nif-code to erlang processes:
+%%
+%% {?SOCKET_TAG, Socket :: socket(), Tag :: atom(), Info :: term()}
+%%
+-define(SOCKET_TAG, '$socket').
+
+-define(SOCKET_DOMAIN_LOCAL, 1).
+-define(SOCKET_DOMAIN_UNIX, ?SOCKET_DOMAIN_LOCAL).
+-define(SOCKET_DOMAIN_INET, 2).
+-define(SOCKET_DOMAIN_INET6, 3).
+
+-define(SOCKET_TYPE_STREAM, 1).
+-define(SOCKET_TYPE_DGRAM, 2).
+-define(SOCKET_TYPE_RAW, 3).
+%% -define(SOCKET_TYPE_RDM, 4).
+-define(SOCKET_TYPE_SEQPACKET, 5).
+
+-define(SOCKET_PROTOCOL_IP, 1).
+-define(SOCKET_PROTOCOL_TCP, 2).
+-define(SOCKET_PROTOCOL_UDP, 3).
+-define(SOCKET_PROTOCOL_SCTP, 4).
+-define(SOCKET_PROTOCOL_ICMP, 5).
+-define(SOCKET_PROTOCOL_IGMP, 6).
+
+-define(SOCKET_LISTEN_BACKLOG_DEFAULT, 5).
+
+-define(SOCKET_ACCEPT_TIMEOUT_DEFAULT, infinity).
+
+-define(SOCKET_SEND_FLAG_CONFIRM, 0).
+-define(SOCKET_SEND_FLAG_DONTROUTE, 1).
+-define(SOCKET_SEND_FLAG_EOR, 2).
+-define(SOCKET_SEND_FLAG_MORE, 3).
+-define(SOCKET_SEND_FLAG_NOSIGNAL, 4).
+-define(SOCKET_SEND_FLAG_OOB, 5).
+
+-define(SOCKET_SEND_FLAGS_DEFAULT, []).
+-define(SOCKET_SEND_TIMEOUT_DEFAULT, infinity).
+-define(SOCKET_SENDTO_FLAGS_DEFAULT, []).
+-define(SOCKET_SENDTO_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT).
+-define(SOCKET_SENDMSG_FLAGS_DEFAULT, []).
+-define(SOCKET_SENDMSG_TIMEOUT_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT).
+
+-define(SOCKET_RECV_FLAG_CMSG_CLOEXEC, 0).
+-define(SOCKET_RECV_FLAG_ERRQUEUE, 1).
+-define(SOCKET_RECV_FLAG_OOB, 2).
+-define(SOCKET_RECV_FLAG_PEEK, 3).
+-define(SOCKET_RECV_FLAG_TRUNC, 4).
+
+-define(SOCKET_RECV_FLAGS_DEFAULT, []).
+-define(SOCKET_RECV_TIMEOUT_DEFAULT, infinity).
+
+-define(SOCKET_OPT_LEVEL_OTP, 0).
+-define(SOCKET_OPT_LEVEL_SOCKET, 1).
+-define(SOCKET_OPT_LEVEL_IP, 2).
+-define(SOCKET_OPT_LEVEL_IPV6, 3).
+-define(SOCKET_OPT_LEVEL_TCP, 4).
+-define(SOCKET_OPT_LEVEL_UDP, 5).
+-define(SOCKET_OPT_LEVEL_SCTP, 6).
+
+%% *** OTP (socket) options
+-define(SOCKET_OPT_OTP_DEBUG, 1).
+-define(SOCKET_OPT_OTP_IOW, 2).
+-define(SOCKET_OPT_OTP_CTRL_PROC, 3).
+-define(SOCKET_OPT_OTP_RCVBUF, 4).
+%%-define(SOCKET_OPT_OTP_SNDBUF, 5).
+-define(SOCKET_OPT_OTP_RCVCTRLBUF, 6).
+-define(SOCKET_OPT_OTP_SNDCTRLBUF, 7).
+-define(SOCKET_OPT_OTP_FD, 8).
+-define(SOCKET_OPT_OTP_DOMAIN, 16#FF01). % INTERNAL
+-define(SOCKET_OPT_OTP_TYPE, 16#FF02). % INTERNAL
+-define(SOCKET_OPT_OTP_PROTOCOL, 16#FF03). % INTERNAL
+
+%% *** SOCKET (socket) options
+-define(SOCKET_OPT_SOCK_ACCEPTCONN, 1).
+%% -define(SOCKET_OPT_SOCK_ACCEPTFILTER, 2). % FreeBSD
+-define(SOCKET_OPT_SOCK_BINDTODEVICE, 3).
+-define(SOCKET_OPT_SOCK_BROADCAST, 4).
+%% -define(SOCKET_OPT_SOCK_BUSY_POLL, 5).
+-define(SOCKET_OPT_SOCK_DEBUG, 6).
+-define(SOCKET_OPT_SOCK_DOMAIN, 7).
+-define(SOCKET_OPT_SOCK_DONTROUTE, 8).
+%% -define(SOCKET_OPT_SOCK_ERROR, 9).
+-define(SOCKET_OPT_SOCK_KEEPALIVE, 10).
+-define(SOCKET_OPT_SOCK_LINGER, 11).
+%% -define(SOCKET_OPT_SOCK_MARK, 12).
+-define(SOCKET_OPT_SOCK_OOBINLINE, 13).
+%% -define(SOCKET_OPT_SOCK_PASSCRED, 14).
+-define(SOCKET_OPT_SOCK_PEEK_OFF, 15).
+%% -define(SOCKET_OPT_SOCK_PEEKCRED, 16).
+-define(SOCKET_OPT_SOCK_PRIORITY, 17).
+-define(SOCKET_OPT_SOCK_PROTOCOL, 18).
+-define(SOCKET_OPT_SOCK_RCVBUF, 19).
+%% -define(SOCKET_OPT_SOCK_RCVBUFFORCE, 20).
+-define(SOCKET_OPT_SOCK_RCVLOWAT, 21).
+-define(SOCKET_OPT_SOCK_RCVTIMEO, 22).
+-define(SOCKET_OPT_SOCK_REUSEADDR, 23).
+-define(SOCKET_OPT_SOCK_REUSEPORT, 24).
+%% -define(SOCKET_OPT_SOCK_RXQ_OVFL, 25).
+%% -define(SOCKET_OPT_SOCK_SETFIB, 26). % FreeBSD
+-define(SOCKET_OPT_SOCK_SNDBUF, 27).
+%% -define(SOCKET_OPT_SOCK_SNDBUFFORCE, 28).
+-define(SOCKET_OPT_SOCK_SNDLOWAT, 29).
+-define(SOCKET_OPT_SOCK_SNDTIMEO, 30).
+-define(SOCKET_OPT_SOCK_TIMESTAMP, 31).
+-define(SOCKET_OPT_SOCK_TYPE, 32).
+
+%% *** IP (socket) options
+-define(SOCKET_OPT_IP_ADD_MEMBERSHIP, 1).
+-define(SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP, 2).
+-define(SOCKET_OPT_IP_BLOCK_SOURCE, 3).
+%% -define(SOCKET_OPT_IP_DONTFRAG, 4). % FreeBSD
+-define(SOCKET_OPT_IP_DROP_MEMBERSHIP, 5).
+-define(SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP, 6).
+-define(SOCKET_OPT_IP_FREEBIND, 7).
+-define(SOCKET_OPT_IP_HDRINCL, 8).
+-define(SOCKET_OPT_IP_MINTTL, 9).
+-define(SOCKET_OPT_IP_MSFILTER, 10).
+-define(SOCKET_OPT_IP_MTU, 11).
+-define(SOCKET_OPT_IP_MTU_DISCOVER, 12).
+-define(SOCKET_OPT_IP_MULTICAST_ALL, 13).
+-define(SOCKET_OPT_IP_MULTICAST_IF, 14).
+-define(SOCKET_OPT_IP_MULTICAST_LOOP, 15).
+-define(SOCKET_OPT_IP_MULTICAST_TTL, 16).
+-define(SOCKET_OPT_IP_NODEFRAG, 17).
+%% -define(SOCKET_OPT_IP_OPTIONS, 18). % FreeBSD
+-define(SOCKET_OPT_IP_PKTINFO, 19).
+-define(SOCKET_OPT_IP_RECVDSTADDR, 20). % FreeBSD
+-define(SOCKET_OPT_IP_RECVERR, 21).
+-define(SOCKET_OPT_IP_RECVIF, 22).
+-define(SOCKET_OPT_IP_RECVOPTS, 23).
+-define(SOCKET_OPT_IP_RECVORIGDSTADDR, 24).
+-define(SOCKET_OPT_IP_RECVTOS, 25).
+-define(SOCKET_OPT_IP_RECVTTL, 26).
+-define(SOCKET_OPT_IP_RETOPTS, 27).
+-define(SOCKET_OPT_IP_ROUTER_ALERT, 28).
+-define(SOCKET_OPT_IP_SENDSRCADDR, 29). % FreeBSD
+-define(SOCKET_OPT_IP_TOS, 30).
+-define(SOCKET_OPT_IP_TRANSPARENT, 31).
+-define(SOCKET_OPT_IP_TTL, 32).
+-define(SOCKET_OPT_IP_UNBLOCK_SOURCE, 33).
+
+%% *** IPv6 (socket) options
+-define(SOCKET_OPT_IPV6_ADDRFORM, 1).
+-define(SOCKET_OPT_IPV6_ADD_MEMBERSHIP, 2).
+-define(SOCKET_OPT_IPV6_AUTHHDR, 3). % Obsolete?
+%% -define(SOCKET_OPT_IPV6_AUTH_LEVEL, 4). % FreeBSD
+%% -define(SOCKET_OPT_IPV6_CHECKSUM, 5). % FreeBSD
+-define(SOCKET_OPT_IPV6_DROP_MEMBERSHIP, 6).
+-define(SOCKET_OPT_IPV6_DSTOPTS, 7).
+%% -define(SOCKET_OPT_IPV6_ESP_NETWORK_LEVEL, 8). % FreeBSD
+%% -define(SOCKET_OPT_IPV6_ESP_TRANS_LEVEL, 9). % FreeBSD
+%% -define(SOCKET_OPT_IPV6_FAITH, 10). % FreeBSD
+-define(SOCKET_OPT_IPV6_FLOWINFO, 11).
+-define(SOCKET_OPT_IPV6_HOPLIMIT, 12).
+-define(SOCKET_OPT_IPV6_HOPOPTS, 13).
+%% -define(SOCKET_OPT_IPV6_IPCOMP_LEVEL, 14). % FreeBSD
+%% -define(SOCKET_OPT_IPV6_JOIN_GROUP, 15). % FreeBSD
+%% -define(SOCKET_OPT_IPV6_LEAVE_GROUP, 16). % FreeBSD
+-define(SOCKET_OPT_IPV6_MTU, 17).
+-define(SOCKET_OPT_IPV6_MTU_DISCOVER, 18).
+-define(SOCKET_OPT_IPV6_MULTICAST_HOPS, 19).
+-define(SOCKET_OPT_IPV6_MULTICAST_IF, 20).
+-define(SOCKET_OPT_IPV6_MULTICAST_LOOP, 21).
+%% -define(SOCKET_OPT_IPV6_PORTRANGE, 22). % FreeBSD
+%% -define(SOCKET_OPT_IPV6_PKTOPTIONS, 23). % FreeBSD
+-define(SOCKET_OPT_IPV6_RECVERR, 24).
+-define(SOCKET_OPT_IPV6_RECVPKTINFO, 25). % On FreeBSD: PKTINFO
+%% -define(SOCKET_OPT_IPV6_RECVTCLASS, 26).
+-define(SOCKET_OPT_IPV6_ROUTER_ALERT, 27).
+-define(SOCKET_OPT_IPV6_RTHDR, 28).
+%% -define(SOCKET_OPT_IPV6_TCLASS, 29). % FreeBSD
+-define(SOCKET_OPT_IPV6_UNICAST_HOPS, 30).
+%% -define(SOCKET_OPT_IPV6_USE_MIN_MTU, 31). % FreeBSD
+-define(SOCKET_OPT_IPV6_V6ONLY, 32).
+
+%% *** TCP (socket) options
+-define(SOCKET_OPT_TCP_CONGESTION, 1).
+-define(SOCKET_OPT_TCP_CORK, 2).
+%% -define(SOCKET_OPT_TCP_INFO, 3).
+%% -define(SOCKET_OPT_TCP_KEEPCNT, 4).
+%% -define(SOCKET_OPT_TCP_KEEPIDLE, 5).
+%% -define(SOCKET_OPT_TCP_KEEPINTVL, 6).
+-define(SOCKET_OPT_TCP_MAXSEG, 7).
+%% -define(SOCKET_OPT_TCP_MD5SIG, 8).
+-define(SOCKET_OPT_TCP_NODELAY, 9).
+%% -define(SOCKET_OPT_TCP_NOOPT, 10).
+%% -define(SOCKET_OPT_TCP_NOPUSH, 11).
+%% -define(SOCKET_OPT_TCP_SYNCNT, 12).
+%% -define(SOCKET_OPT_TCP_USER_TIMEOUT, 13).
+
+%% *** UDP (socket) options
+-define(SOCKET_OPT_UDP_CORK, 1).
+
+%% *** SCTP (socket) options
+%% -define(SOCKET_OPT_SCTP_ADAPTION_LAYER, 1).
+-define(SOCKET_OPT_SCTP_ASSOCINFO, 2).
+%% -define(SOCKET_OPT_SCTP_AUTH_ACTIVE_KEY, 3).
+%% -define(SOCKET_OPT_SCTP_AUTH_ASCONF, 4).
+%% -define(SOCKET_OPT_SCTP_AUTH_CHUNK, 5).
+%% -define(SOCKET_OPT_SCTP_AUTH_KEY, 6).
+%% -define(SOCKET_OPT_SCTP_AUTH_DELETE_KEY, 7).
+-define(SOCKET_OPT_SCTP_AUTOCLOSE, 8).
+%% -define(SOCKET_OPT_SCTP_CONTEXT, 9).
+%% -define(SOCKET_OPT_SCTP_DEFAULT_SEND_PARAMS, 10).
+%% -define(SOCKET_OPT_SCTP_DELAYED_ACK_TIME, 11).
+-define(SOCKET_OPT_SCTP_DISABLE_FRAGMENTS, 12).
+%% -define(SOCKET_OPT_SCTP_HMAC_IDENT, 13).
+-define(SOCKET_OPT_SCTP_EVENTS, 14).
+%% -define(SOCKET_OPT_SCTP_EXPLICIT_EOR, 15).
+%% -define(SOCKET_OPT_SCTP_FRAGMENT_INTERLEAVE, 16).
+%% -define(SOCKET_OPT_SCTP_GET_PEER_ADDR_INFO, 17).
+-define(SOCKET_OPT_SCTP_INITMSG, 18).
+%% -define(SOCKET_OPT_SCTP_I_WANT_MAPPED_V4_ADDR, 19).
+%% -define(SOCKET_OPT_SCTP_LOCAL_AUTH_CHUNKS, 20).
+-define(SOCKET_OPT_SCTP_MAXSEG, 21).
+%% -define(SOCKET_OPT_SCTP_MAXBURST, 22).
+-define(SOCKET_OPT_SCTP_NODELAY, 23).
+%% -define(SOCKET_OPT_SCTP_PARTIAL_DELIVERY_POINT, 24).
+%% -define(SOCKET_OPT_SCTP_PEER_ADDR_PARAMS, 25).
+%% -define(SOCKET_OPT_SCTP_PEER_AUTH_CHUNKS, 26).
+%% -define(SOCKET_OPT_SCTP_PRIMARY_ADDR, 27).
+%% -define(SOCKET_OPT_SCTP_RESET_STREAMS, 28).
+-define(SOCKET_OPT_SCTP_RTOINFO, 29).
+%% -define(SOCKET_OPT_SCTP_SET_PEER_PRIMARY_ADDR, 30).
+%% -define(SOCKET_OPT_SCTP_STATUS, 31).
+%% -define(SOCKET_OPT_SCTP_USE_EXT_RECVINFO, 32).
+
+-define(SOCKET_SHUTDOWN_HOW_READ, 0).
+-define(SOCKET_SHUTDOWN_HOW_WRITE, 1).
+-define(SOCKET_SHUTDOWN_HOW_READ_WRITE, 2).
+
+
+-define(SOCKET_SUPPORTS_OPTIONS, 16#0001).
+-define(SOCKET_SUPPORTS_SCTP, 16#0002).
+-define(SOCKET_SUPPORTS_IPV6, 16#0003).
+
+
+%% ===========================================================================
+%%
+%% Administrative and utility API
+%%
+%% ===========================================================================
+
+-spec on_load() -> ok.
+
+%% Should we require that the Extra arg is a map?
+on_load() ->
+ on_load(#{}).
+
+-spec on_load(Extra) -> ok when
+ Extra :: map().
+
+on_load(Extra) ->
+ ok = erlang:load_nif(atom_to_list(?MODULE), Extra).
+
+
+
+-spec info() -> list().
+
+info() ->
+ nif_info().
+
+
+
+%% ===========================================================================
+%%
+%% supports - get information about what the platform "supports".
+%%
+%% Generates a list of various info about what the plaform can support.
+%% The most obvious case is 'options'.
+%%
+%% Each item in a 'supports'-list will appear only *one* time.
+%%
+%% ===========================================================================
+
+-type supports_options_socket() :: [{socket_option(), boolean()}].
+-type supports_options_ip() :: [{ip_socket_option(), boolean()}].
+-type supports_options_ipv6() :: [{ipv6_socket_option(), boolean()}].
+-type supports_options_tcp() :: [{tcp_socket_option(), boolean()}].
+-type supports_options_udp() :: [{udp_socket_option(), boolean()}].
+-type supports_options_sctp() :: [{sctp_socket_option(), boolean()}].
+-type supports_options() :: [{socket, supports_options_socket()} |
+ {ip, supports_options_ip()} |
+ {ipv6, supports_options_ipv6()} |
+ {tcp, supports_options_tcp()} |
+ {udp, supports_options_udp()} |
+ {sctp, supports_options_sctp()}].
+
+-spec supports() -> [{options, supports_options()} |
+ {sctp, boolean()} |
+ {ipv6, boolean()}].
+
+supports() ->
+ [{options, supports(options)},
+ {sctp, supports(sctp)},
+ {ipv6, supports(ipv6)}].
+
+
+-dialyzer({nowarn_function, supports/1}).
+-spec supports(options) -> supports_options();
+ (sctp) -> boolean();
+ (ipv6) -> boolean();
+ (Key1) -> false when
+ Key1 :: term().
+
+supports(options) ->
+ nif_supports(?SOCKET_SUPPORTS_OPTIONS);
+supports(sctp) ->
+ nif_supports(?SOCKET_SUPPORTS_SCTP);
+supports(ipv6) ->
+ nif_supports(?SOCKET_SUPPORTS_IPV6);
+supports(_Key1) ->
+ false.
+
+-dialyzer({nowarn_function, supports/2}).
+-spec supports(options, socket) -> supports_options_socket();
+ (options, ip) -> supports_options_ip();
+ (options, ipv6) -> supports_options_ipv6();
+ (options, tcp) -> supports_options_tcp();
+ (options, udp) -> supports_options_udp();
+ (options, sctp) -> supports_options_sctp();
+ (Key1, Key2) -> false when
+ Key1 :: term(),
+ Key2 :: term().
+
+supports(options, Level) ->
+ proplists:get_value(Level, supports(options), false);
+supports(_Key1, _Level) ->
+ false.
+
+
+-dialyzer({nowarn_function, supports/3}).
+-spec supports(options, socket, Opt) -> boolean() when
+ Opt :: socket_option();
+ (options, ip, Opt) -> boolean() when
+ Opt :: ip_socket_option();
+ (options, ipv6, Opt) -> boolean() when
+ Opt :: ipv6_socket_option();
+ (options, tcp, Opt) -> boolean() when
+ Opt :: tcp_socket_option();
+ (options, udp, Opt) -> boolean() when
+ Opt :: udp_socket_option();
+ (options, sctp, Opt) -> boolean() when
+ Opt :: sctp_socket_option();
+ (Key1, Key2, Key3) -> false when
+ Key1 :: term(),
+ Key2 :: term(),
+ Key3 :: term().
+
+supports(options, Level, Opt) ->
+ case supports(options, Level) of
+ S when is_list(S) ->
+ proplists:get_value(Opt, S, false);
+ _ ->
+ false
+ end;
+supports(_Key1, _Key2, _Key3) ->
+ false.
+
+
+
+%% ===========================================================================
+%%
+%% The proper socket API
+%%
+%% ===========================================================================
+
+%% ===========================================================================
+%%
+%% <KOLLA>
+%%
+%% How do we handle the case when an fd has been created (somehow)
+%% and we shall create a socket "from it".
+%% Can we figure out Domain, Type and Protocol from fd?
+%% No we can't: For instance, its not possible to 'get' domain on FreeBSD.
+%%
+%% Instead, require: open(Domain, Stream, Proto, #{fd => FD}).
+%% The last argument, Extra, is used to provide the fd.
+%%
+%% </KOLLA>
+%%
+%%
+%% <KOLLA>
+%%
+%% Possibly add a "registry" in the nif, allowing the user processes to
+%% "register" themselves.
+%% The point of this would be to ensure that these processes are
+%% informed if the socket "terminates". Could possibly be used for
+%% other things? If gen_tcp implements the active feature using
+%% a reader process, the nif may need to know about this process,
+%% since its probably "hidden" from the socket "owner" (someone
+%% needs to handle it if it dies).
+%% Register under a name?
+%%
+%% The nif sets up a monitor to this process, and if it dies the socket
+%% is closed. It is also used if someone wants to monitor the socket.
+%%
+%% We may therefor need monitor function(s):
+%%
+%% socket:monitor(Socket)
+%% socket:demonitor(Socket)
+%%
+%% </KOLLA>
+%%
+
+
+
+%% ===========================================================================
+%%
+%% open - create an endpoint for communication
+%%
+%% Extra: Currently only used for netns
+%%
+
+-spec open(Domain, Type) -> {ok, Socket} | {error, Reason} when
+ Domain :: domain(),
+ Type :: type(),
+ Socket :: socket(),
+ Reason :: term().
+
+open(Domain, Type) ->
+ open(Domain, Type, null).
+
+-spec open(Domain, Type, Protocol) -> {ok, Socket} | {error, Reason} when
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: null | protocol(),
+ Socket :: socket(),
+ Reason :: term().
+
+open(Domain, Type, Protocol) ->
+ open(Domain, Type, Protocol, #{}).
+
+-spec open(Domain, Type, Protocol, Extra) -> {ok, Socket} | {error, Reason} when
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: null | protocol(),
+ Extra :: map(),
+ Socket :: socket(),
+ Reason :: term().
+
+open(Domain, Type, Protocol0, Extra) when is_map(Extra) ->
+ try
+ begin
+ Protocol = default_protocol(Protocol0, Type),
+ EDomain = enc_domain(Domain),
+ EType = enc_type(Domain, Type),
+ EProtocol = enc_protocol(Type, Protocol),
+ case nif_open(EDomain, EType, EProtocol, Extra) of
+ {ok, SockRef} ->
+ Socket = #socket{ref = SockRef},
+ {ok, Socket};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end
+ catch
+ throw:T ->
+ T;
+ %% <WIN32-TEMPORARY>
+ error:notsup:S ->
+ erlang:raise(error, notsup, S);
+ %% </WIN32-TEMPORARY>
+ error:Reason ->
+ {error, Reason}
+ end.
+
+%% Note that this is just a convenience function for when the protocol was
+%% not specified. If its actually specified, then that will be selected.
+%% Also, this only works for the some of the type's (stream, dgram and
+%% seqpacket).
+default_protocol(null, stream) -> tcp;
+default_protocol(null, dgram) -> udp;
+default_protocol(null, seqpacket) -> sctp;
+default_protocol(null, Type) -> throw({error, {no_default_protocol, Type}});
+default_protocol(Protocol, _) -> Protocol.
+
+
+%% ===========================================================================
+%%
+%% bind - bind a name to a socket
+%%
+
+-spec bind(Socket, Addr) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Addr :: any | loopback | sockaddr(),
+ Reason :: term().
+
+bind(#socket{ref = SockRef}, Addr)
+ when ((Addr =:= any) orelse (Addr =:= loopback)) ->
+ try which_domain(SockRef) of
+ inet ->
+ nif_bind(SockRef, ?SOCKADDR_IN4_DEFAULT(Addr));
+ inet6 ->
+ nif_bind(SockRef, ?SOCKADDR_IN6_DEFAULT(Addr))
+ catch
+ %% <WIN32-TEMPORARY>
+ error:notsup:S ->
+ erlang:raise(error, notsup, S);
+ %% </WIN32-TEMPORARY>
+ throw:ERROR ->
+ ERROR
+ end;
+bind(#socket{ref = SockRef} = _Socket, Addr) when is_map(Addr) ->
+ try
+ begin
+ nif_bind(SockRef, ensure_sockaddr(Addr))
+ end
+ catch
+ %% <WIN32-TEMPORARY>
+ error:notsup:S ->
+ erlang:raise(error, notsup, S);
+ %% </WIN32-TEMPORARY>
+ throw:ERROR ->
+ ERROR
+ end.
+
+
+
+%% ===========================================================================
+%%
+%% bind - Add or remove a bind addresses on a socket
+%%
+%% Calling this function is only valid if the socket is:
+%% type = seqpacket
+%% protocol = sctp
+%%
+%% If the domain is inet, then all addresses *must* be IPv4.
+%% If the domain is inet6, the addresses can be aither IPv4 or IPv6.
+%%
+
+-spec bind(Socket, Addrs, Action) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Addrs :: [sockaddr()],
+ Action :: add | remove,
+ Reason :: term().
+
+bind(#socket{ref = SockRef}, Addrs, Action)
+ when is_list(Addrs) andalso ((Action =:= add) orelse (Action =:= remove)) ->
+ try
+ begin
+ ensure_type(SockRef, seqpacket),
+ ensure_proto(SockRef, sctp),
+ validate_addrs(which_domain(SockRef), Addrs),
+ nif_bind(SockRef, Addrs, Action)
+ end
+ catch
+ %% <WIN32-TEMPORARY>
+ error:notsup:S ->
+ erlang:raise(error, notsup, S);
+ %% </WIN32-TEMPORARY>
+ throw:ERROR ->
+ ERROR
+ end.
+
+ensure_type(SockRef, Type) ->
+ case which_type(SockRef) of
+ Type ->
+ ok;
+ _InvalidType ->
+ einval()
+ end.
+
+ensure_proto(SockRef, Proto) ->
+ case which_protocol(SockRef) of
+ Proto ->
+ ok;
+ _InvalidProto ->
+ einval()
+ end.
+
+validate_addrs(inet = _Domain, Addrs) ->
+ validate_inet_addrs(Addrs);
+validate_addrs(inet6 = _Domain, Addrs) ->
+ validate_inet6_addrs(Addrs).
+
+validate_inet_addrs(Addrs) ->
+ Validator = fun(#{family := inet,
+ addrs := Addr}) when is_tuple(Addr) andalso
+ (size(Addr) =:= 4) ->
+ ok;
+ (X) ->
+ throw({error, {invalid_address, X}})
+ end,
+ lists:foreach(Validator, Addrs).
+
+validate_inet6_addrs(Addrs) ->
+ Validator = fun(#{family := inet,
+ addrs := Addr}) when is_tuple(Addr) andalso
+ (size(Addr) =:= 4) ->
+ ok;
+ (#{family := inet6,
+ addrs := Addr}) when is_tuple(Addr) andalso
+ (size(Addr) =:= 8) ->
+ ok;
+ (X) ->
+ throw({error, {invalid_address, X}})
+ end,
+ lists:foreach(Validator, Addrs).
+
+
+%% ===========================================================================
+%%
+%% connect - initiate a connection on a socket
+%%
+
+-spec connect(Socket, SockAddr) -> ok | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: term().
+
+connect(Socket, SockAddr) ->
+ connect(Socket, SockAddr, infinity).
+
+-spec connect(Socket, SockAddr, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Timeout :: timeout(),
+ Reason :: term().
+
+%% <KOLLA>
+%% Is it possible to connect with family = local for the (dest) sockaddr?
+%% </KOLLA>
+connect(_Socket, _SockAddr, Timeout)
+ when (is_integer(Timeout) andalso (Timeout =< 0)) ->
+ {error, timeout};
+connect(#socket{ref = SockRef}, #{family := Fam} = SockAddr, Timeout)
+ when ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso
+ ((Timeout =:= infinity) orelse is_integer(Timeout)) ->
+ TS = timestamp(Timeout),
+ case nif_connect(SockRef, SockAddr) of
+ ok ->
+ %% Connected!
+ ok;
+ {ok, Ref} ->
+ %% Connecting...
+ NewTimeout = next_timeout(TS, Timeout),
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, Ref} ->
+ nif_finalize_connection(SockRef)
+ after NewTimeout ->
+ cancel(SockRef, connect, Ref),
+ {error, timeout}
+ end;
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+%% ===========================================================================
+%%
+%% listen - listen for connections on a socket
+%%
+
+-spec listen(Socket) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Reason :: term().
+
+listen(Socket) ->
+ listen(Socket, ?SOCKET_LISTEN_BACKLOG_DEFAULT).
+
+-spec listen(Socket, Backlog) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Backlog :: pos_integer(),
+ Reason :: term().
+
+listen(#socket{ref = SockRef}, Backlog)
+ when (is_integer(Backlog) andalso (Backlog >= 0)) ->
+ nif_listen(SockRef, Backlog).
+
+
+
+
+%% ===========================================================================
+%%
+%% accept, accept4 - accept a connection on a socket
+%%
+
+-spec accept(LSocket) -> {ok, Socket} | {error, Reason} when
+ LSocket :: socket(),
+ Socket :: socket(),
+ Reason :: term().
+
+accept(Socket) ->
+ accept(Socket, ?SOCKET_ACCEPT_TIMEOUT_DEFAULT).
+
+-spec accept(LSocket, Timeout) -> {ok, Socket} | {error, Reason} when
+ LSocket :: socket(),
+ Timeout :: timeout(),
+ Socket :: socket(),
+ Reason :: term().
+
+%% Do we really need this optimization?
+accept(_, Timeout) when is_integer(Timeout) andalso (Timeout =< 0) ->
+ {error, timeout};
+accept(#socket{ref = LSockRef}, Timeout)
+ when is_integer(Timeout) orelse (Timeout =:= infinity) ->
+ do_accept(LSockRef, Timeout).
+
+do_accept(LSockRef, Timeout) ->
+ TS = timestamp(Timeout),
+ AccRef = make_ref(),
+ case nif_accept(LSockRef, AccRef) of
+ {ok, SockRef} ->
+ Socket = #socket{ref = SockRef},
+ {ok, Socket};
+
+ {error, eagain} ->
+ %% Each call is non-blocking, but even then it takes
+ %% *some* time, so just to be sure, recalculate before
+ %% the receive.
+ NewTimeout = next_timeout(TS, Timeout),
+ receive
+ {?SOCKET_TAG, #socket{ref = LSockRef}, select, AccRef} ->
+ do_accept(LSockRef, next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {AccRef, Reason}} ->
+ {error, Reason}
+
+ after NewTimeout ->
+ cancel(LSockRef, accept, AccRef),
+ {error, timeout}
+ end;
+
+ {error, _} = ERROR ->
+ cancel(LSockRef, accept, AccRef), % Just to be on the safe side...
+ ERROR
+ end.
+
+
+
+%% ===========================================================================
+%%
+%% send, sendto, sendmsg - send a message on a socket
+%%
+
+-spec send(Socket, Data) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Reason :: term().
+
+send(Socket, Data) ->
+ send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, ?SOCKET_SEND_TIMEOUT_DEFAULT).
+
+-spec send(Socket, Data, Flags) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ Reason :: term()
+ ; (Socket, Data, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Timeout :: timeout(),
+ Reason :: term().
+
+send(Socket, Data, Flags) when is_list(Flags) ->
+ send(Socket, Data, Flags, ?SOCKET_SEND_TIMEOUT_DEFAULT);
+send(Socket, Data, Timeout) ->
+ send(Socket, Data, ?SOCKET_SEND_FLAGS_DEFAULT, Timeout).
+
+-spec send(Socket, Data, Flags, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Reason :: term().
+
+send(Socket, Data, Flags, Timeout) when is_list(Data) ->
+ Bin = erlang:list_to_binary(Data),
+ send(Socket, Bin, Flags, Timeout);
+send(#socket{ref = SockRef}, Data, Flags, Timeout)
+ when is_binary(Data) andalso is_list(Flags) ->
+ EFlags = enc_send_flags(Flags),
+ do_send(SockRef, Data, EFlags, Timeout).
+
+do_send(SockRef, Data, EFlags, Timeout) ->
+ TS = timestamp(Timeout),
+ SendRef = make_ref(),
+ case nif_send(SockRef, SendRef, Data, EFlags) of
+ ok ->
+ ok;
+ {ok, Written} ->
+ NewTimeout = next_timeout(TS, Timeout),
+ %% We are partially done, wait for continuation
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef}
+ when (Written > 0) ->
+ <<_:Written/binary, Rest/binary>> = Data,
+ do_send(SockRef, Rest, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
+ do_send(SockRef, Data, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} ->
+ {error, Reason}
+
+ after NewTimeout ->
+ cancel(SockRef, send, SendRef),
+ {error, {timeout, size(Data)}}
+ end;
+ {error, eagain} ->
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
+ do_send(SockRef, Data, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} ->
+ {error, Reason}
+
+ after Timeout ->
+ cancel(SockRef, send, SendRef),
+ {error, {timeout, size(Data)}}
+ end;
+
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+
+
+%% ---------------------------------------------------------------------------
+%%
+
+-spec sendto(Socket, Data, Dest) ->
+ ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: null | sockaddr(),
+ Reason :: term().
+
+sendto(Socket, Data, Dest) ->
+ sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT).
+
+-spec sendto(Socket, Data, Dest, Flags) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: null | sockaddr(),
+ Flags :: send_flags(),
+ Reason :: term()
+ ; (Socket, Data, Dest, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: iodata(),
+ Dest :: null | sockaddr(),
+ Timeout :: timeout(),
+ Reason :: term().
+
+sendto(Socket, Data, Dest, Flags) when is_list(Flags) ->
+ sendto(Socket, Data, Dest, Flags, ?SOCKET_SENDTO_TIMEOUT_DEFAULT);
+sendto(Socket, Data, Dest, Timeout) ->
+ sendto(Socket, Data, Dest, ?SOCKET_SENDTO_FLAGS_DEFAULT, Timeout).
+
+
+-spec sendto(Socket, Data, Dest, Flags, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Dest :: null | sockaddr(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Reason :: term().
+
+sendto(Socket, Data, Dest, Flags, Timeout) when is_list(Data) ->
+ Bin = erlang:list_to_binary(Data),
+ sendto(Socket, Bin, Dest, Flags, Timeout);
+sendto(#socket{ref = SockRef}, Data, Dest, Flags, Timeout)
+ when is_binary(Data) andalso
+ (Dest =:= null) andalso
+ is_list(Flags) andalso
+ (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ EFlags = enc_send_flags(Flags),
+ do_sendto(SockRef, Data, Dest, EFlags, Timeout);
+sendto(#socket{ref = SockRef}, Data, #{family := Fam} = Dest, Flags, Timeout)
+ when is_binary(Data) andalso
+ ((Fam =:= inet) orelse (Fam =:= inet6) orelse (Fam =:= local)) andalso
+ is_list(Flags) andalso
+ (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ EFlags = enc_send_flags(Flags),
+ do_sendto(SockRef, Data, Dest, EFlags, Timeout).
+
+do_sendto(SockRef, Data, Dest, EFlags, Timeout) ->
+ TS = timestamp(Timeout),
+ SendRef = make_ref(),
+ case nif_sendto(SockRef, SendRef, Data, Dest, EFlags) of
+ ok ->
+ %% We are done
+ ok;
+
+ {ok, Written} ->
+ %% We are partially done, wait for continuation
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef}
+ when (Written > 0) ->
+ <<_:Written/binary, Rest/binary>> = Data,
+ do_sendto(SockRef, Rest, Dest, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
+ do_sendto(SockRef, Data, Dest, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} ->
+ {error, Reason}
+
+ after Timeout ->
+ cancel(SockRef, sendto, SendRef),
+ {error, timeout}
+ end;
+
+ {error, eagain} ->
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
+ do_sendto(SockRef, Data, Dest, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {SendRef, Reason}} ->
+ {error, Reason}
+
+ after Timeout ->
+ cancel(SockRef, sendto, SendRef),
+ {error, timeout}
+ end;
+
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+
+%% ---------------------------------------------------------------------------
+%%
+%% The only part of the msghdr() that *must* exist (a connected
+%% socket need not specify the addr field) is the iov.
+%% The ctrl field is optional, and the addr and flags are not
+%% used when sending.
+%%
+
+-spec sendmsg(Socket, MsgHdr) -> ok | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
+
+sendmsg(Socket, MsgHdr) ->
+ sendmsg(Socket, MsgHdr,
+ ?SOCKET_SENDMSG_FLAGS_DEFAULT, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT).
+
+
+-spec sendmsg(Socket, MsgHdr, Flags) -> ok | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Flags :: send_flags(),
+ Reason :: term()
+ ; (Socket, MsgHdr, Timeout) -> ok | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Timeout :: timeout(),
+ Reason :: term().
+
+sendmsg(Socket, MsgHdr, Flags) when is_list(Flags) ->
+ sendmsg(Socket, MsgHdr, Flags, ?SOCKET_SENDMSG_TIMEOUT_DEFAULT);
+sendmsg(Socket, MsgHdr, Timeout)
+ when is_integer(Timeout) orelse (Timeout =:= infinity) ->
+ sendmsg(Socket, MsgHdr, ?SOCKET_SENDMSG_FLAGS_DEFAULT, Timeout).
+
+
+-spec sendmsg(Socket, MsgHdr, Flags, Timeout) ->
+ ok | {ok, Remaining} | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Flags :: send_flags(),
+ Timeout :: timeout(),
+ Remaining :: erlang:iovec(),
+ Reason :: term().
+
+sendmsg(#socket{ref = SockRef}, #{iov := IOV} = MsgHdr, Flags, Timeout)
+ when is_list(IOV) andalso
+ is_list(Flags) andalso
+ (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ try ensure_msghdr(MsgHdr) of
+ M ->
+ EFlags = enc_send_flags(Flags),
+ do_sendmsg(SockRef, M, EFlags, Timeout)
+ catch
+ throw:T ->
+ T;
+ error:Reason ->
+ {error, Reason}
+ end.
+
+do_sendmsg(SockRef, MsgHdr, EFlags, Timeout) ->
+ TS = timestamp(Timeout),
+ SendRef = make_ref(),
+ case nif_sendmsg(SockRef, SendRef, MsgHdr, EFlags) of
+ ok ->
+ %% We are done
+ ok;
+
+ {ok, Written} when is_integer(Written) andalso (Written > 0) ->
+ %% We should not retry here since the protocol may not
+ %% be able to handle a message being split. Leave it to
+ %% the caller to figure out (call again with the rest).
+ %%
+ %% We should really not need to cancel, since this is
+ %% accepted for sendmsg!
+ %%
+ cancel(SockRef, sendmsg, SendRef),
+ {ok, do_sendmsg_rest(maps:get(iov, MsgHdr), Written)};
+
+ {error, eagain} ->
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, SendRef} ->
+ do_sendmsg(SockRef, MsgHdr, EFlags,
+ next_timeout(TS, Timeout))
+
+ after Timeout ->
+ cancel(SockRef, sendmsg, SendRef),
+ {error, timeout}
+ end;
+
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+do_sendmsg_rest([B|IOVec], Written) when (Written >= size(B)) ->
+ do_sendmsg_rest(IOVec, Written - size(B));
+do_sendmsg_rest([B|IOVec], Written) ->
+ <<_:Written/binary, Rest/binary>> = B,
+ [Rest|IOVec].
+
+ensure_msghdr(#{ctrl := []} = M) ->
+ ensure_msghdr(maps:remove(ctrl, M));
+ensure_msghdr(#{iov := IOV} = M) when is_list(IOV) andalso (IOV =/= []) ->
+ M#{iov := erlang:iolist_to_iovec(IOV)};
+ensure_msghdr(_) ->
+ einval().
+
+
+
+
+%% ===========================================================================
+%%
+%% recv, recvfrom, recvmsg - receive a message from a socket
+%%
+%% Description:
+%% There is a special case for the argument Length. If its set to zero (0),
+%% it means "give me everything you have".
+%%
+%% Returns: {ok, Binary} | {error, Reason}
+%% Binary - The received data as a binary
+%% Reason - The error reason:
+%% timeout | {timeout, AccData} |
+%% posix() | {posix(), AccData} |
+%% atom() | {atom(), AccData}
+%% AccData - The data (as a binary) that we did manage to receive
+%% before the timeout.
+%%
+%% Arguments:
+%% Socket - The socket to read from.
+%% Length - The number of bytes to read.
+%% Flags - A list of "options" for the read.
+%% Timeout - Time-out in milliseconds.
+
+-spec recv(Socket) -> {ok, Data} | {error, Reason} when
+ Socket :: socket(),
+ Data :: binary(),
+ Reason :: term().
+
+recv(Socket) ->
+ recv(Socket, 0).
+
+-spec recv(Socket, Length) -> {ok, Data} | {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Data :: binary(),
+ Reason :: term().
+
+recv(Socket, Length) ->
+ recv(Socket, Length,
+ ?SOCKET_RECV_FLAGS_DEFAULT,
+ ?SOCKET_RECV_TIMEOUT_DEFAULT).
+
+-spec recv(Socket, Length, Flags) -> {ok, Data} | {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Data :: binary(),
+ Reason :: term()
+ ; (Socket, Length, Timeout) -> {ok, Data} | {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Timeout :: timeout(),
+ Data :: binary(),
+ Reason :: term().
+
+recv(Socket, Length, Flags) when is_list(Flags) ->
+ recv(Socket, Length, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT);
+recv(Socket, Length, Timeout) ->
+ recv(Socket, Length, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
+
+-spec recv(Socket, Length, Flags, Timeout) -> {ok, Data} | {error, Reason} when
+ Socket :: socket(),
+ Length :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Data :: binary(),
+ Reason :: term().
+
+recv(#socket{ref = SockRef}, Length, Flags, Timeout)
+ when (is_integer(Length) andalso (Length >= 0)) andalso
+ is_list(Flags) andalso
+ (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ EFlags = enc_recv_flags(Flags),
+ do_recv(SockRef, undefined, Length, EFlags, <<>>, Timeout).
+
+%% We need to pass the "old recv ref" around because of the special case
+%% with Length = 0. This case makes it neccessary to have a timeout function
+%% clause since we may never wait for anything (no receive select), and so the
+%% the only timeout check will be the function clause.
+do_recv(SockRef, _OldRef, Length, EFlags, Acc, Timeout)
+ when (Timeout =:= infinity) orelse
+ (is_integer(Timeout) andalso (Timeout > 0)) ->
+ TS = timestamp(Timeout),
+ RecvRef = make_ref(),
+ case nif_recv(SockRef, RecvRef, Length, EFlags) of
+ {ok, true = _Complete, Bin} when (size(Acc) =:= 0) ->
+ {ok, Bin};
+ {ok, true = _Complete, Bin} ->
+ {ok, <<Acc/binary, Bin/binary>>};
+
+ %% It depends on the amount of bytes we tried to read:
+ %% 0 - Read everything available
+ %% We got something, but there may be more - keep reading.
+ %% > 0 - We got a part of the message and we will be notified
+ %% when there is more to read (a select message)
+ {ok, false = _Complete, Bin} when (Length =:= 0) ->
+ do_recv(SockRef, RecvRef,
+ Length, EFlags,
+ <<Acc/binary, Bin/binary>>,
+ next_timeout(TS, Timeout));
+
+ {ok, false = _Completed, Bin} when (size(Acc) =:= 0) ->
+ %% We got the first chunk of it.
+ %% We will be notified (select message) when there
+ %% is more to read.
+ NewTimeout = next_timeout(TS, Timeout),
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} ->
+ do_recv(SockRef, RecvRef,
+ Length-size(Bin), EFlags,
+ Bin,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} ->
+ {error, Reason}
+
+ after NewTimeout ->
+ cancel(SockRef, recv, RecvRef),
+ {error, {timeout, Acc}}
+ end;
+
+ {ok, false = _Completed, Bin} ->
+ %% We got a chunk of it!
+ NewTimeout = next_timeout(TS, Timeout),
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} ->
+ do_recv(SockRef, RecvRef,
+ Length-size(Bin), EFlags,
+ <<Acc/binary, Bin/binary>>,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} ->
+ {error, Reason}
+
+ after NewTimeout ->
+ cancel(SockRef, recv, RecvRef),
+ {error, {timeout, Acc}}
+ end;
+
+ %% We return with the accumulated binary (if its non-empty)
+ {error, eagain} when (Length =:= 0) andalso (size(Acc) > 0) ->
+ %% CAN WE REALLY DO THIS? THE NIF HAS SELECTED!! OR?
+ {ok, Acc};
+
+ {error, eagain} ->
+ %% There is nothing just now, but we will be notified when there
+ %% is something to read (a select message).
+ NewTimeout = next_timeout(TS, Timeout),
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} ->
+ do_recv(SockRef, RecvRef,
+ Length, EFlags,
+ Acc,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} ->
+ {error, Reason}
+
+ after NewTimeout ->
+ cancel(SockRef, recv, RecvRef),
+ {error, timeout}
+ end;
+
+ {error, closed = Reason} ->
+ do_close(SockRef),
+ if
+ (size(Acc) =:= 0) ->
+ {error, Reason};
+ true ->
+ {error, {Reason, Acc}}
+ end;
+
+ {error, _} = ERROR when (size(Acc) =:= 0) ->
+ ERROR;
+
+ {error, Reason} ->
+ {error, {Reason, Acc}}
+
+ end;
+
+do_recv(SockRef, RecvRef, 0 = _Length, _Eflags, Acc, _Timeout) ->
+ %% The current recv operation is to be cancelled, so no need for a ref...
+ %% The cancel will end our 'read everything you have' and "activate"
+ %% any waiting reader.
+ cancel(SockRef, recv, RecvRef),
+ {ok, Acc};
+do_recv(_SockRef, _RecvRef, _Length, _EFlags, Acc, _Timeout)
+ when (size(Acc) > 0) ->
+ {error, {timeout, Acc}};
+do_recv(_SockRef, _RecvRef, _Length, _EFlags, _Acc, _Timeout) ->
+ {error, timeout}.
+
+
+
+%% ---------------------------------------------------------------------------
+%%
+%% With recvfrom we get messages, which means that regardless of how
+%% much we want to read, we return when we get a message.
+%% The MaxSize argument basically defines the size of our receive
+%% buffer. By setting the size to zero (0), we use the configured
+%% size (see setopt).
+%% It may be impossible to know what (buffer) size is appropriate
+%% "in advance", and in those cases it may be convenient to use the
+%% (recv) 'peek' flag. When this flag is provided the message is *not*
+%% "consumed" from the underlying (OS) buffers, so another recvfrom call
+%% is needed, possibly with a then adjusted buffer size.
+%%
+
+-spec recvfrom(Socket) -> {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term().
+
+recvfrom(Socket) ->
+ recvfrom(Socket, 0).
+
+-spec recvfrom(Socket, BufSz) -> {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term().
+
+recvfrom(Socket, BufSz) ->
+ recvfrom(Socket, BufSz,
+ ?SOCKET_RECV_FLAGS_DEFAULT,
+ ?SOCKET_RECV_TIMEOUT_DEFAULT).
+
+-spec recvfrom(Socket, Flags, Timeout) ->
+ {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term()
+ ; (Socket, BufSz, Flags) ->
+ {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term()
+ ; (Socket, BufSz, Timeout) ->
+ {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term().
+
+recvfrom(Socket, Flags, Timeout) when is_list(Flags) ->
+ recvfrom(Socket, 0, Flags, Timeout);
+recvfrom(Socket, BufSz, Flags) when is_list(Flags) ->
+ recvfrom(Socket, BufSz, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT);
+recvfrom(Socket, BufSz, Timeout) ->
+ recvfrom(Socket, BufSz, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
+
+-spec recvfrom(Socket, BufSz, Flags, Timeout) ->
+ {ok, {Source, Data}} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ Source :: sockaddr() | undefined,
+ Data :: binary(),
+ Reason :: term().
+
+recvfrom(#socket{ref = SockRef}, BufSz, Flags, Timeout)
+ when (is_integer(BufSz) andalso (BufSz >= 0)) andalso
+ is_list(Flags) andalso
+ (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ EFlags = enc_recv_flags(Flags),
+ do_recvfrom(SockRef, BufSz, EFlags, Timeout).
+
+do_recvfrom(SockRef, BufSz, EFlags, Timeout) ->
+ TS = timestamp(Timeout),
+ RecvRef = make_ref(),
+ case nif_recvfrom(SockRef, RecvRef, BufSz, EFlags) of
+ {ok, {_Source, _NewData}} = OK ->
+ OK;
+
+ {error, eagain} ->
+ %% There is nothing just now, but we will be notified when there
+ %% is something to read (a select message).
+ NewTimeout = next_timeout(TS, Timeout),
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} ->
+ do_recvfrom(SockRef, BufSz, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} ->
+ {error, Reason}
+
+ after NewTimeout ->
+ cancel(SockRef, recvfrom, RecvRef),
+ {error, timeout}
+ end;
+
+ {error, _Reason} = ERROR ->
+ ERROR
+
+ end.
+
+
+%% ---------------------------------------------------------------------------
+%%
+
+-spec recvmsg(Socket) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
+
+recvmsg(Socket) ->
+ recvmsg(Socket, 0, 0,
+ ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT).
+
+-spec recvmsg(Socket, Flags) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ MsgHdr :: msghdr(),
+ Reason :: term()
+ ; (Socket, Timeout) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
+
+recvmsg(Socket, Flags) when is_list(Flags) ->
+ recvmsg(Socket, 0, 0, Flags, ?SOCKET_RECV_TIMEOUT_DEFAULT);
+recvmsg(Socket, Timeout) ->
+ recvmsg(Socket, 0, 0, ?SOCKET_RECV_FLAGS_DEFAULT, Timeout).
+
+-spec recvmsg(Socket, Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: term()
+ ; (Socket, BufSz, CtrlSz) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
+
+recvmsg(Socket, Flags, Timeout) when is_list(Flags) ->
+ recvmsg(Socket, 0, 0, Flags, Timeout);
+recvmsg(Socket, BufSz, CtrlSz) when is_integer(BufSz) andalso is_integer(CtrlSz) ->
+ recvmsg(Socket, BufSz, CtrlSz,
+ ?SOCKET_RECV_FLAGS_DEFAULT, ?SOCKET_RECV_TIMEOUT_DEFAULT).
+
+
+-spec recvmsg(Socket,
+ BufSz, CtrlSz,
+ Flags, Timeout) -> {ok, MsgHdr} | {error, Reason} when
+ Socket :: socket(),
+ BufSz :: non_neg_integer(),
+ CtrlSz :: non_neg_integer(),
+ Flags :: recv_flags(),
+ Timeout :: timeout(),
+ MsgHdr :: msghdr(),
+ Reason :: term().
+
+recvmsg(#socket{ref = SockRef}, BufSz, CtrlSz, Flags, Timeout)
+ when (is_integer(BufSz) andalso (BufSz >= 0)) andalso
+ (is_integer(CtrlSz) andalso (CtrlSz >= 0)) andalso
+ is_list(Flags) andalso
+ (is_integer(Timeout) orelse (Timeout =:= infinity)) ->
+ EFlags = enc_recv_flags(Flags),
+ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout).
+
+do_recvmsg(SockRef, BufSz, CtrlSz, EFlags, Timeout) ->
+ TS = timestamp(Timeout),
+ RecvRef = make_ref(),
+ case nif_recvmsg(SockRef, RecvRef, BufSz, CtrlSz, EFlags) of
+ {ok, _MsgHdr} = OK ->
+ OK;
+
+ {error, eagain} ->
+ %% There is nothing just now, but we will be notified when there
+ %% is something to read (a select message).
+ NewTimeout = next_timeout(TS, Timeout),
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, RecvRef} ->
+ do_recvmsg(SockRef, BufSz, CtrlSz, EFlags,
+ next_timeout(TS, Timeout));
+
+ {?SOCKET_TAG, _Socket, abort, {RecvRef, Reason}} ->
+ {error, Reason}
+
+ after NewTimeout ->
+ cancel(SockRef, recvmsg, RecvRef),
+ {error, timeout}
+ end;
+
+ {error, closed} = ERROR ->
+ do_close(SockRef),
+ ERROR;
+
+ {error, _Reason} = ERROR ->
+ ERROR
+
+ end.
+
+
+
+
+%% ===========================================================================
+%%
+%% close - close a file descriptor
+%%
+%% Closing a socket is a two stage rocket (because of linger).
+%% We need to perform the actual socket close while in BLOCKING mode.
+%% But that would hang the entire VM, so what we do is divide the
+%% close in two steps:
+%% 1) nif_close + the socket_stop (nif) callback function
+%% This is for everything that can be done safely NON-BLOCKING.
+%% 2) nif_finalize_close which is executed by a *dirty* scheduler
+%% Before we call the socket close function, we set the socket
+%% BLOCKING. Thereby linger is handled properly.
+
+-spec close(Socket) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Reason :: term().
+
+close(#socket{ref = SockRef}) ->
+ do_close(SockRef).
+
+do_close(SockRef) ->
+ case nif_close(SockRef) of
+ ok ->
+ nif_finalize_close(SockRef);
+ {ok, CloseRef} ->
+ %% We must wait for the socket_stop callback function to
+ %% complete its work
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, close, CloseRef} ->
+ nif_finalize_close(SockRef)
+ end;
+ {error, _} = ERROR ->
+ ERROR
+ end.
+
+
+
+
+%% ===========================================================================
+%%
+%% shutdown - shut down part of a full-duplex connection
+%%
+
+-spec shutdown(Socket, How) -> ok | {error, Reason} when
+ Socket :: socket(),
+ How :: shutdown_how(),
+ Reason :: term().
+
+shutdown(#socket{ref = SockRef}, How) ->
+ try
+ begin
+ EHow = enc_shutdown_how(How),
+ nif_shutdown(SockRef, EHow)
+ end
+ catch
+ throw:T ->
+ T;
+ %% <WIN32-TEMPORARY>
+ error:notsup:S ->
+ erlang:raise(error, notsup, S);
+ %% </WIN32-TEMPORARY>
+ error:Reason ->
+ {error, Reason}
+ end.
+
+
+
+
+%% ===========================================================================
+%%
+%% setopt - manipulate individual properties of a socket
+%%
+%% What properties are valid depend on what kind of socket it is
+%% (domain, type and protocol)
+%% If its an "invalid" option (or value), we should not crash but return some
+%% useful error...
+%%
+%% <KOLLA>
+%%
+%% WE NEED TO MAKE SURE THAT THE USER DOES NOT MAKE US BLOCKING
+%% AS MUCH OF THE CODE EXPECTS TO BE NON-BLOCKING!!
+%%
+%% </KOLLA>
+
+-spec setopt(Socket, otp, otp_socket_option(), Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, socket, socket_option(), Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, ip, ip_socket_option(), Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, ipv6, ipv6_socket_option(), Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, tcp, tcp_socket_option(), Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, udp, udp_socket_option(), Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, sctp, sctp_socket_option(), Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, Level, Key, Value) -> ok | {error, Reason} when
+ Socket :: socket(),
+ Level :: non_neg_integer(),
+ Key :: non_neg_integer(),
+ Value :: binary(),
+ Reason :: term().
+
+setopt(#socket{ref = SockRef}, Level, Key, Value) ->
+ try
+ begin
+ Domain = which_domain(SockRef),
+ Type = which_type(SockRef),
+ Protocol = which_protocol(SockRef),
+ {EIsEncoded, ELevel} = enc_setopt_level(Level),
+ EKey = enc_setopt_key(Level, Key, Domain, Type, Protocol),
+ EVal = enc_setopt_value(Level, Key, Value, Domain, Type, Protocol),
+ nif_setopt(SockRef, EIsEncoded, ELevel, EKey, EVal)
+ end
+ catch
+ throw:T ->
+ T;
+ %% <WIN32-TEMPORARY>
+ error:notsup:S ->
+ erlang:raise(error, notsup, S);
+ %% </WIN32-TEMPORARY>
+ error:Reason ->
+ {error, Reason} % Process more?
+ end.
+
+
+
+
+%% ===========================================================================
+%%
+%% getopt - retrieve individual properties of a socket
+%%
+%% What properties are valid depend on what kind of socket it is
+%% (domain, type and protocol).
+%% If its an "invalid" option, we should not crash but return some
+%% useful error...
+%%
+%% When specifying level as an integer, and therefor using "native mode",
+%% we should make it possible to specify common types instead of the
+%% value size. Example: int | bool | {string, pos_integer()} | non_neg_integer()
+%%
+
+-spec getopt(Socket, otp, otp_socket_option()) -> {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, socket, socket_option()) -> {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, ip, ip_socket_option()) -> {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, ipv6, ipv6_socket_option()) -> {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, tcp, tcp_socket_option()) -> {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, udp, udp_socket_option()) -> {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, sctp, sctp_socket_option()) -> {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Value :: term(),
+ Reason :: term()
+ ; (Socket, Level, Key) -> ok | {ok, Value} | {error, Reason} when
+ Socket :: socket(),
+ Level :: integer(),
+ Key :: {NativeOpt, ValueSize},
+ NativeOpt :: integer(),
+ ValueSize :: int | bool | non_neg_integer(),
+ Value :: term(),
+ Reason :: term().
+
+getopt(#socket{ref = SockRef}, Level, Key) ->
+ try
+ begin
+ Domain = which_domain(SockRef),
+ Type = which_type(SockRef),
+ Protocol = which_protocol(SockRef),
+ {EIsEncoded, ELevel} = enc_getopt_level(Level),
+ EKey = enc_getopt_key(Level, Key, Domain, Type, Protocol),
+ %% We may need to decode the value (for the same reason
+ %% we (may have) needed to encode the value for setopt).
+ case nif_getopt(SockRef, EIsEncoded, ELevel, EKey) of
+ ok ->
+ ok;
+ {ok, EVal} ->
+ Val = dec_getopt_value(Level, Key, EVal,
+ Domain, Type, Protocol),
+ {ok, Val};
+ {error, _} = ERROR ->
+ ERROR
+ end
+ end
+ catch
+ throw:E:_S ->
+ E;
+ %% <WIN32-TEMPORARY>
+ error:notsup:S ->
+ erlang:raise(error, notsup, S);
+ %% </WIN32-TEMPORARY>
+ error:Reason:_Stack ->
+ {error, Reason} % Process more?
+ end.
+
+
+%% These are internal "shortcut" functions for the options
+%% domain, type and protocol.
+
+-spec which_domain(SockRef) -> Domain when
+ SockRef :: reference(),
+ Domain :: domain().
+
+which_domain(SockRef) ->
+ case nif_getopt(SockRef, true,
+ ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_DOMAIN) of
+ {ok, Domain} ->
+ Domain;
+ {error, _} = ERROR ->
+ throw(ERROR)
+ end.
+
+
+-spec which_type(SockRef) -> Type when
+ SockRef :: reference(),
+ Type :: type().
+
+which_type(SockRef) ->
+ case nif_getopt(SockRef, true,
+ ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_TYPE) of
+ {ok, Type} ->
+ Type;
+ {error, _} = ERROR ->
+ throw(ERROR)
+ end.
+
+-spec which_protocol(SockRef) -> Protocol when
+ SockRef :: reference(),
+ Protocol :: protocol().
+
+which_protocol(SockRef) ->
+ case nif_getopt(SockRef, true,
+ ?SOCKET_OPT_LEVEL_OTP, ?SOCKET_OPT_OTP_PROTOCOL) of
+ {ok, Proto} ->
+ Proto;
+ {error, _} = ERROR ->
+ throw(ERROR)
+ end.
+
+
+
+
+%% ===========================================================================
+%%
+%% sockname - return the current address of the socket.
+%%
+%%
+
+-spec sockname(Socket) -> {ok, SockAddr} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: term().
+
+sockname(#socket{ref = SockRef}) ->
+ nif_sockname(SockRef).
+
+
+
+%% ===========================================================================
+%%
+%% peername - return the address of the peer *connected* to the socket.
+%%
+%%
+
+-spec peername(Socket) -> {ok, SockAddr} | {error, Reason} when
+ Socket :: socket(),
+ SockAddr :: sockaddr(),
+ Reason :: term().
+
+peername(#socket{ref = SockRef}) ->
+ nif_peername(SockRef).
+
+
+
+%% ===========================================================================
+%%
+%% Encode / decode
+%%
+%% ===========================================================================
+
+-spec enc_domain(Domain) -> non_neg_integer() when
+ Domain :: domain().
+
+enc_domain(local) -> ?SOCKET_DOMAIN_LOCAL;
+enc_domain(inet) -> ?SOCKET_DOMAIN_INET;
+enc_domain(inet6) -> ?SOCKET_DOMAIN_INET6;
+enc_domain(Domain) -> throw({error, {invalid_domain, Domain}}).
+
+-spec enc_type(Domain, Type) -> non_neg_integer() when
+ Domain :: domain(),
+ Type :: type().
+
+%% What combos are valid?
+enc_type(_, stream) -> ?SOCKET_TYPE_STREAM;
+enc_type(_, dgram) -> ?SOCKET_TYPE_DGRAM;
+enc_type(_, raw) -> ?SOCKET_TYPE_RAW;
+enc_type(_, seqpacket) -> ?SOCKET_TYPE_SEQPACKET;
+enc_type(_, Type) -> throw({error, {invalid_type, Type}}).
+
+-spec enc_protocol(Type, Protocol) -> non_neg_integer() |
+ {raw, non_neg_integer()} when
+ Type :: type(),
+ Protocol :: protocol().
+
+enc_protocol(dgram, ip) -> ?SOCKET_PROTOCOL_IP;
+enc_protocol(stream, tcp) -> ?SOCKET_PROTOCOL_TCP;
+enc_protocol(dgram, udp) -> ?SOCKET_PROTOCOL_UDP;
+enc_protocol(seqpacket, sctp) -> ?SOCKET_PROTOCOL_SCTP;
+enc_protocol(raw, icmp) -> ?SOCKET_PROTOCOL_ICMP;
+enc_protocol(raw, igmp) -> ?SOCKET_PROTOCOL_IGMP;
+enc_protocol(raw, {raw, P} = RAW) when is_integer(P) -> RAW;
+enc_protocol(Type, Proto) ->
+ throw({error, {invalid_protocol, {Type, Proto}}}).
+
+
+-spec enc_send_flags(Flags) -> non_neg_integer() when
+ Flags :: send_flags().
+
+enc_send_flags(Flags) ->
+ EFlags = [{confirm, ?SOCKET_SEND_FLAG_CONFIRM},
+ {dontroute, ?SOCKET_SEND_FLAG_DONTROUTE},
+ {eor, ?SOCKET_SEND_FLAG_EOR},
+ {more, ?SOCKET_SEND_FLAG_MORE},
+ {nosignal, ?SOCKET_SEND_FLAG_NOSIGNAL},
+ {oob, ?SOCKET_SEND_FLAG_OOB}],
+ enc_flags(Flags, EFlags).
+
+-spec enc_recv_flags(Flags) -> non_neg_integer() when
+ Flags :: recv_flags().
+
+enc_recv_flags(Flags) ->
+ EFlags = [{cmsg_cloexec, ?SOCKET_RECV_FLAG_CMSG_CLOEXEC},
+ {errqueue, ?SOCKET_RECV_FLAG_ERRQUEUE},
+ {oob, ?SOCKET_RECV_FLAG_OOB},
+ {peek, ?SOCKET_RECV_FLAG_PEEK},
+ {trunc, ?SOCKET_RECV_FLAG_TRUNC}],
+ enc_flags(Flags, EFlags).
+
+
+enc_flags([], _) ->
+ 0;
+enc_flags(Flags, EFlags) ->
+ F = fun(Flag, Acc) ->
+ case lists:keysearch(Flag, 1, EFlags) of
+ {value, {Flag, EFlag}} ->
+ Acc bor (1 bsl EFlag);
+ false ->
+ throw({error, {unknown_flag, Flag}})
+ end
+ end,
+ lists:foldl(F, 0, Flags).
+
+
+%% +++ Encode setopt level +++
+
+-spec enc_setopt_level(Level) -> {IsEncoded, EncodedLevel} when
+ Level :: sockopt_level(),
+ IsEncoded :: boolean(),
+ EncodedLevel :: integer().
+
+enc_setopt_level(otp) ->
+ {true, ?SOCKET_OPT_LEVEL_OTP};
+enc_setopt_level(socket) ->
+ {true, ?SOCKET_OPT_LEVEL_SOCKET};
+enc_setopt_level(ip) ->
+ {true, ?SOCKET_OPT_LEVEL_IP};
+enc_setopt_level(ipv6) ->
+ {true, ?SOCKET_OPT_LEVEL_IPV6};
+enc_setopt_level(tcp) ->
+ {true, ?SOCKET_OPT_LEVEL_TCP};
+enc_setopt_level(udp) ->
+ {true, ?SOCKET_OPT_LEVEL_UDP};
+enc_setopt_level(sctp) ->
+ {true, ?SOCKET_OPT_LEVEL_SCTP};
+%% Any option that is of an plain level must be provided as a binary
+%% already fully encoded!
+enc_setopt_level(L) when is_integer(L) ->
+ {false, L}.
+
+
+%% +++ Encode setopt key +++
+
+%% We should ...really... do something with the domain, type and protocol args...
+%% Also, any option (key) which has an integer level (plain) must also be provided
+%% in a plain mode, that is, as an integer.
+%% Also, not all options are available on all platforms. That is something we
+%% don't check here, but in the nif-code.
+
+enc_setopt_key(Level, Opt, Domain, Type, Protocol) ->
+ enc_sockopt_key(Level, Opt, set, Domain, Type, Protocol).
+
+
+%% +++ Encode setopt value +++
+%%
+%% For the most part this function does *not* do an actual encode,
+%% it simply validates the value type. But in some cases it will
+%% encode the value into an more "manageable" type.
+%% It also handles "aliases" (see linger).
+
+-dialyzer({nowarn_function, enc_setopt_value/6}).
+-spec enc_setopt_value(otp, otp_socket_option(),
+ Value, Domain, Type, Protocol) -> term() when
+ Value :: term(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (socket, socket_option(),
+ Value, Domain, Type, Protocol) -> term() when
+ Value :: term(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (ip, ip_socket_option(),
+ Value, Domain, Type, Protocol) -> term() when
+ Value :: term(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (ipv6, ipv6_socket_option(),
+ Value, Domain, Type, Protocol) -> term() when
+ Value :: term(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (tcp, tcp_socket_option(),
+ Value, Domain, Type, Protocol) -> term() when
+ Value :: term(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (udp, udp_socket_option(),
+ Value, Domain, Type, Protocol) -> term() when
+ Value :: term(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (sctp, sctp_socket_option(),
+ Value, Domain, Type, Protocol) -> term() when
+ Value :: term(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Opt,
+ Value, Domain, Type, Protocol) -> term() when
+ Level :: integer(),
+ Opt :: integer(),
+ Value :: binary(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol().
+
+enc_setopt_value(otp, debug, V, _, _, _) when is_boolean(V) ->
+ V;
+enc_setopt_value(otp, iow, V, _, _, _) when is_boolean(V) ->
+ V;
+enc_setopt_value(otp, controlling_process, V, _, _, _) when is_pid(V) ->
+ V;
+enc_setopt_value(otp, rcvbuf, V, _, _, _) when (V =:= default) ->
+ 0; % This will cause the nif-code to choose the default value
+enc_setopt_value(otp, rcvbuf, V, _, _, _) when is_integer(V) andalso (V > 0) ->
+ V;
+%% N: Number of reads (when specifying length = 0)
+%% V: Size of the "read" buffer
+enc_setopt_value(otp, rcvbuf, {N, BufSz} = V, _, stream = _T, tcp = _P)
+ when (is_integer(N) andalso (N > 0)) andalso
+ (is_integer(BufSz) andalso (BufSz > 0)) ->
+ V;
+enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when (V =:= default) ->
+ 0;
+enc_setopt_value(otp, rcvctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) ->
+ V;
+enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when (V =:= default) ->
+ 0;
+enc_setopt_value(otp, sndctrlbuf, V, _, _, _) when is_integer(V) andalso (V > 0) ->
+ V;
+enc_setopt_value(otp = L, Opt, V, _D, _T, _P) ->
+ not_supported({L, Opt, V});
+
+enc_setopt_value(socket, bindtodevice, V, _D, _T, _P) when is_list(V) ->
+ V;
+enc_setopt_value(socket, broadcast, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(socket, debug, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(socket, dontroute, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(socket, keepalive, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(socket, linger, abort, D, T, P) ->
+ enc_setopt_value(socket, linger, {true, 0}, D, T, P);
+enc_setopt_value(socket, linger, {OnOff, Secs} = V, _D, _T, _P)
+ when is_boolean(OnOff) andalso is_integer(Secs) andalso (Secs >= 0) ->
+ V;
+enc_setopt_value(socket, oobinline, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(socket, peek_off, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(socket, priority, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(socket, rcvbuf, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(socket, rcvlowat, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(socket, rcvtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P)
+ when is_integer(Sec) andalso is_integer(USec) ->
+ V;
+enc_setopt_value(socket, reuseaddr, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(socket, reuseport, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(socket, sndbuf, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(socket, sndlowat, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(socket, sndtimeo, #{sec := Sec, usec := USec} = V, _D, _T, _P)
+ when is_integer(Sec) andalso is_integer(USec) ->
+ V;
+enc_setopt_value(socket, timestamp, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(socket = L, Opt, V, _D, _T, _P) ->
+ not_supported({L, Opt, V});
+
+enc_setopt_value(ip, add_membership, #{multiaddr := MA,
+ interface := IF} = V, _D, _T, _P)
+ when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
+ ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) ->
+ V;
+enc_setopt_value(ip, add_source_membership, #{multiaddr := MA,
+ interface := IF,
+ sourceaddr := SA} = V, _D, _T, _P)
+ when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
+ (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
+ (is_tuple(SA) andalso (size(SA) =:= 4)) ->
+ V;
+enc_setopt_value(ip, block_source, #{multiaddr := MA,
+ interface := IF,
+ sourceaddr := SA} = V, _D, _T, _P)
+ when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
+ (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
+ (is_tuple(SA) andalso (size(SA) =:= 4)) ->
+ V;
+enc_setopt_value(ip, drop_membership, #{multiaddr := MA,
+ interface := IF} = V, _D, _T, _P)
+ when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
+ ((IF =:= any) orelse (is_tuple(IF) andalso (size(IF) =:= 4))) ->
+ V;
+enc_setopt_value(ip, drop_source_membership, #{multiaddr := MA,
+ interface := IF,
+ sourceaddr := SA} = V, _D, _T, _P)
+ when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
+ (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
+ (is_tuple(SA) andalso (size(SA) =:= 4)) ->
+ V;
+enc_setopt_value(ip, freebind, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, hdrincl, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, minttl, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(ip, msfilter, null = V, _D, _T, _P) ->
+ V;
+enc_setopt_value(ip, msfilter, #{multiaddr := MA,
+ interface := IF,
+ fmode := FMode,
+ slist := SL} = V, _D, _T, _P)
+ when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
+ (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
+ ((FMode =:= include) orelse (FMode =:= exclude)) andalso
+ is_list(SL) ->
+ ensure_ip_msfilter_slist(SL),
+ V;
+enc_setopt_value(ip, mtu_discover, V, _D, _T, _P)
+ when (V =:= want) orelse
+ (V =:= dont) orelse
+ (V =:= do) orelse
+ (V =:= probe) orelse
+ is_integer(V) ->
+ V;
+enc_setopt_value(ip, multicast_all, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, multicast_if, V, _D, _T, _P)
+ when (V =:= any) orelse (is_tuple(V) andalso (size(V) =:= 4)) ->
+ V;
+enc_setopt_value(ip, multicast_loop, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, multicast_ttl, V, _D, _T, _P)
+ when is_integer(V) andalso (0 =< V) andalso (V =< 255) ->
+ V;
+enc_setopt_value(ip, nodefrag, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, pktinfo, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, recvdstaddr, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, recverr, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, recvif, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, recvopts, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, recvorigdstaddr, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, recvtos, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, recvttl, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, retopts, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, router_alert, V, _D, _T, _P)
+ when is_integer(V) ->
+ V;
+enc_setopt_value(ip, sendsrcaddr, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, tos, V, _D, _T, _P)
+ when (V =:= lowdelay) orelse
+ (V =:= throughput) orelse
+ (V =:= reliability) orelse
+ (V =:= mincost) orelse
+ is_integer(V) ->
+ V;
+enc_setopt_value(ip, transparent, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ip, ttl, V, _D, _T, _P)
+ when is_integer(V) ->
+ V;
+enc_setopt_value(ip, unblock_source, #{multiaddr := MA,
+ interface := IF,
+ sourceaddr := SA} = V, _D, _T, _P)
+ when (is_tuple(MA) andalso (size(MA) =:= 4)) andalso
+ (is_tuple(IF) andalso (size(IF) =:= 4)) andalso
+ (is_tuple(SA) andalso (size(SA) =:= 4)) ->
+ V;
+enc_setopt_value(ip = L, Opt, V, _D, _T, _P) ->
+ not_supported({L, Opt, V});
+
+enc_setopt_value(ipv6, addrform, inet = V, _D, _T, _P) ->
+ enc_domain(V);
+enc_setopt_value(ipv6, add_membership, #{multiaddr := MA,
+ interface := IF} = V, _D, _T, _P)
+ when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso
+ (is_integer(IF) andalso (IF >= 0))) ->
+ V;
+%% Is this obsolete? When get, the result is enoprotoopt and in the
+%% header file it says 'obsolete'...
+%% But there might be (old?) versions of linux where it still works...
+enc_setopt_value(ipv6, authhdr, V, _D, T, _P)
+ when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
+ V;
+enc_setopt_value(ipv6, dstopts, V, _D, T, _P)
+ when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
+ V;
+enc_setopt_value(ipv6, drop_membership, #{multiaddr := MA,
+ interface := IF} = V, _D, _T, _P)
+ when ((is_tuple(MA) andalso (size(MA) =:= 8)) andalso
+ (is_integer(IF) andalso (IF >= 0))) ->
+ V;
+enc_setopt_value(ipv6, flowinfo, V, _D, T, _P)
+ when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
+ V;
+enc_setopt_value(ipv6, hoplimit, V, _D, T, _P)
+ when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
+ V;
+enc_setopt_value(ipv6, hopopts, V, _D, T, _P)
+ when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
+ V;
+enc_setopt_value(ipv6, mtu, V, _D, _T, _P) when is_integer(V) ->
+ V;
+enc_setopt_value(ipv6, mtu_discover, V, _D, _T, _P)
+ when (V =:= want) orelse
+ (V =:= dont) orelse
+ (V =:= do) orelse
+ (V =:= probe) orelse
+ is_integer(V) ->
+ V;
+enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P)
+ when (V =:= default) ->
+ -1;
+enc_setopt_value(ipv6, multicast_hops, V, _D, _T, _P)
+ when is_integer(V) andalso (V >= 0) andalso (V =< 255) ->
+ V;
+enc_setopt_value(ipv6, multicast_if, V, _D, _T, _P)
+ when is_integer(V) ->
+ V;
+enc_setopt_value(ipv6, multicast_loop, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ipv6, recverr, V, _D, _T, _P)
+ when is_boolean(V) ->
+ V;
+enc_setopt_value(ipv6, Opt, V, _D, _T, _P)
+ when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso
+ is_boolean(V) ->
+ V;
+enc_setopt_value(ipv6, router_alert, V, _D, T, _P)
+ when is_integer(V) andalso (T =:= raw) ->
+ V;
+enc_setopt_value(ipv6, rthdr, V, _D, T, _P)
+ when is_boolean(V) andalso ((T =:= dgram) orelse (T =:= raw)) ->
+ V;
+enc_setopt_value(ipv6, unicast_hops, V, _D, _T, _P)
+ when (V =:= default) ->
+ -1;
+enc_setopt_value(ipv6, unicast_hops, V, _D, _T, _P)
+ when is_integer(V) andalso (V >= 0) andalso (V =< 255) ->
+ V;
+enc_setopt_value(ipv6, v6only, V, _D, _T, _P) when is_boolean(V) ->
+ V;
+enc_setopt_value(ipv6 = L, Opt, V, _D, _T, _P) ->
+ not_supported({L, Opt, V});
+
+enc_setopt_value(tcp, congestion, V, _D, T, P)
+ when is_list(V) andalso
+ (T =:= stream) andalso
+ (P =:= tcp) ->
+ V;
+enc_setopt_value(tcp, maxseg, V, _D, T, P)
+ when is_integer(V) andalso
+ (T =:= stream) andalso
+ (P =:= tcp) ->
+ V;
+enc_setopt_value(tcp, nodelay, V, _D, T, P)
+ when is_boolean(V) andalso
+ (T =:= stream) andalso
+ (P =:= tcp) ->
+ V;
+enc_setopt_value(tcp = L, Opt, V, _D, _T, _P) ->
+ not_supported({L, Opt, V});
+
+enc_setopt_value(udp, cork, V, _D, T, P)
+ when is_boolean(V) andalso (T =:= dgram) andalso (P =:= udp) ->
+ V;
+enc_setopt_value(udp = L, Opt, _V, _D, _T, _P) ->
+ not_supported({L, Opt});
+
+enc_setopt_value(sctp, associnfo, #{assoc_id := AssocId,
+ asocmaxrxt := MaxRxt,
+ num_peer_dests := NumPeerDests,
+ peer_rwnd := PeerRWND,
+ local_rwnd := LocalRWND,
+ cookie_life := CLife} = V,
+ _D, _T, P)
+ when is_integer(AssocId) andalso
+ is_integer(MaxRxt) andalso (MaxRxt >= 0) andalso
+ is_integer(NumPeerDests) andalso (NumPeerDests >= 0) andalso
+ is_integer(PeerRWND) andalso (PeerRWND >= 0) andalso
+ is_integer(LocalRWND) andalso (LocalRWND >= 0) andalso
+ is_integer(CLife) andalso (CLife >= 0) andalso
+ (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp, autoclose, V, _D, _T, P)
+ when is_integer(V) andalso (V >= 0) andalso (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp, disable_fragments, V, _D, _T, P)
+ when is_boolean(V) andalso (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp, events, #{data_in := DataIn,
+ association := Assoc,
+ address := Addr,
+ send_failure := SndFailure,
+ peer_error := PeerError,
+ shutdown := Shutdown,
+ partial_delivery := PartialDelivery,
+ adaptation_layer := AdaptLayer,
+ authentication := Auth,
+ sender_dry := SndDry} = V, _D, _T, P)
+ when is_boolean(DataIn) andalso
+ is_boolean(Assoc) andalso
+ is_boolean(Addr) andalso
+ is_boolean(SndFailure) andalso
+ is_boolean(PeerError) andalso
+ is_boolean(Shutdown) andalso
+ is_boolean(PartialDelivery) andalso
+ is_boolean(AdaptLayer) andalso
+ is_boolean(Auth) andalso
+ is_boolean(SndDry) andalso
+ (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp, initmsg, #{num_outstreams := NumOut,
+ max_instreams := MaxIn,
+ max_attempts := MaxAttempts,
+ max_init_timeo := MaxInitTO} = V,
+ _D, _T, P)
+ when is_integer(NumOut) andalso (NumOut >= 0) andalso
+ is_integer(MaxIn) andalso (MaxIn >= 0) andalso
+ is_integer(MaxAttempts) andalso (MaxAttempts >= 0) andalso
+ is_integer(MaxInitTO) andalso (MaxInitTO >= 0) andalso
+ (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp, maxseg, V, _D, _T, P)
+ when is_integer(V) andalso (V >= 0) andalso (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp, nodelay, V, _D, _T, P)
+ when is_boolean(V) andalso (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp, rtoinfo, #{assoc_id := AssocId,
+ initial := Init,
+ max := Max,
+ min := Min} = V,
+ _D, _T, P)
+ when is_integer(AssocId) andalso
+ is_integer(Init) andalso (Init >= 0) andalso
+ is_integer(Max) andalso (Max >= 0) andalso
+ is_integer(Min) andalso (Min >= 0) andalso
+ (P =:= sctp) ->
+ V;
+enc_setopt_value(sctp = L, Opt, V, _D, _T, _P) ->
+ not_supported({L, Opt, V});
+
+%% enc_setopt_value(raw = L, Opt, _V, _D, _T, _P) ->
+%% not_supported({L, Opt});
+
+%% Is this correct? What about getopt?
+enc_setopt_value(L, Opt, V, _, _, _)
+ when is_integer(L) andalso is_integer(Opt) andalso is_binary(V) ->
+ V.
+
+
+
+
+%% +++ Encode getopt value +++
+
+enc_getopt_level(Level) ->
+ enc_setopt_level(Level).
+
+
+%% +++ Encode getopt key +++
+
+enc_getopt_key(Level, Opt, Domain, Type, Protocol) ->
+ enc_sockopt_key(Level, Opt, get, Domain, Type, Protocol).
+
+
+%% +++ Decode getopt value +++
+%%
+%% For the most part, we simply let the value pass through, but for some
+%% values we may need to do an actual decode.
+%%
+
+%% Let the user deal with this for now...
+dec_getopt_value(_L, _Opt, V, _D, _T, _P) ->
+ V.
+
+
+
+%% +++ Encode socket option key +++
+
+%% Most options are usable both for set and get, but some are
+%% are only available for e.g. get.
+-spec enc_sockopt_key(Level, Opt,
+ Direction,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: otp,
+ Direction :: set | get,
+ Opt :: otp_socket_option(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: socket,
+ Direction :: set | get,
+ Opt :: socket_option(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: ip,
+ Direction :: set | get,
+ Opt :: ip_socket_option(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: ipv6,
+ Direction :: set | get,
+ Opt :: ipv6_socket_option(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: tcp,
+ Direction :: set | get,
+ Opt :: tcp_socket_option(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: udp,
+ Direction :: set | get,
+ Opt :: udp_socket_option(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: sctp,
+ Direction :: set | get,
+ Opt :: sctp_socket_option(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: integer(),
+ Direction :: set,
+ Opt :: integer(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol()
+ ; (Level, Direction, Opt,
+ Domain, Type, Protocol) -> non_neg_integer() when
+ Level :: integer(),
+ Direction :: get,
+ Opt :: {NativeOpt, ValueSize},
+ NativeOpt :: integer(),
+ ValueSize :: non_neg_integer(),
+ Domain :: domain(),
+ Type :: type(),
+ Protocol :: protocol().
+
+
+%% +++ OTP socket options +++
+enc_sockopt_key(otp, debug, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_DEBUG;
+enc_sockopt_key(otp, iow, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_IOW;
+enc_sockopt_key(otp, controlling_process, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_CTRL_PROC;
+enc_sockopt_key(otp, rcvbuf, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_RCVBUF;
+enc_sockopt_key(otp, rcvctrlbuf, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_RCVCTRLBUF;
+enc_sockopt_key(otp, sndctrlbuf, _, _, _, _) ->
+ ?SOCKET_OPT_OTP_SNDCTRLBUF;
+enc_sockopt_key(otp, fd, get = _Dir, _, _, _) ->
+ ?SOCKET_OPT_OTP_FD;
+enc_sockopt_key(otp = L, Opt, _, _, _, _) ->
+ not_supported({L, Opt});
+
+%% +++ SOCKET socket options +++
+enc_sockopt_key(socket = _L, acceptconn = _Opt, get = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_ACCEPTCONN;
+enc_sockopt_key(socket = L, acceptfilter = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+%% Before linux 3.8, this socket option could be set.
+%% Maximum size of buffer for name: IFNAMSZIZ
+%% So, we let the implementation decide.
+enc_sockopt_key(socket = _L, bindtodevice = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_BINDTODEVICE;
+enc_sockopt_key(socket, broadcast = _Opt, _Dir, _D, dgram = _T, _P) ->
+ ?SOCKET_OPT_SOCK_BROADCAST;
+enc_sockopt_key(socket = L, busy_poll = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(socket = _L, debug = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_DEBUG;
+enc_sockopt_key(socket, domain = _Opt, get = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_DOMAIN;
+enc_sockopt_key(socket, dontroute = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_DONTROUTE;
+enc_sockopt_key(socket = L, error = Opt, get = _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+%% This is only for connection-oriented sockets, but who are those?
+%% Type = stream or Protocol = tcp?
+%% For now, we just let is pass and it will fail later if not ok...
+enc_sockopt_key(socket, keepalive = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_KEEPALIVE;
+enc_sockopt_key(socket, linger = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_LINGER;
+enc_sockopt_key(socket = L, mark = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(socket = _L, oobinline = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_OOBINLINE;
+enc_sockopt_key(socket = L, passcred = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(socket = _L, peek_off = _Opt, _Dir, local = _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_PEEK_OFF;
+enc_sockopt_key(socket = L, peekcred = Opt, get = _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(socket, priority = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_PRIORITY;
+enc_sockopt_key(socket, protocol = _Opt, get = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_PROTOCOL;
+enc_sockopt_key(socket, rcvbuf = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_RCVBUF;
+enc_sockopt_key(socket = L, rcvbufforce = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+%% May not work on linux.
+enc_sockopt_key(socket = _L, rcvlowat = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_RCVLOWAT;
+enc_sockopt_key(socket = _L, rcvtimeo = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_RCVTIMEO;
+enc_sockopt_key(socket = _L, reuseaddr = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_REUSEADDR;
+enc_sockopt_key(socket = _L, reuseport = _Opt, _Dir, D, _T, _P)
+ when ((D =:= inet) orelse (D =:= inet6)) ->
+ ?SOCKET_OPT_SOCK_REUSEPORT;
+enc_sockopt_key(socket = L, rxq_ovfl = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(socket = L, setfib = Opt, set = _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(socket = _L, sndbuf = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_SNDBUF;
+enc_sockopt_key(socket = L, sndbufforce = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+%% Not changeable on linux.
+enc_sockopt_key(socket = _L, sndlowat = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_SNDLOWAT;
+enc_sockopt_key(socket = _L, sndtimeo = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_SNDTIMEO;
+enc_sockopt_key(socket = _L, timestamp = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_TIMESTAMP;
+enc_sockopt_key(socket = _L, type = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SOCK_TYPE;
+enc_sockopt_key(socket = L, UnknownOpt, _Dir, _D, _T, _P) ->
+ unknown({L, UnknownOpt});
+
+%% +++ IP socket options +++
+enc_sockopt_key(ip = _L, add_membership = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_ADD_MEMBERSHIP;
+enc_sockopt_key(ip = _L, add_source_membership = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_ADD_SOURCE_MEMBERSHIP;
+enc_sockopt_key(ip = _L, block_source = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_BLOCK_SOURCE;
+%% FreeBSD only?
+%% Only respected on udp and raw ip (unless the hdrincl option has been set).
+enc_sockopt_key(ip = L, dontfrag = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ip = _L, drop_membership = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_DROP_MEMBERSHIP;
+enc_sockopt_key(ip = _L, drop_source_membership = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_DROP_SOURCE_MEMBERSHIP;
+%% Linux only?
+enc_sockopt_key(ip = _L, freebind = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_FREEBIND;
+enc_sockopt_key(ip = _L, hdrincl = _Opt, _Dir, _D, raw = _T, _P) ->
+ ?SOCKET_OPT_IP_HDRINCL;
+enc_sockopt_key(ip = _L, minttl = _Opt, _Dir, _D, raw = _T, _P) ->
+ ?SOCKET_OPT_IP_MINTTL;
+enc_sockopt_key(ip = _L, msfilter = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_MSFILTER;
+enc_sockopt_key(ip = _L, mtu = _Opt, get = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_MTU;
+enc_sockopt_key(ip = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_MTU_DISCOVER;
+enc_sockopt_key(ip = _L, multicast_all = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_MULTICAST_ALL;
+enc_sockopt_key(ip = _L, multicast_if = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_MULTICAST_IF;
+enc_sockopt_key(ip = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_MULTICAST_LOOP;
+enc_sockopt_key(ip = _L, multicast_ttl = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_MULTICAST_TTL;
+enc_sockopt_key(ip = _L, nodefrag = _Opt, _Dir, _D, raw = _T, _P) ->
+ ?SOCKET_OPT_IP_NODEFRAG;
+enc_sockopt_key(ip = L, options = Opt, _Dir, _D, _T, _P) ->
+ not_supported({Opt, L});
+enc_sockopt_key(ip = _L, pktinfo = _Opt, _Dir, _D, dgram = _T, _P) ->
+ ?SOCKET_OPT_IP_PKTINFO;
+enc_sockopt_key(ip = _L, recvdstaddr = _Opt, _Dir, _D, T, _P) when (T =:= dgram) ->
+ ?SOCKET_OPT_IP_RECVDSTADDR;
+enc_sockopt_key(ip = _L, recverr = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_RECVERR;
+enc_sockopt_key(ip = _L, recvif = _Opt, _Dir, _D, T, _P)
+ when (T =:= dgram) orelse (T =:= raw) ->
+ ?SOCKET_OPT_IP_RECVIF;
+enc_sockopt_key(ip = _L, recvopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
+ ?SOCKET_OPT_IP_RECVOPTS;
+enc_sockopt_key(ip = _L, recvorigdstaddr = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_RECVORIGDSTADDR;
+enc_sockopt_key(ip, recvtos = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_RECVTOS;
+enc_sockopt_key(ip = _L, recvttl = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
+ ?SOCKET_OPT_IP_RECVTTL;
+enc_sockopt_key(ip = _L, retopts = _Opt, _Dir, _D, T, _P) when (T =/= stream) ->
+ ?SOCKET_OPT_IP_RETOPTS;
+enc_sockopt_key(ip, router_alert = _Opt, _Dir, _D, raw = _T, _P) ->
+ ?SOCKET_OPT_IP_ROUTER_ALERT;
+enc_sockopt_key(ip, sendsrcaddr = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_SENDSRCADDR;
+%% On FreeBSD it specifies that this option is only valid
+%% for stream, dgram and "some" raw sockets...
+%% No such condition on linux (in the man page)...
+enc_sockopt_key(ip, tos = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_TOS;
+enc_sockopt_key(ip = _L, transparent = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_TRANSPARENT;
+enc_sockopt_key(ip, ttl = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_TTL;
+enc_sockopt_key(ip = _L, unblock_source = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IP_UNBLOCK_SOURCE;
+enc_sockopt_key(ip = L, UnknownOpt, _Dir, _D, _T, _P) ->
+ unknown({L, UnknownOpt});
+
+%% IPv6 socket options
+enc_sockopt_key(ipv6 = _L, addrform = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_ADDRFORM;
+enc_sockopt_key(ipv6, add_membership = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_ADD_MEMBERSHIP;
+enc_sockopt_key(ipv6 = _L, authhdr = _Opt, _Dir, _D, T, _P)
+ when ((T =:= dgram) orelse (T =:= raw)) ->
+ ?SOCKET_OPT_IPV6_AUTHHDR;
+enc_sockopt_key(ipv6 = L, auth_level = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = L, checksum = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6, drop_membership = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_DROP_MEMBERSHIP;
+enc_sockopt_key(ipv6 = _L, dstopts = _Opt, _Dir, _D, T, _P)
+ when (T =:= dgram) orelse (T =:= raw) ->
+ ?SOCKET_OPT_IPV6_DSTOPTS;
+enc_sockopt_key(ipv6 = L, esp_trans_level = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = L, esp_network_level = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = _L, flowinfo = _Opt, _Dir, _D, T, _P)
+ when (T =:= dgram) orelse (T =:= raw) ->
+ ?SOCKET_OPT_IPV6_DSTOPTS;
+enc_sockopt_key(ipv6, hoplimit = _Opt, _Dir, _D, T, _P)
+ when (T =:= dgram) orelse (T =:= raw) ->
+ ?SOCKET_OPT_IPV6_HOPLIMIT;
+enc_sockopt_key(ipv6 = _L, hopopts = _Opt, _Dir, _D, T, _P)
+ when ((T =:= dgram) orelse (T =:= raw)) ->
+ ?SOCKET_OPT_IPV6_HOPOPTS;
+enc_sockopt_key(ipv6 = L, ipcomp_level = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = L, join_group = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = L, leave_group = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = _L, mtu = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_MTU;
+enc_sockopt_key(ipv6 = _L, mtu_discover = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_MTU_DISCOVER;
+enc_sockopt_key(ipv6 = _L, multicast_hops = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_MULTICAST_HOPS;
+enc_sockopt_key(ipv6 = _L, multicast_if = _Opt, _Dir, _D, T, _P)
+ when (T =:= dgram) orelse (T =:= raw) ->
+ ?SOCKET_OPT_IPV6_MULTICAST_IF;
+enc_sockopt_key(ipv6 = _L, multicast_loop = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_MULTICAST_LOOP;
+enc_sockopt_key(ipv6 = L, portrange = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = L, pktoptions = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = _L, recverr = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_RECVERR;
+enc_sockopt_key(ipv6 = _L, Opt, _Dir, _D, T, _P)
+ when ((Opt =:= recvpktinfo) orelse (Opt =:= pktinfo)) andalso
+ ((T =:= dgram) orelse (T =:= raw)) ->
+ ?SOCKET_OPT_IPV6_RECVPKTINFO;
+enc_sockopt_key(ipv6 = L, recvtclass = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = _L, router_alert = _Opt, _Dir, _D, T, _P) when (T =:= raw) ->
+ ?SOCKET_OPT_IPV6_ROUTER_ALERT;
+enc_sockopt_key(ipv6 = _L, rthdr = _Opt, _Dir, _D, T, _P)
+ when ((T =:= dgram) orelse (T =:= raw)) ->
+ ?SOCKET_OPT_IPV6_RTHDR;
+enc_sockopt_key(ipv6 = L, tclass = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = _L, unicast_hops = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_UNICAST_HOPS;
+enc_sockopt_key(ipv6 = L, use_min_mtu = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(ipv6 = _L, v6only = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_IPV6_V6ONLY;
+enc_sockopt_key(ipv6 = L, UnknownOpt, _Dir, _D, _T, _P) ->
+ unknown({L, UnknownOpt});
+
+%% TCP socket options
+%% There are other options that would be useful; info,
+%% but they are difficult to get portable...
+enc_sockopt_key(tcp, congestion = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_TCP_CONGESTION;
+enc_sockopt_key(tcp = L, cork = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp = L, keepidle = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp = L, keepintvl = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp = L, keepcnt = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp, maxseg = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_TCP_MAXSEG;
+enc_sockopt_key(tcp = L, md5sig = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp, nodelay = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_TCP_NODELAY;
+enc_sockopt_key(tcp = L, noopt = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp = L, nopush = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp = L, syncnt = Opt, _Dir, _D, _T, _P) -> % Only set? 1..255
+ not_supported({L, Opt});
+enc_sockopt_key(tcp = L, user_timeout = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(tcp = L, UnknownOpt, _Dir, _D, _T, _P) ->
+ unknown({L, UnknownOpt});
+
+%% UDP socket options
+enc_sockopt_key(udp, cork = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_UDP_CORK;
+enc_sockopt_key(udp = L, UnknownOpt, _Dir, _D, _T, _P) ->
+ unknown({L, UnknownOpt});
+
+%% SCTP socket options
+enc_sockopt_key(sctp = L, adaption_layer = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = _L, associnfo = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_ASSOCINFO;
+enc_sockopt_key(sctp = L, auth_active_key = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, auth_asconf = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, auth_chunk = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, auth_key = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, auth_delete_key = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp, autoclose = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_AUTOCLOSE;
+enc_sockopt_key(sctp = L, context = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, default_send_params = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, delayed_ack_time = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = _L, disable_fragments = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_DISABLE_FRAGMENTS;
+enc_sockopt_key(sctp = L, hmac_ident = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = _L, events = _Opt, set = _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_EVENTS;
+enc_sockopt_key(sctp = L, explicit_eor = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, fragment_interleave = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, get_peer_addr_info = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = _L, initmsg = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_INITMSG;
+enc_sockopt_key(sctp = L, i_want_mapped_v4_addr = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, local_auth_chunks = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = _L, maxseg = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_MAXSEG;
+enc_sockopt_key(sctp = L, maxburst = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp, nodelay = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_NODELAY;
+enc_sockopt_key(sctp = L, partial_delivery_point = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, peer_addr_params = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, peer_auth_chunks = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, primary_addr = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, reset_streams = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = _L, rtoinfo = _Opt, _Dir, _D, _T, _P) ->
+ ?SOCKET_OPT_SCTP_RTOINFO;
+enc_sockopt_key(sctp = L, set_peer_primary_addr = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, status = Opt, get = _Dir, _D, _T, _P) ->
+ not_supported({L, Opt}); % ?SOCKET_OPT_SCTP_RTOINFO;
+enc_sockopt_key(sctp = L, use_exp_recvinfo = Opt, _Dir, _D, _T, _P) ->
+ not_supported({L, Opt});
+enc_sockopt_key(sctp = L, UnknownOpt, _Dir, _D, _T, _P) ->
+ unknown({L, UnknownOpt});
+
+%% +++ "Native" socket options +++
+enc_sockopt_key(Level, Opt, set = _Dir, _D, _T, _P)
+ when is_integer(Level) andalso is_integer(Opt) ->
+ Opt;
+enc_sockopt_key(Level, {NativeOpt, ValueSize} = Opt, get = _Dir, _D, _T, _P)
+ when is_integer(Level) andalso
+ is_integer(NativeOpt) andalso
+ ((is_integer(ValueSize) andalso (ValueSize >= 0)) orelse
+ ((ValueSize =:= int) orelse (ValueSize =:= bool))) ->
+ Opt;
+
+enc_sockopt_key(Level, Opt, _Dir, _Domain, _Type, _Protocol) ->
+ unknown({Level, Opt}).
+
+
+
+enc_shutdown_how(read) ->
+ ?SOCKET_SHUTDOWN_HOW_READ;
+enc_shutdown_how(write) ->
+ ?SOCKET_SHUTDOWN_HOW_WRITE;
+enc_shutdown_how(read_write) ->
+ ?SOCKET_SHUTDOWN_HOW_READ_WRITE.
+
+
+
+
+%% ===========================================================================
+%%
+%% Misc utility functions
+%%
+%% ===========================================================================
+
+-dialyzer({nowarn_function, ensure_ip_msfilter_slist/1}).
+ensure_ip_msfilter_slist(SL) ->
+ EnsureSA = fun(SA) when is_tuple(SA) andalso (size(SA) =:= 4) -> ok;
+ (_) -> einval()
+ end,
+ lists:foreach(EnsureSA, SL).
+
+
+ensure_sockaddr(#{family := inet} = SockAddr) ->
+ maps:merge(?SOCKADDR_IN4_DEFAULTS, SockAddr);
+ensure_sockaddr(#{family := inet6} = SockAddr) ->
+ maps:merge(?SOCKADDR_IN6_DEFAULTS, SockAddr);
+ensure_sockaddr(#{family := local, path := Path} = SockAddr)
+ when is_list(Path) andalso
+ (length(Path) > 0) andalso
+ (length(Path) =< 255) ->
+ BinPath = unicode:characters_to_binary(Path, file:native_name_encoding()),
+ ensure_sockaddr(SockAddr#{path => BinPath});
+ensure_sockaddr(#{family := local, path := Path} = SockAddr)
+ when is_binary(Path) andalso
+ (byte_size(Path) > 0) andalso
+ (byte_size(Path) =< 255) ->
+ SockAddr;
+ensure_sockaddr(_SockAddr) ->
+ einval().
+
+
+
+cancel(SockRef, Op, OpRef) ->
+ case nif_cancel(SockRef, Op, OpRef) of
+ %% The select has already completed
+ {error, select_sent} ->
+ flush_select_msgs(SockRef, OpRef);
+ Other ->
+ Other
+ end.
+
+flush_select_msgs(SockRef, Ref) ->
+ receive
+ {?SOCKET_TAG, #socket{ref = SockRef}, select, Ref} ->
+ flush_select_msgs(SockRef, Ref)
+ after 0 ->
+ ok
+ end.
+
+
+%% formated_timestamp() ->
+%% format_timestamp(os:timestamp()).
+
+%% format_timestamp(Now) ->
+%% N2T = fun(N) -> calendar:now_to_local_time(N) end,
+%% format_timestamp(Now, N2T, true).
+
+%% format_timestamp({_N1, _N2, N3} = N, N2T, true) ->
+%% FormatExtra = ".~.2.0w",
+%% ArgsExtra = [N3 div 10000],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra);
+%% format_timestamp({_N1, _N2, _N3} = N, N2T, false) ->
+%% FormatExtra = "",
+%% ArgsExtra = [],
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra).
+
+%% format_timestamp(N, N2T, FormatExtra, ArgsExtra) ->
+%% {Date, Time} = N2T(N),
+%% {YYYY,MM,DD} = Date,
+%% {Hour,Min,Sec} = Time,
+%% FormatDate =
+%% io_lib:format("~.4w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w" ++ FormatExtra,
+%% [YYYY, MM, DD, Hour, Min, Sec] ++ ArgsExtra),
+%% lists:flatten(FormatDate).
+
+
+%% A timestamp in ms
+
+timestamp(infinity) ->
+ undefined;
+timestamp(_) ->
+ timestamp().
+
+timestamp() ->
+ {A,B,C} = os:timestamp(),
+ A*1000000000+B*1000+(C div 1000).
+
+next_timeout(_, infinity = Timeout) ->
+ Timeout;
+next_timeout(TS, Timeout) ->
+ NewTimeout = Timeout - tdiff(TS, timestamp()),
+ if
+ (NewTimeout > 0) ->
+ NewTimeout;
+ true ->
+ 0
+ end.
+
+tdiff(T1, T2) ->
+ T2 - T1.
+
+
+
+%% p(F) ->
+%% p(F, []).
+
+%% p(F, A) ->
+%% p(get(sname), F, A).
+
+%% p(undefined, F, A) ->
+%% p("***", F, A);
+%% p(SName, F, A) ->
+%% TS = formated_timestamp(),
+%% io:format(user,"[~s][~s,~p] " ++ F ++ "~n", [TS, SName, self()|A]),
+%% io:format("[~s][~s,~p] " ++ F ++ "~n", [TS, SName, self()|A]).
+
+
+
+%% ===========================================================================
+%%
+%% Error functions
+%%
+%% ===========================================================================
+
+-spec not_supported(What) -> no_return() when
+ What :: term().
+
+not_supported(What) ->
+ error({not_supported, What}).
+
+-spec unknown(What) -> no_return() when
+ What :: term().
+
+unknown(What) ->
+ error({unknown, What}).
+
+-spec einval() -> no_return().
+
+einval() ->
+ error(einval).
+
+-spec error(Reason) -> no_return() when
+ Reason :: term().
+
+error(Reason) ->
+ throw({error, Reason}).
+
+
+%% ===========================================================================
+%%
+%% Below follows the actual NIF-functions.
+%%
+%% ===========================================================================
+
+nif_info() ->
+ erlang:nif_error(undef).
+
+nif_supports(_Key) ->
+ erlang:nif_error(undef).
+
+nif_open(_Domain, _Type, _Protocol, _Extra) ->
+ erlang:nif_error(undef).
+
+nif_bind(_SRef, _SockAddr) ->
+ erlang:nif_error(undef).
+
+nif_bind(_SRef, _SockAddrs, _Action) ->
+ erlang:nif_error(undef).
+
+nif_connect(_SRef, _SockAddr) ->
+ erlang:nif_error(undef).
+
+nif_finalize_connection(_SRef) ->
+ erlang:nif_error(undef).
+
+nif_listen(_SRef, _Backlog) ->
+ erlang:nif_error(undef).
+
+nif_accept(_SRef, _Ref) ->
+ erlang:nif_error(undef).
+
+nif_send(_SockRef, _SendRef, _Data, _Flags) ->
+ erlang:nif_error(undef).
+
+nif_sendto(_SRef, _SendRef, _Data, _Dest, _Flags) ->
+ erlang:nif_error(undef).
+
+nif_sendmsg(_SRef, _SendRef, _MsgHdr, _Flags) ->
+ erlang:nif_error(undef).
+
+nif_recv(_SRef, _RecvRef, _Length, _Flags) ->
+ erlang:nif_error(undef).
+
+nif_recvfrom(_SRef, _RecvRef, _Length, _Flags) ->
+ erlang:nif_error(undef).
+
+nif_recvmsg(_SRef, _RecvRef, _BufSz, _CtrlSz, _Flags) ->
+ erlang:nif_error(undef).
+
+nif_cancel(_SRef, _Op, _Ref) ->
+ erlang:nif_error(undef).
+
+nif_close(_SRef) ->
+ erlang:nif_error(undef).
+
+nif_shutdown(_SRef, _How) ->
+ erlang:nif_error(undef).
+
+nif_finalize_close(_SRef) ->
+ erlang:nif_error(undef).
+
+nif_setopt(_Ref, _IsEnc, _Lev, _Key, _Val) ->
+ erlang:nif_error(undef).
+
+nif_getopt(_Ref, _IsEnc, _Lev, _Key) ->
+ erlang:nif_error(undef).
+
+nif_sockname(_Ref) ->
+ erlang:nif_error(undef).
+
+nif_peername(_Ref) ->
+ erlang:nif_error(undef).
+