diff options
Diffstat (limited to 'lib/kernel')
27 files changed, 888 insertions, 66 deletions
diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index f8867ccf25..70623ab9aa 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2018. All Rights Reserved. +# Copyright Ericsson AB 1997-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. @@ -36,6 +36,18 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml + +# The doc build has problems with if-defing out modules... +ifeq ($(USE_ESOCK),yes) +XML_REF3_ESOCK_FILES = net.xml +ESOCK_USE_NET_XML=<xi:include href="net.xml"\/> +ESOCK_USE_NET_SPECS_XML=<xi:include href="../specs/specs_net.xml"/> +else +XML_REF3_ESOCK_FILES = +ESOCK_USE_NET_SPECS_XML = +ESOCK_USE_NET_XML = +endif + XML_REF3_FILES = application.xml \ auth.xml \ code.xml \ @@ -62,6 +74,7 @@ XML_REF3_FILES = application.xml \ logger_disk_log_h.xml \ logger_filters.xml \ logger_formatter.xml \ + $(XML_REF3_ESOCK_FILES) \ net_adm.xml \ net_kernel.xml \ os.xml \ @@ -112,6 +125,7 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FIGURES # ---------------------------------------------------- @@ -138,7 +152,7 @@ SPECS_FLAGS = -I../../include $(HTMLDIR)/%: % $(INSTALL_DATA) $< $@ -docs: man pdf html +docs: ref_man specs man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -148,19 +162,32 @@ html: images $(HTML_REF_MAN_FILE) man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES) +ref_man: ref_man.xml +specs: specs.xml + images: $(IMAGE_FILES:%=$(HTMLDIR)/%) +info: + @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)" + @echo "XML_REF3_ESOCK_FILES: $(XML_REF3_ESOCK_FILES)" + @echo "XML_REF3_FILES: $(XML_REF3_FILES)" + @echo "XML_REF4_FILES: $(XML_REF4_FILES)" + @echo "XML_REF6_FILES: $(XML_REF6_FILES)" + @echo "XML_PART_FILES: $(XML_PART_FILES)" + @echo "XML_CHAPTER_FILES: $(XML_CHAPTER_FILES)" + @echo "BOOK_FILES: $(BOOK_FILES)" + debug opt: clean clean_docs: rm -rf $(HTMLDIR)/* rm -rf $(XMLDIR) - rm -f $(MAN3DIR)/* - rm -f $(MAN4DIR)/* - rm -f $(MAN6DIR)/* - rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) - rm -f $(SPECDIR)/* - rm -f errs core *~ *.eps + rm -f $(MAN3DIR)/* + rm -f $(MAN4DIR)/* + rm -f $(MAN6DIR)/* + rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* + rm -f errs core *~ *.eps $(SPECDIR)/specs_erl_prim_loader_stub.xml: $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ @@ -175,6 +202,14 @@ $(SPECDIR)/specs_zlib_stub.xml: $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ -o$(dir $@) -module zlib_stub +ref_man.xml: ref_man.xml.src + ($(PERL) -p -e 's?%ESOCK_USE_NET_XML%?$(ESOCK_USE_NET_XML)?' \ + $<) > $@ +specs.xml: specs.xml.src + ($(PERL) -p -e 's?%ESOCK_USE_NET_SPECS_XML%?$(ESOCK_USE_NET_SPECS_XML)?' \ + $<) > $@ + + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index b3e8149cc2..2c09c84b25 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -957,10 +957,9 @@ f.txt: {person, "kalle", 25}. </item> </taglist> <p><c><anno>IoDevice</anno></c> is really the pid of the process that - handles the file. This process is linked to the process - that originally opened the file. If any process to which - the <c><anno>IoDevice</anno></c> is linked terminates, the file is - closed and the process itself is terminated. + handles the file. This process monitors the process that originally + opened the file (the owner process). If the owner process terminates, + the file is closed and the process itself terminates too. An <c><anno>IoDevice</anno></c> returned from this call can be used as an argument to the I/O functions (see <seealso marker="stdlib:io"><c>io(3)</c></seealso>).</p> @@ -1444,7 +1443,11 @@ f.txt: {person, "kalle", 25}. break this module's atomicity guarantees as it can race with a concurrent call to <seealso marker="#write_file_info/2"><c>write_file_info/1,2</c> - </seealso></p> + </seealso>.</p> + <p>This option has no effect when the function is + given an I/O device instead of a file name. Use + <seealso marker="#open/2"><c>open/2</c></seealso> with the + <c>raw</c> mode to obtain a file descriptor first.</p> <note> <p>As file times are stored in POSIX time on most OS, it is faster to query file information with option <c>posix</c>.</p> diff --git a/lib/kernel/doc/src/logger_disk_log_h.xml b/lib/kernel/doc/src/logger_disk_log_h.xml index aa577f3c62..3d8e82ce7c 100644 --- a/lib/kernel/doc/src/logger_disk_log_h.xml +++ b/lib/kernel/doc/src/logger_disk_log_h.xml @@ -133,7 +133,7 @@ logger:add_handler(my_disk_log_h, logger_disk_log_h, #{config => #{file => "./my_disk_log", type => wrap, max_no_files => 4, - max_no_bytes => 10000}, + max_no_bytes => 10000, filesync_repeat_interval => 1000}}). </code> <p>To use the disk_log handler instead of the default standard diff --git a/lib/kernel/doc/src/net.xml b/lib/kernel/doc/src/net.xml new file mode 100644 index 0000000000..6fbc37076c --- /dev/null +++ b/lib/kernel/doc/src/net.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2018</year><year>2018</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>net</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>net.xml</file> + </header> + <module since="OTP 22.0">net</module> + <modulesummary>Network interface.</modulesummary> + <description> + <p>This module provides an API for the network interface.</p> + <note> + <p>There is currently <em>no</em> support for Windows. </p> + </note> + </description> + + <datatypes> + <datatype> + <name name="address_info"/> + </datatype> + <datatype> + <name name="name_info"/> + </datatype> + <datatype> + <name name="name_info_flags"/> + </datatype> + <datatype> + <name name="name_info_flag"/> + </datatype> + <datatype> + <name name="name_info_flag_ext"/> + </datatype> + <datatype> + <name name="network_interface_name"/> + </datatype> + <datatype> + <name name="network_interface_index"/> + </datatype> + </datatypes> + + <funcs> + <func> + <name name="gethostname" arity="0"/> + <fsummary>Get hostname.</fsummary> + <desc> + <p>Returns the name of the current host.</p> + </desc> + </func> + + <func> + <name name="getnameinfo" arity="1" since="OTP 22.0"/> + <name name="getnameinfo" arity="2" since="OTP 22.0"/> + <fsummary>Address-to-name transaltion.</fsummary> + <desc> + <p>Address-to-name translation in a protocol-independant manner.</p> + <p>This function is the inverse of + <seealso marker="#getaddrinfo/1"><c>getaddrinfo</c></seealso>. + It converts a socket address to a corresponding host and service.</p> + </desc> + </func> + + <func> + <name name="getaddrinfo" arity="1" since="OTP 22.0"/> + <name name="getaddrinfo" arity="2" clause_i="1" since="OTP 22.0"/> + <name name="getaddrinfo" arity="2" clause_i="2" since="OTP 22.0"/> + <name name="getaddrinfo" arity="2" clause_i="3" since="OTP 22.0"/> + <fsummary>Network address and service transation.</fsummary> + <desc> + <p>Network address and service translation.</p> + <p>This function is the inverse of + <seealso marker="#getnameinfo/1"><c>getnameinfo</c></seealso>. + It converts host and service to a corresponding socket address.</p> + <p>One of the <c>Host</c> and <c>Service</c> may be <c>undefined</c> + but <em>not</em> both.</p> + </desc> + </func> + + <func> + <name name="if_name2index" arity="1" since="OTP 22.0"/> + <fsummary>Mappings between network interface names and indexes.</fsummary> + <desc> + <p>Mappings between network interface names and indexes.</p> + </desc> + </func> + + <func> + <name name="if_index2name" arity="1" since="OTP 22.0"/> + <fsummary>Mappings between network interface index and names.</fsummary> + <desc> + <p>Mappings between network interface index and names.</p> + </desc> + </func> + + <func> + <name name="if_names" arity="0" since="OTP 22.0"/> + <fsummary>Get network interface names and indexes.</fsummary> + <desc> + <p>Get network interface names and indexes.</p> + </desc> + </func> + + </funcs> + +</erlref> + diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml.src index d3b947527f..72e3409123 100644 --- a/lib/kernel/doc/src/ref_man.xml +++ b/lib/kernel/doc/src/ref_man.xml.src @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1996</year><year>2018</year> + <year>1996</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,6 +60,7 @@ <xi:include href="logger_formatter.xml"/> <xi:include href="logger_std_h.xml"/> <xi:include href="logger_disk_log_h.xml"/> + %ESOCK_USE_NET_XML% <xi:include href="net_adm.xml"/> <xi:include href="net_kernel.xml"/> <xi:include href="os.xml"/> diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml.src index b8c25ca53b..ccb26b9458 100644 --- a/lib/kernel/doc/src/specs.xml +++ b/lib/kernel/doc/src/specs.xml.src @@ -26,6 +26,7 @@ <xi:include href="../specs/specs_logger_formatter.xml"/> <xi:include href="../specs/specs_logger_std_h.xml"/> <xi:include href="../specs/specs_logger_disk_log_h.xml"/> + %ESOCK_USE_NET_SPECS_XML% <xi:include href="../specs/specs_net_adm.xml"/> <xi:include href="../specs/specs_net_kernel.xml"/> <xi:include href="../specs/specs_os.xml"/> diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index fcb599859b..88752431eb 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2018. All Rights Reserved. +# Copyright Ericsson AB 1996-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. @@ -123,6 +123,7 @@ MODULES = \ logger_server \ logger_simple_h \ logger_sup \ + net \ net_adm \ net_kernel \ os \ @@ -180,6 +181,7 @@ ERL_COMPILE_FLAGS += -Werror endif ERL_COMPILE_FLAGS += -I../include + # ---------------------------------------------------- # Targets # ---------------------------------------------------- diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl index 7a14e2635c..f31a1722ce 100644 --- a/lib/kernel/src/erl_epmd.erl +++ b/lib/kernel/src/erl_epmd.erl @@ -33,10 +33,10 @@ -define(erlang_daemon_port, 4369). -endif. -ifndef(epmd_dist_high). --define(epmd_dist_high, 4370). +-define(epmd_dist_high, 6). -endif. -ifndef(epmd_dist_low). --define(epmd_dist_low, 4370). +-define(epmd_dist_low, 5). -endif. %% External exports @@ -342,6 +342,13 @@ wait_for_reg_reply(Socket, SoFar) -> receive {tcp, Socket, Data0} -> case SoFar ++ Data0 of + [$v, Result, A, B, C, D] -> + case Result of + 0 -> + {alive, Socket, ?u32(A, B, C, D)}; + _ -> + {error, duplicate_name} + end; [$y, Result, A, B] -> case Result of 0 -> diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 42261d371d..7b9067d079 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -40,6 +40,15 @@ lc_graph/0, lc_graph_to_dot/2, lc_graph_merge/2, alloc_blocks_size/1]). +%% Reroutes calls to the given MFA to error_handler:breakpoint/3 +%% +%% Note that this is potentially unsafe as compiled code may assume that the +%% targeted function returns a specific type, triggering undefined behavior if +%% this function were to return something else. +%% +%% For reference, the debugger avoids the issue by purging the affected module +%% and interpreting all functions in the module, ensuring that no assumptions +%% are made with regard to return or argument types. -spec breakpoint(MFA, Flag) -> non_neg_integer() when MFA :: {Module :: module(), Function :: atom(), diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 853fa26871..cde03ce1c4 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -239,20 +239,30 @@ make_dir(Name) -> del_dir(Name) -> check_and_call(del_dir, [file_name(Name)]). --spec read_file_info(Filename) -> {ok, FileInfo} | {error, Reason} when - Filename :: name_all(), +-spec read_file_info(File) -> {ok, FileInfo} | {error, Reason} when + File :: name_all() | io_device(), FileInfo :: file_info(), Reason :: posix() | badarg. +read_file_info(IoDevice) + when is_pid(IoDevice); is_record(IoDevice, file_descriptor) -> + read_file_info(IoDevice, []); + read_file_info(Name) -> check_and_call(read_file_info, [file_name(Name)]). --spec read_file_info(Filename, Opts) -> {ok, FileInfo} | {error, Reason} when - Filename :: name_all(), +-spec read_file_info(File, Opts) -> {ok, FileInfo} | {error, Reason} when + File :: name_all() | io_device(), Opts :: [file_info_option()], FileInfo :: file_info(), Reason :: posix() | badarg. +read_file_info(IoDevice, Opts) when is_pid(IoDevice), is_list(Opts) -> + file_request(IoDevice, {read_handle_info, Opts}); + +read_file_info(#file_descriptor{module = Module} = Handle, Opts) when is_list(Opts) -> + Module:read_handle_info(Handle, Opts); + read_file_info(Name, Opts) when is_list(Opts) -> Args = [file_name(Name), Opts], case check_args(Args) of diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index 34d5497a4a..c03fbb548a 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -314,6 +314,14 @@ file_request(truncate, Reply -> std_reply(Reply, State) end; +file_request({read_handle_info, Opts}, + #state{handle=Handle}=State) -> + case ?CALL_FD(Handle, read_handle_info, [Opts]) of + {error,Reason}=Reply -> + {stop,Reason,Reply,State}; + Reply -> + {reply,Reply,State} + end; file_request(Unknown, #state{}=State) -> Reason = {request, Unknown}, diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 7940903658..24aff83fbd 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1645,6 +1645,7 @@ fmt_addr({ok,Addr}, Proto) -> {{0,0,0,0,0,0,0,0},Port} -> "*:" ++ fmt_port(Port, Proto); {{127,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto); {{0,0,0,0,0,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto); + {local, Path} -> "local:" ++ binary_to_list(Path); {IP,Port} -> inet_parse:ntoa(IP) ++ ":" ++ fmt_port(Port, Proto) end. diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 8fe6bdd1ca..c2ff6b63e9 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -74,6 +74,7 @@ logger_simple_h, logger_std_h, logger_sup, + net, net_adm, net_kernel, os, diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index 2b078ef091..8477a0fc93 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -457,12 +457,12 @@ maybe_ensure_file(State) -> %% In order to play well with tools like logrotate, we need to be able %% to re-create the file if it has disappeared (e.g. if rotated by %% logrotate) -ensure_file(#{fd:=Fd0,inode:=INode0,file_name:=FileName,modes:=Modes}=State) -> +ensure_file(#{inode:=INode0,file_name:=FileName,modes:=Modes}=State) -> case file:read_file_info(FileName,[raw]) of {ok,#file_info{inode=INode0}} -> State#{last_check=>timestamp()}; _ -> - close_log_file(Fd0), + close_log_file(State), case file:open(FileName,Modes) of {ok,Fd} -> {ok,#file_info{inode=INode}} = diff --git a/lib/kernel/src/net.erl b/lib/kernel/src/net.erl new file mode 100644 index 0000000000..b8ffa64043 --- /dev/null +++ b/lib/kernel/src/net.erl @@ -0,0 +1,324 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-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). + +%% We should really ifdef this module depending on if we actually built +%% the system with esock support (socket and prim_net), but our doc-building +%% can't handle the "variables" we need (USE_ESOCK). So instead, we just +%% leave everything hanging... +%% If one of the "hanging" functions is called when esock has been disabled, +%% the function will through a 'notsup' error (erlang:error/1). + +%% Administrative and utility functions +-export([ + 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]). + +%% Should we define these here or refer to the prim_net module +-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 info() -> list(). + +-ifdef(USE_ESOCK). +info() -> + prim_net:info(). +-else. +-dialyzer({nowarn_function, info/0}). +info() -> + erlang:error(notsup). +-endif. + + +-spec command(Cmd :: term()) -> term(). + +-ifdef(USE_ESOCK). +command(Cmd) -> + prim_net:command(Cmd). +-else. +-dialyzer({nowarn_function, command/1}). +command(_Cmd) -> + erlang:error(notsup). +-endif. + + + +%% =========================================================================== +%% +%% The proper net API +%% +%% =========================================================================== + +%% =========================================================================== +%% +%% gethostname - Get the name of the current host. +%% +%% + +-spec gethostname() -> {ok, HostName} | {error, Reason} when + HostName :: string(), + Reason :: term(). + +-ifdef(USE_ESOCK). +gethostname() -> + prim_net:gethostname(). +-else. +-dialyzer({nowarn_function, gethostname/0}). +gethostname() -> + erlang:error(notsup). +-endif. + + +%% =========================================================================== +%% +%% 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(). + +-ifdef(USE_ESOCK). +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)) -> + prim_net:getnameinfo(socket:ensure_sockaddr(SockAddr), Flags); +getnameinfo(#{family := Fam, path := _Path} = SockAddr, Flags) + when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) -> + prim_net:getnameinfo(SockAddr, Flags). +-else. +-dialyzer({nowarn_function, getnameinfo/2}). +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)) -> + erlang:error(notsup); +getnameinfo(#{family := Fam, path := _Path} = _SockAddr, Flags) + when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) -> + erlang:error(notsup). +-endif. + + +%% =========================================================================== +%% +%% 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(). + +-ifdef(USE_ESOCK). +getaddrinfo(Host, Service) + when (is_list(Host) orelse (Host =:= undefined)) andalso + (is_list(Service) orelse (Service =:= undefined)) andalso + (not ((Service =:= undefined) andalso (Host =:= undefined))) -> + prim_net:getaddrinfo(Host, Service). +-else. +-dialyzer({nowarn_function, getaddrinfo/2}). +getaddrinfo(Host, Service) + when (is_list(Host) orelse (Host =:= undefined)) andalso + (is_list(Service) orelse (Service =:= undefined)) andalso + (not ((Service =:= undefined) andalso (Host =:= undefined))) -> + erlang:error(notsup). +-endif. + + + + +%% =========================================================================== +%% +%% 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(). + +-ifdef(USE_ESOCK). +if_name2index(If) when is_list(If) -> + prim_net:if_name2index(If). +-else. +-dialyzer({nowarn_function, if_name2index/1}). +if_name2index(If) when is_list(If) -> + erlang:error(notsup). +-endif. + + + +%% =========================================================================== +%% +%% 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(). + +-ifdef(USE_ESOCK). +if_index2name(Idx) when is_integer(Idx) -> + prim_net:if_index2name(Idx). +-else. +-dialyzer({nowarn_function, if_index2name/1}). +if_index2name(Idx) when is_integer(Idx) -> + erlang:error(notsup). +-endif. + + + +%% =========================================================================== +%% +%% 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(). + +-ifdef(USE_ESOCK). +if_names() -> + prim_net:if_names(). +-else. +-dialyzer({nowarn_function, if_names/0}). +if_names() -> + erlang:error(notsup). +-endif. + + diff --git a/lib/kernel/src/raw_file_io_compressed.erl b/lib/kernel/src/raw_file_io_compressed.erl index d5ab042d25..66c5621dd1 100644 --- a/lib/kernel/src/raw_file_io_compressed.erl +++ b/lib/kernel/src/raw_file_io_compressed.erl @@ -21,7 +21,8 @@ -export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3, position/2, write/2, pwrite/2, pwrite/3, - read_line/1, read/2, pread/2, pread/3]). + read_line/1, read/2, pread/2, pread/3, + read_handle_info/2]). %% OTP internal. -export([ipread_s32bu_p32bu/3, sendfile/8]). @@ -118,6 +119,9 @@ ipread_s32bu_p32bu(Fd, Offset, MaxSize) -> sendfile(_,_,_,_,_,_,_,_) -> {error, enotsup}. +read_handle_info(Fd, Opts) -> + wrap_call(Fd, [Opts]). + wrap_call(Fd, Command) -> {_Owner, Pid} = get_fd_data(Fd), try gen_statem:call(Pid, Command, infinity) of diff --git a/lib/kernel/src/raw_file_io_delayed.erl b/lib/kernel/src/raw_file_io_delayed.erl index d2ad7550a1..766467437e 100644 --- a/lib/kernel/src/raw_file_io_delayed.erl +++ b/lib/kernel/src/raw_file_io_delayed.erl @@ -23,7 +23,8 @@ -export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3, position/2, write/2, pwrite/2, pwrite/3, - read_line/1, read/2, pread/2, pread/3]). + read_line/1, read/2, pread/2, pread/3, + read_handle_info/2]). %% OTP internal. -export([ipread_s32bu_p32bu/3, sendfile/8]). @@ -304,6 +305,9 @@ ipread_s32bu_p32bu(Fd, Offset, MaxSize) -> sendfile(_,_,_,_,_,_,_,_) -> {error, enotsup}. +read_handle_info(Fd, Opts) -> + wrap_call(Fd, [Opts]). + wrap_call(Fd, Command) -> #{ pid := Pid } = get_fd_data(Fd), try gen_statem:call(Pid, Command, infinity) of diff --git a/lib/kernel/src/raw_file_io_list.erl b/lib/kernel/src/raw_file_io_list.erl index 2e16e63f0e..e4fe434e13 100644 --- a/lib/kernel/src/raw_file_io_list.erl +++ b/lib/kernel/src/raw_file_io_list.erl @@ -21,7 +21,8 @@ -export([close/1, sync/1, datasync/1, truncate/1, advise/4, allocate/3, position/2, write/2, pwrite/2, pwrite/3, - read_line/1, read/2, pread/2, pread/3]). + read_line/1, read/2, pread/2, pread/3, + read_handle_info/2]). %% OTP internal. -export([ipread_s32bu_p32bu/3, sendfile/8]). @@ -126,3 +127,7 @@ sendfile(Fd, Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags) -> Args = [Dest, Offset, Bytes, ChunkSize, Headers, Trailers, Flags], PrivateFd = Fd#file_descriptor.data, ?CALL_FD(PrivateFd, sendfile, Args). + +read_handle_info(Fd, Opts) -> + PrivateFd = Fd#file_descriptor.data, + ?CALL_FD(PrivateFd, read_handle_info, [Opts]). diff --git a/lib/kernel/test/erl_distribution_wb_SUITE.erl b/lib/kernel/test/erl_distribution_wb_SUITE.erl index 8256444bdc..bb42a0ac39 100644 --- a/lib/kernel/test/erl_distribution_wb_SUITE.erl +++ b/lib/kernel/test/erl_distribution_wb_SUITE.erl @@ -47,6 +47,9 @@ R end). +-define(EPMD_DIST_HIGH, 6). +-define(EPMD_DIST_LOW, 5). + -define(DFLAG_PUBLISHED,1). -define(DFLAG_ATOM_CACHE,2). -define(DFLAG_EXTENDED_REFERENCES,4). @@ -57,15 +60,18 @@ -define(DFLAG_NEW_FUN_TAGS,16#80). -define(DFLAG_EXTENDED_PIDS_PORTS,16#100). -define(DFLAG_UTF8_ATOMS, 16#10000). +-define(DFLAG_BIG_CREATION, 16#40000). %% From R9 and forward extended references is compulsory %% From R10 and forward extended pids and ports are compulsory %% From R20 and forward UTF8 atoms are compulsory %% From R21 and forward NEW_FUN_TAGS is compulsory (no more tuple fallback {fun, ...}) +%% From R23 and forward BIG_CREATION is compulsory -define(COMPULSORY_DFLAGS, (?DFLAG_EXTENDED_REFERENCES bor ?DFLAG_EXTENDED_PIDS_PORTS bor ?DFLAG_UTF8_ATOMS bor - ?DFLAG_NEW_FUN_TAGS)). + ?DFLAG_NEW_FUN_TAGS bor + ?DFLAG_BIG_CREATION)). -define(PASS_THROUGH, $p). @@ -208,9 +214,9 @@ pending_up_md5(Node,OurName,Cookie) -> {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, [{active,false}, {packet,2}]), - send_name(SocketA,OurName,5), + send_name(SocketA,OurName, ?EPMD_DIST_HIGH), ok = recv_status(SocketA), - {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) + {hidden,Node,?EPMD_DIST_HIGH,HisChallengeA} = recv_challenge(SocketA), % See 1) OurChallengeA = gen_challenge(), OurDigestA = gen_digest(HisChallengeA, Cookie), send_challenge_reply(SocketA, OurChallengeA, OurDigestA), @@ -224,11 +230,11 @@ pending_up_md5(Node,OurName,Cookie) -> {ok, SocketB} = gen_tcp:connect(atom_to_list(NB),PortNo, [{active,false}, {packet,2}]), - send_name(SocketB,OurName,5), + send_name(SocketB,OurName, ?EPMD_DIST_HIGH), alive = recv_status(SocketB), send_status(SocketB, true), gen_tcp:close(SocketA), - {hidden,Node,5,HisChallengeB} = recv_challenge(SocketB), % See 1) + {hidden,Node,?EPMD_DIST_HIGH,HisChallengeB} = recv_challenge(SocketB), % See 1) OurChallengeB = gen_challenge(), OurDigestB = gen_digest(HisChallengeB, Cookie), send_challenge_reply(SocketB, OurChallengeB, OurDigestB), @@ -254,7 +260,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node -> Else -> exit(Else) end, - EpmdSocket = register(OurName, LSocket, 1, 5), + EpmdSocket = register_node(OurName, LSocket, ?EPMD_DIST_LOW, ?EPMD_DIST_HIGH), {NA, NB} = split(Node), rpc:cast(Node, net_adm, ping, [OurName]), receive after 1000 -> ok end, @@ -262,7 +268,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node -> {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, [{active,false}, {packet,2}]), - send_name(SocketA,OurName,5), + send_name(SocketA,OurName, ?EPMD_DIST_HIGH), %% We are still not marked up on the other side, as our first message %% is not sent. SocketB = case gen_tcp:accept(LSocket) of @@ -275,11 +281,11 @@ simultaneous_md5(Node, OurName, Cookie) when OurName < Node -> %% Now we are expected to close A gen_tcp:close(SocketA), %% But still Socket B will continue - {normal,Node,5} = recv_name(SocketB), % See 1) + {normal,Node,?EPMD_DIST_HIGH} = recv_name(SocketB), % See 1) send_status(SocketB, ok_simultaneous), MyChallengeB = gen_challenge(), - send_challenge(SocketB, OurName, MyChallengeB,5), - HisChallengeB = recv_challenge_reply(SocketB, MyChallengeB, Cookie), + send_challenge(SocketB, OurName, MyChallengeB, ?EPMD_DIST_HIGH), + {ok,HisChallengeB} = recv_challenge_reply(SocketB, MyChallengeB, Cookie), DigestB = gen_digest(HisChallengeB,Cookie), send_challenge_ack(SocketB, DigestB), inet:setopts(SocketB, [{active, false}, @@ -301,7 +307,8 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node -> Else -> exit(Else) end, - EpmdSocket = register(OurName, LSocket, 1, 5), + EpmdSocket = register_node(OurName, LSocket, + ?EPMD_DIST_LOW, ?EPMD_DIST_HIGH), {NA, NB} = split(Node), rpc:cast(Node, net_adm, ping, [OurName]), receive after 1000 -> ok end, @@ -315,16 +322,16 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node -> Else2 -> exit(Else2) end, - send_name(SocketA,OurName,5), + send_name(SocketA,OurName, ?EPMD_DIST_HIGH), ok_simultaneous = recv_status(SocketA), %% Socket B should die during this case catch begin - {normal,Node,5} = recv_name(SocketB), % See 1) + {normal,Node,?EPMD_DIST_HIGH} = recv_name(SocketB), % See 1) send_status(SocketB, ok_simultaneous), MyChallengeB = gen_challenge(), send_challenge(SocketB, OurName, MyChallengeB, 5), - HisChallengeB = recv_challenge_reply( + {ok,HisChallengeB} = recv_challenge_reply( SocketB, MyChallengeB, Cookie), @@ -346,7 +353,7 @@ simultaneous_md5(Node, OurName, Cookie) when OurName > Node -> end, gen_tcp:close(SocketB), %% But still Socket A will continue - {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) + {hidden,Node,?EPMD_DIST_HIGH,HisChallengeA} = recv_challenge(SocketA), % See 1) OurChallengeA = gen_challenge(), OurDigestA = gen_digest(HisChallengeA, Cookie), send_challenge_reply(SocketA, OurChallengeA, OurDigestA), @@ -372,7 +379,7 @@ missing_compulsory_dflags(Config) when is_list(Config) -> [{active,false}, {packet,2}]), BadNode = list_to_atom(atom_to_list(Name2)++"@"++atom_to_list(NB)), - send_name(SocketA,BadNode,5,0), + send_name(SocketA,BadNode, ?EPMD_DIST_HIGH, 0), not_allowed = recv_status(SocketA), gen_tcp:close(SocketA), stop_node(Node), @@ -516,16 +523,16 @@ send_challenge_reply(Socket, Challenge, Digest) -> recv_challenge_reply(Socket, ChallengeA, Cookie) -> case gen_tcp:recv(Socket, 0) of - {ok,[$r,CB3,CB2,CB1,CB0 | SumB]} when length(SumB) == 16 -> + {ok,[$r,CB3,CB2,CB1,CB0 | SumB]=Data} when length(SumB) == 16 -> SumA = gen_digest(ChallengeA, Cookie), ChallengeB = ?u32(CB3,CB2,CB1,CB0), if SumB == SumA -> - ChallengeB; + {ok,ChallengeB}; true -> - ?shutdown(bad_challenge_reply) + {error,Data} end; - _ -> - ?shutdown(no_node) + Err -> + {error,Err} end. send_challenge_ack(Socket, Digest) -> @@ -620,6 +627,13 @@ wait_for_reg_reply(Socket, SoFar) -> receive {tcp, Socket, Data0} -> case SoFar ++ Data0 of + [$v, Result, A, B, C, D] -> + case Result of + 0 -> + {alive, Socket, ?u32(A, B, C, D)}; + _ -> + {error, duplicate_name} + end; [$y, Result, A, B] -> case Result of 0 -> @@ -640,7 +654,7 @@ wait_for_reg_reply(Socket, SoFar) -> end. -register(NodeName, ListenSocket, VLow, VHigh) -> +register_node(NodeName, ListenSocket, VLow, VHigh) -> {ok,{_,TcpPort}} = inet:sockname(ListenSocket), case do_register_node(NodeName, TcpPort, VLow, VHigh) of {alive, Socket, _Creation} -> diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index cd7dc8ea9d..747f1d9e1b 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -58,6 +58,8 @@ -export([ file_info_basic_file/1, file_info_basic_directory/1, file_info_bad/1, file_info_times/1, file_write_file_info/1, file_wfi_helpers/1]). +-export([ file_handle_info_basic_file/1, file_handle_info_basic_directory/1, + file_handle_info_times/1]). -export([rename/1, access/1, truncate/1, datasync/1, sync/1, read_write/1, pread_write/1, append/1, exclusive/1]). -export([ e_delete/1, e_rename/1, e_make_dir/1, e_del_dir/1]). @@ -153,7 +155,10 @@ groups() -> {pos, [], [pos1, pos2, pos3]}, {file_info, [], [file_info_basic_file, file_info_basic_directory, - file_info_bad, file_info_times, file_write_file_info, + file_info_bad, file_info_times, + file_handle_info_basic_file, file_handle_info_basic_directory, + file_handle_info_times, + file_write_file_info, file_wfi_helpers]}, {consult, [], [consult1, path_consult]}, {eval, [], [eval1, path_eval]}, @@ -1417,7 +1422,8 @@ file_info_basic_directory(Config) when is_list(Config) -> {win32, _} -> test_directory("/", read_write), test_directory("c:/", read_write), - test_directory("c:\\", read_write); + test_directory("c:\\", read_write), + test_directory("\\\\localhost\\c$", read_write); _ -> test_directory("/", read) end, @@ -1549,6 +1555,180 @@ filter_atime(Atime, Config) -> Atime end. +%% Test read_file_info on I/O devices. + +file_handle_info_basic_file(Config) when is_list(Config) -> + RootDir = proplists:get_value(priv_dir, Config), + + %% Create a short file. + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_basic_test.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name, write), + io:put_chars(Fd1, "foo bar"), + ok = ?FILE_MODULE:close(Fd1), + + %% Test that the file has the expected attributes. + %% The times are tricky, so we will save them to a separate test case. + + {ok, Fd} = ?FILE_MODULE:open(Name, read), + {ok,FileInfo} = ?FILE_MODULE:read_file_info(Fd), + ok = ?FILE_MODULE:close(Fd), + + {ok, FdRaw} = ?FILE_MODULE:open(Name, [read, raw]), + {ok,FileInfoRaw} = ?FILE_MODULE:read_file_info(FdRaw), + ok = ?FILE_MODULE:close(FdRaw), + + #file_info{size=Size,type=Type,access=Access, + atime=AccessTime,mtime=ModifyTime} = FileInfo = FileInfoRaw, + io:format("Access ~p, Modify ~p", [AccessTime, ModifyTime]), + Size = 7, + Type = regular, + read_write = Access, + true = abs(time_dist(filter_atime(AccessTime, Config), + filter_atime(ModifyTime, + Config))) < 5, + all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), + + [] = flush(), + ok. + +file_handle_info_basic_directory(Config) when is_list(Config) -> + %% Note: filename:join/1 removes any trailing slash, + %% which is essential for ?FILE_MODULE:file_info/1 to work on + %% platforms such as Windows95. + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), + + %% Test that the RootDir directory has the expected attributes. + test_directory_handle(RootDir, read_write), + + %% Note that on Windows file systems, + %% "/" or "c:/" are *NOT* directories. + %% Therefore, test that ?FILE_MODULE:file_info/1 behaves as if they were + %% directories. + case os:type() of + {win32, _} -> + test_directory_handle("/", read_write), + test_directory_handle("c:/", read_write), + test_directory_handle("c:\\", read_write), + test_directory_handle("\\\\localhost\\c$", read_write); + _ -> + test_directory_handle("/", read) + end, + ok. + +test_directory_handle(Name, ExpectedAccess) -> + {ok, DirFd} = file:open(Name, [read, directory]), + try + {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd), + {ok,FileInfo} = ?FILE_MODULE:read_file_info(DirFd, [raw]), + #file_info{size=Size,type=Type,access=Access, + atime=AccessTime,mtime=ModifyTime} = FileInfo, + io:format("Testing directory ~s", [Name]), + io:format("Directory size is ~p", [Size]), + io:format("Access ~p", [Access]), + io:format("Access time ~p; Modify time~p", + [AccessTime, ModifyTime]), + Type = directory, + Access = ExpectedAccess, + all_integers(tuple_to_list(AccessTime) ++ tuple_to_list(ModifyTime)), + [] = flush(), + ok + after + file:close(DirFd) + end. + +%% Test that the file times behave as they should. + +file_handle_info_times(Config) when is_list(Config) -> + %% We have to try this twice, since if the test runs across the change + %% of a month the time diff calculations will fail. But it won't happen + %% if you run it twice in succession. + test_server:m_out_of_n( + 1,2, + fun() -> file_handle_info_int(Config) end), + ok. + +file_handle_info_int(Config) -> + %% Note: filename:join/1 removes any trailing slash, + %% which is essential for ?FILE_MODULE:file_info/1 to work on + %% platforms such as Windows95. + + RootDir = filename:join([proplists:get_value(priv_dir, Config)]), + io:format("RootDir = ~p", [RootDir]), + + Name = filename:join(RootDir, + atom_to_list(?MODULE) + ++"_file_info.fil"), + {ok,Fd1} = ?FILE_MODULE:open(Name, write), + io:put_chars(Fd1,"foo"), + {ok,FileInfo1} = ?FILE_MODULE:read_file_info(Fd1), + ok = ?FILE_MODULE:close(Fd1), + + {ok,Fd1Raw} = ?FILE_MODULE:open(Name, [read, raw]), + {ok,FileInfo1Raw} = ?FILE_MODULE:read_file_info(Fd1Raw), + ok = ?FILE_MODULE:close(Fd1Raw), + + %% We assert that everything but the size is the same, on some OSs the + %% size may not have been flushed to disc and we do not want to do a + %% sync to force it. + FileInfo1Raw = FileInfo1#file_info{ size = FileInfo1Raw#file_info.size }, + + #file_info{type=regular,atime=AccTime1,mtime=ModTime1} = FileInfo1, + + Now = erlang:localtime(), %??? + io:format("Now ~p",[Now]), + io:format("Open file Acc ~p Mod ~p",[AccTime1,ModTime1]), + true = abs(time_dist(filter_atime(Now, Config), + filter_atime(AccTime1, + Config))) < 8, + true = abs(time_dist(Now,ModTime1)) < 8, + + %% Sleep until we can be sure the seconds value has changed. + %% Note: FAT-based filesystem (like on Windows 95) have + %% a resolution of 2 seconds. + timer:sleep(2200), + + %% close the file, and watch the modify date change + + {ok,Fd2} = ?FILE_MODULE:open(Name, read), + {ok,FileInfo2} = ?FILE_MODULE:read_file_info(Fd2), + ok = ?FILE_MODULE:close(Fd2), + + {ok,Fd2Raw} = ?FILE_MODULE:open(Name, [read, raw]), + {ok,FileInfo2Raw} = ?FILE_MODULE:read_file_info(Fd2Raw), + ok = ?FILE_MODULE:close(Fd2Raw), + + #file_info{size=Size,type=regular,access=Access, + atime=AccTime2,mtime=ModTime2} = FileInfo2 = FileInfo2Raw, + io:format("Closed file Acc ~p Mod ~p",[AccTime2,ModTime2]), + true = time_dist(ModTime1,ModTime2) >= 0, + + %% this file is supposed to be binary, so it'd better keep it's size + Size = 3, + Access = read_write, + + %% Do some directory checking + + {ok,Fd3} = ?FILE_MODULE:open(RootDir, [read, directory]), + {ok,FileInfo3} = ?FILE_MODULE:read_file_info(Fd3), + ok = ?FILE_MODULE:close(Fd3), + + {ok,Fd3Raw} = ?FILE_MODULE:open(RootDir, [read, directory, raw]), + {ok,FileInfo3Raw} = ?FILE_MODULE:read_file_info(Fd3Raw), + ok = ?FILE_MODULE:close(Fd3Raw), + + #file_info{size=DSize,type=directory,access=DAccess, + atime=AccTime3,mtime=ModTime3} = FileInfo3 = FileInfo3Raw, + %% this dir was modified only a few secs ago + io:format("Dir Acc ~p; Mod ~p; Now ~p", [AccTime3, ModTime3, Now]), + true = abs(time_dist(Now,ModTime3)) < 5, + DAccess = read_write, + io:format("Dir size is ~p",[DSize]), + + [] = flush(), + ok. + %% Test the write_file_info/2 function. file_write_file_info(Config) when is_list(Config) -> diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 421510f9d6..de87bd9472 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2018. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. @@ -2019,7 +2019,7 @@ recvtclass(_Config) -> %% platforms - change {unix,_} to false? %% pktoptions is not supported for IPv4 -recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); @@ -2031,7 +2031,7 @@ recvtos_ok({unix,_}, _) -> true; recvtos_ok(_, _) -> false. %% pktoptions is not supported for IPv4 -recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); @@ -2043,7 +2043,7 @@ recvttl_ok({unix,_}, _) -> true; recvttl_ok(_, _) -> false. %% pktoptions is not supported for IPv6 -recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Using the option returns einval, so it is not implemented. @@ -2224,18 +2224,19 @@ collect_accepts(N,Tmo) -> A = millis(), receive {accepted,P,Msg} -> - [{P,Msg}] ++ collect_accepts(N-1,Tmo-(millis() - A)) + NextN = if N =:= infinity -> N; true -> N - 1 end, + [{P,Msg}] ++ collect_accepts(NextN, Tmo - (millis()-A)) after Tmo -> [] end. -define(EXPECT_ACCEPTS(Pattern,N,Timeout), (fun() -> - case collect_accepts(if N =:= infinity -> -1; true -> N end,Timeout) of + case collect_accepts((N), (Timeout)) of Pattern -> ok; - Other -> - {error,{unexpected,{Other,process_info(self(),messages)}}} + Other__ -> + {error,{unexpected,{Other__,process_info(self(),messages)}}} end end)()). diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index 730886865c..70d8caf478 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2018. All Rights Reserved. +%% Copyright Ericsson AB 1998-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. @@ -646,7 +646,7 @@ sendtclass(_Config) -> %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); %% Using the option returns einval, so it is not implemented. -recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% @@ -675,7 +675,7 @@ recvtclass_ok(_, _) -> false. %% Using the option returns einval, so it is not implemented. sendtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); -sendtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0}); +sendtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); sendtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); sendtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); sendtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); @@ -689,7 +689,8 @@ sendttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); %% Using the option returns enoprotoopt, so it is not implemented. sendttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); %% Option has no effect -sendttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,5,0}); +sendttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +sendttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); %% sendttl_ok({unix,_}, _) -> true; sendttl_ok(_, _) -> false. @@ -697,6 +698,8 @@ sendttl_ok(_, _) -> false. %% Using the option returns einval, so it is not implemented. sendtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); sendtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% Option has no effect +sendtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% sendtclass_ok({unix,_}, _) -> true; sendtclass_ok(_, _) -> false. diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl index 8eab36e308..5bff9cc292 100644 --- a/lib/kernel/test/global_SUITE.erl +++ b/lib/kernel/test/global_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2018. All Rights Reserved. +%% Copyright Ericsson AB 1997-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. @@ -2512,8 +2512,10 @@ re_register_name(Config) when is_list(Config) -> Me = self(), Pid1 = spawn(fun() -> proc(Me) end), yes = global:register_name(name, Pid1), + wait_for_monitor(Pid1), Pid2 = spawn(fun() -> proc(Me) end), _ = global:re_register_name(name, Pid2), + wait_for_monitor(Pid2), Pid2 ! die, Pid1 ! die, receive {Pid1, MonitoredBy1} -> [] = MonitoredBy1 end, @@ -2522,6 +2524,15 @@ re_register_name(Config) when is_list(Config) -> init_condition(Config), ok. +wait_for_monitor(Pid) -> + case process_info(Pid, monitored_by) of + {monitored_by, []} -> + timer:sleep(1), + wait_for_monitor(Pid); + {monitored_by, [_]} -> + ok + end. + proc(Parent) -> receive die -> ok end, {monitored_by, MonitoredBy} = process_info(self(), monitored_by), diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index 298a364a91..173e25c520 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -23,7 +23,8 @@ init_per_group/2,end_per_group/2, get_columns_and_rows/1, exit_initial/1, job_control_local/1, job_control_remote/1, - job_control_remote_noshell/1,ctrl_keys/1]). + job_control_remote_noshell/1,ctrl_keys/1, + get_columns_and_rows_escript/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% For spawn @@ -40,7 +41,8 @@ suite() -> {timetrap,{minutes,3}}]. all() -> - [get_columns_and_rows, exit_initial, job_control_local, + [get_columns_and_rows_escript,get_columns_and_rows, + exit_initial, job_control_local, job_control_remote, job_control_remote_noshell, ctrl_keys]. @@ -72,6 +74,60 @@ end_per_group(_GroupName, Config) -> -define(dbg(Data),noop). -endif. +string_to_term(Str) -> + {ok,Tokens,_EndLine} = erl_scan:string(Str ++ "."), + {ok,AbsForm} = erl_parse:parse_exprs(Tokens), + {value,Value,_Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()), + Value. + +run_unbuffer_escript(Rows, Columns, EScript, NoTermStdIn, NoTermStdOut) -> + DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"), + TmpFile = filename:join(DataDir, "tmp"), + ok = file:write_file(TmpFile, <<>>), + CommandModifier = + case {NoTermStdIn, NoTermStdOut} of + {false, false} -> ""; + {true, false} -> io_lib:format(" < ~s", [TmpFile]); + {false, true} -> io_lib:format(" > ~s ; cat ~s", [TmpFile, TmpFile]); + {true, true} -> io_lib:format(" > ~s < ~s ; cat ~s", [TmpFile, TmpFile, TmpFile]) + end, + Command = io_lib:format("unbuffer -p bash -c \"stty rows ~p; stty columns ~p; escript ~s ~s\"", + [Rows, Columns, EScript, CommandModifier]), + %% io:format("Command: ~s ~n", [Command]), + Out = os:cmd(Command), + %% io:format("Out: ~p ~n", [Out]), + string_to_term(Out). + +get_columns_and_rows_escript(Config) when is_list(Config) -> + ExpectUnbufferInstalled = + try + "79" = string:trim(os:cmd("unbuffer -p bash -c \"stty columns 79 ; tput cols\"")), + true + catch + _:_ -> false + end, + case ExpectUnbufferInstalled of + false -> + {skip, + "The unbuffer tool (https://core.tcl-lang.org/expect/index) does not seem to be installed.~n" + "On Ubuntu/Debian: \"sudo apt-get install expect\""}; + true -> + DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"), + IoColumnsErl = filename:join(DataDir, "io_columns.erl"), + IoRowsErl = filename:join(DataDir, "io_rows.erl"), + [ + begin + {ok, 42} = run_unbuffer_escript(99, 42, IoColumnsErl, NoTermStdIn, NoTermStdOut), + {ok, 99} = run_unbuffer_escript(99, 42, IoRowsErl, NoTermStdIn, NoTermStdOut) + end + || + {NoTermStdIn, NoTermStdOut} <- [{false, false}, {true, false}, {false, true}] + ], + {error,enotsup} = run_unbuffer_escript(99, 42, IoRowsErl, true, true), + {error,enotsup} = run_unbuffer_escript(99, 42, IoColumnsErl, true, true), + ok + end. + %% Test that the shell can access columns and rows. get_columns_and_rows(Config) when is_list(Config) -> case proplists:get_value(default_shell,Config) of diff --git a/lib/kernel/test/interactive_shell_SUITE_data/.gitignore b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore new file mode 100644 index 0000000000..1c2f433de1 --- /dev/null +++ b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore @@ -0,0 +1 @@ +tmp
\ No newline at end of file diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl new file mode 100644 index 0000000000..32d0cf25df --- /dev/null +++ b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl @@ -0,0 +1,6 @@ +-module(io_columns). + +-export([main/1]). + +main(_) -> + io:format("~p",[io:columns()]). diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl new file mode 100644 index 0000000000..53ceb464b0 --- /dev/null +++ b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl @@ -0,0 +1,6 @@ +-module(io_rows). + +-export([main/1]). + +main(_) -> + io:format("~p",[io:rows()]). |