diff options
96 files changed, 3970 insertions, 2577 deletions
diff --git a/erts/doc/specs/.gitignore b/erts/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/erts/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile index 6578923fe1..cfa5527474 100644 --- a/erts/doc/src/Makefile +++ b/erts/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2010. All Rights Reserved. +# Copyright Ericsson AB 1997-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -16,6 +16,9 @@ # # %CopyrightEnd% # + +SPECS_ESRC = ../../preloaded/src/ + include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk @@ -43,6 +46,12 @@ XML_REF1_FILES = epmd.xml \ run_erl.xml \ start.xml +XML_REF3_EFILES = \ + erl_prim_loader.xml \ + erlang.xml \ + init.xml \ + zlib.xml + XML_REF3_FILES = \ driver_entry.xml \ erl_nif.xml \ @@ -98,18 +107,26 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_EFILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +KERNEL_SRC=$(ERL_TOP)/lib/kernel/src +KERNEL_INCLUDE=$(ERL_TOP)/lib/kernel/include +SPECS_FLAGS = -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE) + # ---------------------------------------------------- # Targets # ---------------------------------------------------- $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man $(INFO_FILE) +docs: man pdf html $(INFO_FILE) $(TOP_PDF_FILE): $(XML_FILES) @@ -132,8 +149,25 @@ clean: rm -f $(MAN1DIR)/* rm -f $(MAN3DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ +$(SPECDIR)/specs_driver_entry.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module driver_entry +$(SPECDIR)/specs_erl_nif.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_nif +$(SPECDIR)/specs_erl_set_memory_block.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_set_memory_block +$(SPECDIR)/specs_erl_driver.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_driver +$(SPECDIR)/specs_erts_alloc.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erts_alloc + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/erts/doc/src/epmd.xml b/erts/doc/src/epmd.xml index 411e627c85..3e7005410f 100644 --- a/erts/doc/src/epmd.xml +++ b/erts/doc/src/epmd.xml @@ -120,7 +120,7 @@ <item> <p>Let this instance of <c>epmd</c> listen only on the comma-separated list of IP addresses and on the loopback address - (which is implicitely added to the list if it has not been + (which is implicitly added to the list if it has not been specified). This can also be set using the <c><![CDATA[ERL_EPMD_ADDRESS]]></c> environment variable, see the section <seealso marker="#environment_variables">Environment @@ -243,7 +243,7 @@ <p>This environment variable may be set to a comma-separated list of IP addresses, in which case the <c>epmd</c> daemon will listen only on the specified address(es) and on the - loopback address (which is implicitely added to the list if it + loopback address (which is implicitly added to the list if it has not been specified). The default behaviour is to listen on all available IP addresses.</p> </item> diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 514ee5ffaf..02082e57c6 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -1010,7 +1010,7 @@ list of IP addresses, in which case the <seealso marker="epmd">epmd</seealso> daemon will listen only on the specified address(es) and on the - loopback address (which is implicitely added to the list if it + loopback address (which is implicitly added to the list if it has not been specified).</p> </item> <tag><c><![CDATA[ERL_EPMD_PORT]]></c></tag> diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index ccaa9b725f..fa3daaeecc 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -55,33 +55,31 @@ <c>-loader_debug</c> are also experimental</p></warning> </description> + <datatypes> + <datatype> + <name name="host"/> + </datatype> + </datatypes> + <funcs> <func> - <name>start(Id, Loader, Hosts) -> {ok, Pid} | {error, What}</name> + <name name="start" arity="3"/> <fsummary>Start the Erlang low level loader</fsummary> - <type> - <v>Id = term()</v> - <v>Loader = atom() | string()</v> - <v>Hosts = [Host]</v> - <v>Host = atom()</v> - <v>Pid = pid()</v> - <v>What = term()</v> - </type> <desc> <p>Starts the Erlang low level loader. This function is called by the <c>init</c> process (and module). The <c>init</c> - process reads the command line flags <c>-id Id</c>, - <c>-loader Loader</c>, and <c>-hosts Hosts</c>. These are + process reads the command line flags <c>-id <anno>Id</anno></c>, + <c>-loader <anno>Loader</anno></c>, and <c>-hosts <anno>Hosts</anno></c>. These are the arguments supplied to the <c>start/3</c> function.</p> <p>If <c>-loader</c> is not given, the default loader is <c>efile</c> which tells the system to read from the file system.</p> - <p>If <c>-loader</c> is <c>inet</c>, the <c>-id Id</c>, - <c>-hosts Hosts</c>, and <c>-setcookie Cookie</c> flags must - also be supplied. <c>Hosts</c> identifies hosts which this + <p>If <c>-loader</c> is <c>inet</c>, the <c>-id <anno>Id</anno></c>, + <c>-hosts <anno>Hosts</anno></c>, and <c>-setcookie Cookie</c> flags must + also be supplied. <c><anno>Hosts</anno></c> identifies hosts which this node can contact in order to load modules. One Erlang runtime system with a <c>erl_boot_server</c> process must be - started on each of hosts given in <c>Hosts</c> in order to + started on each of hosts given in <c><anno>Hosts</anno></c> in order to answer the requests. See <seealso marker="kernel:erl_boot_server">erl_boot_server(3)</seealso>.</p> <p>If <c>-loader</c> is something else, the given port program @@ -90,35 +88,26 @@ </desc> </func> <func> - <name>get_file(Filename) -> {ok, Bin, FullName} | error</name> + <name name="get_file" arity="1"/> <fsummary>Get a file</fsummary> - <type> - <v>Filename = string()</v> - <v>Bin = binary()</v> - <v>FullName = string()</v> - </type> <desc> <p>This function fetches a file using the low level loader. - <c>Filename</c> is either an absolute file name or just the name + <c><anno>Filename</anno></c> is either an absolute file name or just the name of the file, for example <c>"lists.beam"</c>. If an internal path is set to the loader, this path is used to find the file. If a user supplied loader is used, the path can be stripped off if it is obsolete, and the loader does not use a path. - <c>FullName</c> is the complete name of the fetched file. - <c>Bin</c> is the contents of the file as a binary.</p> + <c><anno>FullName</anno></c> is the complete name of the fetched file. + <c><anno>Bin</anno></c> is the contents of the file as a binary.</p> - <p>The <c>Filename</c> can also be a file in an archive. For example - <c>/otp/root/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin/mnesia_backup.beam</c> + <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example + <c>/otp/root/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin/mnesia_backup.beam</c>. See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p> </desc> </func> <func> - <name>get_path() -> {ok, Path}</name> + <name name="get_path" arity="0"/> <fsummary>Get the path set in the loader</fsummary> - <type> - <v>Path = [Dir]</v> - <v>Dir = string()</v> - </type> <desc> <p>This function gets the path set in the loader. The path is set by the <c>init</c> process according to information found @@ -126,35 +115,26 @@ </desc> </func> <func> - <name>list_dir(Dir) -> {ok, Filenames} | error</name> + <name name="list_dir" arity="1"/> <fsummary>List files in a directory</fsummary> - <type> - <v>Dir = name()</v> - <v>Filenames = [Filename]</v> - <v>Filename = string()</v> - </type> <desc> <p>Lists all the files in a directory. Returns - <c>{ok, Filenames}</c> if successful. Otherwise, it returns - <c>error</c>. <c>Filenames</c> is a list of + <c>{ok, <anno>Filenames</anno>}</c> if successful. Otherwise, it returns + <c>error</c>. <c><anno>Filenames</anno></c> is a list of the names of all the files in the directory. The names are not sorted.</p> - <p>The <c>Dir</c> can also be a directory in an archive. For example - <c>/otp/root/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c> + <p>The <c><anno>Dir</anno></c> can also be a directory in an archive. For example + <c>/otp/root/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>. See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p> </desc> </func> <func> - <name>read_file_info(Filename) -> {ok, FileInfo} | error</name> + <name name="read_file_info" arity="1"/> <fsummary>Get information about a file</fsummary> - <type> - <v>Filename = name()</v> - <v>FileInfo = #file_info{}</v> - </type> <desc> <p>Retrieves information about a file. Returns - <c>{ok, FileInfo}</c> if successful, otherwise - <c>error</c>. <c>FileInfo</c> is a record + <c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise + <c>error</c>. <c><anno>FileInfo</anno></c> is a record <c>file_info</c>, defined in the Kernel include file <c>file.hrl</c>. Include the following directive in the module from which the function is called:</p> @@ -162,18 +142,14 @@ -include_lib("kernel/include/file.hrl").</code> <p>See <seealso marker="kernel:file">file(3)</seealso> for more info about the record <c>file_info</c>.</p> - <p>The <c>Filename</c> can also be a file in an archive. For example - <c>/otp/root/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin/mnesia_backup.beam</c> + <p>The <c><anno>Filename</anno></c> can also be a file in an archive. For example + <c>/otp/root/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin/mnesia_backup.beam</c>. See <seealso marker="kernel:code">code(3)</seealso> about archive files.</p> </desc> </func> <func> - <name>set_path(Path) -> ok</name> + <name name="set_path" arity="1"/> <fsummary>Set the path of the loader</fsummary> - <type> - <v>Path = [Dir]</v> - <v>Dir = string()</v> - </type> <desc> <p>This function sets the path of the loader if <c>init</c> interprets a <c>path</c> command in the start script.</p> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index f98e15cb52..7cfab0785d 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -48,22 +48,24 @@ "Allowed in guard tests".</p> </description> - <section> - <title>DATA TYPES</title> - <marker id="iolist_definition"></marker> - <code type="none"> -ext_binary() - a binary data object, - structured according to the Erlang external term format - -iodata() = iolist() | binary() + <datatypes> + <datatype> + <name><marker id="type-ext_binary">ext_binary()</marker></name> + <desc> + <p>A binary data object, structured according to + the Erlang external term format.</p> + </desc> + </datatype> + <datatype> + <name name="timestamp"></name> + <desc><p>See <seealso marker="#now/0">now/0</seealso>.</p> + </desc> + </datatype> + </datatypes> -iolist() = [char() | binary() | iolist()] - a binary is allowed as the tail of the list</code> - </section> <funcs> <func> - <name>abs(Number) -> int() | float()</name> + <name>abs(Number) -> integer() | float()</name> <fsummary>Arithmetical absolute value</fsummary> <type> <v>Number = number()</v> @@ -80,7 +82,7 @@ iolist() = [char() | binary() | iolist()] </desc> </func> <func> - <name>erlang:adler32(Data) -> int()</name> + <name>erlang:adler32(Data) -> integer()</name> <fsummary>Compute adler32 checksum</fsummary> <type> <v>Data = iodata()</v> @@ -90,10 +92,10 @@ iolist() = [char() | binary() | iolist()] </desc> </func> <func> - <name>erlang:adler32(OldAdler, Data) -> int()</name> + <name>erlang:adler32(OldAdler, Data) -> integer()</name> <fsummary>Compute adler32 checksum</fsummary> <type> - <v>OldAdler = int()</v> + <v>OldAdler = integer()</v> <v>Data = iodata()</v> </type> <desc> @@ -112,11 +114,11 @@ iolist() = [char() | binary() | iolist()] </desc> </func> <func> - <name>erlang:adler32_combine(FirstAdler, SecondAdler, SecondSize) -> int()</name> + <name>erlang:adler32_combine(FirstAdler, SecondAdler, SecondSize) -> integer()</name> <fsummary>Combine two adler32 checksums</fsummary> <type> - <v>FirstAdler = SecondAdler = int()</v> - <v>SecondSize = int()</v> + <v>FirstAdler = SecondAdler = integer()</v> + <v>SecondSize = integer()</v> </type> <desc> <p>Combines two previously computed adler32 checksums. @@ -155,20 +157,16 @@ iolist() = [char() | binary() | iolist()] </desc> </func> <func> - <name>apply(Fun, Args) -> term() | empty()</name> + <name name="apply" arity="2"/> <fsummary>Apply a function to an argument list</fsummary> - <type> - <v>Fun = fun()</v> - <v>Args = [term()]</v> - </type> <desc> - <p>Call a fun, passing the elements in <c>Args</c> as + <p>Call a fun, passing the elements in <c><anno>Args</anno></c> as arguments.</p> <p>Note: If the number of elements in the arguments are known at compile-time, the call is better written as - <c>Fun(Arg1, Arg2, ... ArgN)</c>.</p> + <c><anno>Fun</anno>(Arg1, Arg2, ... ArgN)</c>.</p> <warning> - <p>Earlier, <c>Fun</c> could also be given as + <p>Earlier, <c><anno>Fun</anno></c> could also be given as <c>{Module, Function}</c>, equivalent to <c>apply(Module, Function, Args)</c>. This usage is deprecated and will stop working in a future release of @@ -177,15 +175,11 @@ iolist() = [char() | binary() | iolist()] </desc> </func> <func> - <name>apply(Module, Function, Args) -> term() | empty()</name> + <name name="apply" arity="3"/> <fsummary>Apply a function to an argument list</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>Returns the result of applying <c>Function</c> in - <c>Module</c> to <c>Args</c>. The applied function must + <c><anno>Module</anno></c> to <c><anno>Args</anno></c>. The applied function must be exported from <c>Module</c>. The arity of the function is the length of <c>Args</c>.</p> <pre> @@ -198,7 +192,7 @@ iolist() = [char() | binary() | iolist()] "Erlang"</pre> <p>Note: If the number of arguments are known at compile-time, the call is better written as - <c>Module:Function(Arg1, Arg2, ..., ArgN)</c>.</p> + <c><anno>Module</anno>:<anno>Function</anno>(Arg1, Arg2, ..., ArgN)</c>.</p> <p>Failure: <c>error_handler:undefined_function/3</c> is called if the applied function is not exported. The error handler can be redefined (see @@ -258,8 +252,8 @@ iolist() = [char() | binary() | iolist()] <type> <v>Subject = binary()</v> <v>PosLen = {Start,Length}</v> - <v>Start = int()</v> - <v>Length = int()</v> + <v>Start = integer() >= 0</v> + <v>Length = integer() >= 0</v> </type> <desc> <p>Extracts the part of the binary described by <c>PosLen</c>.</p> @@ -291,8 +285,8 @@ iolist() = [char() | binary() | iolist()] <fsummary>Extracts a part of a binary</fsummary> <type> <v>Subject = binary()</v> - <v>Start = int()</v> - <v>Length = int()</v> + <v>Start = integer() >= 0</v> + <v>Length = integer() >= 0</v> </type> <desc> <p>The same as <c>binary_part(Subject, {Pos, Len})</c>.</p> @@ -390,7 +384,7 @@ iolist() = [char() | binary() | iolist()] <name>binary_to_term(Binary) -> term()</name> <fsummary>Decode an Erlang external term format binary</fsummary> <type> - <v>Binary = ext_binary()</v> + <v>Binary = <seealso marker="#type-ext_binary">ext_binary()</seealso></v> </type> <desc> <p>Returns an Erlang term which is the result of decoding @@ -411,7 +405,7 @@ iolist() = [char() | binary() | iolist()] <fsummary>Decode an Erlang external term format binary</fsummary> <type> <v>Opts = [safe]</v> - <v>Binary = ext_binary()</v> + <v>Binary = <seealso marker="#type-ext_binary">ext_binary()</seealso></v> </type> <desc> <p>As <c>binary_to_term/1</c>, but takes options that affect decoding @@ -442,7 +436,7 @@ iolist() = [char() | binary() | iolist()] </desc> </func> <func> - <name>bit_size(Bitstring) -> int()</name> + <name>bit_size(Bitstring) -> integer() >= 0</name> <fsummary>Return the size of a bitstring</fsummary> <type> <v>Bitstring = bitstring()</v> @@ -461,7 +455,7 @@ iolist() = [char() | binary() | iolist()] <name>erlang:bump_reductions(Reductions) -> void()</name> <fsummary>Increment the reduction counter</fsummary> <type> - <v>Reductions = int()</v> + <v>Reductions = integer() >= 0</v> </type> <desc> <p>This implementation-dependent function increments @@ -478,7 +472,7 @@ iolist() = [char() | binary() | iolist()] </desc> </func> <func> - <name>byte_size(Bitstring) -> int()</name> + <name>byte_size(Bitstring) -> integer() >= 0</name> <fsummary>Return the size of a bitstring (or binary)</fsummary> <type> <v>Bitstring = bitstring()</v> @@ -500,7 +494,7 @@ iolist() = [char() | binary() | iolist()] <fsummary>Cancel a timer</fsummary> <type> <v>TimerRef = reference()</v> - <v>Time = int()</v> + <v>Time = integer() >= 0</v> </type> <desc> <p>Cancels a timer, where <c>TimerRef</c> was returned by @@ -524,7 +518,7 @@ iolist() = [char() | binary() | iolist()] </func> <func> - <name>check_process_code(Pid, Module) -> bool()</name> + <name>check_process_code(Pid, Module) -> boolean()</name> <fsummary>Check if a process is executing old code for a module</fsummary> <type> <v>Pid = pid()</v> @@ -544,7 +538,7 @@ false</pre> </desc> </func> <func> - <name>concat_binary(ListOfBinaries)</name> + <name name="concat_binary" arity="1"/> <fsummary>Concatenate a list of binaries (deprecated)</fsummary> <desc> <p>Do not use; use @@ -553,7 +547,7 @@ false</pre> </desc> </func> <func> - <name>erlang:crc32(Data) -> int()</name> + <name>erlang:crc32(Data) -> integer() >= 0</name> <fsummary>Compute crc32 (IEEE 802.3) checksum</fsummary> <type> <v>Data = iodata()</v> @@ -563,10 +557,10 @@ false</pre> </desc> </func> <func> - <name>erlang:crc32(OldCrc, Data) -> int()</name> + <name>erlang:crc32(OldCrc, Data) -> integer() >= 0</name> <fsummary>Compute crc32 (IEEE 802.3) checksum</fsummary> <type> - <v>OldCrc = int()</v> + <v>OldCrc = integer() >= 0</v> <v>Data = iodata()</v> </type> <desc> @@ -585,11 +579,11 @@ false</pre> </desc> </func> <func> - <name>erlang:crc32_combine(FirstCrc, SecondCrc, SecondSize) -> int()</name> + <name>erlang:crc32_combine(FirstCrc, SecondCrc, SecondSize) -> integer() >= 0</name> <fsummary>Combine two crc32 (IEEE 802.3) checksums</fsummary> <type> - <v>FirstCrc = SecondCrc = int()</v> - <v>SecondSize = int()</v> + <v>FirstCrc = SecondCrc = integer() >= 0</v> + <v>SecondSize = integer() >= 0</v> </type> <desc> <p>Combines two previously computed crc32 checksums. @@ -609,10 +603,10 @@ false</pre> </desc> </func> <func> - <name>date() -> {Year, Month, Day}</name> + <name>date() -> Date</name> <fsummary>Current date</fsummary> <type> - <v>Year = Month = Day = int()</v> + <v>Date = <seealso marker="calendar#type-date">calendar:date()</seealso></v> </type> <desc> <p>Returns the current date as <c>{Year, Month, Day}</c>.</p> @@ -631,20 +625,20 @@ false</pre> <v>Options = [Opt]</v> <v>Packet = binary() | HttpPacket</v> <v>Rest = binary()</v> - <v>Length = int() | undefined</v> + <v>Length = integer() > 0 | undefined</v> <v>Reason = term()</v> <v> Type, Opt -- see below</v> <v></v> <v>HttpPacket = HttpRequest | HttpResponse | HttpHeader | http_eoh | HttpError</v> <v>HttpRequest = {http_request, HttpMethod, HttpUri, HttpVersion}</v> <v>HttpResponse = {http_response, HttpVersion, integer(), HttpString}</v> - <v>HttpHeader = {http_header, int(), HttpField, Reserved=term(), Value=HttpString}</v> + <v>HttpHeader = {http_header, integer(), HttpField, Reserved=term(), Value=HttpString}</v> <v>HttpError = {http_error, HttpString}</v> <v>HttpMethod = HttpMethodAtom | HttpString</v> <v>HttpMethodAtom = 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE'</v> - <v>HttpUri = '*' | {absoluteURI, http|https, Host=HttpString, Port=int()|undefined, Path=HttpString} | + <v>HttpUri = '*' | {absoluteURI, http|https, Host=HttpString, Port=integer()|undefined, Path=HttpString} | {scheme, Scheme=HttpString, HttpString} | {abs_path, HttpString} | HttpString</v> - <v>HttpVersion = {Major=int(), Minor=int()}</v> + <v>HttpVersion = {Major=integer(), Minor=integer()}</v> <v>HttpString = string() | binary()</v> <v>HttpField = HttpFieldAtom | HttpString</v> <v>HttpFieldAtom = 'Cache-Control' | 'Connection' | 'Date' | 'Pragma' | 'Transfer-Encoding' | 'Upgrade' | 'Via' | 'Accept' | 'Accept-Charset' | 'Accept-Encoding' | 'Accept-Language' | 'Authorization' | 'From' | 'Host' | 'If-Modified-Since' | 'If-Match' | 'If-None-Match' | 'If-Range' | 'If-Unmodified-Since' | 'Max-Forwards' | 'Proxy-Authorization' | 'Range' | 'Referer' | 'User-Agent' | 'Age' | 'Location' | 'Proxy-Authenticate' | 'Public' | 'Retry-After' | 'Server' | 'Vary' | 'Warning' | 'Www-Authenticate' | 'Allow' | 'Content-Base' | 'Content-Encoding' | 'Content-Language' | 'Content-Length' | 'Content-Location' | 'Content-Md5' | 'Content-Range' | 'Content-Type' | 'Etag' | 'Expires' | 'Last-Modified' | 'Accept-Ranges' | 'Set-Cookie' | 'Set-Cookie2' | 'X-Forwarded-For' | 'Cookie' | 'Keep-Alive' | 'Proxy-Connection'</v> @@ -719,14 +713,14 @@ false</pre> </taglist> <p>The following options are available:</p> <taglist> - <tag><c>{packet_size, int()}</c></tag> + <tag><c>{packet_size, integer()}</c></tag> <item><p>Sets the max allowed size of the packet body. If the packet header indicates that the length of the packet is longer than the max allowed length, the packet is considered invalid. Default is 0 which means no size limit.</p> </item> - <tag><c>{line_length, int()}</c></tag> + <tag><c>{line_length, integer()}</c></tag> <item><p>Applies only to line oriented protocols (<c>line</c>, <c>http</c>). Lines longer than this will be truncated.</p> @@ -800,7 +794,7 @@ false</pre> </desc> </func> <func> - <name>demonitor(MonitorRef, OptionList) -> true|false</name> + <name>demonitor(MonitorRef, OptionList) -> boolean()</name> <fsummary>Stop monitoring</fsummary> <type> <v>MonitorRef = reference()</v> @@ -867,14 +861,11 @@ false</pre> </desc> </func> <func> - <name>disconnect_node(Node) -> bool() | ignored</name> + <name name="disconnect_node" arity="1"/> <fsummary>Force the disconnection of a node</fsummary> - <type> - <v>Node = atom()</v> - </type> <desc> <p>Forces the disconnection of a node. This will appear to - the node <c>Node</c> as if the local node has crashed. This + the node <c><anno>Node</anno></c> as if the local node has crashed. This BIF is mainly used in the Erlang network authentication protocols. Returns <c>true</c> if disconnection succeeds, otherwise <c>false</c>. If the local node is not alive, @@ -1069,15 +1060,11 @@ b</pre> </desc> </func> <func> - <name>erlang:fun_info(Fun) -> [{Item, Info}]</name> + <name name="fun_info" arity="1"/> <fsummary>Information about a fun</fsummary> - <type> - <v>Fun = fun()</v> - <v>Item, Info -- see below</v> - </type> <desc> <p>Returns a list containing information about the fun - <c>Fun</c>. Each element of the list is a tuple. The order of + <c><anno>Fun</anno></c>. Each element of the list is a tuple. The order of the tuples is not defined, and more tuples may be added in a future release.</p> <warning> @@ -1176,7 +1163,7 @@ b</pre> <p>Returns information about <c>Fun</c> as specified by <c>Item</c>, in the form <c>{Item,Info}</c>.</p> <p>For any fun, <c>Item</c> can be any of the atoms - <c>module</c>, <c>name</c>, <c>arity</c>, or <c>env</c>.</p> + <c>module</c>, <c>name</c>, <c>arity</c>, <c>env</c>, or <c>type</c>.</p> <p>For a local fun, <c>Item</c> can also be any of the atoms <c>index</c>, <c>new_index</c>, <c>new_uniq</c>, <c>uniq</c>, and <c>pid</c>. For an external fun, the value @@ -1197,11 +1184,11 @@ b</pre> </desc> </func> <func> - <name>erlang:function_exported(Module, Function, Arity) -> bool()</name> + <name>erlang:function_exported(Module, Function, Arity) -> boolean()</name> <fsummary>Check if a function is exported and loaded</fsummary> <type> <v>Module = Function = atom()</v> - <v>Arity = int()</v> + <v>Arity = arity()</v> </type> <desc> <p>Returns <c>true</c> if the module <c>Module</c> is loaded @@ -1229,7 +1216,7 @@ b</pre> </desc> </func> <func> - <name>garbage_collect(Pid) -> bool()</name> + <name>garbage_collect(Pid) -> boolean()</name> <fsummary>Force an immediate garbage collection of a process</fsummary> <type> <v>Pid = pid()</v> @@ -1276,11 +1263,8 @@ b</pre> </desc> </func> <func> - <name>erlang:get_cookie() -> Cookie | nocookie</name> + <name name="get_cookie" arity="0"/> <fsummary>Get the magic cookie of the local node</fsummary> - <type> - <v>Cookie = atom()</v> - </type> <desc> <p>Returns the magic cookie of the local node, if the node is alive; otherwise the atom <c>nocookie</c>.</p> @@ -1311,7 +1295,7 @@ b</pre> <fsummary>Get the call stack back-trace of the last exception</fsummary> <type> <v>Module = Function = atom()</v> - <v>Arity = int()</v> + <v>Arity = arity()</v> <v>Args = [term()]</v> </type> <desc> @@ -1379,7 +1363,7 @@ os_prompt%</pre> <name>halt(Status)</name> <fsummary>Halt the Erlang runtime system</fsummary> <type> - <v>Status = int()>=0 | string()</v> + <v>Status = integer() >= 0 | string()</v> </type> <desc> <p><c>Status</c> must be a non-negative integer, or a string. @@ -1472,7 +1456,7 @@ os_prompt%</pre> <name>integer_to_list(Integer) -> string()</name> <fsummary>Text representation of an integer</fsummary> <type> - <v>Integer = int()</v> + <v>Integer = integer()</v> </type> <desc> <p>Returns a string which corresponds to the text @@ -1483,15 +1467,11 @@ os_prompt%</pre> </desc> </func> <func> - <name>integer_to_list(Integer, Base) -> string()</name> + <name name="integer_to_list" arity="2"/> <fsummary>Text representation of an integer</fsummary> - <type> - <v>Integer = int()</v> - <v>Base = 2..36</v> - </type> <desc> <p>Returns a string which corresponds to the text - representation of <c>Integer</c> in base <c>Base</c>.</p> + representation of <c><anno>Integer</anno></c> in base <c><anno>Base</anno></c>.</p> <pre> > <input>integer_to_list(1023, 16).</input> "3FF"</pre> @@ -1518,7 +1498,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>iolist_size(Item) -> int()</name> + <name>iolist_size(Item) -> integer() >= 0</name> <fsummary>Size of an iolist</fsummary> <type> <v>Item = iolist() | binary()</v> @@ -1533,7 +1513,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_alive() -> bool()</name> + <name>is_alive() -> boolean()</name> <fsummary>Check whether the local node is alive</fsummary> <desc> <p>Returns <c>true</c> if the local node is alive; that is, if @@ -1542,7 +1522,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_atom(Term) -> bool()</name> + <name>is_atom(Term) -> boolean()</name> <fsummary>Check whether a term is an atom</fsummary> <type> <v>Term = term()</v> @@ -1554,7 +1534,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_binary(Term) -> bool()</name> + <name>is_binary(Term) -> boolean()</name> <fsummary>Check whether a term is a binary</fsummary> <type> <v>Term = term()</v> @@ -1569,7 +1549,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_bitstring(Term) -> bool()</name> + <name>is_bitstring(Term) -> boolean()</name> <fsummary>Check whether a term is a bitstring</fsummary> <type> <v>Term = term()</v> @@ -1582,7 +1562,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_boolean(Term) -> bool()</name> + <name>is_boolean(Term) -> boolean()</name> <fsummary>Check whether a term is a boolean</fsummary> <type> <v>Term = term()</v> @@ -1595,11 +1575,11 @@ os_prompt%</pre> </desc> </func> <func> - <name>erlang:is_builtin(Module, Function, Arity) -> bool()</name> + <name>erlang:is_builtin(Module, Function, Arity) -> boolean()</name> <fsummary>Check if a function is a BIF implemented in C</fsummary> <type> <v>Module = Function = atom()</v> - <v>Arity = int()</v> + <v>Arity = arity()</v> </type> <desc> <p>Returns <c>true</c> if <c>Module:Function/Arity</c> is @@ -1608,7 +1588,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_float(Term) -> bool()</name> + <name>is_float(Term) -> boolean()</name> <fsummary>Check whether a term is a float</fsummary> <type> <v>Term = term()</v> @@ -1620,7 +1600,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_function(Term) -> bool()</name> + <name>is_function(Term) -> boolean()</name> <fsummary>Check whether a term is a fun</fsummary> <type> <v>Term = term()</v> @@ -1632,11 +1612,11 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_function(Term, Arity) -> bool()</name> + <name>is_function(Term, Arity) -> boolean()</name> <fsummary>Check whether a term is a fun with a given arity</fsummary> <type> <v>Term = term()</v> - <v>Arity = int()</v> + <v>Arity = arity()</v> </type> <desc> <p>Returns <c>true</c> if <c>Term</c> is a fun that can be @@ -1653,7 +1633,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_integer(Term) -> bool()</name> + <name>is_integer(Term) -> boolean()</name> <fsummary>Check whether a term is an integer</fsummary> <type> <v>Term = term()</v> @@ -1665,7 +1645,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_list(Term) -> bool()</name> + <name>is_list(Term) -> boolean()</name> <fsummary>Check whether a term is a list</fsummary> <type> <v>Term = term()</v> @@ -1677,7 +1657,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_number(Term) -> bool()</name> + <name>is_number(Term) -> boolean()</name> <fsummary>Check whether a term is a number</fsummary> <type> <v>Term = term()</v> @@ -1689,7 +1669,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_pid(Term) -> bool()</name> + <name>is_pid(Term) -> boolean()</name> <fsummary>Check whether a term is a pid</fsummary> <type> <v>Term = term()</v> @@ -1701,7 +1681,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_port(Term) -> bool()</name> + <name>is_port(Term) -> boolean()</name> <fsummary>Check whether a term is a port</fsummary> <type> <v>Term = term()</v> @@ -1713,7 +1693,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_process_alive(Pid) -> bool()</name> + <name>is_process_alive(Pid) -> boolean()</name> <fsummary>Check whether a process is alive</fsummary> <type> <v>Pid = pid()</v> @@ -1728,7 +1708,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_record(Term, RecordTag) -> bool()</name> + <name>is_record(Term, RecordTag) -> boolean()</name> <fsummary>Check whether a term appears to be a record</fsummary> <type> <v>Term = term()</v> @@ -1751,12 +1731,12 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_record(Term, RecordTag, Size) -> bool()</name> + <name>is_record(Term, RecordTag, Size) -> boolean()</name> <fsummary>Check whether a term appears to be a record</fsummary> <type> <v>Term = term()</v> <v>RecordTag = atom()</v> - <v>Size = int()</v> + <v>Size = integer()</v> </type> <desc> <p><c>RecordTag</c> must be an atom. Returns <c>true</c> if @@ -1771,7 +1751,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_reference(Term) -> bool()</name> + <name>is_reference(Term) -> boolean()</name> <fsummary>Check whether a term is a reference</fsummary> <type> <v>Term = term()</v> @@ -1783,7 +1763,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>is_tuple(Term) -> bool()</name> + <name>is_tuple(Term) -> boolean()</name> <fsummary>Check whether a term is a tuple</fsummary> <type> <v>Term = term()</v> @@ -1795,7 +1775,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>length(List) -> int()</name> + <name>length(List) -> integer() >= 0</name> <fsummary>Length of a list</fsummary> <type> <v>List = [term()]</v> @@ -1916,7 +1896,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>list_to_integer(String) -> int()</name> + <name>list_to_integer(String) -> integer()</name> <fsummary>Convert from text representation to an integer</fsummary> <type> <v>String = string()</v> @@ -1932,19 +1912,15 @@ os_prompt%</pre> </desc> </func> <func> - <name>list_to_integer(String, Base) -> int()</name> + <name name="list_to_integer" arity="2"/> <fsummary>Convert from text representation to an integer</fsummary> - <type> - <v>String = string()</v> - <v>Base = 2..36</v> - </type> <desc> <p>Returns an integer whose text representation in base - <c>Base</c> is <c>String</c>.</p> + <c><anno>Base</anno></c> is <c><anno>String</anno></c>.</p> <pre> > <input>list_to_integer("3FF", 16).</input> 1023</pre> - <p>Failure: <c>badarg</c> if <c>String</c> contains a bad + <p>Failure: <c>badarg</c> if <c><anno>String</anno></c> contains a bad representation of an integer.</p> </desc> </func> @@ -2095,12 +2071,10 @@ os_prompt%</pre> </desc> </func> <func> - <name>erlang:localtime() -> {Date, Time}</name> + <name>erlang:localtime() -> DateTime</name> <fsummary>Current local date and time</fsummary> <type> - <v>Date = {Year, Month, Day}</v> - <v>Time = {Hour, Minute, Second}</v> - <v> Year = Month = Day = Hour = Minute = Second = int()</v> + <v>DateTime = <seealso marker="calendar#type-datetime">calendar:datetime()</seealso></v> </type> <desc> <p>Returns the current local date and time @@ -2113,17 +2087,12 @@ os_prompt%</pre> </desc> </func> <func> - <name>erlang:localtime_to_universaltime({Date1, Time1}) -> {Date2, Time2}</name> + <name name="localtime_to_universaltime" arity="1"/> <fsummary>Convert from local to Universal Time Coordinated (UTC) date and time</fsummary> - <type> - <v>Date1 = Date2 = {Year, Month, Day}</v> - <v>Time1 = Time2 = {Hour, Minute, Second}</v> - <v> Year = Month = Day = Hour = Minute = Second = int()</v> - </type> <desc> <p>Converts local date and time to Universal Time Coordinated (UTC), if this is supported by the underlying OS. Otherwise, - no conversion is done and <c>{Date1, Time1}</c> is returned.</p> + no conversion is done and <c>{<anno>Date1</anno>, <anno>Time1</anno>}</c> is returned.</p> <pre> > <input>erlang:localtime_to_universaltime({{1996,11,6},{14,45,17}}).</input> {{1996,11,6},{13,45,17}}</pre> @@ -2135,9 +2104,8 @@ os_prompt%</pre> <name>erlang:localtime_to_universaltime({Date1, Time1}, IsDst) -> {Date2, Time2}</name> <fsummary>Convert from local to Universal Time Coordinated (UTC) date and time</fsummary> <type> - <v>Date1 = Date2 = {Year, Month, Day}</v> - <v>Time1 = Time2 = {Hour, Minute, Second}</v> - <v> Year = Month = Day = Hour = Minute = Second = int()</v> + <v>Date1 = Date2 = <seealso marker="calendar#type-date">calendar:date()</seealso></v> + <v>Time1 = Time2 = <seealso marker="calendar#type-time">calendar:time()</seealso></v> <v>IsDst = true | false | undefined</v> </type> <desc> @@ -2177,7 +2145,7 @@ os_prompt%</pre> <name>erlang:make_tuple(Arity, InitialValue) -> tuple()</name> <fsummary>Create a new tuple of a given arity</fsummary> <type> - <v>Arity = int()</v> + <v>Arity = arity()</v> <v>InitialValue = term()</v> </type> <desc> @@ -2192,7 +2160,7 @@ os_prompt%</pre> <name>erlang:make_tuple(Arity, Default, InitList) -> tuple()</name> <fsummary>Create a new tuple with given arity and contents</fsummary> <type> - <v>Arity = int()</v> + <v>Arity = arity()</v> <v>Default = term()</v> <v>InitList = [{Position,term()}]</v> <v>Position = integer()</v> @@ -2211,14 +2179,11 @@ os_prompt%</pre> </desc> </func> <func> - <name>max(Term1, Term2) -> Maximum</name> + <name name="max" arity="2"/> <fsummary>Return the largest of two term</fsummary> - <type> - <v>Term1 = Term2 = Maximum = term()</v> - </type> <desc> - <p>Return the largest of <c>Term1</c> and <c>Term2</c>; - if the terms compares equal, <c>Term1</c> will be returned.</p> + <p>Return the largest of <c><anno>Term1</anno></c> and <c><anno>Term2</anno></c>; + if the terms compare equal, <c><anno>Term1</anno></c> will be returned.</p> </desc> </func> <func> @@ -2468,18 +2433,15 @@ os_prompt%</pre> </desc> </func> <func> - <name>min(Term1, Term2) -> Minimum</name> + <name name="min" arity="2"/> <fsummary>Return the smallest of two term</fsummary> - <type> - <v>Term1 = Term2 = Minimum = term()</v> - </type> <desc> - <p>Return the smallest of <c>Term1</c> and <c>Term2</c>; - if the terms compare equal, <c>Term1</c> will be returned.</p> + <p>Return the smallest of <c><anno>Term1</anno></c> and <c><anno>Term2</anno></c>; + if the terms compare equal, <c><anno>Term1</anno></c> will be returned.</p> </desc> </func> <func> - <name>module_loaded(Module) -> bool()</name> + <name>module_loaded(Module) -> boolean()</name> <fsummary>Check if a module is loaded</fsummary> <type> <v>Module = atom()</v> @@ -2602,7 +2564,7 @@ os_prompt%</pre> <fsummary>Monitor the status of a node</fsummary> <type> <v>Node = node()</v> - <v>Flag = bool()</v> + <v>Flag = boolean()</v> </type> <desc> <p>Monitors the status of the node <c>Node</c>. If <c>Flag</c> @@ -2628,7 +2590,7 @@ os_prompt%</pre> <fsummary>Monitor the status of a node</fsummary> <type> <v>Node = node()</v> - <v>Flag = bool()</v> + <v>Flag = boolean()</v> <v>Options = [Option]</v> <v>Option = allow_passive_connect</v> </type> @@ -2711,11 +2673,8 @@ os_prompt%</pre> </desc> </func> <func> - <name>nodes() -> Nodes</name> + <name name="nodes" arity="0"/> <fsummary>All visible nodes in the system</fsummary> - <type> - <v>Nodes = [node()]</v> - </type> <desc> <p>Returns a list of all visible nodes in the system, excluding the local node. Same as <c>nodes(visible)</c>.</p> @@ -2765,11 +2724,12 @@ os_prompt%</pre> </desc> </func> <func> - <name>now() -> {MegaSecs, Secs, MicroSecs}</name> - <fsummary>Elapsed time since 00:00 GMT</fsummary> + <name>now() -> timestamp()</name> <type> - <v>MegaSecs = Secs = MicroSecs = int()</v> + <v>timestamp() = {MegaSecs, Secs, MicroSecs}</v> + <v>MegaSecs = Secs = MicroSecs = integer() >= 0</v> </type> + <fsummary>Elapsed time since 00:00 GMT</fsummary> <desc> <p>Returns the tuple <c>{MegaSecs, Secs, MicroSecs}</c> which is the elapsed time since 00:00 GMT, January 1, 1970 (zero hour) @@ -2792,12 +2752,12 @@ os_prompt%</pre> <v>PortName = {spawn, Command} | {spawn_driver, Command} | {spawn_executable, FileName} | {fd, In, Out}</v> <v> Command = string()</v> <v> FileName = [ FileNameChar ] | binary()</v> - <v> FileNameChar = int() (1..255 or any Unicode codepoint, see description)</v> - <v> In = Out = int()</v> + <v> FileNameChar = integer() (1..255 or any Unicode codepoint, see description)</v> + <v> In = Out = integer()</v> <v>PortSettings = [Opt]</v> <v> Opt = {packet, N} | stream | {line, L} | {cd, Dir} | {env, Env} | {args, [ ArgString ]} | {arg0, ArgString} | exit_status | use_stdio | nouse_stdio | stderr_to_stdout | in | out | binary | eof</v> <v> N = 1 | 2 | 4</v> - <v> L = int()</v> + <v> L = integer()</v> <v> Dir = string()</v> <v> ArgString = [ FileNameChar ] | binary()</v> <v> Env = [{Name, Val}]</v> @@ -3283,7 +3243,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>port_command(Port, Data, OptionList) -> true|false</name> + <name>port_command(Port, Data, OptionList) -> boolean()</name> <fsummary>Send data to a port</fsummary> <type> <v>Port = port() | atom()</v> @@ -3399,7 +3359,7 @@ os_prompt%</pre> <fsummary>Perform a synchronous control operation on a port</fsummary> <type> <v>Port = port() | atom()</v> - <v>Operation = int()</v> + <v>Operation = integer()</v> <v>Data = Res = iodata()</v> </type> <desc> @@ -3423,7 +3383,7 @@ os_prompt%</pre> <fsummary>Synchronous call to a port with term data</fsummary> <type> <v>Port = port() | atom()</v> - <v>Operation = int()</v> + <v>Operation = integer()</v> <v>Data = term()</v> </type> <desc> @@ -4109,7 +4069,7 @@ os_prompt%</pre> <v>Reason = term()</v> <v>Stacktrace = [{Module, Function, Arity | Args} | {Fun, Args}]</v> <v> Module = Function = atom()</v> - <v> Arity = int()</v> + <v> Arity = arity()</v> <v> Args = [term()]</v> <v> Fun = [fun()]</v> </type> @@ -4146,7 +4106,7 @@ os_prompt%</pre> </desc> </func> <func> - <name>erlang:read_timer(TimerRef) -> int() | false</name> + <name>erlang:read_timer(TimerRef) -> integer() >= 0 | false</name> <fsummary>Number of milliseconds remaining for a timer</fsummary> <type> <v>TimerRef = reference()</v> @@ -4262,7 +4222,7 @@ true</pre> </desc> </func> <func> - <name>round(Number) -> int()</name> + <name>round(Number) -> integer()</name> <fsummary>Return an integer by rounding a number</fsummary> <type> <v>Number = number()</v> @@ -4346,7 +4306,7 @@ true</pre> <name>erlang:send_after(Time, Dest, Msg) -> TimerRef</name> <fsummary>Start a timer</fsummary> <type> - <v>Time = int()</v> + <v>Time = integer() >= 0</v> <v> 0 <= Time <= 4294967295</v> <v>Dest = pid() | RegName </v> <v> LocalPid = pid() (of a process, alive or dead, on the local node)</v> @@ -4375,17 +4335,12 @@ true</pre> </desc> </func> <func> - <name>erlang:send_nosuspend(Dest, Msg) -> bool()</name> + <name name="send_nosuspend" arity="2"/> <fsummary>Try to send a message without ever blocking</fsummary> - <type> - <v>Dest = pid() | port() | RegName | {RegName, Node}</v> - <v> RegName = atom()</v> - <v> Node = node()</v> - <v>Msg = term()</v> - </type> + <type name="dst"/> <desc> <p>The same as - <seealso marker="#send/3">erlang:send(Dest, Msg, [nosuspend])</seealso>, but returns <c>true</c> if + <seealso marker="#send/3">erlang:send(<anno>Dest</anno>, <anno>Msg</anno>, [nosuspend])</seealso>, but returns <c>true</c> if the message was sent and <c>false</c> if the message was not sent because the sender would have had to be suspended.</p> <p>This function is intended for send operations towards an @@ -4393,7 +4348,7 @@ true</pre> (Erlang) process. If the connection to the remote node (usually not a real Erlang node, but a node written in C or Java) is overloaded, this function <em>will not send the message</em> but return <c>false</c> instead.</p> - <p>The same happens, if <c>Dest</c> refers to a local port that + <p>The same happens, if <c><anno>Dest</anno></c> refers to a local port that is busy. For all other destinations (allowed for the ordinary send operator <c>'!'</c>) this function sends the message and returns <c>true</c>.</p> @@ -4426,18 +4381,12 @@ true</pre> </desc> </func> <func> - <name>erlang:send_nosuspend(Dest, Msg, Options) -> bool()</name> + <name name="send_nosuspend" arity="3"/> <fsummary>Try to send a message without ever blocking</fsummary> - <type> - <v>Dest = pid() | port() | RegName | {RegName, Node}</v> - <v> RegName = atom()</v> - <v> Node = node()</v> - <v>Msg = term()</v> - <v>Option = noconnect</v> - </type> + <type name="dst"/> <desc> <p>The same as - <seealso marker="#send/3">erlang:send(Dest, Msg, [nosuspend | Options])</seealso>, + <seealso marker="#send/3">erlang:send(<anno>Dest</anno>, <anno>Msg</anno>, [nosuspend | <anno>Options</anno>])</seealso>, but with boolean return value.</p> <p>This function behaves like <seealso marker="#send_nosuspend/2">erlang:send_nosuspend/2)</seealso>, @@ -4462,17 +4411,13 @@ true</pre> </desc> </func> <func> - <name>erlang:set_cookie(Node, Cookie) -> true</name> + <name name="set_cookie" arity="2"/> <fsummary>Set the magic cookie of a node</fsummary> - <type> - <v>Node = node()</v> - <v>Cookie = atom()</v> - </type> <desc> - <p>Sets the magic cookie of <c>Node</c> to the atom - <c>Cookie</c>. If <c>Node</c> is the local node, the function + <p>Sets the magic cookie of <c><anno>Node</anno></c> to the atom + <c><anno>Cookie</anno></c>. If <c><anno>Node</anno></c> is the local node, the function also sets the cookie of all other unknown nodes to - <c>Cookie</c> (see + <c><anno>Cookie</anno></c> (see <seealso marker="doc/reference_manual:distributed">Distributed Erlang</seealso> in the Erlang Reference Manual).</p> <p>Failure: <c>function_clause</c> if the local node is not alive.</p> @@ -4497,7 +4442,7 @@ true</pre> </desc> </func> <func> - <name>size(Item) -> int()</name> + <name>size(Item) -> integer() >= 0</name> <fsummary>Size of a tuple or binary</fsummary> <type> <v>Item = tuple() | binary()</v> @@ -4512,28 +4457,21 @@ true</pre> </desc> </func> <func> - <name>spawn(Fun) -> pid()</name> + <name name="spawn" arity="1"/> <fsummary>Create a new process with a fun as entry point</fsummary> - <type> - <v>Fun = fun()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Fun</c> to the empty list <c>[]</c>. Otherwise works + of <c><anno>Fun</anno></c> to the empty list <c>[]</c>. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> </desc> </func> <func> - <name>spawn(Node, Fun) -> pid()</name> + <name name="spawn" arity="2"/> <fsummary>Create a new process with a fun as entry point on a given node</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Fun</c> to the empty list <c>[]</c> on <c>Node</c>. If - <c>Node</c> does not exist, a useless pid is returned. + of <c><anno>Fun</anno></c> to the empty list <c>[]</c> on <c><anno>Node</anno></c>. If + <c><anno>Node</anno></c> does not exist, a useless pid is returned. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> </desc> @@ -4564,47 +4502,35 @@ true</pre> </desc> </func> <func> - <name>spawn(Node, Module, Function, Args) -> pid()</name> + <name name="spawn" arity="4"/> <fsummary>Create a new process with a function as entry point on a given node</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Module:Function</c> to <c>Args</c> on <c>Node</c>. If - <c>Node</c> does not exists, a useless pid is returned. + of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c> on <c>Node</c>. If + <c><anno>Node</anno></c> does not exists, a useless pid is returned. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> </desc> </func> <func> - <name>spawn_link(Fun) -> pid()</name> + <name name="spawn_link" arity="1"/> <fsummary>Create and link to a new process with a fun as entry point</fsummary> - <type> - <v>Fun = fun()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Fun</c> to the empty list []. A link is created between + of <c><anno>Fun</anno></c> to the empty list []. A link is created between the calling process and the new process, atomically. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> </desc> </func> <func> - <name>spawn_link(Node, Fun) -> pid()</name> + <name name="spawn_link" arity="2"/> <fsummary>Create and link to a new process with a fun as entry point on a specified node</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Fun</c> to the empty list [] on <c>Node</c>. A link is + of <c><anno>Fun</anno></c> to the empty list [] on <c><anno>Node</anno></c>. A link is created between the calling process and the new process, - atomically. If <c>Node</c> does not exist, a useless pid is + atomically. If <c><anno>Node</anno></c> does not exist, a useless pid is returned (and due to the link, an exit signal with exit reason <c>noconnection</c> will be received). Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> @@ -4626,47 +4552,35 @@ true</pre> </desc> </func> <func> - <name>spawn_link(Node, Module, Function, Args) -> pid()</name> + <name name="spawn_link" arity="4"/> <fsummary>Create and link to a new process with a function as entry point on a given node</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Module:Function</c> to <c>Args</c> on <c>Node</c>. A + of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c> on <c>Node</c>. A link is created between the calling process and the new - process, atomically. If <c>Node</c> does not exist, a useless + process, atomically. If <c><anno>Node</anno></c> does not exist, a useless pid is returned (and due to the link, an exit signal with exit reason <c>noconnection</c> will be received). Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> </desc> </func> <func> - <name>spawn_monitor(Fun) -> {pid(),reference()}</name> + <name name="spawn_monitor" arity="1"/> <fsummary>Create and monitor a new process with a fun as entry point</fsummary> - <type> - <v>Fun = fun()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Fun</c> to the empty list [] and reference for a monitor + of <c><anno>Fun</anno></c> to the empty list [] and reference for a monitor created to the new process. Otherwise works like <seealso marker="#spawn/3">spawn/3</seealso>.</p> </desc> </func> <func> - <name>spawn_monitor(Module, Function, Args) -> {pid(),reference()}</name> + <name name="spawn_monitor" arity="3"/> <fsummary>Create and monitor a new process with a function as entry point</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - </type> <desc> <p>A new process is started by the application - of <c>Module:Function</c> to <c>Args</c>, and the process is + of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c>, and the process is monitored at the same time. Returns the pid and a reference for the monitor. Otherwise works like @@ -4674,19 +4588,11 @@ true</pre> </desc> </func> <func> - <name>spawn_opt(Fun, [Option]) -> pid() | {pid(),reference()}</name> + <name name="spawn_opt" arity="2"/> <fsummary>Create a new process with a fun as entry point</fsummary> - <type> - <v>Fun = fun()</v> - <v>Option = link | monitor | {priority, Level} | {fullsweep_after, Number} | {min_heap_size, Size} | {min_bin_vheap_size, VSize}</v> - <v> Level = low | normal | high</v> - <v> Number = int()</v> - <v> Size = int()</v> - <v> VSize = int()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Fun</c> to the empty list <c>[]</c>. Otherwise + of <c><anno>Fun</anno></c> to the empty list <c>[]</c>. Otherwise works like <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p> <p>If the option <c>monitor</c> is given, the newly created @@ -4695,37 +4601,19 @@ true</pre> </desc> </func> <func> - <name>spawn_opt(Node, Fun, [Option]) -> pid()</name> + <name name="spawn_opt" arity="3"/> <fsummary>Create a new process with a fun as entry point on a given node</fsummary> - <type> - <v>Node = node()</v> - <v>Fun = fun()</v> - <v>Option = link | {priority, Level} | {fullsweep_after, Number} | {min_heap_size, Size} | {min_bin_vheap_size, VSize}</v> - <v> Level = low | normal | high</v> - <v> Number = int()</v> - <v> Size = int()</v> - <v> VSize = int()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Fun</c> to the empty list <c>[]</c> on <c>Node</c>. If - <c>Node</c> does not exist, a useless pid is returned. + of <c><anno>Fun</anno></c> to the empty list <c>[]</c> on <c><anno>Node</anno></c>. If + <c><anno>Node</anno></c> does not exist, a useless pid is returned. Otherwise works like <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p> </desc> </func> <func> - <name>spawn_opt(Module, Function, Args, [Option]) -> pid() | {pid(),reference()}</name> + <name name="spawn_opt" arity="4"/> <fsummary>Create a new process with a function as entry point</fsummary> - <type> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Option = link | monitor | {priority, Level} | {fullsweep_after, Number} | {min_heap_size, Size} | {min_bin_vheap_size, VSize}</v> - <v> Level = low | normal | high</v> - <v> Number = int()</v> - <v> Size = int()</v> - <v> VSize = int()</v> - </type> <desc> <p>Works exactly like <seealso marker="#spawn/3">spawn/3</seealso>, except that an @@ -4744,17 +4632,17 @@ true</pre> <p>Monitor the new process (just like <seealso marker="#monitor/2">monitor/2</seealso> does).</p> </item> - <tag><c>{priority, Level}</c></tag> + <tag><c>{priority, <anno>Level</anno>}</c></tag> <item> <p>Sets the priority of the new process. Equivalent to executing - <seealso marker="#process_flag_priority">process_flag(priority, Level)</seealso> in the start function of the new process, + <seealso marker="#process_flag_priority">process_flag(priority, <anno>Level</anno>)</seealso> in the start function of the new process, except that the priority will be set before the process is selected for execution for the first time. For more information on priorities see <seealso marker="#process_flag_priority">process_flag(priority, Level)</seealso>.</p> </item> - <tag><c>{fullsweep_after, Number}</c></tag> + <tag><c>{fullsweep_after, <anno>Number</anno>}</c></tag> <item> <p>This option is only useful for performance tuning. In general, you should not use this option unless you @@ -4776,18 +4664,18 @@ true</pre> <p>Here are a few cases when it could be useful to change <c>fullsweep_after</c>. Firstly, if binaries that are no longer used should be thrown away as soon as possible. - (Set <c>Number</c> to zero.) Secondly, a process that + (Set <c><anno>Number</anno></c> to zero.) Secondly, a process that mostly have short-lived data will be fullsweeped seldom or never, meaning that the old heap will contain mostly garbage. To ensure a fullsweep once in a while, set - <c>Number</c> to a suitable value such as 10 or 20. + <c><anno>Number</anno></c> to a suitable value such as 10 or 20. Thirdly, in embedded systems with limited amount of RAM and no virtual memory, one might want to preserve memory - by setting <c>Number</c> to zero. (The value may be set + by setting <c><anno>Number</anno></c> to zero. (The value may be set globally, see <seealso marker="#system_flag/2">erlang:system_flag/2</seealso>.)</p> </item> - <tag><c>{min_heap_size, Size}</c></tag> + <tag><c>{min_heap_size, <anno>Size</anno>}</c></tag> <item> <p>This option is only useful for performance tuning. In general, you should not use this option unless you @@ -4802,9 +4690,9 @@ true</pre> slow down the system due to worse data locality. Therefore, it is recommended to use this option only for fine-tuning an application and to measure the execution - time with various <c>Size</c> values.</p> + time with various <c><anno>Size</anno></c> values.</p> </item> - <tag><c>{min_bin_vheap_size, VSize}</c></tag> + <tag><c>{min_bin_vheap_size, <anno>VSize</anno>}</c></tag> <item> <p>This option is only useful for performance tuning. In general, you should not use this option unless you @@ -4818,29 +4706,19 @@ true</pre> Setting too high value, however, might waste memory. Therefore, it is recommended to use this option only for fine-tuning an application and to measure the execution - time with various <c>VSize</c> values.</p> + time with various <c><anno>VSize</anno></c> values.</p> </item> </taglist> </desc> </func> <func> - <name>spawn_opt(Node, Module, Function, Args, [Option]) -> pid()</name> + <name name="spawn_opt" arity="5"/> <fsummary>Create a new process with a function as entry point on a given node</fsummary> - <type> - <v>Node = node()</v> - <v>Module = Function = atom()</v> - <v>Args = [term()]</v> - <v>Option = link | {priority, Level} | {fullsweep_after, Number} | {min_heap_size, Size} | {min_bin_vheap_size, VSize}</v> - <v> Level = low | normal | high</v> - <v> Number = int()</v> - <v> Size = int()</v> - <v> VSize = int()</v> - </type> <desc> <p>Returns the pid of a new process started by the application - of <c>Module:Function</c> to <c>Args</c> on <c>Node</c>. If - <c>Node</c> does not exist, a useless pid is returned. + of <c><anno>Module</anno>:<anno>Function</anno></c> to <c><anno>Args</anno></c> on <c>Node</c>. If + <c><anno>Node</anno></c> does not exist, a useless pid is returned. Otherwise works like <seealso marker="#spawn_opt/4">spawn_opt/4</seealso>.</p> </desc> @@ -4874,7 +4752,7 @@ true</pre> <name>erlang:start_timer(Time, Dest, Msg) -> TimerRef</name> <fsummary>Start a timer</fsummary> <type> - <v>Time = int()</v> + <v>Time = integer() >= 0</v> <v> 0 <= Time <= 4294967295</v> <v>Dest = LocalPid | RegName </v> <v> LocalPid = pid() (of a process, alive or dead, on the local node)</v> @@ -4983,7 +4861,7 @@ true</pre> </desc> </func> <func> - <name>erlang:suspend_process(Suspendee, OptList) -> true | false</name> + <name>erlang:suspend_process(Suspendee, OptList) -> boolean()</name> <fsummary>Suspend a process</fsummary> <type> <v>Suspendee = pid()</v> @@ -5083,15 +4961,12 @@ true</pre> </desc> </func> <func> - <name>erlang:suspend_process(Suspendee) -> true</name> + <name name="suspend_process" arity="1"/> <fsummary>Suspend a process</fsummary> - <type> - <v>Suspendee = pid()</v> - </type> <desc> - <p>Suspends the process identified by <c>Suspendee</c>. The + <p>Suspends the process identified by <c><anno>Suspendee</anno></c>. The same as calling - <seealso marker="#suspend_process/2">erlang:suspend_process(Suspendee, [])</seealso>. For more information see the documentation of <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>. + <seealso marker="#suspend_process/2">erlang:suspend_process(<anno>Suspendee</anno>, [])</seealso>. For more information see the documentation of <seealso marker="#suspend_process/2">erlang:suspend_process/2</seealso>. </p> <warning> <p>This BIF is intended for debugging only.</p> @@ -5416,7 +5291,7 @@ true</pre> <p>Types:</p> <list type="bulleted"> <item><c>Allocator = undefined | glibc</c></item> - <item><c>Version = [int()]</c></item> + <item><c>Version = [integer()]</c></item> <item><c>Features = [atom()]</c></item> <item><c>Settings = [{Subsystem, [{Parameter, Value}]}]</c></item> <item><c>Subsystem = atom()</c></item> @@ -5682,7 +5557,7 @@ true</pre> </item> <tag><c>fullsweep_after</c></tag> <item> - <p>Returns <c>{fullsweep_after, int()}</c> which is the + <p>Returns <c>{fullsweep_after, integer()}</c> which is the <c>fullsweep_after</c> garbage collection setting used by default. For more information see <c>garbage_collection</c> described below.</p> @@ -6050,7 +5925,7 @@ true</pre> <v> MonitorPid = pid()</v> <v> Options = [Option]</v> <v> Option = {long_gc, Time} | {large_heap, Size} | busy_port | busy_dist_port</v> - <v> Time = Size = int()</v> + <v> Time = Size = integer()</v> </type> <desc> <p>Returns the current system monitoring settings set by @@ -6084,7 +5959,7 @@ true</pre> <type> <v>MonitorPid = pid()</v> <v>Option = {long_gc, Time} | {large_heap, Size} | busy_port | busy_dist_port</v> - <v> Time = Size = int()</v> + <v> Time = Size = integer()</v> <v>MonSettings = {OldMonitorPid, [Option]}</v> <v> OldMonitorPid = pid()</v> </type> @@ -6314,7 +6189,7 @@ true</pre> <name>time() -> {Hour, Minute, Second}</name> <fsummary>Current time</fsummary> <type> - <v>Hour = Minute = Second = int()</v> + <v>Hour = Minute = Second = integer() >= 0</v> </type> <desc> <p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p> @@ -6342,11 +6217,11 @@ true</pre> </desc> </func> <func> - <name>erlang:trace(PidSpec, How, FlagList) -> int()</name> + <name>erlang:trace(PidSpec, How, FlagList) -> integer() >= 0</name> <fsummary>Set trace flags for a process or processes</fsummary> <type> <v>PidSpec = pid() | existing | new | all</v> - <v>How = bool()</v> + <v>How = boolean()</v> <v>FlagList = [Flag]</v> <v> Flag -- see below</v> </type> @@ -6747,7 +6622,7 @@ true</pre> <type> <v>PidOrFunc = pid() | new | {Module, Function, Arity} | on_load</v> <v> Module = Function = atom()</v> - <v> Arity = int()</v> + <v> Arity = arity()</v> <v>Item, Res -- see below</v> </type> <desc> @@ -6850,7 +6725,7 @@ true</pre> </desc> </func> <func> - <name>erlang:trace_pattern(MFA, MatchSpec) -> int()</name> + <name>erlang:trace_pattern(MFA, MatchSpec) -> integer() >= 0</name> <fsummary>Set trace patterns for global call tracing</fsummary> <desc> <p>The same as @@ -6859,7 +6734,7 @@ true</pre> </desc> </func> <func> - <name>erlang:trace_pattern(MFA, MatchSpec, FlagList) -> int()</name> + <name>erlang:trace_pattern(MFA, MatchSpec, FlagList) -> integer() >= 0</name> <fsummary>Set trace patterns for tracing of function calls</fsummary> <type> <v>MFA, MatchSpec, FlagList -- see below</v> @@ -7039,7 +6914,7 @@ true</pre> </desc> </func> <func> - <name>trunc(Number) -> int()</name> + <name>trunc(Number) -> integer()</name> <fsummary>Return an integer by the truncating a number</fsummary> <type> <v>Number = number()</v> @@ -7053,7 +6928,7 @@ true</pre> </desc> </func> <func> - <name>tuple_size(Tuple) -> int()</name> + <name>tuple_size(Tuple) -> integer() >= 0</name> <fsummary>Return the size of a tuple</fsummary> <type> <v>Tuple = tuple()</v> @@ -7081,12 +6956,10 @@ true</pre> </desc> </func> <func> - <name>erlang:universaltime() -> {Date, Time}</name> + <name>erlang:universaltime() -> DateTime</name> <fsummary>Current date and time according to Universal Time Coordinated (UTC)</fsummary> <type> - <v>Date = {Year, Month, Day}</v> - <v>Time = {Hour, Minute, Second}</v> - <v> Year = Month = Day = Hour = Minute = Second = int()</v> + <v>DateTime = <seealso marker="calendar#type-datetime">calendar:datetime()</seealso></v> </type> <desc> <p>Returns the current date and time according to Universal @@ -7104,9 +6977,8 @@ true</pre> <name>erlang:universaltime_to_localtime({Date1, Time1}) -> {Date2, Time2}</name> <fsummary>Convert from Universal Time Coordinated (UTC) to local date and time</fsummary> <type> - <v>Date1 = Date2 = {Year, Month, Day}</v> - <v>Time1 = Time2 = {Hour, Minute, Second}</v> - <v> Year = Month = Day = Hour = Minute = Second = int()</v> + <v>Date1 = Date2 = <seealso marker="calendar#type-date">calendar:date()</seealso></v> + <v>Time1 = Time2 = <seealso marker="calendar#type-time">calendar:time()</seealso></v> </type> <desc> <p>Converts Universal Time Coordinated (UTC) date and time to @@ -7193,7 +7065,7 @@ true</pre> </desc> </func> <func> - <name>erlang:yield() -> true</name> + <name name="yield" arity="0"/> <fsummary>Let other processes get a chance to execute</fsummary> <desc> <p>Voluntarily let other processes (if any) get a chance to diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index b0d0cda4fa..d5c43f6e57 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -47,15 +47,12 @@ </description> <funcs> <func> - <name>boot(BootArgs) -> void()</name> + <name name="boot" arity="1"/> <fsummary>Start the Erlang runtime system</fsummary> - <type> - <v>BootArgs = [binary()]</v> - </type> <desc> <p>Starts the Erlang runtime system. This function is called when the emulator is started and coordinates system start-up.</p> - <p><c>BootArgs</c> are all command line arguments except + <p><c><anno>BootArgs</anno></c> are all command line arguments except the emulator flags, that is, flags and plain arguments. See <seealso marker="erts:erl">erl(1)</seealso>.</p> <p><c>init</c> itself interprets some of the flags, see @@ -67,17 +64,12 @@ </desc> </func> <func> - <name>get_argument(Flag) -> {ok, Arg} | error</name> + <name name="get_argument" arity="1"/> <fsummary>Get the values associated with a command line user flag</fsummary> - <type> - <v>Flag = atom()</v> - <v>Arg = [Values]</v> - <v> Values = [string()]</v> - </type> <desc> <p>Returns all values associated with the command line user flag - <c>Flag</c>. If <c>Flag</c> is provided several times, each - <c>Values</c> is returned in preserved order.</p> + <c><anno>Flag</anno></c>. If <c><anno>Flag</anno></c> is provided several times, each + <c><anno>Values</anno></c> is returned in preserved order.</p> <pre> % <input>erl -a b c -a d</input> ... @@ -113,48 +105,37 @@ </desc> </func> <func> - <name>get_arguments() -> Flags</name> + <name name="get_arguments" arity="0"/> <fsummary>Get all command line user flags</fsummary> - <type> - <v>Flags = [{Flag, Values}]</v> - <v> Flag = atom()</v> - <v> Values = [string()]</v> - </type> <desc> <p>Returns all command line flags, as well as the system defined flags, see <c>get_argument/1</c>.</p> </desc> </func> <func> - <name>get_plain_arguments() -> [Arg]</name> + <name name="get_plain_arguments" arity="0"/> <fsummary>Get all non-flag command line arguments</fsummary> - <type> - <v>Arg = string()</v> - </type> <desc> <p>Returns any plain command line arguments as a list of strings (possibly empty).</p> </desc> </func> <func> - <name>get_status() -> {InternalStatus, ProvidedStatus}</name> + <name name="get_status" arity="0"/> <fsummary>Get system status information</fsummary> - <type> - <v>InternalStatus = starting | started | stopping</v> - <v>ProvidedStatus = term()</v> - </type> + <type name="internal_status"/> <desc> <p>The current status of the <c>init</c> process can be inspected. During system startup (initialization), - <c>InternalStatus</c> is <c>starting</c>, and - <c>ProvidedStatus</c> indicates how far the boot script has + <c><anno>InternalStatus</anno></c> is <c>starting</c>, and + <c><anno>ProvidedStatus</anno></c> indicates how far the boot script has been interpreted. Each <c>{progress, Info}</c> term - interpreted in the boot script affects <c>ProvidedStatus</c>, - that is, <c>ProvidedStatus</c> gets the value of <c>Info</c>.</p> + interpreted in the boot script affects <c><anno>ProvidedStatus</anno></c>, + that is, <c><anno>ProvidedStatus</anno></c> gets the value of <c>Info</c>.</p> </desc> </func> <func> - <name>reboot() -> void()</name> + <name name="reboot" arity="0"/> <fsummary>Take down and restart an Erlang node smoothly</fsummary> <desc> <p>All applications are taken down smoothly, all code is @@ -168,7 +149,7 @@ </desc> </func> <func> - <name>restart() -> void()</name> + <name name="restart" arity="0"/> <fsummary>Restart the running Erlang node</fsummary> <desc> <p>The system is restarted <em>inside</em> the running Erlang @@ -183,20 +164,17 @@ </desc> </func> <func> - <name>script_id() -> Id</name> + <name name="script_id" arity="0"/> <fsummary>Get the identity of the used boot script</fsummary> - <type> - <v>Id = term()</v> - </type> <desc> <p>Get the identity of the boot script used to boot the system. - <c>Id</c> can be any Erlang term. In the delivered boot - scripts, <c>Id</c> is <c>{Name, Vsn}</c>. <c>Name</c> and + <c><anno>Id</anno></c> can be any Erlang term. In the delivered boot + scripts, <c><anno>Id</anno></c> is <c>{Name, Vsn}</c>. <c>Name</c> and <c>Vsn</c> are strings.</p> </desc> </func> <func> - <name>stop() -> void()</name> + <name name="stop" arity="0"/> <fsummary>Take down an Erlang node smoothly</fsummary> <desc> <p>All applications are taken down smoothly, all code is @@ -210,15 +188,12 @@ </desc> </func> <func> - <name>stop(Status) -> void()</name> + <name name="stop" arity="1"/> <fsummary>Take down an Erlang node smoothly</fsummary> - <type> - <v>Status = int()>=0 | string()</v> - </type> <desc> <p>All applications are taken down smoothly, all code is unloaded, and all ports are closed before the system - terminates by calling <c>halt(Status)</c>. If the + terminates by calling <c>halt(<anno>Status</anno>)</c>. If the <c>-heart</c> command line flag was given, the <c>heart</c> program is terminated before the Erlang node terminates. Refer to <c>heart(3)</c> for more diff --git a/erts/doc/src/specs.xml b/erts/doc/src/specs.xml new file mode 100644 index 0000000000..e5c2f4783f --- /dev/null +++ b/erts/doc/src/specs.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="latin1" ?> +<specs xmlns:xi="http://www.w3.org/2001/XInclude"> + <xi:include href="../specs/specs_erl_prim_loader.xml"/> + <xi:include href="../specs/specs_erlang.xml"/> + <xi:include href="../specs/specs_init.xml"/> + <xi:include href="../specs/specs_zlib.xml"/> +</specs> diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index b1e768bce9..47a649af02 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2005</year><year>2010</year> + <year>2005</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -76,96 +76,92 @@ list_to_binary([Compressed|Last])</pre> </taglist> </description> - <section> - <title>DATA TYPES</title> - <code type="none"> -iodata = iolist() | binary() - -iolist = [char() | binary() | iolist()] - a binary is allowed as the tail of the list - -zstream = a zlib stream, see open/0</code> - </section> + <datatypes> + <datatype> + <name name="zstream"/> + <desc> + <p>A zlib stream, see <seealso marker="#open/0">open/0</seealso>. + </p> + </desc> + </datatype> + <datatype> + <name name="zlevel"/> + </datatype> + <datatype> + <name name="zmemlevel"/> + </datatype> + <datatype> + <name name="zmethod"/> + </datatype> + <datatype> + <name name="zstrategy"/> + </datatype> + <datatype> + <name name="zwindowbits"/> + <desc> + <p>Normally in the range <c>-15..-9 | 9..15</c>.</p> + </desc> + </datatype> + </datatypes> <funcs> <func> - <name>open() -> Z </name> + <name name="open" arity="0"/> <fsummary>Open a stream and return a stream reference</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> <p>Open a zlib stream.</p> </desc> </func> <func> - <name>close(Z) -> ok</name> + <name name="close" arity="1"/> <fsummary>Close a stream</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> - <p>Closes the stream referenced by <c>Z</c>.</p> + <p>Closes the stream referenced by <c><anno>Z</anno></c>.</p> </desc> </func> <func> - <name>deflateInit(Z) -> ok</name> + <name name="deflateInit" arity="1"/> <fsummary>Initialize a session for compression</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> - <p>Same as <c>zlib:deflateInit(Z, default)</c>.</p> + <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p> </desc> </func> <func> - <name>deflateInit(Z, Level) -> ok</name> + <name name="deflateInit" arity="2"/> <fsummary>Initialize a session for compression</fsummary> - <type> - <v>Z = zstream()</v> - <v>Level = none | default | best_speed | best_compression | 0..9</v> - </type> <desc> <p>Initialize a zlib stream for compression.</p> - <p><c>Level</c> decides the compression level to be used, 0 + <p><c><anno>Level</anno></c> decides the compression level to be used, 0 (<c>none</c>), gives no compression at all, 1 (<c>best_speed</c>) gives best speed and 9 (<c>best_compression</c>) gives best compression.</p> </desc> </func> <func> - <name>deflateInit(Z, Level, Method, WindowBits, MemLevel, Strategy) -> ok</name> + <name name="deflateInit" arity="6"/> <fsummary>Initialize a session for compression</fsummary> - <type> - <v>Z = zstream()</v> - <v>Level = none | default | best_speed | best_compression | 0..9</v> - <v>Method = deflated</v> - <v>WindowBits = 9..15|-9..-15</v> - <v>MemLevel = 1..9</v> - <v>Strategy = default|filtered|huffman_only</v> - </type> <desc> <p>Initiates a zlib stream for compression.</p> - <p>The <c>Level</c> parameter decides the compression level to be + <p>The <c><anno>Level</anno></c> parameter decides the compression level to be used, 0 (<c>none</c>), gives no compression at all, 1 (<c>best_speed</c>) gives best speed and 9 (<c>best_compression</c>) gives best compression.</p> - <p>The <c>Method</c> parameter decides which compression method to use, + <p>The <c><anno>Method</anno></c> parameter decides which compression method to use, currently the only supported method is <c>deflated</c>.</p> - <p>The <c>WindowBits</c> parameter is the base two logarithm + <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm of the window size (the size of the history buffer). It should be in the range 9 through 15. Larger values of this parameter result in better compression at the expense of memory usage. The default value is 15 if - <c>deflateInit/2</c>. A negative <c>WindowBits</c> + <c>deflateInit/2</c>. A negative <c><anno>WindowBits</anno></c> value suppresses the zlib header (and checksum) from the stream. Note that the zlib source mentions this only as a undocumented feature.</p> - <p>The <c>MemLevel</c> parameter specifies how much memory + <p>The <c><anno>MemLevel</anno></c> parameter specifies how much memory should be allocated for the internal compression - state. <c>MemLevel</c>=1 uses minimum memory but is slow and - reduces compression ratio; <c>MemLevel</c>=9 uses maximum + state. <c><anno>MemLevel</anno></c>=1 uses minimum memory but is slow and + reduces compression ratio; <c><anno>MemLevel</anno></c>=9 uses maximum memory for optimal speed. The default value is 8.</p> - <p>The <c>Strategy</c> parameter is used to tune the + <p>The <c><anno>Strategy</anno></c> parameter is used to tune the compression algorithm. Use the value <c>default</c> for normal data, <c>filtered</c> for data produced by a filter (or predictor), or <c>huffman_only</c> to force Huffman @@ -175,54 +171,43 @@ zstream = a zlib stream, see open/0</code> tuned to compress them better. The effect of <c>filtered</c>is to force more Huffman coding and less string matching; it is somewhat intermediate between - <c>default</c> and <c>huffman_only</c>. The <c>Strategy</c> + <c>default</c> and <c>huffman_only</c>. The <c><anno>Strategy</anno></c> parameter only affects the compression ratio but not the correctness of the compressed output even if it is not set appropriately.</p> </desc> </func> <func> - <name>deflate(Z, Data) -> Compressed</name> + <name name="deflate" arity="2"/> <fsummary>Compress data</fsummary> - <type> - <v>Z = zstream()</v> - <v>Data = iodata()</v> - <v>Compressed = iolist()</v> - </type> <desc> - <p>Same as <c>deflate(Z, Data, none)</c>.</p> + <p>Same as <c>deflate(<anno>Z</anno>, <anno>Data</anno>, none)</c>.</p> </desc> </func> <func> - <name>deflate(Z, Data, Flush) -> </name> + <name name="deflate" arity="3"/> <fsummary>Compress data</fsummary> - <type> - <v>Z = zstream()</v> - <v>Data = iodata()</v> - <v>Flush = none | sync | full | finish</v> - <v>Compressed = iolist()</v> - </type> <desc> <p><c>deflate/3</c> compresses as much data as possible, and stops when the input buffer becomes empty. It may introduce some output latency (reading input without producing any output) except when forced to flush.</p> - <p>If the parameter <c>Flush</c> is set to <c>sync</c>, all + <p>If the parameter <c><anno>Flush</anno></c> is set to <c>sync</c>, all pending output is flushed to the output buffer and the output is aligned on a byte boundary, so that the decompressor can get all input data available so far. Flushing may degrade compression for some compression algorithms and so it should be used only when necessary.</p> - <p>If <c>Flush</c> is set to <c>full</c>, all output is flushed as with + <p>If <c><anno>Flush</anno></c> is set to <c>full</c>, all output is flushed as with <c>sync</c>, and the compression state is reset so that decompression can restart from this point if previous compressed data has been damaged or if random access is desired. Using <c>full</c> too often can seriously degrade the compression.</p> - <p>If the parameter <c>Flush</c> is set to <c>finish</c>, + <p>If the parameter <c><anno>Flush</anno></c> is set to <c>finish</c>, pending input is processed, pending output is flushed and <c>deflate/3</c> returns. Afterwards the only possible operations on the stream are <c>deflateReset/1</c> or <c>deflateEnd/1</c>.</p> - <p><c>Flush</c> can be set to <c>finish</c> immediately after + <p><c><anno>Flush</anno></c> can be set to <c>finish</c> immediately after <c>deflateInit</c> if all compression is to be done in one step.</p> <pre> @@ -234,13 +219,8 @@ list_to_binary([B1,B2])</pre> </desc> </func> <func> - <name>deflateSetDictionary(Z, Dictionary) -> Adler32</name> + <name name="deflateSetDictionary" arity="2"/> <fsummary>Initialize the compression dictionary</fsummary> - <type> - <v>Z = zstream()</v> - <v>Dictionary = binary()</v> - <v>Adler32 = integer()</v> - </type> <desc> <p>Initializes the compression dictionary from the given byte sequence without producing any compressed output. This @@ -253,11 +233,8 @@ list_to_binary([B1,B2])</pre> </desc> </func> <func> - <name>deflateReset(Z) -> ok</name> + <name name="deflateReset" arity="1"/> <fsummary>Reset the deflate session</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> <p>This function is equivalent to <c>deflateEnd/1</c> followed by <c>deflateInit/[1|2|6]</c>, but does not free @@ -267,34 +244,26 @@ list_to_binary([B1,B2])</pre> </desc> </func> <func> - <name>deflateParams(Z, Level, Strategy) -> ok </name> + <name name="deflateParams" arity="3"/> <fsummary>Dynamicly update deflate parameters</fsummary> - <type> - <v>Z = zstream()</v> - <v>Level = none | default | best_speed | best_compression | 0..9</v> - <v>Strategy = default|filtered|huffman_only</v> - </type> <desc> <p>Dynamically update the compression level and compression - strategy. The interpretation of <c>Level</c> and - <c>Strategy</c> is as in <c>deflateInit/6</c>. This can be + strategy. The interpretation of <c><anno>Level</anno></c> and + <c><anno>Strategy</anno></c> is as in <c>deflateInit/6</c>. This can be used to switch between compression and straight copy of the input data, or to switch to a different kind of input data requiring a different strategy. If the compression level is changed, the input available so far is compressed with the old level (and may be flushed); the new level will take effect only at the next call of <c>deflate/3</c>.</p> - <p>Before the call of deflateParams, the stream state must be set as for + <p>Before the call of <c>deflateParams</c>, the stream state must be set as for a call of <c>deflate/3</c>, since the currently available input may have to be compressed and flushed.</p> </desc> </func> <func> - <name>deflateEnd(Z) -> ok</name> + <name name="deflateEnd" arity="1"/> <fsummary>End deflate session</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> <p>End the deflate session and cleans all data used. Note that this function will throw an <c>data_error</c> @@ -304,43 +273,31 @@ list_to_binary([B1,B2])</pre> </desc> </func> <func> - <name>inflateInit(Z) -> ok </name> + <name name="inflateInit" arity="1"/> <fsummary>Initialize a session for decompression</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> <p>Initialize a zlib stream for decompression.</p> </desc> </func> <func> - <name>inflateInit(Z, WindowBits) -> ok </name> + <name name="inflateInit" arity="2"/> <fsummary>Initialize a session for decompression</fsummary> - <type> - <v>Z = zstream()</v> - <v>WindowBits = 9..15|-9..-15</v> - </type> <desc> <p>Initialize decompression session on zlib stream.</p> - <p>The <c>WindowBits</c> parameter is the base two logarithm + <p>The <c><anno>WindowBits</anno></c> parameter is the base two logarithm of the maximum window size (the size of the history buffer). It should be in the range 9 through 15. The default value is 15 if <c>inflateInit/1</c> is used. If a compressed stream with a larger window size is given as input, inflate() will throw the <c>data_error</c> - exception. A negative <c>WindowBits</c> value makes zlib ignore the + exception. A negative <c><anno>WindowBits</anno></c> value makes zlib ignore the zlib header (and checksum) from the stream. Note that the zlib source mentions this only as a undocumented feature.</p> </desc> </func> <func> - <name>inflate(Z, Data) -> DeCompressed </name> + <name name="inflate" arity="2"/> <fsummary>Decompress data</fsummary> - <type> - <v>Z = zstream()</v> - <v>Data = iodata()</v> - <v>DeCompressed = iolist()</v> - </type> <desc> <p><c>inflate/2</c> decompresses as much data as possible. It may some introduce some output latency (reading @@ -353,12 +310,8 @@ list_to_binary([B1,B2])</pre> </desc> </func> <func> - <name>inflateSetDictionary(Z, Dictionary) -> ok</name> + <name name="inflateSetDictionary" arity="2"/> <fsummary>Initialize the decompression dictionary</fsummary> - <type> - <v>Z = zstream()</v> - <v>Dictionary = binary()</v> - </type> <desc> <p>Initializes the decompression dictionary from the given uncompressed byte sequence. This function must be called @@ -381,11 +334,8 @@ unpack(Z, Compressed, Dict) -> </desc> </func> <func> - <name>inflateReset(Z) -> ok</name> + <name name="inflateReset" arity="1"/> <fsummary>>Reset the inflate session</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> <p>This function is equivalent to <c>inflateEnd/1</c> followed by <c>inflateInit/1</c>, but does not free and reallocate all @@ -394,11 +344,8 @@ unpack(Z, Compressed, Dict) -> </desc> </func> <func> - <name>inflateEnd(Z) -> ok</name> + <name name="inflateEnd" arity="1"/> <fsummary>End inflate session</fsummary> - <type> - <v>Z = zstream()</v> - </type> <desc> <p>End the inflate session and cleans all data used. Note that this function will throw a <c>data_error</c> exception @@ -407,62 +354,39 @@ unpack(Z, Compressed, Dict) -> </desc> </func> <func> - <name>setBufSize(Z, Size) -> ok</name> + <name name="setBufSize" arity="2"/> <fsummary>Set buffer size</fsummary> - <type> - <v>Z = zstream()</v> - <v>Size = integer()</v> - </type> <desc> <p>Sets the intermediate buffer size.</p> </desc> </func> <func> - <name>getBufSize(Z) -> Size</name> + <name name="getBufSize" arity="1"/> <fsummary>Get buffer size</fsummary> - <type> - <v>Z = zstream()</v> - <v>Size = integer()</v> - </type> <desc> <p>Get the size of intermediate buffer.</p> </desc> </func> <func> - <name>crc32(Z) -> CRC</name> + <name name="crc32" arity="1"/> <fsummary>Get current CRC</fsummary> - <type> - <v>Z = zstream()</v> - <v>CRC = integer()</v> - </type> <desc> <p>Get the current calculated CRC checksum.</p> </desc> </func> <func> - <name>crc32(Z, Binary) -> CRC</name> + <name name="crc32" arity="2"/> <fsummary>Calculate CRC</fsummary> - <type> - <v>Z = zstream()</v> - <v>Binary = binary()</v> - <v>CRC = integer()</v> - </type> <desc> - <p>Calculate the CRC checksum for <c>Binary</c>.</p> + <p>Calculate the CRC checksum for <c><anno>Binary</anno></c>.</p> </desc> </func> <func> - <name>crc32(Z, PrevCRC, Binary) -> CRC </name> + <name name="crc32" arity="3"/> <fsummary>Calculate CRC</fsummary> - <type> - <v>Z = zstream()</v> - <v>PrevCRC = integer()</v> - <v>Binary = binary()</v> - <v>CRC = integer()</v> - </type> - <desc> - <p>Update a running CRC checksum for <c>Binary</c>. - If <c>Binary</c> is the empty binary, this function returns + <desc> + <p>Update a running CRC checksum for <c><anno>Binary</anno></c>. + If <c><anno>Binary</anno></c> is the empty binary, this function returns the required initial value for the crc.</p> <pre> Crc = lists:foldl(fun(Bin,Crc0) -> @@ -471,49 +395,31 @@ Crc = lists:foldl(fun(Bin,Crc0) -> </desc> </func> <func> - <name>crc32_combine(Z, CRC1, CRC2, Size2) -> CRC </name> + <name name="crc32_combine" arity="4"/> <fsummary>Combine two CRC's</fsummary> - <type> - <v>Z = zstream()</v> - <v>CRC = integer()</v> - <v>CRC1 = integer()</v> - <v>CRC2 = integer()</v> - <v>Size2 = integer()</v> - </type> <desc> <p>Combine two CRC checksums into one. For two binaries, <c>Bin1</c> and <c>Bin2</c> with sizes of <c>Size1</c> and - <c>Size2</c>, with CRC checksums <c>CRC1</c> and - <c>CRC2</c>. <c>crc32_combine/4</c> returns the <c>CRC</c> + <c><anno>Size2</anno></c>, with CRC checksums <c><anno>CRC1</anno></c> and + <c><anno>CRC2</anno></c>. <c>crc32_combine/4</c> returns the <c><anno>CRC</anno></c> checksum of <c><<Bin1/binary,Bin2/binary>></c>, requiring - only <c>CRC1</c>, <c>CRC2</c>, and <c>Size2</c>. + only <c><anno>CRC1</anno></c>, <c><anno>CRC2</anno></c>, and <c><anno>Size2</anno></c>. </p> </desc> </func> <func> - <name>adler32(Z, Binary) -> Checksum</name> + <name name="adler32" arity="2"/> <fsummary>Calculate the adler checksum</fsummary> - <type> - <v>Z = zstream()</v> - <v>Binary = binary()</v> - <v>Checksum = integer()</v> - </type> <desc> - <p>Calculate the Adler-32 checksum for <c>Binary</c>.</p> + <p>Calculate the Adler-32 checksum for <c><anno>Binary</anno></c>.</p> </desc> </func> <func> - <name>adler32(Z, PrevAdler, Binary) -> Checksum</name> + <name name="adler32" arity="3"/> <fsummary>Calculate the adler checksum</fsummary> - <type> - <v>Z = zstream()</v> - <v>PrevAdler = integer()</v> - <v>Binary = binary()</v> - <v>Checksum = integer()</v> - </type> - <desc> - <p>Update a running Adler-32 checksum for <c>Binary</c>. - If <c>Binary</c> is the empty binary, this function returns + <desc> + <p>Update a running Adler-32 checksum for <c><anno>Binary</anno></c>. + If <c><anno>Binary</anno></c> is the empty binary, this function returns the required initial value for the checksum.</p> <pre> Crc = lists:foldl(fun(Bin,Crc0) -> @@ -522,81 +428,56 @@ Crc = lists:foldl(fun(Bin,Crc0) -> </desc> </func> <func> - <name>adler32_combine(Z, Adler1, Adler2, Size2) -> Adler </name> + <name name="adler32_combine" arity="4"/> <fsummary>Combine two Adler-32 checksums</fsummary> - <type> - <v>Z = zstream()</v> - <v>Adler = integer()</v> - <v>Adler1 = integer()</v> - <v>Adler2 = integer()</v> - <v>Size2 = integer()</v> - </type> <desc> <p>Combine two Adler-32 checksums into one. For two binaries, <c>Bin1</c> and <c>Bin2</c> with sizes of <c>Size1</c> and - <c>Size2</c>, with Adler-32 checksums <c>Adler1</c> and - <c>Adler2</c>. <c>adler32_combine/4</c> returns the <c>Adler</c> + <c><anno>Size2</anno></c>, with Adler-32 checksums <c><anno>Adler1</anno></c> and + <c><anno>Adler2</anno></c>. <c>adler32_combine/4</c> returns the <c><anno>Adler</anno></c> checksum of <c><<Bin1/binary,Bin2/binary>></c>, requiring - only <c>Adler1</c>, <c>Adler2</c>, and <c>Size2</c>. + only <c><anno>Adler1</anno></c>, <c><anno>Adler2</anno></c>, and <c><anno>Size2</anno></c>. </p> </desc> </func> <func> - <name>compress(Binary) -> Compressed </name> + <name name="compress" arity="1"/> <fsummary>Compress a binary with standard zlib functionality</fsummary> - <type> - <v>Binary = Compressed = binary()</v> - </type> <desc> <p>Compress a binary (with zlib headers and checksum).</p> </desc> </func> <func> - <name>uncompress(Binary) -> Decompressed</name> + <name name="uncompress" arity="1"/> <fsummary>Uncompress a binary with standard zlib functionality</fsummary> - <type> - <v>Binary = Decompressed = binary()</v> - </type> <desc> <p>Uncompress a binary (with zlib headers and checksum).</p> </desc> </func> <func> - <name>zip(Binary) -> Compressed</name> + <name name="zip" arity="1"/> <fsummary>Compress a binary without the zlib headers</fsummary> - <type> - <v>Binary = Compressed = binary()</v> - </type> <desc> <p>Compress a binary (without zlib headers and checksum).</p> </desc> </func> <func> - <name>unzip(Binary) -> Decompressed</name> + <name name="unzip" arity="1"/> <fsummary>Uncompress a binary without the zlib headers</fsummary> - <type> - <v>Binary = Decompressed = binary()</v> - </type> <desc> <p>Uncompress a binary (without zlib headers and checksum).</p> </desc> </func> <func> - <name>gzip(Data) -> Compressed</name> + <name name="gzip" arity="1"/> <fsummary>Compress a binary with gz header</fsummary> - <type> - <v>Binary = Compressed = binary()</v> - </type> <desc> <p>Compress a binary (with gz headers and checksum).</p> </desc> </func> <func> - <name>gunzip(Bin) -> Decompressed</name> + <name name="gunzip" arity="1"/> <fsummary>Uncompress a binary with gz header</fsummary> - <type> - <v>Binary = Decompressed = binary()</v> - </type> <desc> <p>Uncompress a binary (with gz headers and checksum).</p> </desc> diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c index 6e60512f6d..d680b67dd6 100644 --- a/erts/etc/win32/Install.c +++ b/erts/etc/win32/Install.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2010. All Rights Reserved. + * Copyright Ericsson AB 2003-2011. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -213,6 +213,9 @@ int main(int argc, char **argv) fprintf(stderr,"Cannot continue installation, bailing out.\n"); exit(1); } + + /* OBS!!! If the format of the init file is changed, do not forget + to update release_handler:write_ini_file(...) */ ini_file = create_init_file(); ini_section = create_init_section("erlang"); add_init_section(ini_file,ini_section); diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex dccb92a5af..20c82c52bb 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex b537546071..9202b5be4f 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex 901def5eba..faa2cf573c 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex 542e266f88..d400269ed0 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/erl_prim_loader.erl b/erts/preloaded/src/erl_prim_loader.erl index 024b20eadb..ccfa7978c8 100644 --- a/erts/preloaded/src/erl_prim_loader.erl +++ b/erts/preloaded/src/erl_prim_loader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -102,8 +102,14 @@ debug(#prim_state{debug = Deb}, Term) -> %%% Interface Functions. %%% -------------------------------------------------------- --spec start(term(), atom() | string(), host() | [host()]) -> - {'ok', pid()} | {'error', term()}. +-spec start(Id, Loader, Hosts) -> + {'ok', Pid} | {'error', What} when + Id :: term(), + Loader :: atom() | string(), + Hosts :: Host | [Host], + Host :: host(), + Pid :: pid(), + What :: term(). start(Id, Pgm, Hosts) when is_atom(Hosts) -> start(Id, Pgm, [Hosts]); start(Id, Pgm0, Hosts) -> @@ -123,18 +129,6 @@ start(Id, Pgm0, Hosts) -> {error,Reason} end. -start_it("ose_inet"=Cmd, Id, Pid, Hosts) -> - %% Setup reserved port for ose_inet driver (only OSE) - case catch erlang:open_port({spawn,Cmd}, [binary]) of - {'EXIT',Why} -> - ?dbg(ose_inet_port_open_fail, Why), - Why; - OseInetPort -> - ?dbg(ose_inet_port, OseInetPort), - OseInetPort - end, - start_it("inet", Id, Pid, Hosts); - %% Hosts must be a list on form ['1.2.3.4' ...] start_it("inet", Id, Pid, Hosts) -> process_flag(trap_exit, true), @@ -174,15 +168,20 @@ init_ack(Pid) -> Pid ! {self(),ok}, ok. --spec set_path([string()]) -> 'ok'. +-spec set_path(Path) -> 'ok' when + Path :: [Dir :: string()]. set_path(Paths) when is_list(Paths) -> request({set_path,Paths}). --spec get_path() -> {'ok', [string()]}. +-spec get_path() -> {'ok', Path} when + Path :: [Dir :: string()]. get_path() -> request({get_path,[]}). --spec get_file(atom() | string()) -> {'ok', binary(), string()} | 'error'. +-spec get_file(Filename) -> {'ok', Bin, FullName} | 'error' when + Filename :: atom() | string(), + Bin :: binary(), + FullName :: string(). get_file(File) when is_atom(File) -> get_file(atom_to_list(File)); get_file(File) -> @@ -202,13 +201,15 @@ get_files(ModFiles, Fun) -> ok end. --spec list_dir(string()) -> {'ok', [string()]} | 'error'. +-spec list_dir(Dir) -> {'ok', Filenames} | 'error' when + Dir :: string(), + Filenames :: [Filename :: string()]. list_dir(Dir) -> check_file_result(list_dir, Dir, request({list_dir,Dir})). -%% -> {ok,Info} | error --spec read_file_info(string()) -> {'ok', tuple()} | 'error'. - +-spec read_file_info(Filename) -> {'ok', FileInfo} | 'error' when + Filename :: string(), + FileInfo :: file:file_info(). read_file_info(File) -> check_file_result(read_file_info, File, request({read_file_info,File})). @@ -1360,9 +1361,7 @@ pathtype(Name) when is_list(Name) -> absolute; _ -> relative - end; - {ose,_} -> - unix_pathtype(Name) + end end. unix_pathtype(Name) -> diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 4679a916c7..5deb69edab 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -53,22 +53,31 @@ -compile({no_auto_import,[spawn_opt/4]}). -compile({no_auto_import,[spawn_opt/5]}). -%%-------------------------------------------------------------------------- +-export_type([timestamp/0]). --type date() :: {pos_integer(), pos_integer(), pos_integer()}. --type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. --type date_time() :: {date(), time()}. +-type timestamp() :: {MegaSecs :: non_neg_integer(), + Secs :: non_neg_integer(), + MicroSecs :: non_neg_integer()}. %%-------------------------------------------------------------------------- +-spec apply(Fun, Args) -> term() when + Fun :: function(), + Args :: [term()]. apply(Fun, Args) -> erlang:apply(Fun, Args). +-spec apply(Module, Function, Args) -> term() when + Module :: module(), + Function :: atom(), + Args :: [term()]. apply(Mod, Name, Args) -> erlang:apply(Mod, Name, Args). %% Spawns with a fun +-spec spawn(Fun) -> pid() when + Fun :: function(). spawn(F) when is_function(F) -> spawn(erlang, apply, [F, []]); spawn({M,F}=MF) when is_atom(M), is_atom(F) -> @@ -76,6 +85,9 @@ spawn({M,F}=MF) when is_atom(M), is_atom(F) -> spawn(F) -> erlang:error(badarg, [F]). +-spec spawn(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn(N, F) when N =:= node() -> spawn(F); spawn(N, F) when is_function(F) -> @@ -85,6 +97,8 @@ spawn(N, {M,F}=MF) when is_atom(M), is_atom(F) -> spawn(N, F) -> erlang:error(badarg, [N, F]). +-spec spawn_link(Fun) -> pid() when + Fun :: function(). spawn_link(F) when is_function(F) -> spawn_link(erlang, apply, [F, []]); spawn_link({M,F}=MF) when is_atom(M), is_atom(F) -> @@ -92,6 +106,9 @@ spawn_link({M,F}=MF) when is_atom(M), is_atom(F) -> spawn_link(F) -> erlang:error(badarg, [F]). +-spec spawn_link(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn_link(N, F) when N =:= node() -> spawn_link(F); spawn_link(N, F) when is_function(F) -> @@ -103,16 +120,30 @@ spawn_link(N, F) -> %% Spawn and atomically set up a monitor. +-spec spawn_monitor(Fun) -> {pid(), reference()} when + Fun :: function(). spawn_monitor(F) when is_function(F, 0) -> erlang:spawn_opt({erlang,apply,[F,[]],[monitor]}); spawn_monitor(F) -> erlang:error(badarg, [F]). +-spec spawn_monitor(Module, Function, Args) -> {pid(), reference()} when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_monitor(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> erlang:spawn_opt({M,F,A,[monitor]}); spawn_monitor(M, F, A) -> erlang:error(badarg, [M,F,A]). +-spec spawn_opt(Fun, Options) -> pid() | {pid(), reference()} when + Fun :: function(), + Options :: [Option], + Option :: link | monitor | {priority, Level} + | {fullsweep_after, Number :: non_neg_integer()} + | {min_heap_size, Size :: non_neg_integer()} + | {min_bin_vheap_size, VSize :: non_neg_integer()}, + Level :: low | normal | high. spawn_opt(F, O) when is_function(F) -> spawn_opt(erlang, apply, [F, []], O); spawn_opt({M,F}=MF, O) when is_atom(M), is_atom(F) -> @@ -122,6 +153,15 @@ spawn_opt({M,F,A}, O) -> % For (undocumented) backward compatibility spawn_opt(F, O) -> erlang:error(badarg, [F, O]). +-spec spawn_opt(Node, Fun, Options) -> pid() | {pid(), reference()} when + Node :: node(), + Fun :: function(), + Options :: [Option], + Option :: link | monitor | {priority, Level} + | {fullsweep_after, Number :: non_neg_integer()} + | {min_heap_size, Size :: non_neg_integer()} + | {min_bin_vheap_size, VSize :: non_neg_integer()}, + Level :: low | normal | high. spawn_opt(N, F, O) when N =:= node() -> spawn_opt(F, O); spawn_opt(N, F, O) when is_function(F) -> @@ -133,6 +173,11 @@ spawn_opt(N, F, O) -> %% Spawns with MFA +-spec spawn(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) -> spawn(M,F,A); spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) -> @@ -158,6 +203,11 @@ spawn(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) -> spawn(N,M,F,A) -> erlang:error(badarg, [N, M, F, A]). +-spec spawn_link(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(N,M,F,A) when N =:= node(), is_atom(M), is_atom(F), is_list(A) -> spawn_link(M,F,A); spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) -> @@ -183,6 +233,17 @@ spawn_link(N,M,F,A) when is_atom(N), is_atom(M), is_atom(F) -> spawn_link(N,M,F,A) -> erlang:error(badarg, [N, M, F, A]). +-spec spawn_opt(Module, Function, Args, Options) -> + pid() | {pid(), reference()} when + Module :: module(), + Function :: atom(), + Args :: [term()], + Options :: [Option], + Option :: link | monitor | {priority, Level} + | {fullsweep_after, Number :: non_neg_integer()} + | {min_heap_size, Size :: non_neg_integer()} + | {min_bin_vheap_size, VSize :: non_neg_integer()}, + Level :: low | normal | high. spawn_opt(M, F, A, Opts) -> case catch erlang:spawn_opt({M,F,A,Opts}) of {'EXIT',{Reason,_}} -> @@ -191,6 +252,18 @@ spawn_opt(M, F, A, Opts) -> Res end. +-spec spawn_opt(Node, Module, Function, Args, Options) -> + pid() | {pid(), reference()} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Options :: [Option], + Option :: link | monitor | {priority, Level} + | {fullsweep_after, Number :: non_neg_integer()} + | {min_heap_size, Size :: non_neg_integer()} + | {min_bin_vheap_size, VSize :: non_neg_integer()}, + Level :: low | normal | high. spawn_opt(N, M, F, A, O) when N =:= node(), is_atom(M), is_atom(F), is_list(A), is_list(O) -> @@ -260,18 +333,25 @@ crasher(Node,Mod,Fun,Args,Opts,Reason) -> [Mod,Fun,Args,Opts,Node]), exit(Reason). --spec yield() -> 'true'. +-spec erlang:yield() -> 'true'. yield() -> erlang:yield(). --spec nodes() -> [node()]. +-spec nodes() -> Nodes when + Nodes :: [node()]. nodes() -> erlang:nodes(visible). --spec disconnect_node(node()) -> boolean(). +-spec disconnect_node(Node) -> boolean() | ignored when + Node :: node(). disconnect_node(Node) -> net_kernel:disconnect(Node). +-spec erlang:fun_info(Fun) -> [{Item, Info}] when + Fun :: function(), + Item :: arity | env | index | name + | module | new_index | new_uniq | pid | type | uniq, + Info :: term(). fun_info(Fun) when is_function(Fun) -> Keys = [type,env,arity,name,uniq,index,new_uniq,new_index,module,pid], fun_info_1(Keys, Fun, []). @@ -283,24 +363,37 @@ fun_info_1([K|Ks], Fun, A) -> end; fun_info_1([], _, A) -> A. --type dst() :: pid() | port() | atom() | {atom(), node()}. +-type dst() :: pid() + | port() + | (RegName :: atom()) + | {RegName :: atom(), Node :: node()}. --spec send_nosuspend(dst(), term()) -> boolean(). +-spec erlang:send_nosuspend(Dest, Msg) -> boolean() when + Dest :: dst(), + Msg :: term(). send_nosuspend(Pid, Msg) -> send_nosuspend(Pid, Msg, []). --spec send_nosuspend(dst(), term(), ['noconnect' | 'nosuspend']) -> boolean(). +-spec erlang:send_nosuspend(Dest, Msg, Options) -> boolean() when + Dest :: dst(), + Msg :: term(), + Options :: [noconnect]. send_nosuspend(Pid, Msg, Opts) -> case erlang:send(Pid, Msg, [nosuspend|Opts]) of ok -> true; _ -> false end. --spec localtime_to_universaltime(date_time()) -> date_time(). +-spec erlang:localtime_to_universaltime({Date1, Time1}) -> {Date2, Time2} when + Date1 :: calendar:date(), + Date2 :: calendar:date(), + Time1 :: calendar:time(), + Time2 :: calendar:time(). localtime_to_universaltime(Localtime) -> erlang:localtime_to_universaltime(Localtime, undefined). --spec suspend_process(pid()) -> 'true'. +-spec erlang:suspend_process(Suspendee) -> 'true' when + Suspendee :: pid(). suspend_process(P) -> case catch erlang:suspend_process(P, []) of {'EXIT', {Reason, _}} -> erlang:error(Reason, [P]); @@ -426,6 +519,9 @@ delay_trap(Result, Timeout) -> receive after Timeout -> Result end. %% Messages to us use our cookie. IF we change our cookie, other nodes %% have to reflect that, which we cannot forsee. %% +-spec erlang:set_cookie(Node, Cookie) -> true when + Node :: node(), + Cookie :: atom(). set_cookie(Node, C) when Node =/= nonode@nohost, is_atom(Node) -> case is_atom(C) of true -> @@ -434,14 +530,19 @@ set_cookie(Node, C) when Node =/= nonode@nohost, is_atom(Node) -> error(badarg) end. --spec get_cookie() -> atom(). +-spec erlang:get_cookie() -> Cookie | nocookie when + Cookie :: atom(). get_cookie() -> auth:get_cookie(). +-spec concat_binary(ListOfBinaries) -> binary() when + ListOfBinaries :: iolist(). concat_binary(List) -> list_to_binary(List). --spec integer_to_list(integer(), 1..255) -> string(). +-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) @@ -469,6 +570,9 @@ integer_to_list(I0, Base, R0) -> end. +-spec list_to_integer(String, Base) -> integer() when + String :: string(), + Base :: 2..36. list_to_integer(L, 10) -> erlang:list_to_integer(L); list_to_integer(L, Base) @@ -689,10 +793,16 @@ await_proc_exit(Proc, Op, Data) -> end end. --spec min(term(), term()) -> term(). +-spec min(Term1, Term2) -> Minimum when + Term1 :: term(), + Term2 :: term(), + Minimum :: term(). min(A, B) when A > B -> B; min(A, _) -> A. --spec max(term(), term()) -> term(). +-spec max(Term1, Term2) -> Maximum when + Term1 :: term(), + Term2 :: term(), + Maximum :: term(). max(A, B) when A < B -> B; max(A, _) -> A. diff --git a/erts/preloaded/src/init.erl b/erts/preloaded/src/init.erl index 24430a3d40..e52c813029 100644 --- a/erts/preloaded/src/init.erl +++ b/erts/preloaded/src/init.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,10 +28,10 @@ %% : $Var in the boot script is expanded to %% Value. %% -loader LoaderMethod -%% : efile, inet, ose_inet +%% : efile, inet %% (Optional - default efile) %% -hosts [Node] : List of hosts from which we can boot. -%% (Mandatory if -loader inet or ose_inet) +%% (Mandatory if -loader inet) %% -mode embedded : Load all modules at startup, no automatic loading %% -mode interactive : Auto load modules (default system behaviour). %% -path : Override path in bootfile. @@ -79,19 +79,24 @@ debug(false, _) -> ok; debug(_, T) -> erlang:display(T). --spec get_arguments() -> [{atom(), [string()]}]. +-spec get_arguments() -> Flags when + Flags :: [{Flag :: atom(), Values :: [string()]}]. get_arguments() -> request(get_arguments). --spec get_plain_arguments() -> [string()]. +-spec get_plain_arguments() -> [Arg] when + Arg :: string(). get_plain_arguments() -> bs2ss(request(get_plain_arguments)). --spec get_argument(atom()) -> 'error' | {'ok', [[string()]]}. +-spec get_argument(Flag) -> {'ok', Arg} | 'error' when + Flag :: atom(), + Arg :: [Values :: [string()]]. get_argument(Arg) -> request({get_argument, Arg}). --spec script_id() -> term(). +-spec script_id() -> Id when + Id :: term(). script_id() -> request(script_id). @@ -105,7 +110,9 @@ bs2ss(L0) when is_list(L0) -> bs2ss(L) -> L. --spec get_status() -> {internal_status(), term()}. +-spec get_status() -> {InternalStatus, ProvidedStatus} when + InternalStatus :: internal_status(), + ProvidedStatus :: term(). get_status() -> request(get_status). @@ -150,10 +157,12 @@ reboot() -> init ! {stop,reboot}, ok. -spec stop() -> 'ok'. stop() -> init ! {stop,stop}, ok. --spec stop(non_neg_integer() | string()) -> 'ok'. +-spec stop(Status) -> 'ok' when + Status :: non_neg_integer() | string(). stop(Status) -> init ! {stop,{stop,Status}}, ok. --spec boot([binary()]) -> no_return(). +-spec boot(BootArgs) -> no_return() when + BootArgs :: [binary()]. boot(BootArgs) -> register(init, self()), process_flag(trap_exit, true), @@ -1024,7 +1033,7 @@ start_it({eval,Bin}) -> TsR -> reverse([{dot,1} | TsR]) end, {ok,Expr} = erl_parse:parse_exprs(Ts1), - erl_eval:exprs(Expr, []), + erl_eval:exprs(Expr, erl_eval:new_bindings()), ok; start_it([_|_]=MFA) -> Ref = make_ref(), diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 51d6cd0a0b..6cc7b27114 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -124,7 +124,6 @@ -type zwindowbits() :: -15..-9 | 9..47. -type zmemlevel() :: 1..9. -type zstrategy() :: 'default' | 'filtered' | 'huffman_only'. --type zflush() :: 'none' | 'sync' | 'full' | 'finish'. %%------------------------------------------------------------------------ @@ -134,7 +133,8 @@ open() -> open_port({spawn, "zlib_drv"}, [binary]). %% close and release z_stream --spec close(zstream()) -> 'ok'. +-spec close(Z) -> 'ok' when + Z :: zstream(). close(Z) -> try true = port_close(Z), @@ -145,16 +145,25 @@ close(Z) -> catch _:_ -> erlang:error(badarg) end. --spec deflateInit(zstream()) -> 'ok'. +-spec deflateInit(Z) -> 'ok' when + Z :: zstream(). deflateInit(Z) -> call(Z, ?DEFLATE_INIT, <<?Z_DEFAULT_COMPRESSION:32>>). --spec deflateInit(zstream(), zlevel()) -> 'ok'. +-spec deflateInit(Z, Level) -> 'ok' when + Z :: zstream(), + Level :: zlevel(). deflateInit(Z, Level) -> call(Z, ?DEFLATE_INIT, <<(arg_level(Level)):32>>). --spec deflateInit(zstream(), zlevel(), zmethod(), - zwindowbits(), zmemlevel(), zstrategy()) -> 'ok'. +-spec deflateInit(Z, Level, Method, + WindowBits, MemLevel, Strategy) -> 'ok' when + Z :: zstream(), + Level :: zlevel(), + Method :: zmethod(), + WindowBits :: zwindowbits(), + MemLevel :: zmemlevel(), + Strategy :: zstrategy(). deflateInit(Z, Level, Method, WindowBits, MemLevel, Strategy) -> call(Z, ?DEFLATE_INIT2, <<(arg_level(Level)):32, (arg_method(Method)):32, @@ -162,24 +171,38 @@ deflateInit(Z, Level, Method, WindowBits, MemLevel, Strategy) -> (arg_mem(MemLevel)):32, (arg_strategy(Strategy)):32>>). --spec deflateSetDictionary(zstream(), binary()) -> integer(). +-spec deflateSetDictionary(Z, Dictionary) -> Adler32 when + Z :: zstream(), + Dictionary :: binary(), + Adler32 :: integer(). deflateSetDictionary(Z, Dictionary) -> call(Z, ?DEFLATE_SETDICT, Dictionary). --spec deflateReset(zstream()) -> 'ok'. +-spec deflateReset(Z) -> 'ok' when + Z :: zstream(). deflateReset(Z) -> call(Z, ?DEFLATE_RESET, []). --spec deflateParams(zstream(), zlevel(), zstrategy()) -> 'ok'. +-spec deflateParams(Z, Level, Strategy) -> ok when + Z :: zstream(), + Level :: zlevel(), + Strategy :: zstrategy(). deflateParams(Z, Level, Strategy) -> call(Z, ?DEFLATE_PARAMS, <<(arg_level(Level)):32, (arg_strategy(Strategy)):32>>). --spec deflate(zstream(), iodata()) -> iolist(). +-spec deflate(Z, Data) -> Compressed when + Z :: zstream(), + Data :: iodata(), + Compressed :: iolist(). deflate(Z, Data) -> deflate(Z, Data, none). --spec deflate(zstream(), iodata(), zflush()) -> iolist(). +-spec deflate(Z, Data, Flush) -> Compressed when + Z :: zstream(), + Data :: iodata(), + Flush :: none | sync | full | finish, + Compressed :: iolist(). deflate(Z, Data, Flush) -> try port_command(Z, Data) of true -> @@ -191,19 +214,25 @@ deflate(Z, Data, Flush) -> erlang:error(badarg) end. --spec deflateEnd(zstream()) -> 'ok'. +-spec deflateEnd(Z) -> 'ok' when + Z :: zstream(). deflateEnd(Z) -> call(Z, ?DEFLATE_END, []). --spec inflateInit(zstream()) -> 'ok'. +-spec inflateInit(Z) -> 'ok' when + Z :: zstream(). inflateInit(Z) -> call(Z, ?INFLATE_INIT, []). --spec inflateInit(zstream(), zwindowbits()) -> 'ok'. +-spec inflateInit(Z, WindowBits) -> 'ok' when + Z :: zstream(), + WindowBits :: zwindowbits(). inflateInit(Z, WindowBits) -> call(Z, ?INFLATE_INIT2, <<(arg_bitsz(WindowBits)):32>>). --spec inflateSetDictionary(zstream(), binary()) -> 'ok'. +-spec inflateSetDictionary(Z, Dictionary) -> 'ok' when + Z :: zstream(), + Dictionary :: binary(). inflateSetDictionary(Z, Dictionary) -> call(Z, ?INFLATE_SETDICT, Dictionary). @@ -211,11 +240,15 @@ inflateSetDictionary(Z, Dictionary) -> inflateSync(Z) -> call(Z, ?INFLATE_SYNC, []). --spec inflateReset(zstream()) -> 'ok'. +-spec inflateReset(Z) -> 'ok' when + Z :: zstream(). inflateReset(Z) -> call(Z, ?INFLATE_RESET, []). --spec inflate(zstream(), iodata()) -> iolist(). +-spec inflate(Z, Data) -> Decompressed when + Z :: zstream(), + Data :: iodata(), + Decompressed :: iolist(). inflate(Z, Data) -> try port_command(Z, Data) of true -> @@ -227,50 +260,81 @@ inflate(Z, Data) -> erlang:error(badarg) end. --spec inflateEnd(zstream()) -> 'ok'. +-spec inflateEnd(Z) -> 'ok' when + Z :: zstream(). inflateEnd(Z) -> call(Z, ?INFLATE_END, []). --spec setBufSize(zstream(), non_neg_integer()) -> 'ok'. +-spec setBufSize(Z, Size) -> 'ok' when + Z :: zstream(), + Size :: non_neg_integer(). setBufSize(Z, Size) -> call(Z, ?SET_BUFSZ, <<Size:32>>). --spec getBufSize(zstream()) -> non_neg_integer(). +-spec getBufSize(Z) -> Size when + Z :: zstream(), + Size :: non_neg_integer(). getBufSize(Z) -> call(Z, ?GET_BUFSZ, []). --spec crc32(zstream()) -> integer(). +-spec crc32(Z) -> CRC when + Z :: zstream(), + CRC :: integer(). crc32(Z) -> call(Z, ?CRC32_0, []). --spec crc32(zstream(), binary()) -> integer(). +-spec crc32(Z, Binary) -> CRC when + Z :: zstream(), + Binary :: binary(), + CRC :: integer(). crc32(Z, Binary) -> call(Z, ?CRC32_1, Binary). --spec crc32(zstream(), integer(), binary()) -> integer(). +-spec crc32(Z, PrevCRC, Binary) -> CRC when + Z :: zstream(), + PrevCRC :: integer(), + Binary :: binary(), + CRC :: integer(). crc32(Z, CRC, Binary) when is_binary(Binary), is_integer(CRC) -> call(Z, ?CRC32_2, <<CRC:32, Binary/binary>>); crc32(_Z, _CRC, _Binary) -> erlang:error(badarg). --spec adler32(zstream(), binary()) -> integer(). +-spec adler32(Z, Binary) -> CheckSum when + Z :: zstream(), + Binary :: binary(), + CheckSum :: integer(). adler32(Z, Binary) -> call(Z, ?ADLER32_1, Binary). --spec adler32(zstream(), integer(), binary()) -> integer(). +-spec adler32(Z, PrevAdler, Binary) -> CheckSum when + Z :: zstream(), + PrevAdler :: integer(), + Binary :: binary(), + CheckSum :: integer(). adler32(Z, Adler, Binary) when is_binary(Binary), is_integer(Adler) -> call(Z, ?ADLER32_2, <<Adler:32, Binary/binary>>); adler32(_Z, _Adler, _Binary) -> erlang:error(badarg). --spec crc32_combine(zstream(), integer(), integer(), integer()) -> integer(). +-spec crc32_combine(Z, CRC1, CRC2, Size2) -> CRC when + Z :: zstream(), + CRC :: integer(), + CRC1 :: integer(), + CRC2 :: integer(), + Size2 :: integer(). crc32_combine(Z, CRC1, CRC2, Len2) when is_integer(CRC1), is_integer(CRC2), is_integer(Len2) -> call(Z, ?CRC32_COMBINE, <<CRC1:32, CRC2:32, Len2:32>>); crc32_combine(_Z, _CRC1, _CRC2, _Len2) -> erlang:error(badarg). --spec adler32_combine(zstream(), integer(), integer(), integer()) -> integer(). +-spec adler32_combine(Z, Adler1, Adler2, Size2) -> Adler when + Z :: zstream(), + Adler :: integer(), + Adler1 :: integer(), + Adler2 :: integer(), + Size2 :: integer(). adler32_combine(Z, Adler1, Adler2, Len2) when is_integer(Adler1), is_integer(Adler2), is_integer(Len2) -> call(Z, ?ADLER32_COMBINE, <<Adler1:32, Adler2:32, Len2:32>>); @@ -282,7 +346,9 @@ getQSize(Z) -> call(Z, ?GET_QSIZE, []). %% compress/uncompress zlib with header --spec compress(binary()) -> binary(). +-spec compress(Binary) -> Compressed when + Binary :: binary(), + Compressed :: binary(). compress(Binary) -> Z = open(), deflateInit(Z, default), @@ -291,7 +357,9 @@ compress(Binary) -> close(Z), list_to_binary(Bs). --spec uncompress(binary()) -> binary(). +-spec uncompress(Binary) -> Decompressed when + Binary :: binary(), + Decompressed :: binary(). uncompress(Binary) when byte_size(Binary) >= 8 -> Z = open(), inflateInit(Z), @@ -303,7 +371,9 @@ uncompress(Binary) when is_binary(Binary) -> erlang:error(data_error); uncompress(_) -> erlang:error(badarg). %% unzip/zip zlib without header (zip members) --spec zip(binary()) -> binary(). +-spec zip(Binary) -> Compressed when + Binary :: binary(), + Compressed :: binary(). zip(Binary) -> Z = open(), deflateInit(Z, default, deflated, -?MAX_WBITS, 8, default), @@ -312,7 +382,9 @@ zip(Binary) -> close(Z), list_to_binary(Bs). --spec unzip(binary()) -> binary(). +-spec unzip(Binary) -> Decompressed when + Binary :: binary(), + Decompressed :: binary(). unzip(Binary) -> Z = open(), inflateInit(Z, -?MAX_WBITS), @@ -321,7 +393,9 @@ unzip(Binary) -> close(Z), list_to_binary(Bs). --spec gzip(iodata()) -> binary(). +-spec gzip(Data) -> Compressed when + Data :: iodata(), + Compressed :: binary(). gzip(Data) when is_binary(Data); is_list(Data) -> Z = open(), deflateInit(Z, default, deflated, 16+?MAX_WBITS, 8, default), @@ -331,7 +405,9 @@ gzip(Data) when is_binary(Data); is_list(Data) -> iolist_to_binary(Bs); gzip(_) -> erlang:error(badarg). --spec gunzip(iodata()) -> binary(). +-spec gunzip(Binary) -> Decompressed when + Binary :: binary(), + Decompressed :: binary(). gunzip(Data) when is_binary(Data); is_list(Data) -> Z = open(), inflateInit(Z, 16+?MAX_WBITS), diff --git a/lib/erl_docgen/priv/bin/xref_mod_app.escript b/lib/erl_docgen/priv/bin/xref_mod_app.escript index 13671ef2f8..c2bd62f9e0 100755 --- a/lib/erl_docgen/priv/bin/xref_mod_app.escript +++ b/lib/erl_docgen/priv/bin/xref_mod_app.escript @@ -73,7 +73,12 @@ usage() -> modapp(TopDir) -> AppDirs = filelib:wildcard(filename:join([TopDir,"lib","*"])), AM = [appmods(D) || D <- AppDirs], - lists:keysort(1, [{M,A} || {A,Ms} <- AM, M <- Ms]). + ERTS = [preloaded(TopDir) || lists:keyfind("erts", 1, AM) =:= false], + lists:keysort(1, [{M,A} || {A,Ms} <- ERTS++AM, M <- Ms]). + +preloaded(TopDir) -> + {"preloaded",Mods} = appmods(filename:join([TopDir,"erts","preloaded"])), + {"erts",Mods}. %% It's OK if too much data is generated as long as all applications %% and all modules are mentioned. diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index 982572aeef..a9052f29e5 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -433,6 +433,8 @@ <!-- Search "local types" as well --> <xsl:variable name="local_types" select="ancestor::desc/preceding-sibling::type + [string-length(@name) > 0] + | ancestor::type_desc/preceding-sibling::type [string-length(@name) > 0]"/> <xsl:variable name="has_anno_in_local_type"> <xsl:for-each select="$local_types"> diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl index 25b62f68c5..8db4714249 100644 --- a/lib/erl_docgen/priv/xsl/db_man.xsl +++ b/lib/erl_docgen/priv/xsl/db_man.xsl @@ -363,6 +363,8 @@ <!-- Search "local types" as well --> <xsl:variable name="local_types" select="ancestor::desc/preceding-sibling::type + [string-length(@name) > 0] + | ancestor::type_desc/preceding-sibling::type [string-length(@name) > 0]"/> <xsl:variable name="has_anno_in_local_type"> <xsl:for-each select="$local_types"> diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl index 5119e3e36a..a3d601d861 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl @@ -410,6 +410,8 @@ <!-- Search "local types" as well --> <xsl:variable name="local_types" select="ancestor::desc/preceding-sibling::type + [string-length(@name) > 0] + | ancestor::type_desc/preceding-sibling::type [string-length(@name) > 0]"/> <xsl:variable name="has_anno_in_local_type"> <xsl:for-each select="$local_types"> diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 0b47c7b6e1..f1be658054 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -702,7 +702,7 @@ type(erlang, demonitor, 1, Xs) -> type(erlang, demonitor, 2, Xs) -> strict(arg_types(erlang, demonitor, 2), Xs, fun (_) -> t_boolean() end); type(erlang, disconnect_node, 1, Xs) -> - strict(arg_types(erlang, disconnect_node, 1), Xs, fun (_) -> t_boolean() end); + strict(arg_types(erlang, disconnect_node, 1), Xs, fun (_) -> t_sup([t_boolean(), t_atom('ignored')]) end); type(erlang, display, 1, _) -> t_atom('true'); type(erlang, display_string, 1, Xs) -> strict(arg_types(erlang, display_string, 1), Xs, fun(_) -> t_atom('true') end); @@ -1124,7 +1124,7 @@ type(erlang, nodes, 0, _) -> t_list(t_node()); type(erlang, nodes, 1, Xs) -> strict(arg_types(erlang, nodes, 1), Xs, fun (_) -> t_list(t_node()) end); type(erlang, now, 0, _) -> - t_time(); + t_timestamp(); type(erlang, open_port, 2, Xs) -> strict(arg_types(erlang, open_port, 2), Xs, fun (_) -> t_port() end); type(erlang, phash, 2, Xs) -> @@ -1585,8 +1585,7 @@ type(erlang, system_info, 1, Xs) -> ['multi_scheduling_blockers'] -> t_list(t_pid()); ['os_type'] -> - t_tuple([t_sup([t_atom('ose'), % XXX: undocumented - t_atom('unix'), + t_tuple([t_sup([t_atom('unix'), t_atom('vxworks'), t_atom('win32')]), t_atom()]); @@ -2693,7 +2692,7 @@ type(os, getpid, 0, _) -> t_string(); type(os, putenv, 2, Xs) -> strict(arg_types(os, putenv, 2), Xs, fun (_) -> t_atom('true') end); type(os, timestamp, 0, _) -> - t_time(); + t_timestamp(); %%-- re ----------------------------------------------------------------------- type(re, compile, 1, Xs) -> strict(arg_types(re, compile, 1), Xs, @@ -4458,6 +4457,9 @@ t_date() -> t_time() -> t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]). +t_timestamp() -> + t_tuple([t_non_neg_fixnum(), t_non_neg_fixnum(), t_non_neg_fixnum()]). + t_packet() -> t_sup([t_binary(), t_iolist(), t_httppacket()]). diff --git a/lib/inets/Makefile b/lib/inets/Makefile index ec05efa461..f4c2746b0a 100644 --- a/lib/inets/Makefile +++ b/lib/inets/Makefile @@ -36,6 +36,8 @@ SPECIAL_TARGETS = # ---------------------------------------------------- include $(ERL_TOP)/make/otp_subdir.mk +.PHONY: info gclean + info: @echo "OS: $(OS)" @echo "DOCB: $(DOCB)" @@ -44,3 +46,5 @@ info: @echo "APP_VSN: $(APP_VSN)" @echo "" +gclean: + git clean -fXd diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index f6b6827e93..d1671ac9bd 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -144,7 +144,7 @@ filename() = string() <v>Result = {status_line(), headers(), Body} | {status_code(), Body} | request_id() </v> <v>Body = string() | binary()</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> <v>Reason = term() </v> </type> <desc> @@ -194,16 +194,16 @@ filename() = string() <v>Result = {status_line(), headers(), Body} | {status_code(), Body} | request_id() </v> <v>Body = string() | binary()</v> - <v>Profile = profile() </v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> <v>Reason = {connect_failed, term()} | {send_failed, term()} | term() </v> </type> <desc> <p>Sends a HTTP-request. The function can be both synchronous - and asynchronous. In the later case the function will return - <c>{ok, RequestId}</c> and later on the information will be delivered - to the <c>receiver</c> depending on that value. </p> + and asynchronous. In the later case the function will return + <c>{ok, RequestId}</c> and later on the information will be delivered + to the <c>receiver</c> depending on that value. </p> <p>Http option (<c>http_option()</c>) details: </p> <taglist> @@ -211,7 +211,7 @@ filename() = string() <item> <p>Timeout time for the request. </p> <p>The clock starts ticking as soon as the request has been - sent. </p> + sent. </p> <p>Time is in milliseconds. </p> <p>Defaults to <c>infinity</c>. </p> </item> @@ -219,7 +219,7 @@ filename() = string() <tag><c><![CDATA[connect_timeout]]></c></tag> <item> <p>Connection timeout time, used during the initial request, - when the client is <em>connecting</em> to the server. </p> + when the client is <em>connecting</em> to the server. </p> <p>Time is in milliseconds. </p> <p>Defaults to the value of the <c>timeout</c> option. </p> </item> @@ -227,60 +227,61 @@ filename() = string() <tag><c><![CDATA[ssl]]></c></tag> <item> <p>This is the default ssl config option, currently defaults to - <c>essl</c>, see below. </p> + <c>essl</c>, see below. </p> <p>Defaults to <c>[]</c>. </p> </item> <tag><c><![CDATA[ossl]]></c></tag> <item> <p>If using the OpenSSL based (old) implementation of SSL, - these SSL-specific options are used. </p> + these SSL-specific options are used. </p> <p>Defaults to <c>[]</c>. </p> </item> <tag><c><![CDATA[essl]]></c></tag> <item> <p>If using the Erlang based (new) implementation of SSL, - these SSL-specific options are used. </p> + these SSL-specific options are used. </p> <p>Defaults to <c>[]</c>. </p> </item> <tag><c><![CDATA[autoredirect]]></c></tag> <item> - <p>Should the client automatically retrieve the information - from the new URI and return that as the result instead - of a 30X-result code. </p> - <p>Note that for some 30X-result codes automatic redirect - is not allowed. In these cases the 30X-result will always - be returned. </p> - <p>Defaults to <c>true</c>. </p> + <p>Should the client automatically retrieve the information + from the new URI and return that as the result instead + of a 30X-result code. </p> + <p>Note that for some 30X-result codes automatic redirect + is not allowed. In these cases the 30X-result will always + be returned. </p> + <p>Defaults to <c>true</c>. </p> </item> <tag><c><![CDATA[proxy_auth]]></c></tag> <item> <p>A proxy-authorization header using the provided user name and - password will be added to the request. </p> + password will be added to the request. </p> </item> <tag><c><![CDATA[version]]></c></tag> <item> <p>Can be used to make the client act as an <c>HTTP/1.0</c> or - <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c> - client. When using <c>HTTP/1.0</c> persistent connections will - not be used. </p> - <p>Defaults to the string <c>"HTTP/1.1"</c>. </p> + <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c> + client. When using <c>HTTP/1.0</c> persistent connections will + not be used. </p> + <p>Defaults to the string <c>"HTTP/1.1"</c>. </p> </item> <tag><c><![CDATA[relaxed]]></c></tag> <item> - <p>If set to <c>true</c> workarounds for known server deviations from - the HTTP-standard are enabled. </p> + <p>If set to <c>true</c> workarounds for known server deviations + from the HTTP-standard are enabled. </p> <p>Defaults to <c>false</c>. </p> </item> <tag><c><![CDATA[url_encode]]></c></tag> <item> - <p>Will apply Percent-encoding, also known as URL encoding on the URL.</p> + <p>Will apply Percent-encoding, also known as URL encoding on the + URL.</p> <p>Defaults to <c>false</c>. </p> </item> </taglist> @@ -296,77 +297,77 @@ filename() = string() <tag><c><![CDATA[stream]]></c></tag> <item> <p>Streams the body of a 200 or 206 response to the calling - process or to a file. When streaming to the calling process - using the option <c>self</c> the following stream messages - will be sent to that process: <c>{http, {RequestId, - stream_start, Headers}, {http, {RequestId, stream, - BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When - streaming to to the calling processes using the option - <c>{self, once}</c> the first message will have an additional - element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>, - this is the process id that should be used as an argument to - <c>http:stream_next/1</c> to trigger the next message to be sent to - the calling process. </p> + process or to a file. When streaming to the calling process + using the option <c>self</c> the following stream messages + will be sent to that process: <c>{http, {RequestId, + stream_start, Headers}, {http, {RequestId, stream, + BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When + streaming to to the calling processes using the option + <c>{self, once}</c> the first message will have an additional + element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>, + this is the process id that should be used as an argument to + <c>http:stream_next/1</c> to trigger the next message to be sent to + the calling process. </p> <p>Note that it is possible that chunked encoding will add - headers so that there are more headers in the <c>stream_end</c> - message than in the <c>stream_start</c>. - When streaming to a file and the request is asynchronous the - message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p> + headers so that there are more headers in the <c>stream_end</c> + message than in the <c>stream_start</c>. + When streaming to a file and the request is asynchronous the + message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p> <p>Defaults to <c>none</c>. </p> </item> <tag><c><![CDATA[body_format]]></c></tag> <item> <p>Defines if the body shall be delivered as a string or as a - binary. This option is only valid for the synchronous - request. </p> + binary. This option is only valid for the synchronous + request. </p> <p>Defaults to <c>string</c>. </p> </item> <tag><c><![CDATA[full_result]]></c></tag> <item> <p>Should a "full result" be returned to the caller (that is, - the body, the headers and the entire status-line) or not - (the body and the status code). </p> + the body, the headers and the entire status-line) or not + (the body and the status code). </p> <p>Defaults to <c>true</c>. </p> </item> <tag><c><![CDATA[header_as_is]]></c></tag> <item> <p>Shall the headers provided by the user be made - lower case or be regarded as case sensitive. </p> + lower case or be regarded as case sensitive. </p> <p>Note that the http standard requires them to be - case insenstive. This feature should only be used if there is - no other way to communicate with the server or for testing - purpose. Also note that when this option is used no headers - will be automatically added, all necessary headers have to be - provided by the user. </p> - <p>Defaults to <c>false</c>. </p> + case insenstive. This feature should only be used if there is + no other way to communicate with the server or for testing + purpose. Also note that when this option is used no headers + will be automatically added, all necessary headers have to be + provided by the user. </p> + <p>Defaults to <c>false</c>. </p> </item> <tag><c><![CDATA[socket_opts]]></c></tag> <item> <p>Socket options to be used for this and subsequent - request(s). </p> - <p>Overrides any value set by the - <seealso marker="#set_options">set_options</seealso> - function. </p> + request(s). </p> + <p>Overrides any value set by the + <seealso marker="#set_options">set_options</seealso> + function. </p> <p>Note that the validity of the options are <em>not</em> - checked in any way. </p> + checked in any way. </p> <p>Note that this may change the socket behaviour - (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>) - for an already existing one, and therefore an already connected - request handler. </p> + (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>) + for an already existing one, and therefore an already connected + request handler. </p> <p>By default the socket options set by the - <seealso marker="#set_options">set_options/1,2</seealso> - function are used when establishing a connection. </p> + <seealso marker="#set_options">set_options/1,2</seealso> + function are used when establishing a connection. </p> </item> <tag><c><![CDATA[receiver]]></c></tag> <item> <p>Defines how the client will deliver the result of an - asynchroneous request (<c>sync</c> has the value - <c>false</c>). </p> + asynchroneous request (<c>sync</c> has the value + <c>false</c>). </p> <taglist> <tag><c><![CDATA[pid()]]></c></tag> @@ -380,7 +381,7 @@ filename() = string() <tag><c><![CDATA[function/1]]></c></tag> <item> <p>Information will be delivered to the receiver via calls - to the provided fun: </p> + to the provided fun: </p> <pre> Receiver(ReplyInfo) </pre> @@ -389,7 +390,7 @@ Receiver(ReplyInfo) <tag><c><![CDATA[{Module, Funcion, Args}]]></c></tag> <item> <p>Information will be delivered to the receiver via calls - to the callback function: </p> + to the callback function: </p> <pre> apply(Module, Function, [ReplyInfo | Args]) </pre> @@ -410,7 +411,7 @@ apply(Module, Function, [ReplyInfo | Args]) </pre> <p>Defaults to the <c>pid()</c> of the process calling the request - function (<c>self()</c>). </p> + function (<c>self()</c>). </p> </item> </taglist> @@ -425,7 +426,7 @@ apply(Module, Function, [ReplyInfo | Args]) <type> <v>RequestId = request_id() - A unique identifier as returned by request/4</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> <p>Cancels an asynchronous HTTP-request. </p> @@ -514,11 +515,10 @@ apply(Module, Function, [ReplyInfo | Args]) This option is used to switch on (or off) different levels of erlang trace on the client. It is a debug feature.</d> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> - <p>Sets options to be used for subsequent - requests.</p> + <p>Sets options to be used for subsequent requests.</p> <note> <p>If possible the client will keep its connections alive and use persistent connections @@ -548,7 +548,7 @@ apply(Module, Function, [ReplyInfo | Args]) </type> <desc> <p>Triggers the next message to be streamed, e.i. - same behavior as active once for sockets.</p> + same behavior as active once for sockets. </p> <marker id="verify_cookies"></marker> <marker id="store_cookies"></marker> @@ -562,14 +562,14 @@ apply(Module, Function, [ReplyInfo | Args]) <type> <v>SetCookieHeaders = headers() - where field = "set-cookie"</v> <v>Url = url()</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> <p>Saves the cookies defined in SetCookieHeaders - in the client profile's cookie database. You need to - call this function if you have set the option <c>cookies</c> to <c>verify</c>. - If no profile is specified the default profile will be used. - </p> + in the client profile's cookie database. You need to + call this function if you have set the option <c>cookies</c> + to <c>verify</c>. + If no profile is specified the default profile will be used. </p> <marker id="cookie_header"></marker> </desc> @@ -582,13 +582,12 @@ apply(Module, Function, [ReplyInfo | Args]) making a request to Url using the profile <c>Profile</c>.</fsummary> <type> <v>Url = url()</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> <p>Returns the cookie header that would be sent - when making a request to <c>Url</c> using the profile <c>Profile</c>. - If no profile is specified the default profile will be used. - </p> + when making a request to <c>Url</c> using the profile <c>Profile</c>. + If no profile is specified the default profile will be used. </p> <marker id="reset_cookies"></marker> </desc> @@ -600,12 +599,12 @@ apply(Module, Function, [ReplyInfo | Args]) <name>reset_cookies(Profile) -> void()</name> <fsummary>Reset the cookie database.</fsummary> <type> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> - <p>Resets (clears) the cookie database for the specified <c>Profile</c>. - If no profile is specified the default profile will be used. - </p> + <p>Resets (clears) the cookie database for the specified + <c>Profile</c>. If no profile is specified the default profile + will be used. </p> </desc> </func> @@ -615,17 +614,16 @@ apply(Module, Function, [ReplyInfo | Args]) <name>which_cookies(Profile) -> cookies()</name> <fsummary>Dumps out the entire cookie database.</fsummary> <type> - <v>Profile = profile()</v> - <v>cookies() = [cooie_stores()]</v> - <v>cookie_stores() = {cookies, icookies()} | {session_cookies, icookies()}</v> - <v>icookies() = [icookie()]</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>cookies() = [cookie_stores()]</v> + <v>cookie_stores() = {cookies, cookies()} | {session_cookies, cookies()}</v> + <v>cookies() = [cookie()]</v> <v>cookie() = term()</v> </type> <desc> <p>This function produces a list of the entire cookie database. - It is intended for debugging/testing purposes. - If no profile is specified the default profile will be used. - </p> + It is intended for debugging/testing purposes. + If no profile is specified the default profile will be used. </p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 0926df8581..34f26bf45b 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,63 @@ <file>notes.xml</file> </header> + <section><title>Inets 5.7</title> + + <section><title>Improvements and New Features</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[httpc|httpd] Added support for IPv6 with ssl. </p> + <p>Own Id: OTP-5566</p> + </item> + + </list> + + </section> + + <section><title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[httpc] Remove unnecessary usage of iolist_to_binary when + processing body (for PUT and POST). </p> + <p>Filipe David Manana</p> + <p>Own Id: OTP-9317</p> + </item> + + <item> + <p>[ftp] FTP client doesn't work with IPv6 host.</p> + <p>Attila Rajmund Nohl</p> + <p>Own Id: OTP-9342 Aux Id: seq11853</p> + </item> + + <item> + <p>[httpd] Peer/sockname resolv doesn't work with IPv6 addrs + in HTTP. </p> + <p>Attila Rajmund Nohl.</p> + <p>Own Id: OTP-9343</p> + </item> + + <item> + <p>[httpc] Clients started stand-alone not properly handled. + Also it was not documented how to use them, that is that + once started, they are represented by a <c>pid()</c> and not by + their <c>profile()</c>. </p> + <p>Own Id: OTP-9365</p> + </item> + + </list> + </section> + + </section> <!-- 5.7 --> + + <section><title>Inets 5.6</title> <section><title>Improvements and New Features</title> diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index fe6cb0c191..ac72963347 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1038,10 +1038,12 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> Port = key_search(port, Opts, ?FTP_PORT), Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT), Progress = key_search(progress, Opts, ignore), + IpFamily = key_search(ipfamily, Opts, inet), State2 = State#state{client = From, mode = Mode, - progress = progress(Progress)}, + progress = progress(Progress), + ipfamily = IpFamily}, ?fcrd("handle_call(open) -> setup ctrl connection with", [{host, Host}, {port, Port}, {timeout, Timeout}]), diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 6ffa5e8ba5..fe8e93af1f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -64,17 +64,16 @@ default_profile() -> profile_name(?DEFAULT_PROFILE) -> httpc_manager; +profile_name(Profile) when is_pid(Profile) -> + Profile; profile_name(Profile) -> - profile_name("httpc_manager_", Profile). + Prefix = lists:flatten(io_lib:format("~w_", [?MODULE])), + profile_name(Prefix, Profile). profile_name(Prefix, Profile) when is_atom(Profile) -> list_to_atom(Prefix ++ atom_to_list(Profile)); -profile_name(Prefix, Profile) when is_pid(Profile) -> - ProfileStr0 = - string:strip(string:strip(erlang:pid_to_list(Profile), left, $<), right, $>), - F = fun($.) -> $_; (X) -> X end, - ProfileStr = [F(C) || C <- ProfileStr0], - list_to_atom(Prefix ++ "pid_" ++ ProfileStr). +profile_name(_Prefix, Profile) when is_pid(Profile) -> + Profile. %%-------------------------------------------------------------------------- @@ -115,9 +114,11 @@ request(Url, Profile) -> %% {keyfile, path()} | {password, string()} | {cacertfile, path()} | %% {ciphers, string()} %% Options - [Option] -%% Option - {sync, Boolean} | {body_format, BodyFormat} | -%% {full_result, Boolean} | {stream, To} | -%% {headers_as_is, Boolean} +%% Option - {sync, Boolean} | +%% {body_format, BodyFormat} | +%% {full_result, Boolean} | +%% {stream, To} | +%% {headers_as_is, Boolean} %% StatusLine = {HTTPVersion, StatusCode, ReasonPhrase}</v> %% HTTPVersion = string() %% StatusCode = integer() @@ -518,17 +519,15 @@ mk_chunkify_fun(ProcessBody) -> eof -> {ok, <<"0\r\n\r\n">>, eof_body}; {ok, Data, NewAcc} -> - {ok, mk_chunk_bin(Data), NewAcc} + Chunk = [ + integer_to_list(iolist_size(Data), 16), + "\r\n", + Data, + "\r\n"], + {ok, Chunk, NewAcc} end end. -mk_chunk_bin(Data) -> - Bin = iolist_to_binary(Data), - iolist_to_binary([hex_size(Bin), "\r\n", Bin, "\r\n"]). - -hex_size(Bin) -> - hd(io_lib:format("~.16B", [size(Bin)])). - handle_answer(RequestId, false, _) -> {ok, RequestId}; @@ -552,9 +551,7 @@ return_answer(Options, {{"HTTP/0.9",_,_}, _, BinBody}) -> {ok, Body}; return_answer(Options, {StatusLine, Headers, BinBody}) -> - Body = maybe_format_body(BinBody, Options), - case proplists:get_value(full_result, Options, true) of true -> {ok, {StatusLine, Headers, Body}}; diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 1f0e012e7e..9ac9ee6f7b 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -515,7 +515,7 @@ handle_info({Proto, _Socket, Data}, {stop, normal, NewState} end, - ?hcri("data processed", []), + ?hcri("data processed", [{final_result, FinalResult}]), FinalResult; @@ -629,8 +629,9 @@ handle_info(timeout_queue, #state{timers = Timers} = State) -> Timers#timers{queue_timer = undefined}}}; %% Setting up the connection to the server somehow failed. -handle_info({init_error, _, ClientErrMsg}, +handle_info({init_error, Tag, ClientErrMsg}, State = #state{request = Request}) -> + ?hcrv("init error", [{tag, Tag}, {client_error, ClientErrMsg}]), NewState = answer_request(Request, ClientErrMsg, State), {stop, normal, NewState}; @@ -707,9 +708,9 @@ terminate(normal, %% And, just in case, close our side (**really** overkill) http_transport:close(SocketType, Socket); -terminate(Reason, #state{session = #session{id = Id, - socket = Socket, - socket_type = SocketType}, +terminate(Reason, #state{session = #session{id = Id, + socket = Socket, + socket_type = SocketType}, request = undefined, profile_name = ProfileName, timers = Timers, @@ -1403,7 +1404,7 @@ try_to_enable_pipeline_or_keep_alive( answer_request(#request{id = RequestId, from = From} = Request, Msg, #state{timers = Timers, profile_name = ProfileName} = State) -> - ?hcrt("answer request", [{request, Request}]), + ?hcrt("answer request", [{request, Request}, {msg, Msg}]), httpc_response:send(From, Msg), RequestTimers = Timers#timers.request_timers, TimerRef = diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 7f66b477eb..9015bf1ce2 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -52,7 +52,7 @@ cancel = [], % [{RequestId, HandlerPid, ClientPid}] handler_db, % ets() - Entry: #handler_info{} cookie_db, % cookie_db() - session_db, % ets() - Entry: #tcp_session{} + session_db, % ets() - Entry: #session{} profile_name, % atom() options = #options{} }). @@ -178,7 +178,7 @@ request_done(RequestId, ProfileName) -> %%-------------------------------------------------------------------- %% Function: insert_session(Session, ProfileName) -> _ -%% Session - #tcp_session{} +%% Session - #session{} %% ProfileName - atom() %% %% Description: Inserts session information into the httpc manager @@ -669,7 +669,7 @@ select_session(Method, HostPort, Scheme, SessionType, (SessionType =:= keep_alive) of true -> %% Look for handlers connecting to this host (HostPort) - %% tcp_session with record name field (tcp_session) and + %% session with record name field (session) and %% socket fields ignored. The fields id (part of: HostPort), %% client_close, scheme and type specified. %% The fields id (part of: HandlerPid) and queue_length diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 01b51d531a..9b8190ebed 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -33,8 +33,8 @@ peername/2, sockname/2, resolve/0 ]). - -export([negotiate/3]). +-export([ipv4_name/1, ipv6_name/1]). -include_lib("inets/src/inets_app/inets_internal.hrl"). -include("http_internal.hrl"). @@ -142,8 +142,8 @@ connect({ossl, SslConfig}, {Host, Port}, _, Timeout) -> ERROR end; -connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> - Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig, +connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) -> + Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig, ?hlrt("connect using essl", [{host, Host}, {port, Port}, @@ -176,8 +176,8 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> listen(SocketType, Port) -> listen(SocketType, undefined, Port). -listen(ip_comm = SocketType, Addr, Port) -> - listen(SocketType, Addr, Port, undefined); +listen(ip_comm = _SocketType, Addr, Port) -> + listen_ip_comm(Addr, Port, undefined); %% Wrapper for backaward compatibillity listen({ssl, SSLConfig}, Addr, Port) -> @@ -187,35 +187,33 @@ listen({ssl, SSLConfig}, Addr, Port) -> {ssl_config, SSLConfig}]), listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port); -listen({ossl, SSLConfig} = Ssl, Addr, Port) -> +listen({ossl, SSLConfig}, Addr, Port) -> ?hlrt("listen (ossl)", [{addr, Addr}, {port, Port}, {ssl_config, SSLConfig}]), - Opt = sock_opt(Ssl, Addr, SSLConfig), - ?hlrt("listen options", [{opt, Opt}]), - ssl:listen(Port, [{ssl_imp, old} | Opt]); + listen_ssl(Addr, Port, [{ssl_imp, old} | SSLConfig]); -listen({essl, SSLConfig} = Ssl, Addr, Port) -> +listen({essl, SSLConfig}, Addr, Port) -> ?hlrt("listen (essl)", [{addr, Addr}, {port, Port}, {ssl_config, SSLConfig}]), - Opt = sock_opt(Ssl, Addr, SSLConfig), - ?hlrt("listen options", [{opt, Opt}]), - Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt], - ssl:listen(Port, Opt2). + listen_ssl(Addr, Port, [{ssl_imp, new}, {reuseaddr, true} | SSLConfig]). + listen(ip_comm, Addr, Port, Fd) -> - case (catch listen_ip_comm(Addr, Port, Fd)) of + listen_ip_comm(Addr, Port, Fd). + +listen_ip_comm(Addr, Port, Fd) -> + case (catch do_listen_ip_comm(Addr, Port, Fd)) of {'EXIT', Reason} -> {error, {exit, Reason}}; Else -> Else end. - -listen_ip_comm(Addr, Port, Fd) -> +do_listen_ip_comm(Addr, Port, Fd) -> {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd), case IpFamily of inet6fb4 -> @@ -248,6 +246,41 @@ listen_ip_comm(Addr, Port, Fd) -> gen_tcp:listen(NewPort, Opts2) end. + +listen_ssl(Addr, Port, Opts0) -> + IpFamily = ipfamily_default(Addr, Port), + BaseOpts = [{backlog, 128}, {reuseaddr, true} | Opts0], + Opts = sock_opts(Addr, BaseOpts), + case IpFamily of + inet6fb4 -> + Opts2 = [inet6 | Opts], + ?hlrt("try ipv6 listen", [{opts, Opts2}]), + case (catch ssl:listen(Port, Opts2)) of + {error, Reason} when ((Reason =:= nxdomain) orelse + (Reason =:= eafnosupport)) -> + Opts3 = [inet | Opts], + ?hlrt("ipv6 listen failed - try ipv4 instead", + [{reason, Reason}, {opts, Opts3}]), + ssl:listen(Port, Opts3); + + {'EXIT', Reason} -> + Opts3 = [inet | Opts], + ?hlrt("ipv6 listen exit - try ipv4 instead", + [{reason, Reason}, {opts, Opts3}]), + ssl:listen(Port, Opts3); + + Other -> + ?hlrt("ipv6 listen done", [{other, Other}]), + Other + end; + + _ -> + Opts2 = [IpFamily | Opts], + ?hlrt("listen", [{opts, Opts2}]), + ssl:listen(Port, Opts2) + end. + + ipfamily_default(Addr, Port) -> httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4). @@ -257,9 +290,9 @@ get_socket_info(Addr, Port, Fd0) -> %% The presence of a file descriptor takes precedence case get_fd(Port, Fd0, IpFamilyDefault) of {Fd, IpFamily} -> - {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily}; + {0, sock_opts(Addr, [{fd, Fd} | BaseOpts]), IpFamily}; undefined -> - {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault} + {Port, sock_opts(Addr, BaseOpts), IpFamilyDefault} end. get_fd(Port, undefined = _Fd, IpFamilyDefault) -> @@ -499,44 +532,28 @@ close({essl, _}, Socket) -> %% connection, usning either gen_tcp or ssl. %%------------------------------------------------------------------------- peername(ip_comm, Socket) -> - case inet:peername(Socket) of - {ok,{{A, B, C, D}, Port}} -> - PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, PeerName}; - {ok,{{A, B, C, D, E, F, G, H}, Port}} -> - PeerName = http_util:integer_to_hexlist(A) ++ ":"++ - http_util:integer_to_hexlist(B) ++ ":" ++ - http_util:integer_to_hexlist(C) ++ ":" ++ - http_util:integer_to_hexlist(D) ++ ":" ++ - http_util:integer_to_hexlist(E) ++ ":" ++ - http_util:integer_to_hexlist(F) ++ ":" ++ - http_util:integer_to_hexlist(G) ++":"++ - http_util:integer_to_hexlist(H), - {Port, PeerName}; - {error, _} -> - {-1, "unknown"} - end; + do_peername(inet:peername(Socket)); %% Wrapper for backaward compatibillity peername({ssl, SSLConfig}, Socket) -> peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); peername({ossl, _}, Socket) -> - peername_ssl(Socket); + do_peername(ssl:peername(Socket)); peername({essl, _}, Socket) -> - peername_ssl(Socket). - -peername_ssl(Socket) -> - case ssl:peername(Socket) of - {ok,{{A, B, C, D}, Port}} -> - PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, PeerName}; - {error, _} -> - {-1, "unknown"} - end. + do_peername(ssl:peername(Socket)). + +do_peername({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + PeerName = ipv4_name(Addr), + {Port, PeerName}; +do_peername({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + PeerName = ipv6_name(Addr), + {Port, PeerName}; +do_peername({error, _}) -> + {-1, "unknown"}. %%------------------------------------------------------------------------- @@ -550,44 +567,28 @@ peername_ssl(Socket) -> %% other end of connection, using either gen_tcp or ssl. %%------------------------------------------------------------------------- sockname(ip_comm, Socket) -> - case inet:sockname(Socket) of - {ok,{{A, B, C, D}, Port}} -> - SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, SockName}; - {ok,{{A, B, C, D, E, F, G, H}, Port}} -> - SockName = http_util:integer_to_hexlist(A) ++ ":"++ - http_util:integer_to_hexlist(B) ++ ":" ++ - http_util:integer_to_hexlist(C) ++ ":" ++ - http_util:integer_to_hexlist(D) ++ ":" ++ - http_util:integer_to_hexlist(E) ++ ":" ++ - http_util:integer_to_hexlist(F) ++ ":" ++ - http_util:integer_to_hexlist(G) ++":"++ - http_util:integer_to_hexlist(H), - {Port, SockName}; - {error, _} -> - {-1, "unknown"} - end; + do_sockname(inet:sockname(Socket)); %% Wrapper for backaward compatibillity sockname({ssl, SSLConfig}, Socket) -> sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); sockname({ossl, _}, Socket) -> - sockname_ssl(Socket); + do_sockname(ssl:sockname(Socket)); sockname({essl, _}, Socket) -> - sockname_ssl(Socket). - -sockname_ssl(Socket) -> - case ssl:sockname(Socket) of - {ok,{{A, B, C, D}, Port}} -> - SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, SockName}; - {error, _} -> - {-1, "unknown"} - end. + do_sockname(ssl:sockname(Socket)). + +do_sockname({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + SockName = ipv4_name(Addr), + {Port, SockName}; +do_sockname({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + SockName = ipv6_name(Addr), + {Port, SockName}; +do_sockname({error, _}) -> + {-1, "unknown"}. %%------------------------------------------------------------------------- @@ -601,29 +602,49 @@ resolve() -> Name. +%%------------------------------------------------------------------------- +%% ipv4_name(Ipv4Addr) -> string() +%% ipv6_name(Ipv6Addr) -> string() +%% Ipv4Addr = ip4_address() +%% Ipv6Addr = ip6_address() +%% +%% Description: Returns the local hostname. +%%------------------------------------------------------------------------- +ipv4_name({A, B, C, D}) -> + integer_to_list(A) ++ "." ++ + integer_to_list(B) ++ "." ++ + integer_to_list(C) ++ "." ++ + integer_to_list(D). + +ipv6_name({A, B, C, D, E, F, G, H}) -> + http_util:integer_to_hexlist(A) ++ ":"++ + http_util:integer_to_hexlist(B) ++ ":" ++ + http_util:integer_to_hexlist(C) ++ ":" ++ + http_util:integer_to_hexlist(D) ++ ":" ++ + http_util:integer_to_hexlist(E) ++ ":" ++ + http_util:integer_to_hexlist(F) ++ ":" ++ + http_util:integer_to_hexlist(G) ++ ":" ++ + http_util:integer_to_hexlist(H). + + %%%======================================================================== %%% Internal functions %%%======================================================================== +%% -- sock_opts -- %% Address any comes from directive: BindAddress "*" -sock_opt(ip_comm, any = Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]); -sock_opt(ip_comm, undefined, Opts) -> - sock_opt2(Opts); -sock_opt(_, any = _Addr, Opts) -> - sock_opt2(Opts); -sock_opt(_, undefined = _Addr, Opts) -> - sock_opt2(Opts); -sock_opt(_, {_,_,_,_} = Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]); -sock_opt(ip_comm, Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]); -sock_opt(_, Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]). - -sock_opt2(Opts) -> +sock_opts(undefined, Opts) -> + sock_opts(Opts); +sock_opts(any = Addr, Opts) -> + sock_opts([{ip, Addr} | Opts]); +sock_opts(Addr, Opts) -> + sock_opts([{ip, Addr} | Opts]). + +sock_opts(Opts) -> [{packet, 0}, {active, false} | Opts]. + +%% -- negotiate -- negotiate(ip_comm,_,_) -> ?hlrt("negotiate(ip_comm)", []), ok; diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 47f3fbba58..8b0fcb185d 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,6 +18,15 @@ {"%VSN%", [ + {"5.6", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {load_module, http_transport, soft_purge, soft_purge, [http_transport]}, + {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]}, + {update, ftp, soft, soft_purge, soft_purge, []} + ] + }, {"5.5.2", [ {restart_application, inets} @@ -40,6 +49,15 @@ } ], [ + {"5.6", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {load_module, http_transport, soft_purge, soft_purge, [http_transport]}, + {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]}, + {update, ftp, soft, soft_purge, soft_purge, []} + ] + }, {"5.5.2", [ {restart_application, inets} diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl index d0d07a8358..3ebd02229e 100644 --- a/lib/inets/test/ftp_suite_lib.erl +++ b/lib/inets/test/ftp_suite_lib.erl @@ -1129,10 +1129,16 @@ ticket_6035(Config) -> LogFile = filename:join([PrivDir,"ticket_6035.log"]), try begin + p("ticket_6035 -> select ftpd host"), Host = dirty_select_ftpd_host(Config), + p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]), Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), + p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)", + [Pid, LogFile]), error_logger:logfile({open, LogFile}), - ok = kill_ftp_proc_6035(Pid,LogFile), + p("ticket_6035 -> error logfile open => now kill waiter process"), + true = kill_ftp_proc_6035(Pid, LogFile), + p("ticket_6035 -> waiter process killed => now close error logfile"), error_logger:logfile(close), p("ticket_6035 -> done", []), ok @@ -1146,7 +1152,7 @@ kill_ftp_proc_6035(Pid, LogFile) -> p("kill_ftp_proc_6035 -> entry"), receive open -> - p("kill_ftp_proc_6035 -> received open: send shutdown"), + p("kill_ftp_proc_6035 -> received open => now issue shutdown"), exit(Pid, shutdown), kill_ftp_proc_6035(Pid, LogFile); {open_failed, Reason} -> @@ -1159,11 +1165,11 @@ kill_ftp_proc_6035(Pid, LogFile) -> is_error_report_6035(LogFile) end. -open_wait_6035(FtpServer, From) -> - p("open_wait_6035 -> try connect to ~s", [FtpServer]), +open_wait_6035({Tag, FtpServer}, From) -> + p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]), case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of {ok, Pid} -> - p("open_wait_6035 -> connected, now login"), + p("open_wait_6035 -> connected (~p), now login", [Pid]), LoginResult = ftp:user(Pid,"anonymous","kldjf"), p("open_wait_6035 -> login result: ~p", [LoginResult]), From ! open, @@ -1191,22 +1197,27 @@ is_error_report_6035(LogFile) -> Res = case file:read_file(LogFile) of {ok, Bin} -> - p("is_error_report_6035 -> logfile read"), - read_log_6035(binary_to_list(Bin)); + Txt = binary_to_list(Bin), + p("is_error_report_6035 -> logfile read: ~n~p", [Txt]), + read_log_6035(Txt); _ -> - ok + false end, p("is_error_report_6035 -> logfile read result: " "~n ~p", [Res]), - file:delete(LogFile), + %% file:delete(LogFile), Res. read_log_6035("=ERROR REPORT===="++_Rest) -> - error_report; -read_log_6035([_H|T]) -> + p("read_log_6035 -> ERROR REPORT detected"), + true; +read_log_6035([H|T]) -> + p("read_log_6035 -> OTHER: " + "~p", [H]), read_log_6035(T); read_log_6035([]) -> - ok. + p("read_log_6035 -> done"), + false. %%-------------------------------------------------------------------- diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 1998bd3950..6edd5371af 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -64,16 +64,6 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ - proxy_options, - proxy_head, - proxy_get, - proxy_trace, - proxy_post, - proxy_put, - proxy_delete, - proxy_auth, - proxy_headers, - proxy_emulate_lower_versions, http_options, http_head, http_get, @@ -88,15 +78,6 @@ all() -> http_headers, http_headers_dummy, http_bad_response, - ssl_head, - ossl_head, - essl_head, - ssl_get, - ossl_get, - essl_get, - ssl_trace, - ossl_trace, - essl_trace, http_redirect, http_redirect_loop, http_internal_server_error, @@ -106,21 +87,44 @@ all() -> http_emulate_lower_versions, http_relaxed, page_does_not_exist, - proxy_page_does_not_exist, - proxy_https_not_supported, - http_stream, - http_stream_once, - proxy_stream, parse_url, options, - ipv6, headers_as_is, + {group, proxy}, + {group, ssl}, + {group, stream}, + {group, ipv6}, {group, tickets}, initial_server_connect ]. groups() -> - [{tickets, [], [hexed_query_otp_6191, + [ + {proxy, [], [proxy_options, + proxy_head, + proxy_get, + proxy_trace, + proxy_post, + proxy_put, + proxy_delete, + proxy_auth, + proxy_headers, + proxy_emulate_lower_versions, + proxy_page_does_not_exist, + proxy_https_not_supported]}, + {ssl, [], [ssl_head, + ossl_head, + essl_head, + ssl_get, + ossl_get, + essl_get, + ssl_trace, + ossl_trace, + essl_trace]}, + {stream, [], [http_stream, + http_stream_once, + proxy_stream]}, + {tickets, [], [hexed_query_otp_6191, empty_body_otp_6243, empty_response_header_otp_6830, transfer_encoding_otp_6807, @@ -139,7 +143,10 @@ groups() -> {otp_8154, [], [otp_8154_1]}, {otp_8106, [], [otp_8106_pid, otp_8106_fun, - otp_8106_mfa]}]. + otp_8106_mfa]}, + {ipv6, [], [ipv6_ipcomm, ipv6_essl]} + ]. + init_per_group(_GroupName, Config) -> @@ -213,36 +220,38 @@ end_per_suite(Config) -> %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- + init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); -init_per_testcase(initial_server_connect, Config) -> +init_per_testcase(initial_server_connect = Case, Config) -> %% Try to check if crypto actually exist or not, %% this test case does not work unless it does - case (catch crypto:start()) of - ok -> - application:start(public_key), - application:start(ssl), + try + begin + ensure_started(crypto), + ensure_started(public_key), + ensure_started(ssl), inets:start(), - Config; - _ -> - {skip,"Could not start crypto"} + Config + end + catch + throw:{error, {failed_starting, App, ActualError}} -> + tsp("init_per_testcase(~w) -> failed starting ~w: " + "~n ~p", [Case, App, ActualError]), + SkipString = + "Could not start " ++ atom_to_list(App), + {skip, SkipString}; + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + {skip, SkipString} end; init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). -init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> - tsp("init_per_testcase_ssl -> stop ssl"), - application:stop(ssl), - Config2 = lists:keydelete(local_ssl_server, 1, Config), - %% Will start inets - tsp("init_per_testcase_ssl -> try start http server (including inets)"), - Server = inets_test_lib:start_http_server( - filename:join(PrivDir, SslConfFile), Tag), - tsp("init_per_testcase -> Server: ~p", [Server]), - [{local_ssl_server, Server} | Config2]. - init_per_testcase(Case, Timeout, Config) -> io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n", [?MODULE, Case, Timeout]), @@ -261,13 +270,16 @@ init_per_testcase(Case, Timeout, Config) -> NewConfig = case atom_to_list(Case) of [$s, $s, $l | _] -> - init_per_testcase_ssl(ssl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); + init_per_testcase_ssl(ssl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); [$o, $s, $s, $l | _] -> - init_per_testcase_ssl(ossl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); + init_per_testcase_ssl(ossl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); [$e, $s, $s, $l | _] -> - init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); + init_per_testcase_ssl(essl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); "proxy_" ++ Rest -> io:format("init_per_testcase -> Rest: ~p~n", [Rest]), @@ -275,16 +287,23 @@ init_per_testcase(Case, Timeout, Config) -> "https_not_supported" -> tsp("init_per_testcase -> [proxy case] start inets"), inets:start(), - tsp("init_per_testcase -> [proxy case] start ssl"), - application:start(crypto), - application:start(public_key), - case (catch application:start(ssl)) of + tsp("init_per_testcase -> " + "[proxy case] start crypto, public_key and ssl"), + try ensure_started([crypto, public_key, ssl]) of ok -> - [{watchdog, Dog} | TmpConfig]; - _ -> - [{skip, "SSL does not seem to be supported"} - | TmpConfig] + [{watchdog, Dog} | TmpConfig] + catch + throw:{error, {failed_starting, App, _}} -> + SkipString = + "Could not start " ++ atom_to_list(App), + {skip, SkipString}; + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + {skip, SkipString} end; + _ -> %% We use erlang.org for the proxy tests %% and after the switch to erlang-web, many @@ -321,6 +340,33 @@ init_per_testcase(Case, Timeout, Config) -> [{skip, "proxy not responding"} | TmpConfig] end end; + + "ipv6_" ++ _Rest -> + %% Ensure needed apps (crypto, public_key and ssl) started + try ensure_started([crypto, public_key, ssl]) of + ok -> + Profile = ipv6, + %% A stand-alone profile is represented by a pid() + {ok, ProfilePid} = + inets:start(httpc, + [{profile, Profile}, + {data_dir, PrivDir}], stand_alone), + httpc:set_options([{ipfamily, inet6}], ProfilePid), + tsp("httpc profile pid: ~p", [ProfilePid]), + [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig] + catch + throw:{error, {failed_starting, App, ActualError}} -> + tsp("init_per_testcase(~w) -> failed starting ~w: " + "~n ~p", [Case, App, ActualError]), + SkipString = + "Could not start " ++ atom_to_list(App), + {skip, SkipString}; + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + {skip, SkipString} + end; _ -> TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), Server = @@ -330,9 +376,7 @@ init_per_testcase(Case, Timeout, Config) -> [{watchdog, Dog}, {local_server, Server} | TmpConfig2] end, - %% httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, - %% ["localhost", ?IPV6_LOCAL_HOST]}}]), - + %% This will fail for the ipv6_ - cases (but that is ok) httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ["localhost", ?IPV6_LOCAL_HOST]}}, {ipfamily, inet6fb4}]), @@ -341,6 +385,19 @@ init_per_testcase(Case, Timeout, Config) -> NewConfig. +init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> + tsp("init_per_testcase_ssl(~w) -> stop ssl", [Tag]), + application:stop(ssl), + Config2 = lists:keydelete(local_ssl_server, 1, Config), + %% Will start inets + tsp("init_per_testcase_ssl(~w) -> try start http server (including inets)", + [Tag]), + Server = inets_test_lib:start_http_server( + filename:join(PrivDir, SslConfFile), Tag), + tsp("init_per_testcase(~w) -> Server: ~p", [Tag, Server]), + [{local_ssl_server, Server} | Config2]. + + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -349,13 +406,36 @@ init_per_testcase(Case, Timeout, Config) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(http_save_to_file, Config) -> - PrivDir = ?config(priv_dir, Config), +end_per_testcase(http_save_to_file = Case, Config) -> + io:format(user, "~n~n*** END ~w:~w ***~n~n", + [?MODULE, Case]), + PrivDir = ?config(priv_dir, Config), FullPath = filename:join(PrivDir, "dummy.html"), file:delete(FullPath), finish(Config); -end_per_testcase(_, Config) -> +end_per_testcase(Case, Config) -> + io:format(user, "~n~n*** END ~w:~w ***~n~n", + [?MODULE, Case]), + case atom_to_list(Case) of + "ipv6_" ++ _Rest -> + tsp("end_per_testcase(~w) -> stop ssl", [Case]), + application:stop(ssl), + tsp("end_per_testcase(~w) -> stop public_key", [Case]), + application:stop(public_key), + tsp("end_per_testcase(~w) -> stop crypto", [Case]), + application:stop(crypto), + ProfilePid = ?config(profile, Config), + tsp("end_per_testcase(~w) -> stop httpc profile (~p)", + [Case, ProfilePid]), + unlink(ProfilePid), + inets:stop(stand_alone, ProfilePid), + tsp("end_per_testcase(~w) -> httpc profile (~p) stopped", + [Case, ProfilePid]), + ok; + _ -> + ok + end, finish(Config). finish(Config) -> @@ -364,6 +444,7 @@ finish(Config) -> undefined -> ok; _ -> + tsp("finish -> stop watchdog (~p)", [Dog]), test_server:timetrap_cancel(Dog) end. @@ -565,7 +646,7 @@ http_relaxed(suite) -> http_relaxed(Config) when is_list(Config) -> ok = httpc:set_options([{ipv6, disabled}]), % also test the old option %% ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_reason_phrase.html", @@ -591,7 +672,7 @@ http_dummy_pipe(suite) -> []; http_dummy_pipe(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html", @@ -905,7 +986,7 @@ http_headers_dummy(suite) -> []; http_headers_dummy(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html", @@ -970,7 +1051,7 @@ http_bad_response(suite) -> []; http_bad_response(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html", @@ -1089,9 +1170,9 @@ ssl_get(SslTag, Config) when is_list(Config) -> httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []), inets_test_lib:check_body(Body); {ok, _} -> - {skip, "Failed to start local http-server"}; + {skip, "local http-server not started"}; _ -> - {skip, "Failed to start SSL"} + {skip, "SSL not started"} end. @@ -1149,9 +1230,9 @@ ssl_trace(SslTag, Config) when is_list(Config) -> tsf({failed, Error}) end; {ok, _} -> - {skip, "Failed to start local http-server"}; + {skip, "local http-server not started"}; _ -> - {skip, "Failed to start SSL"} + {skip, "SSL not started"} end. @@ -1170,7 +1251,7 @@ http_redirect(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), tsp("http_redirect -> start dummy server inet"), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), tsp("http_redirect -> server port = ~p", [Port]), URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html", @@ -1282,7 +1363,7 @@ http_redirect_loop(suite) -> []; http_redirect_loop(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html", @@ -1299,7 +1380,7 @@ http_internal_server_error(suite) -> []; http_internal_server_error(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html", @@ -1335,7 +1416,7 @@ http_userinfo(suite) -> http_userinfo(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URLAuth = "http://alladin:sesame@localhost:" ++ integer_to_list(Port) ++ "/userinfo.html", @@ -1361,7 +1442,7 @@ http_cookie(suite) -> []; http_cookie(Config) when is_list(Config) -> ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URLStart = ?URL_START ++ integer_to_list(Port), @@ -1735,7 +1816,7 @@ http_stream_once(Config) when is_list(Config) -> p("http_stream_once -> set ipfamily to inet", []), ok = httpc:set_options([{ipfamily, inet}]), p("http_stream_once -> start dummy server", []), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), PortStr = integer_to_list(Port), p("http_stream_once -> once", []), @@ -1871,28 +1952,79 @@ parse_url(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -ipv6() -> - [{require,ipv6_hosts}]. -ipv6(doc) -> - ["Test ipv6."]; -ipv6(suite) -> - []; -ipv6(Config) when is_list(Config) -> - {ok, Hostname} = inet:gethostname(), - - case lists:member(list_to_atom(Hostname), - ct:get_config(ipv6_hosts)) of - true -> - {DummyServerPid, Port} = dummy_server(self(), ipv6), - - URL = "http://[" ++ ?IPV6_LOCAL_HOST ++ "]:" ++ + +ipv6_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_ipcomm(doc) -> + ["Test ip_comm ipv6."]; +ipv6_ipcomm(suite) -> + []; +ipv6_ipcomm(Config) when is_list(Config) -> + HTTPOptions = [], + SocketType = ip_comm, + Scheme = "http", + Extra = [], + ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). + + +%%------------------------------------------------------------------------- + +ipv6_essl() -> + [{require, ipv6_hosts}]. +ipv6_essl(doc) -> + ["Test essl ipv6."]; +ipv6_essl(suite) -> + []; +ipv6_essl(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + SSLConfig = {essl, SSLOptions}, + tsp("ssl_ipv6 -> make request using: " + "~n SSLOptions: ~p", [SSLOptions]), + HTTPOptions = [{ssl, SSLConfig}], + SocketType = essl, + Scheme = "https", + Extra = SSLOptions, + ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). + + +%%------------------------------------------------------------------------- + +ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> + %% Check if we are a IPv6 host + tsp("ipv6 -> verify ipv6 support", []), + case inets_test_lib:has_ipv6_support(Config) of + {ok, Addr} -> + tsp("ipv6 -> ipv6 supported: ~p", [Addr]), + {DummyServerPid, Port} = dummy_server(SocketType, ipv6, Extra), + Profile = ?config(profile, Config), + URL = + Scheme ++ + "://[" ++ http_transport:ipv6_name(Addr) ++ "]:" ++ integer_to_list(Port) ++ "/foobar.html", - {ok, {{_,200,_}, [_ | _], [_|_]}} = - httpc:request(get, {URL, []}, [], []), - - DummyServerPid ! stop, + tsp("ipv6 -> issue request with: " + "~n URL: ~p" + "~n HTTPOptions: ~p", [URL, HTTPOptions]), + case httpc:request(get, {URL, []}, HTTPOptions, [], Profile) of + {ok, {{_,200,_}, [_ | _], [_|_]}} -> + tsp("ipv6 -> expected result"), + DummyServerPid ! stop, + ok; + {ok, Unexpected} -> + tsp("ipv6 -> unexpected result: " + "~n ~p", [Unexpected]), + DummyServerPid ! stop, + tsf({unexpected_result, Unexpected}); + {error, Reason} -> + tsp("ipv6 -> error: " + "~n Reason: ~p", [Reason]), + DummyServerPid ! stop, + tsf(Reason) + end, ok; - false -> + _ -> + tsp("ipv6 -> ipv6 not supported", []), {skip, "Host does not support IPv6"} end. @@ -1945,7 +2077,7 @@ http_invalid_http(suite) -> []; http_invalid_http(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html", @@ -2002,7 +2134,7 @@ transfer_encoding_otp_6807(suite) -> []; transfer_encoding_otp_6807(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/capital_transfer_encoding.html", @@ -2035,7 +2167,7 @@ empty_response_header_otp_6830(suite) -> []; empty_response_header_otp_6830(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html", {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL), @@ -2052,7 +2184,7 @@ no_content_204_otp_6982(suite) -> []; no_content_204_otp_6982(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html", {ok, {{_,204,_}, [], []}} = httpc:request(URL), @@ -2070,7 +2202,7 @@ missing_CR_otp_7304(suite) -> []; missing_CR_otp_7304(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html", {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL), @@ -2089,7 +2221,7 @@ otp_7883_1(suite) -> otp_7883_1(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", {error, socket_closed_remotely} = httpc:request(URL), @@ -2105,7 +2237,7 @@ otp_7883_2(suite) -> otp_7883_2(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", Method = get, @@ -2626,7 +2758,7 @@ otp_8371(suite) -> []; otp_8371(Config) when is_list(Config) -> ok = httpc:set_options([{ipv6, disabled}]), % also test the old option - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/ensure_host_header_with_port.html", @@ -2864,75 +2996,179 @@ receive_streamed_body(RequestId, Body, Pid) -> test_server:fail(Msg) end. +%% Perform a synchronous stop +dummy_server_stop(Pid) -> + Pid ! {stop, self()}, + receive + {stopped, Pid} -> + ok + end. + +dummy_server(IpV) -> + dummy_server(self(), ip_comm, IpV, []). +dummy_server(SocketType, IpV, Extra) -> + dummy_server(self(), SocketType, IpV, Extra). -dummy_server(Caller, IpV) -> - Pid = spawn(httpc_SUITE, dummy_server_init, [Caller, IpV]), +dummy_server(Caller, SocketType, IpV, Extra) -> + Args = [Caller, SocketType, IpV, Extra], + Pid = spawn(httpc_SUITE, dummy_server_init, Args), receive {port, Port} -> {Pid, Port} end. -dummy_server_init(Caller, IpV) -> +dummy_server_init(Caller, ip_comm, IpV, _) -> + BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}], {ok, ListenSocket} = case IpV of ipv4 -> - gen_tcp:listen(0, [binary, inet, {packet, 0}, - {reuseaddr,true}, - {active, false}]); + tsp("ip_comm ipv4 listen", []), + gen_tcp:listen(0, [inet | BaseOpts]); ipv6 -> - gen_tcp:listen(0, [binary, inet6, {packet, 0}, - {reuseaddr,true}, - {active, false}]) + tsp("ip_comm ipv6 listen", []), + gen_tcp:listen(0, [inet6 | BaseOpts]) end, {ok, Port} = inet:port(ListenSocket), - tsp("dummy_server_init -> Port: ~p", [Port]), + tsp("dummy_server_init(ip_comm) -> Port: ~p", [Port]), Caller ! {port, Port}, - dummy_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, - [], ListenSocket). + dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, + [], ListenSocket); +dummy_server_init(Caller, essl, IpV, SSLOptions) -> + BaseOpts = [{ssl_imp, new}, + {backlog, 128}, binary, {reuseaddr,true}, {active, false} | + SSLOptions], + dummy_ssl_server_init(Caller, BaseOpts, IpV); +dummy_server_init(Caller, ossl, IpV, SSLOptions) -> + BaseOpts = [{ssl_imp, old}, + {backlog, 128}, binary, {active, false} | SSLOptions], + dummy_ssl_server_init(Caller, BaseOpts, IpV). + +dummy_ssl_server_init(Caller, BaseOpts, IpV) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + tsp("dummy_ssl_server_init -> ssl ipv4 listen", []), + ssl:listen(0, [inet | BaseOpts]); + ipv6 -> + tsp("dummy_ssl_server_init -> ssl ipv6 listen", []), + ssl:listen(0, [inet6 | BaseOpts]) + end, + tsp("dummy_ssl_server_init -> ListenSocket: ~p", [ListenSocket]), + {ok, {_, Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, + [], ListenSocket). -dummy_server_loop(MFA, Handlers, ListenSocket) -> +dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) -> receive stop -> - lists:foreach(fun(Handler) -> Handler ! stop end, Handlers) + tsp("dummy_ipcomm_server_loop -> stop handlers", []), + lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); + {stop, From} -> + tsp("dummy_ipcomm_server_loop -> " + "stop command from ~p for handlers (~p)", [From, Handlers]), + Stopper = fun(Handler) -> Handler ! stop end, + lists:foreach(Stopper, Handlers), + From ! {stopped, self()} after 0 -> + tsp("dummy_ipcomm_server_loop -> await accept", []), {ok, Socket} = gen_tcp:accept(ListenSocket), + tsp("dummy_ipcomm_server_loop -> accepted: ~p", [Socket]), HandlerPid = dummy_request_handler(MFA, Socket), + tsp("dummy_icomm_server_loop -> handler created: ~p", [HandlerPid]), gen_tcp:controlling_process(Socket, HandlerPid), - HandlerPid ! controller, - dummy_server_loop(MFA, [HandlerPid | Handlers], + tsp("dummy_ipcomm_server_loop -> " + "control transfered to handler", []), + HandlerPid ! ipcomm_controller, + tsp("dummy_ipcomm_server_loop -> " + "handler informed about control transfer", []), + dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers], ListenSocket) end. +dummy_ssl_server_loop(MFA, Handlers, ListenSocket) -> + receive + stop -> + tsp("dummy_ssl_server_loop -> stop handlers", []), + lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); + {stop, From} -> + tsp("dummy_ssl_server_loop -> " + "stop command from ~p for handlers (~p)", [From, Handlers]), + Stopper = fun(Handler) -> Handler ! stop end, + lists:foreach(Stopper, Handlers), + From ! {stopped, self()} + after 0 -> + tsp("dummy_ssl_server_loop -> await accept", []), + {ok, Socket} = ssl:transport_accept(ListenSocket), + tsp("dummy_ssl_server_loop -> accepted: ~p", [Socket]), + HandlerPid = dummy_request_handler(MFA, Socket), + tsp("dummy_ssl_server_loop -> handler created: ~p", [HandlerPid]), + ssl:controlling_process(Socket, HandlerPid), + tsp("dummy_ssl_server_loop -> control transfered to handler", []), + HandlerPid ! ssl_controller, + tsp("dummy_ssl_server_loop -> " + "handler informed about control transfer", []), + dummy_ssl_server_loop(MFA, [HandlerPid | Handlers], + ListenSocket) + end. + dummy_request_handler(MFA, Socket) -> + tsp("spawn request handler", []), spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]). dummy_request_handler_init(MFA, Socket) -> - receive - controller -> - inet:setopts(Socket, [{active, true}]) - end, - dummy_request_handler_loop(MFA, Socket). + SockType = + receive + ipcomm_controller -> + tsp("dummy_request_handler_init -> " + "received ip_comm controller - activate", []), + inet:setopts(Socket, [{active, true}]), + ip_comm; + ssl_controller -> + tsp("dummy_request_handler_init -> " + "received ssl controller - activate", []), + ssl:setopts(Socket, [{active, true}]), + ssl + end, + dummy_request_handler_loop(MFA, SockType, Socket). -dummy_request_handler_loop({Module, Function, Args}, Socket) -> +dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) -> tsp("dummy_request_handler_loop -> entry with" "~n Module: ~p" "~n Function: ~p" "~n Args: ~p", [Module, Function, Args]), receive - {tcp, _, Data} -> - tsp("dummy_request_handler_loop -> Data ~p", [Data]), - case handle_request(Module, Function, [Data | Args], Socket) of - stop -> + {Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) -> + tsp("dummy_request_handler_loop -> [~w] Data ~p", [Proto, Data]), + case handle_request(Module, Function, [Data | Args], Socket, Proto) of + stop when Proto =:= tcp -> gen_tcp:close(Socket); + stop when Proto =:= ssl -> + ssl:close(Socket); NewMFA -> - dummy_request_handler_loop(NewMFA, Socket) + dummy_request_handler_loop(NewMFA, SockType, Socket) end; - stop -> - gen_tcp:close(Socket) + stop when SockType =:= ip_comm -> + gen_tcp:close(Socket); + stop when SockType =:= ssl -> + ssl:close(Socket) end. -handle_request(Module, Function, Args, Socket) -> + +mk_close(tcp) -> fun(Sock) -> gen_tcp:close(Sock) end; +mk_close(ssl) -> fun(Sock) -> ssl:close(Sock) end. + +mk_send(tcp) -> fun(Sock, Data) -> gen_tcp:send(Sock, Data) end; +mk_send(ssl) -> fun(Sock, Data) -> ssl:send(Sock, Data) end. + +handle_request(Module, Function, Args, Socket, Proto) -> + Close = mk_close(Proto), + Send = mk_send(Proto), + handle_request(Module, Function, Args, Socket, Close, Send). + +handle_request(Module, Function, Args, Socket, Close, Send) -> tsp("handle_request -> entry with" "~n Module: ~p" "~n Function: ~p" @@ -2941,7 +3177,7 @@ handle_request(Module, Function, Args, Socket) -> {ok, Result} -> tsp("handle_request -> ok" "~n Result: ~p", [Result]), - case (catch handle_http_msg(Result, Socket)) of + case (catch handle_http_msg(Result, Socket, Close, Send)) of stop -> stop; <<>> -> @@ -2949,7 +3185,8 @@ handle_request(Module, Function, Args, Socket) -> {httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]}; Data -> handle_request(httpd_request, parse, - [Data |[?HTTP_MAX_HEADER_SIZE]], Socket) + [Data |[?HTTP_MAX_HEADER_SIZE]], Socket, + Close, Send) end; NewMFA -> tsp("handle_request -> " @@ -2957,7 +3194,7 @@ handle_request(Module, Function, Args, Socket) -> NewMFA end. -handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> +handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) -> tsp("handle_http_msg -> entry with: " "~n RelUri: ~p" "~n Headers: ~p" @@ -3114,16 +3351,16 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ "Proxy-Authenticate:#1Basic" ++ "\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), - gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")), + Send(Socket, Head), + Send(Socket, http_chunk:encode("<HTML><BODY>fo")), + Send(Socket, http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); "/capital_transfer_encoding.html" -> Head = "HTTP/1.1 200 ok\r\n" ++ "Transfer-Encoding:Chunked\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), - gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")), + Send(Socket, Head), + Send(Socket, http_chunk:encode("<HTML><BODY>fo")), + Send(Socket, http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); "/cookie.html" -> "HTTP/1.1 200 ok\r\n" ++ @@ -3142,20 +3379,20 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> "/once_chunked.html" -> Head = "HTTP/1.1 200 ok\r\n" ++ "Transfer-Encoding:Chunked\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), - gen_tcp:send(Socket, + Send(Socket, Head), + Send(Socket, http_chunk:encode("<HTML><BODY>fo")), + Send(Socket, http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); "/once.html" -> Head = "HTTP/1.1 200 ok\r\n" ++ "Content-Length:32\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, "<HTML><BODY>fo"), + Send(Socket, Head), + Send(Socket, "<HTML><BODY>fo"), test_server:sleep(1000), - gen_tcp:send(Socket, "ob"), + Send(Socket, "ob"), test_server:sleep(1000), - gen_tcp:send(Socket, "ar</BODY></HTML>"); + Send(Socket, "ar</BODY></HTML>"); "/invalid_http.html" -> "HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++ "Transfer-Encoding:chunked\r\n\r\n"; @@ -3178,9 +3415,9 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> ok; close -> %% Nothing to send, just close - gen_tcp:close(Socket); + Close(Socket); _ when is_list(Msg) orelse is_binary(Msg) -> - gen_tcp:send(Socket, Msg) + Send(Socket, Msg) end, tsp("handle_http_msg -> done"), NextRequest. @@ -3316,3 +3553,20 @@ dummy_ssl_server_hang_loop(_) -> stop -> ok end. + + +ensure_started([]) -> + ok; +ensure_started([App|Apps]) -> + ensure_started(App), + ensure_started(Apps); +ensure_started(App) when is_atom(App) -> + case (catch application:start(App)) of + ok -> + ok; + {error, {already_started, _}} -> + ok; + Error -> + throw({error, {failed_starting, App, Error}}) + end. + diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index fde5178879..c4d4bf969b 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -207,8 +207,11 @@ -export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1, ticket_7304/1]). -%%% Misc --export([ipv6_hostname/1, ipv6_address/1]). +%%% IPv6 tests +-export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1, + ipv6_address_ipcomm/0, ipv6_address_ipcomm/1, + ipv6_hostname_essl/0, ipv6_hostname_essl/1, + ipv6_address_essl/0, ipv6_address_essl/1]). %% Help functions -export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]). @@ -241,9 +244,15 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [{group, ip}, {group, ssl}, {group, http_1_1_ip}, - {group, http_1_0_ip}, {group, http_0_9_ip}, - {group, tickets}]. + [ + {group, ip}, + {group, ssl}, + {group, http_1_1_ip}, + {group, http_1_0_ip}, + {group, http_0_9_ip}, + {group, ipv6}, + {group, tickets} + ]. groups() -> [{ip, [], @@ -329,7 +338,8 @@ groups() -> {http_1_0_ip, [], [ip_head_1_0, ip_get_1_0, ip_post_1_0]}, {http_0_9_ip, [], [ip_get_0_9]}, - {ipv6, [], [ipv6_hostname, ipv6_address]}, + {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm, + ipv6_hostname_essl, ipv6_address_essl]}, {tickets, [], [ticket_5775, ticket_5865, ticket_5913, ticket_6003, ticket_7304]}]. @@ -408,10 +418,10 @@ init_per_testcase2(Case, Config) -> "~n Config: ~p" "~n", [?MODULE, Case, Config]), - IpNormal = integer_to_list(?IP_PORT) ++ ".conf", - IpHtacess = integer_to_list(?IP_PORT) ++ "htacess.conf", - SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", - SslHtacess = integer_to_list(?SSL_PORT) ++ "htacess.conf", + IpNormal = integer_to_list(?IP_PORT) ++ ".conf", + IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf", + SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", + SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf", DataDir = ?config(data_dir, Config), SuiteTopDir = ?config(suite_top_dir, Config), @@ -471,9 +481,9 @@ init_per_testcase2(Case, Config) -> io:format(user, "~w:init_per_testcase2(~w) -> ip testcase setups~n", [?MODULE, Case]), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], - normal_acess, IpNormal), + normal_access, IpNormal), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], - mod_htaccess, IpHtacess), + mod_htaccess, IpHtaccess), %% To be used by SSL test cases io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n", @@ -491,9 +501,9 @@ init_per_testcase2(Case, Config) -> end, create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], - normal_acess, SslNormal), + normal_access, SslNormal), create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], - mod_htaccess, SslHtacess), + mod_htaccess, SslHtaccess), %% To be used by IPv6 test cases. Case-clause is so that %% you can do ts:run(inets, httpd_SUITE, <test case>) @@ -501,22 +511,52 @@ init_per_testcase2(Case, Config) -> %% on 'test_host_ipv6_only' that will only be present %% when you run the whole test suite due to shortcomings %% of the test server. - %% case (catch ?config(test_host_ipv6_only, Config)) of - %% {_,IPv6Host,IPv6Adress,_,_} -> - %% create_ipv6_config([{port, ?IP_PORT}, - %% {sock_type, ip_comm} | NewConfig], - %% "ipv6_hostname.conf", IPv6Host), - %% create_ipv6_config([{port, ?IP_PORT}, - %% {sock_type, ip_comm} | NewConfig], - %% "ipv6_address.conf", IPv6Adress); - %% _ -> - %% ok - %% end, - + + io:format(user, "~w:init_per_testcase2(~w) -> " + "maybe generate IPv6 config file(s)", [?MODULE, Case]), + NewConfig2 = + case atom_to_list(Case) of + "ipv6_" ++ _ -> + case (catch inets_test_lib:has_ipv6_support(NewConfig)) of + {ok, IPv6Address0} -> + {ok, Hostname} = inet:gethostname(), + IPv6Address = http_transport:ipv6_name(IPv6Address0), + create_ipv6_config([{port, ?IP_PORT}, + {sock_type, ip_comm}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_hostname_ipcomm.conf", + Hostname), + create_ipv6_config([{port, ?IP_PORT}, + {sock_type, ip_comm}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_address_ipcomm.conf", + IPv6Address), + create_ipv6_config([{port, ?SSL_PORT}, + {sock_type, essl}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_hostname_essl.conf", + Hostname), + create_ipv6_config([{port, ?SSL_PORT}, + {sock_type, essl}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_address_essl.conf", + IPv6Address), + [{ipv6_host, IPv6Address} | NewConfig]; + _ -> + NewConfig + end; + _ -> + NewConfig + end, + io:format(user, "~w:init_per_testcase2(~w) -> done~n", [?MODULE, Case]), - NewConfig. + NewConfig2. init_per_testcase3(Case, Config) -> @@ -547,10 +587,10 @@ init_per_testcase3(Case, Config) -> [?MODULE, Case]), inets:disable_trace(); _ -> - %% TraceLevel = max, io:format(user, "~w:init_per_testcase3(~w) -> enabling trace", [?MODULE, Case]), - TraceLevel = 70, + %% TraceLevel = 70, + TraceLevel = max, TraceDest = io, inets:enable_trace(TraceLevel, TraceDest, httpd) end, @@ -569,7 +609,7 @@ init_per_testcase3(Case, Config) -> inets_test_lib:start_http_server( filename:join(TcTopDir, integer_to_list(?IP_PORT) ++ - "htacess.conf")), + "htaccess.conf")), "mod_htaccess"; "ip_" ++ Rest -> inets_test_lib:start_http_server( @@ -602,7 +642,7 @@ init_per_testcase3(Case, Config) -> case inets_test_lib:start_http_server_ssl( filename:join(TcTopDir, integer_to_list(?SSL_PORT) ++ - "htacess.conf"), SslTag) of + "htaccess.conf"), SslTag) of ok -> "mod_htaccess"; Other -> @@ -627,16 +667,13 @@ init_per_testcase3(Case, Config) -> {skip, "SSL does not seem to be supported"} end; "ipv6_" ++ _ = TestCaseStr -> - {ok, Hostname} = inet:gethostname(), - - case lists:member(list_to_atom(Hostname), - ?config(ipv6_hosts, Config)) of - true -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> inets_test_lib:start_http_server( filename:join(TcTopDir, TestCaseStr ++ ".conf")); - false -> + _ -> {skip, "Host does not support IPv6"} end end, @@ -650,8 +687,8 @@ init_per_testcase3(Case, Config) -> "mod_htaccess" -> ServerRoot = ?config(server_root, Config), Path = filename:join([ServerRoot, "htdocs"]), - catch remove_htacess(Path), - create_htacess_data(Path, ?config(address, Config)), + catch remove_htaccess(Path), + create_htaccess_data(Path, ?config(address, Config)), [{watchdog, Dog} | NewConfig]; "range" -> ServerRoot = ?config(server_root, Config), @@ -2409,30 +2446,76 @@ ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) -> ok. %------------------------------------------------------------------------- -ipv6_hostname(doc) -> + +ipv6_hostname_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_hostname_ipcomm(X) -> + SocketType = ip_comm, + Port = ?IP_PORT, + ipv6_hostname(SocketType, Port, X). + +ipv6_hostname_essl() -> + [{require, ipv6_hosts}]. +ipv6_hostname_essl(X) -> + SocketType = essl, + Port = ?SSL_PORT, + ipv6_hostname(SocketType, Port, X). + +ipv6_hostname(_SocketType, _Port, doc) -> ["Test standard ipv6 address"]; -ipv6_hostname(suite)-> +ipv6_hostname(_SocketType, _Port, suite)-> []; -ipv6_hostname(Config) when is_list(Config) -> +ipv6_hostname(SocketType, Port, Config) when is_list(Config) -> + tsp("ipv6_hostname -> entry with" + "~n SocketType: ~p" + "~n Port: ~p" + "~n Config: ~p", [SocketType, Port, Config]), Host = ?config(host, Config), - httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, node(), - "GET / HTTP/1.1\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.1"}]), + URI = "GET HTTP://" ++ + Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", + tsp("ipv6_hostname -> Host: ~p", [Host]), + httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], + node(), + URI, + [{statuscode, 200}, {version, "HTTP/1.1"}]), ok. %%------------------------------------------------------------------------- -ipv6_address(doc) -> + +ipv6_address_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_address_ipcomm(X) -> + SocketType = ip_comm, + Port = ?IP_PORT, + ipv6_address(SocketType, Port, X). + +ipv6_address_essl() -> + [{require, ipv6_hosts}]. +ipv6_address_essl(X) -> + SocketType = essl, + Port = ?SSL_PORT, + ipv6_address(SocketType, Port, X). + +ipv6_address(_SocketType, _Port, doc) -> ["Test standard ipv6 address"]; -ipv6_address(suite)-> +ipv6_address(_SocketType, _Port, suite)-> []; -ipv6_address(Config) when is_list(Config) -> - httpd_test_lib:verify_request(ip_comm, ?IPV6_LOCAL_HOST, ?IP_PORT, - node(), "GET / HTTP/1.1\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.1"}]), +ipv6_address(SocketType, Port, Config) when is_list(Config) -> + tsp("ipv6_address -> entry with" + "~n SocketType: ~p" + "~n Port: ~p" + "~n Config: ~p", [SocketType, Port, Config]), + Host = ?config(host, Config), + tsp("ipv6_address -> Host: ~p", [Host]), + URI = "GET HTTP://" ++ + Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", + httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], + node(), + URI, + [{statuscode, 200}, {version, "HTTP/1.1"}]), ok. + %%-------------------------------------------------------------------- ticket_5775(doc) -> ["Tests that content-length is correct"]; @@ -2805,22 +2888,22 @@ cleanup_mnesia() -> mnesia:delete_schema([node()]), ok. -create_htacess_data(Path, IpAddress)-> - create_htacess_dirs(Path), +create_htaccess_data(Path, IpAddress)-> + create_htaccess_dirs(Path), create_html_file(filename:join([Path,"ht/open/dummy.html"])), create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])), create_html_file(filename:join([Path,"ht/secret/dummy.html"])), create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])), - create_htacess_file(filename:join([Path,"ht/open/.htaccess"]), + create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]), Path, "user one Aladdin"), - create_htacess_file(filename:join([Path,"ht/secret/.htaccess"]), + create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]), Path, "group group1 group2"), - create_htacess_file(filename:join([Path, + create_htaccess_file(filename:join([Path, "ht/secret/top_secret/.htaccess"]), Path, "user four"), - create_htacess_file(filename:join([Path,"ht/blocknet/.htaccess"]), + create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]), Path, nouser, IpAddress), create_user_group_file(filename:join([Path,"ht","users.file"]), @@ -2835,7 +2918,7 @@ create_html_file(PathAndFileName)-> "<html><head><title>test</title></head> <body>testar</body></html>")). -create_htacess_file(PathAndFileName, BaseDir, RequireData)-> +create_htaccess_file(PathAndFileName, BaseDir, RequireData)-> file:write_file(PathAndFileName, list_to_binary( "AuthUserFile "++ BaseDir ++ @@ -2844,7 +2927,7 @@ create_htacess_file(PathAndFileName, BaseDir, RequireData)-> " Basic\n<Limit>\nrequire " ++ RequireData ++ "\n</Limit>")). -create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> +create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> file:write_file(PathAndFileName,list_to_binary( "AuthUserFile "++ BaseDir ++ "/ht/users.file\nAuthGroupFile " ++ @@ -2858,14 +2941,14 @@ create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> create_user_group_file(PathAndFileName, Data)-> file:write_file(PathAndFileName, list_to_binary(Data)). -create_htacess_dirs(Path)-> +create_htaccess_dirs(Path)-> ok = file:make_dir(filename:join([Path,"ht"])), ok = file:make_dir(filename:join([Path,"ht/open"])), ok = file:make_dir(filename:join([Path,"ht/blocknet"])), ok = file:make_dir(filename:join([Path,"ht/secret"])), ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])). -remove_htacess_dirs(Path)-> +remove_htaccess_dirs(Path)-> file:del_dir(filename:join([Path,"ht/secret/top_secret"])), file:del_dir(filename:join([Path,"ht/secret"])), file:del_dir(filename:join([Path,"ht/blocknet"])), @@ -2888,7 +2971,7 @@ format_ip(IpAddress,Pos)when Pos > 0-> format_ip(IpAddress, _Pos)-> "1" ++ IpAddress. -remove_htacess(Path)-> +remove_htaccess(Path)-> file:delete(filename:join([Path,"ht/open/dummy.html"])), file:delete(filename:join([Path,"ht/secret/dummy.html"])), file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])), @@ -2899,7 +2982,7 @@ remove_htacess(Path)-> file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])), file:delete(filename:join([Path,"ht","users.file"])), file:delete(filename:join([Path,"ht","groups.file"])), - remove_htacess_dirs(Path). + remove_htaccess_dirs(Path). dos_hostname_poll(Type, Host, Port, Node, Hosts) -> @@ -2939,35 +3022,66 @@ create_range_data(Path) -> "12345678901234567890", "12345678901234567890"])). -%% create_ipv6_config(Config, FileName, Ipv6Address) -> -%% ServerRoot = ?config(server_root, Config), -%% TcTopDir = ?config(tc_top_dir, Config), -%% Port = ?config(port, Config), -%% SockType = ?config(sock_type, Config), -%% -%% MaxHdrSz = io_lib:format("~p", [256]), -%% MaxHdrAct = io_lib:format("~p", [close]), -%% -%% Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" -%% " mod_include mod_dir mod_get mod_head" -%% " mod_log mod_disk_log mod_trace", -%% -%% HttpConfig = [cline(["BindAddress ", "[" ++ Ipv6Address ++"]|inet6"]), -%% cline(["Port ", integer_to_list(Port)]), -%% cline(["ServerName ", "httpc_test"]), -%% cline(["SocketType ", atom_to_list(SockType)]), -%% cline([Mod_order]), -%% cline(["ServerRoot ", ServerRoot]), -%% cline(["DocumentRoot ", -%% filename:join(ServerRoot, "htdocs")]), -%% cline(["MaxHeaderSize ",MaxHdrSz]), -%% cline(["MaxHeaderAction ",MaxHdrAct]), -%% cline(["DirectoryIndex ", "index.html "]), -%% cline(["DefaultType ", "text/plain"])], -%% ConfigFile = filename:join([TcTopDir,FileName]), -%% {ok, Fd} = file:open(ConfigFile, [write]), -%% ok = file:write(Fd, lists:flatten(HttpConfig)), -%% ok = file:close(Fd). +create_ipv6_config(Config, FileName, Ipv6Address) -> + ServerRoot = ?config(server_root, Config), + TcTopDir = ?config(tc_top_dir, Config), + Port = ?config(port, Config), + SockType = ?config(sock_type, Config), + Mods = io_lib:format("~p", [httpd_mod]), + Funcs = io_lib:format("~p", [ssl_password_cb]), + Host = ?config(ipv6_host, Config), + + MaxHdrSz = io_lib:format("~p", [256]), + MaxHdrAct = io_lib:format("~p", [close]), + + Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" + " mod_include mod_dir mod_get mod_head" + " mod_log mod_disk_log mod_trace", + + SSL = + if + (SockType =:= ssl) orelse + (SockType =:= ossl) orelse + (SockType =:= essl) -> + [cline(["SSLCertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCertificateKeyFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCACertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLPasswordCallbackModule ", Mods]), + cline(["SSLPasswordCallbackFunction ", Funcs]), + cline(["SSLVerifyClient 0"]), + cline(["SSLVerifyDepth 1"])]; + true -> + [] + end, + + BindAddress = "[" ++ Ipv6Address ++"]|inet6", + + HttpConfig = + [cline(["BindAddress ", BindAddress]), + cline(["Port ", integer_to_list(Port)]), + cline(["ServerName ", Host]), + cline(["SocketType ", atom_to_list(SockType)]), + cline([Mod_order]), + cline(["ServerRoot ", ServerRoot]), + cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]), + cline(["MaxHeaderSize ",MaxHdrSz]), + cline(["MaxHeaderAction ",MaxHdrAct]), + cline(["DirectoryIndex ", "index.html "]), + cline(["DefaultType ", "text/plain"]), + SSL], + ConfigFile = filename:join([TcTopDir,FileName]), + {ok, Fd} = file:open(ConfigFile, [write]), + ok = file:write(Fd, lists:flatten(HttpConfig)), + ok = file:close(Fd). + + +%% tsp(F) -> +%% inets_test_lib:tsp(F). +tsp(F, A) -> + inets_test_lib:tsp(F, A). tsf(Reason) -> - test_server:fail(Reason). + inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 3189a758a5..2903aaafa5 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -22,7 +22,7 @@ -include("inets_test_lib.hrl"). %% Poll functions --export([verify_request/6, verify_request/7, is_expect/1]). +-export([verify_request/6, verify_request/7, verify_request/8, is_expect/1]). -record(state, {request, % string() socket, % socket() @@ -81,33 +81,57 @@ %%------------------------------------------------------------------ verify_request(SocketType, Host, Port, Node, RequestStr, Options) -> verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000). -verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) -> - {ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port), +verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options) + when is_list(TranspOpts) -> + verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, 30000); +verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) + when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) -> + verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut). +verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) -> + tsp("verify_request -> entry with" + "~n SocketType: ~p" + "~n Host: ~p" + "~n Port: ~p" + "~n TranspOpts: ~p" + "~n Node: ~p" + "~n Options: ~p" + "~n TimeOut: ~p", + [SocketType, Host, Port, TranspOpts, Node, Options, TimeOut]), + case (catch inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts)) of + {ok, Socket} -> + tsp("verify_request -> connected - now send message"), + SendRes = inets_test_lib:send(SocketType, Socket, RequestStr), + tsp("verify_request -> send result: " + "~n ~p", [SendRes]), + State = case inets_regexp:match(RequestStr, "printenv") of + nomatch -> + #state{}; + _ -> + #state{print = true} + end, + + case request(State#state{request = RequestStr, + socket = Socket}, TimeOut) of + {error, Reason} -> + tsp("request failed: " + "~n Reason: ~p", [Reason]), + {error, Reason}; + NewState -> + tsp("validate reply: " + "~n NewState: ~p", [NewState]), + ValidateResult = + validate(RequestStr, NewState, Options, Node, Port), + tsp("validation result: " + "~n ~p", [ValidateResult]), + inets_test_lib:close(SocketType, Socket), + ValidateResult + end; - _SendRes = inets_test_lib:send(SocketType, Socket, RequestStr), - - State = case inets_regexp:match(RequestStr, "printenv") of - nomatch -> - #state{}; - _ -> - #state{print = true} - end, - - case request(State#state{request = RequestStr, - socket = Socket}, TimeOut) of - {error, Reason} -> - tsp("request failed: " - "~n Reason: ~p", [Reason]), - {error, Reason}; - NewState -> - tsp("validate reply: " - "~n NewState: ~p", [NewState]), - ValidateResult = validate(RequestStr, NewState, Options, - Node, Port), - tsp("validation result: " - "~n ~p", [ValidateResult]), - inets_test_lib:close(SocketType, Socket), - ValidateResult + ConnectError -> + tsp("verify_request -> connect failed: " + "~n ~p" + "~n", [ConnectError]), + tsf({connect_failure, ConnectError}) end. request(#state{mfa = {Module, Function, Args}, @@ -214,7 +238,10 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _}, headers = Headers, body = Body}, Options, N, P) -> - %io:format("Status~p: H:~p B:~p~n", [StatusCode, Headers, Body]), + %% tsp("validate -> entry with" + %% "~n StatusCode: ~p" + %% "~n Headers: ~p" + %% "~n Body: ~p", [StatusCode, Headers, Body]), check_version(Version, Options), case lists:keysearch(statuscode, 1, Options) of {value, _} -> @@ -342,8 +369,10 @@ print(_, _, #state{print = false}) -> ok. -%% tsp(F) -> -%% tsp(F, []). +tsp(F) -> + inets_test_lib:tsp(F). tsp(F, A) -> - test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + inets_test_lib:tsp(F, A). +tsf(Reason) -> + inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index 6cedaf9638..2e19c41f16 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -26,18 +26,64 @@ -export([start_http_server/1, start_http_server/2]). -export([start_http_server_ssl/1, start_http_server_ssl/2]). -export([hostname/0]). --export([connect_bin/3, connect_byte/3, send/3, close/2]). +-export([connect_bin/3, connect_bin/4, + connect_byte/3, connect_byte/4, + send/3, close/2]). -export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]). -export([info/4, log/4, debug/4, print/4]). +-export([tsp/1, tsp/2, tsf/1]). -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). --export([oscmd/1]). +-export([oscmd/1, has_ipv6_support/1]). -export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]). -export([flush/0]). -export([start_node/1, stop_node/1]). %% -- Misc os command and stuff +has_ipv6_support(Config) -> + case lists:keysearch(ipv6_hosts, 1, Config) of + false -> + %% Do a basic check to se if + %% our own host has a working IPv6 address... + tsp("has_ipv6_support -> no ipv6_hosts config"), + {ok, Hostname} = inet:gethostname(), + case inet:getaddrs(Hostname, inet6) of + {ok, [Addr|_]} when is_tuple(Addr) andalso + (element(1, Addr) =/= 0) -> + %% We actually need to test that the addr can be used, + %% this is done by attempting to create a (tcp) + %% listen socket + tsp("has_ipv6_support -> check Addr: ~p", [Addr]), + case (catch gen_tcp:listen(0, [inet6, {ip, Addr}])) of + {ok, LSock} -> + tsp("has_ipv6_support -> we are ipv6 host"), + gen_tcp:close(LSock), + {ok, Addr}; + _ -> + undefined + end; + _ -> + undefined + end; + {value, {_, Hosts}} when is_list(Hosts) -> + %% Check if our host is in the list of *known* IPv6 hosts + tsp("has_ipv6_support -> Hosts: ~p", [Hosts]), + {ok, Hostname} = inet:gethostname(), + case lists:member(list_to_atom(Hostname), Hosts) of + true -> + tsp("has_ipv6_support -> we are known ipv6 host"), + {ok, [Addr|_]} = inet:getaddrs(Hostname, inet6), + {ok, Addr}; + false -> + undefined + end; + + _ -> + undefined + + end. + oscmd(Cmd) -> string:strip(os:cmd(Cmd), right, $\n). @@ -87,31 +133,34 @@ start_http_server(Conf) -> start_http_server(Conf, ?HTTP_DEFAULT_SSL_KIND). start_http_server(Conf, essl = _SslTag) -> + tsp("start_http_server(essl) -> entry - try start crypto and public_key"), application:start(crypto), + application:start(public_key), do_start_http_server(Conf); -start_http_server(Conf, _SslTag) -> +start_http_server(Conf, SslTag) -> + tsp("start_http_server(~w) -> entry", [SslTag]), do_start_http_server(Conf). do_start_http_server(Conf) -> - tsp("start http server with " + tsp("do_start_http_server -> entry with" "~n Conf: ~p" "~n", [Conf]), application:load(inets), case application:set_env(inets, services, [{httpd, Conf}]) of ok -> + tsp("start_http_server -> httpd conf stored in inets app env"), case application:start(inets) of ok -> + tsp("start_http_server -> inets started"), ok; Error1 -> - test_server:format("<ERROR> Failed starting application: " - "~n Error: ~p" - "~n", [Error1]), + tsp("<ERROR> Failed starting application: " + "~n Error1: ~p", [Error1]), Error1 end; Error2 -> - test_server:format("<ERROR> Failed set application env: " - "~n Error: ~p" - "~n", [Error2]), + tsp("<ERROR> Failed set application env: " + "~n Error: ~p", [Error2]), Error2 end. @@ -285,29 +334,45 @@ os_based_skip(_) -> %% Host -> atom() | string() | {A, B, C, D} %% Port -> integer() -connect_bin(ssl, Host, Port) -> - connect(ssl, Host, Port, [binary, {packet,0}]); -connect_bin(ossl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, old}, binary, {packet,0}]); -connect_bin(essl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true}]); -connect_bin(ip_comm, Host, Port) -> - Opts = [inet6, binary, {packet,0}], +connect_bin(SockType, Host, Port) -> + connect_bin(SockType, Host, Port, []). + +connect_bin(ssl, Host, Port, Opts0) -> + Opts = [binary, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_bin(ossl, Host, Port, Opts0) -> + Opts = [{ssl_imp, old}, binary, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_bin(essl, Host, Port, Opts0) -> + Opts = [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true} | Opts0], + connect(ssl, Host, Port, Opts); +connect_bin(ip_comm, Host, Port, Opts0) -> + Opts = [binary, {packet, 0} | Opts0], connect(ip_comm, Host, Port, Opts). + +connect_byte(SockType, Host, Port) -> + connect_byte(SockType, Host, Port, []). -connect_byte(ssl, Host, Port) -> - connect(ssl, Host, Port, [{packet,0}]); -connect_byte(ossl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, old}, {packet,0}]); -connect_byte(essl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, new}, {packet,0}]); -connect_byte(ip_comm, Host, Port) -> - Opts = [inet6, {packet,0}], +connect_byte(ssl, Host, Port, Opts0) -> + Opts = [{packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_byte(ossl, Host, Port, Opts0) -> + Opts = [{ssl_imp, old}, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_byte(essl, Host, Port, Opts0) -> + Opts = [{ssl_imp, new}, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_byte(ip_comm, Host, Port, Opts0) -> + Opts = [{packet,0} | Opts0], connect(ip_comm, Host, Port, Opts). connect(ssl, Host, Port, Opts) -> + tsp("connect(ssl) -> entry with" + "~n Host: ~p" + "~n Port: ~p" + "~n Opts: ~p", [Host, Port, Opts]), ssl:start(), %% Does not support ipv6 in old ssl case ssl:connect(Host, Port, Opts) of @@ -319,21 +384,28 @@ connect(ssl, Host, Port, Opts) -> Error end; connect(ip_comm, Host, Port, Opts) -> + tsp("connect(ip_comm) -> entry with" + "~n Host: ~p" + "~n Port: ~p" + "~n Opts: ~p", [Host, Port, Opts]), case gen_tcp:connect(Host,Port, Opts) of {ok, Socket} -> - %% tsp("connect success"), + tsp("connect success"), {ok, Socket}; {error, nxdomain} -> - tsp("nxdomain opts: ~p", [Opts]), + tsp("connect error nxdomain when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, eafnosupport} -> - tsp("eafnosupport opts: ~p", [Opts]), + tsp("connect error eafnosupport when opts: ~p", [Opts]), + connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); + {error, econnreset} -> + tsp("connect error econnreset when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, enetunreach} -> - tsp("eafnosupport opts: ~p", [Opts]), + tsp("connect error eafnosupport when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, {enfile,_}} -> - tsp("Error enfile"), + tsp("connect error enfile when opts: ~p", [Opts]), {error, enfile}; Error -> tsp("Unexpected error: " @@ -414,7 +486,22 @@ flush() -> tsp(F) -> tsp(F, []). tsp(F, A) -> - test_server:format("~p ~p ~p:" ++ F ++ "~n", [node(), self(), ?MODULE | A]). + Timestamp = formated_timestamp(), + test_server:format("*** ~s ~p ~p ~w:" ++ F ++ "~n", + [Timestamp, node(), self(), ?MODULE | A]). tsf(Reason) -> test_server:fail(Reason). + +formated_timestamp() -> + format_timestamp( os:timestamp() ). + +format_timestamp({_N1, _N2, N3} = Now) -> + {Date, Time} = calendar:now_to_datetime(Now), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", + [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), + lists:flatten(FormatDate). + diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index c0e25a30e3..4abc1733d3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.6 +INETS_VSN = 5.7 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index de10e31d36..214e994889 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -104,6 +104,8 @@ TOP_SPECS_FILE = specs.xml # ---------------------------------------------------- XML_FLAGS += +SPECS_ESRC = ../../src + SPECS_FLAGS = -I../../include # ---------------------------------------------------- diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 6f85388c22..98cdd416b0 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -288,196 +288,156 @@ <datatypes> <datatype> + <name name="load_ret"/> + </datatype> + <datatype> <name name="load_error_rsn"/> </datatype> </datatypes> <funcs> <func> - <name>set_path(Path) -> true | {error, What}</name> + <name name="set_path" arity="1"/> <fsummary>Set the code server search path</fsummary> - <type> - <v>Path = [Dir]</v> - <v>Dir = string()</v> - <v>What = bad_directory | bad_path</v> - </type> <desc> - <p>Sets the code path to the list of directories <c>Path</c>.</p> + <p>Sets the code path to the list of directories <c><anno>Path</anno></c>.</p> <p>Returns <c>true</c> if successful, or - <c>{error, bad_directory}</c> if any <c>Dir</c> is not + <c>{error, bad_directory}</c> if any <c><anno>Dir</anno></c> is not the name of a directory, or <c>{error, bad_path}</c> if the argument is invalid.</p> </desc> </func> <func> - <name>get_path() -> Path</name> + <name name="get_path" arity="0"/> <fsummary>Return the code server search path</fsummary> - <type> - <v>Path = [Dir]</v> - <v>Dir = string()</v> - </type> <desc> <p>Returns the code path</p> </desc> </func> <func> - <name>add_path(Dir) -> true | {error, What}</name> - <name>add_pathz(Dir) -> true | {error, What}</name> + <name name="add_path" arity="1"/> + <name name="add_pathz" arity="1"/> <fsummary>Add a directory to the end of the code path</fsummary> - <type> - <v>Dir = string()</v> - <v>What = bad_directory</v> - </type> + <type name="add_path_ret"/> <desc> - <p>Adds <c>Dir</c> to the code path. The directory is added as - the last directory in the new path. If <c>Dir</c> already + <p>Adds <c><anno>Dir</anno></c> to the code path. The directory is added as + the last directory in the new path. If <c><anno>Dir</anno></c> already exists in the path, it is not added.</p> <p>Returns <c>true</c> if successful, or - <c>{error, bad_directory}</c> if <c>Dir</c> is not the name + <c>{error, bad_directory}</c> if <c><anno>Dir</anno></c> is not the name of a directory.</p> </desc> </func> <func> - <name>add_patha(Dir) -> true | {error, What}</name> + <name name="add_patha" arity="1"/> <fsummary>Add a directory to the beginning of the code path</fsummary> - <type> - <v>Dir = string()</v> - <v>What = bad_directory</v> - </type> + <type name="add_path_ret"/> <desc> - <p>Adds <c>Dir</c> to the beginning of the code path. If - <c>Dir</c> already exists, it is removed from the old + <p>Adds <c><anno>Dir</anno></c> to the beginning of the code path. If + <c><anno>Dir</anno></c> already exists, it is removed from the old position in the code path.</p> <p>Returns <c>true</c> if successful, or - <c>{error, bad_directory}</c> if <c>Dir</c> is not the name + <c>{error, bad_directory}</c> if <c><anno>Dir</anno></c> is not the name of a directory.</p> </desc> </func> <func> - <name>add_paths(Dirs) -> ok</name> - <name>add_pathsz(Dirs) -> ok</name> + <name name="add_paths" arity="1"/> + <name name="add_pathsz" arity="1"/> <fsummary>Add directories to the end of the code path</fsummary> - <type> - <v>Dirs = [Dir]</v> - <v>Dir = string()</v> - </type> <desc> - <p>Adds the directories in <c>Dirs</c> to the end of the code - path. If a <c>Dir</c> already exists, it is not added. This + <p>Adds the directories in <c><anno>Dirs</anno></c> to the end of the code + path. If a <c><anno>Dir</anno></c> already exists, it is not added. This function always returns <c>ok</c>, regardless of the validity - of each individual <c>Dir</c>.</p> + of each individual <c><anno>Dir</anno></c>.</p> </desc> </func> <func> - <name>add_pathsa(Dirs) -> ok</name> + <name name="add_pathsa" arity="1"/> <fsummary>Add directories to the beginning of the code path</fsummary> - <type> - <v>Dirs = [Dir]</v> - <v>Dir = string()</v> - </type> <desc> - <p>Adds the directories in <c>Dirs</c> to the beginning of - the code path. If a <c>Dir</c> already exists, it is removed + <p>Adds the directories in <c><anno>Dirs</anno></c> to the beginning of + the code path. If a <c><anno>Dir</anno></c> already exists, it is removed from the old position in the code path. This function always returns <c>ok</c>, regardless of the validity of each - individual <c>Dir</c>.</p> + individual <c><anno>Dir</anno></c>.</p> </desc> </func> <func> - <name>del_path(Name | Dir) -> true | false | {error, What}</name> + <name name="del_path" arity="1"/> <fsummary>Delete a directory from the code path</fsummary> - <type> - <v>Name = atom()</v> - <v>Dir = string()</v> - <v>What = bad_name</v> - </type> <desc> <p>Deletes a directory from the code path. The argument can be - an atom <c>Name</c>, in which case the directory with - the name <c>.../Name[-Vsn][/ebin]</c> is deleted from the code + an atom <c><anno>Name</anno></c>, in which case the directory with + the name <c>.../<anno>Name</anno>[-Vsn][/ebin]</c> is deleted from the code path. It is also possible to give the complete directory name - <c>Dir</c> as argument.</p> + <c><anno>Dir</anno></c> as argument.</p> <p>Returns <c>true</c> if successful, or <c>false</c> if the directory is not found, or <c>{error, bad_name}</c> if the argument is invalid.</p> </desc> </func> <func> - <name>replace_path(Name, Dir) -> true | {error, What}</name> + <name name="replace_path" arity="2"/> <fsummary>Replace a directory with another in the code path</fsummary> - <type> - <v>Name = atom()</v> - <v>Dir = string()</v> - <v>What = bad_name | bad_directory | {badarg, term()}</v> - </type> <desc> <p>This function replaces an old occurrence of a directory - named <c>.../Name[-Vsn][/ebin]</c>, in the code path, with - <c>Dir</c>. If <c>Name</c> does not exist, it adds the new - directory <c>Dir</c> last in the code path. The new directory - must also be named <c>.../Name[-Vsn][/ebin]</c>. This function + named <c>.../<anno>Name</anno>[-Vsn][/ebin]</c>, in the code path, with + <c><anno>Dir</anno></c>. If <c><anno>Name</anno></c> does not exist, it adds the new + directory <c><anno>Dir</anno></c> last in the code path. The new directory + must also be named <c>.../<anno>Name</anno>[-Vsn][/ebin]</c>. This function should be used if a new version of the directory (library) is added to a running system.</p> <p>Returns <c>true</c> if successful, or - <c>{error, bad_name}</c> if <c>Name</c> is not found, or - <c>{error, bad_directory}</c> if <c>Dir</c> does not exist, or - <c>{error, {badarg, [Name, Dir]}}</c> if <c>Name</c> or - <c>Dir</c> is invalid.</p> + <c>{error, bad_name}</c> if <c><anno>Name</anno></c> is not found, or + <c>{error, bad_directory}</c> if <c><anno>Dir</anno></c> does not exist, or + <c>{error, {badarg, [<anno>Name</anno>, <anno>Dir</anno>]}}</c> if <c><anno>Name</anno></c> or + <c><anno>Dir</anno></c> is invalid.</p> </desc> </func> <func> - <name>load_file(Module) -> {module, Module} | {error, What}</name> + <name name="load_file" arity="1"/> <fsummary>Load a module</fsummary> - <type> - <v>Module = atom()</v> - <v>What = nofile | sticky_directory | badarg | term()</v> - </type> + <type name="load_ret"/> <desc> - <p>Tries to load the Erlang module <c>Module</c>, using + <p>Tries to load the Erlang module <c><anno>Module</anno></c>, using the code path. It looks for the object code file with an extension that corresponds to the Erlang machine used, for - example <c>Module.beam</c>. The loading fails if the module + example <c><anno>Module</anno>.beam</c>. The loading fails if the module name found in the object code differs from the name - <c>Module</c>. + <c><anno>Module</anno></c>. <seealso marker="#load_binary/3">load_binary/3</seealso> must be used to load object code with a module name that is different from the file name.</p> - <p>Returns <c>{module, Module}</c> if successful, or + <p>Returns <c>{module, <anno>Module</anno>}</c> if successful, or <c>{error, nofile}</c> if no object code is found, or <c>{error, sticky_directory}</c> if the object code resides in - a sticky directory, or <c>{error, badarg}</c> if the argument - is invalid. Also if the loading fails, an error tuple is + a sticky directory. Also if the loading fails, an error tuple is returned. See <seealso marker="erts:erlang#load_module/2">erlang:load_module/2</seealso> - for possible values of <c>What</c>.</p> + for possible values of <c><anno>What</anno></c>.</p> </desc> </func> <func> - <name>load_abs(Filename) -> {module, Module} | {error, What}</name> + <name name="load_abs" arity="1"/> <fsummary>Load a module, residing in a given file</fsummary> - <type> - <v>Filename = string()</v> - <v>Module = atom()</v> - <v>What = nofile | sticky_directory | badarg | term()</v> - </type> + <type name="load_ret"/> + <type name="loaded_filename"/> + <type name="loaded_ret_atoms"/> <desc> - <p>Does the same as <c>load_file(Module)</c>, but - <c>Filename</c> is either an absolute file name, or a + <p>Does the same as <c>load_file(<anno>Module</anno>)</c>, but + <c><anno>Filename</anno></c> is either an absolute file name, or a relative file name. The code path is not searched. It returns a value in the same way as <seealso marker="#load_file/1">load_file/1</seealso>. Note - that <c>Filename</c> should not contain the extension (for + that <c><anno>Filename</anno></c> should not contain the extension (for example <c>".beam"</c>); <c>load_abs/1</c> adds the correct extension itself.</p> </desc> </func> <func> - <name>ensure_loaded(Module) -> {module, Module} | {error, What}</name> + <name name="ensure_loaded" arity="1"/> <fsummary>Ensure that a module is loaded</fsummary> - <type> - <v>Module = atom()</v> - <v>What = nofile | sticky_directory | embedded | badarg | term()</v> - </type> <desc> <p>Tries to to load a module in the same way as <seealso marker="#load_file/1">load_file/1</seealso>, @@ -487,54 +447,45 @@ </desc> </func> <func> - <name>load_binary(Module, Filename, Binary) -> {module, Module} | {error, What}</name> + <name name="load_binary" arity="3"/> <fsummary>Load object code for a module</fsummary> - <type> - <v>Module = atom()</v> - <v>Filename = string()</v> - <v>What = sticky_directory | badarg | term()</v> - </type> + <type name="loaded_filename"/> + <type name="loaded_ret_atoms"/> <desc> <p>This function can be used to load object code on remote - Erlang nodes. The argument <c>Binary</c> must contain - object code for <c>Module</c>. - <c>Filename</c> is only used by the code server to keep a - record of from which file the object code for <c>Module</c> - comes. Accordingly, <c>Filename</c> is not opened and read by + Erlang nodes. The argument <c><anno>Binary</anno></c> must contain + object code for <c><anno>Module</anno></c>. + <c><anno>Filename</anno></c> is only used by the code server to keep a + record of from which file the object code for <c><anno>Module</anno></c> + comes. Accordingly, <c><anno>Filename</anno></c> is not opened and read by the code server.</p> - <p>Returns <c>{module, Module}</c> if successful, or + <p>Returns <c>{module, <anno>Module</anno>}</c> if successful, or <c>{error, sticky_directory}</c> if the object code resides in a sticky directory, or <c>{error, badarg}</c> if any argument is invalid. Also if the loading fails, an error tuple is returned. See <seealso marker="erts:erlang#load_module/2">erlang:load_module/2</seealso> - for possible values of <c>What</c>.</p> + for possible values of <c><anno>What</anno></c>.</p> </desc> </func> <func> - <name>delete(Module) -> true | false</name> + <name name="delete" arity="1"/> <fsummary>Removes current code for a module</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>Removes the current code for <c>Module</c>, that is, - the current code for <c>Module</c> is made old. This means + <p>Removes the current code for <c><anno>Module</anno></c>, that is, + the current code for <c><anno>Module</anno></c> is made old. This means that processes can continue to execute the code in the module, but that no external function calls can be made to it.</p> <p>Returns <c>true</c> if successful, or <c>false</c> if there - is old code for <c>Module</c> which must be purged first, or - if <c>Module</c> is not a (loaded) module.</p> + is old code for <c><anno>Module</anno></c> which must be purged first, or + if <c><anno>Module</anno></c> is not a (loaded) module.</p> </desc> </func> <func> - <name>purge(Module) -> true | false</name> + <name name="purge" arity="1"/> <fsummary>Removes old code for a module</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>Purges the code for <c>Module</c>, that is, removes code + <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old. If some processes still linger in the old code, these processes are killed before the code is removed.</p> <p>Returns <c>true</c> if successful and any process needed to @@ -542,31 +493,26 @@ </desc> </func> <func> - <name>soft_purge(Module) -> true | false</name> + <name name="soft_purge" arity="1"/> <fsummary>Removes old code for a module, unless no process uses it</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>Purges the code for <c>Module</c>, that is, removes code + <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old, but only if no processes linger in it.</p> <p>Returns <c>false</c> if the module could not be purged due to processes lingering in old code, otherwise <c>true</c>.</p> </desc> </func> <func> - <name>is_loaded(Module) -> {file, Loaded} | false</name> + <name name="is_loaded" arity="1"/> <fsummary>Check if a module is loaded</fsummary> - <type> - <v>Module = atom()</v> - <v>Loaded = Absname | preloaded | cover_compiled</v> - <v>Absname = string()</v> - </type> - <desc> - <p>Checks if <c>Module</c> is loaded. If it is, - <c>{file, Loaded}</c> is returned, otherwise <c>false</c>.</p> - <p>Normally, <c>Loaded</c> is the absolute file name - <c>Absname</c> from which the code was obtained. If the module + <type name="loaded_filename"/> + <type name="loaded_ret_atoms"/> + <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute filename</type_desc> + <desc> + <p>Checks if <c><anno>Module</anno></c> is loaded. If it is, + <c>{file, <anno>Loaded</anno>}</c> is returned, otherwise <c>false</c>.</p> + <p>Normally, <c><anno>Loaded</anno></c> is the absolute file name + <c>Filename</c> from which the code was obtained. If the module is preloaded (see <seealso marker="sasl:script">script(4)</seealso>), <c>Loaded==preloaded</c>. If the module is Cover compiled (see @@ -575,32 +521,26 @@ </desc> </func> <func> - <name>all_loaded() -> [{Module, Loaded}]</name> + <name name="all_loaded" arity="0"/> <fsummary>Get all loaded modules</fsummary> - <type> - <v>Module = atom()</v> - <v>Loaded = Absname | preloaded | cover_compiled</v> - <v>Absname = string()</v> - </type> + <type name="loaded_filename"/> + <type name="loaded_ret_atoms"/> + <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute filename</type_desc> <desc> - <p>Returns a list of tuples <c>{Module, Loaded}</c> for all - loaded modules. <c>Loaded</c> is normally the absolute file + <p>Returns a list of tuples <c>{<anno>Module</anno>, <anno>Loaded</anno>}</c> for all + loaded modules. <c><anno>Loaded</anno></c> is normally the absolute file name, as described for <seealso marker="#is_loaded/1">is_loaded/1</seealso>.</p> </desc> </func> <func> - <name>which(Module) -> Which</name> + <name name="which" arity="1"/> <fsummary>The object code file of a module</fsummary> - <type> - <v>Module = atom()</v> - <v>Which = Filename | non_existing | preloaded | cover_compiled</v> - <v>Filename = string()</v> - </type> + <type name="loaded_ret_atoms"/> <desc> <p>If the module is not loaded, this function searches the code path for the first file which contains object code for - <c>Module</c> and returns the absolute file name. If + <c><anno>Module</anno></c> and returns the absolute file name. If the module is loaded, it returns the name of the file which contained the loaded object code. If the module is pre-loaded, <c>preloaded</c> is returned. If the module is Cover compiled, @@ -609,21 +549,16 @@ </desc> </func> <func> - <name>get_object_code(Module) -> {Module, Binary, Filename} | error</name> + <name name="get_object_code" arity="1"/> <fsummary>Get the object code for a module</fsummary> - <type> - <v>Module = atom()</v> - <v>Binary = binary()</v> - <v>Filename = string()</v> - </type> <desc> <p>Searches the code path for the object code of the module - <c>Module</c>. It returns <c>{Module, Binary, Filename}</c> - if successful, and <c>error</c> if not. <c>Binary</c> is a + <c><anno>Module</anno></c>. It returns <c>{<anno>Module</anno>, <anno>Binary</anno>, <anno>Filename</anno>}</c> + if successful, and <c>error</c> if not. <c><anno>Binary</anno></c> is a binary data object which contains the object code for the module. This can be useful if code is to be loaded on a remote node in a distributed system. For example, loading - module <c>Module</c> on a node <c>Node</c> is done as + module <c><anno>Module</anno></c> on a node <c>Node</c> is done as follows:</p> <code type="none"> ... @@ -633,7 +568,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name>root_dir() -> string()</name> + <name name="root_dir" arity="0"/> <fsummary>Root directory of Erlang/OTP</fsummary> <desc> <p>Returns the root directory of Erlang/OTP, which is @@ -644,7 +579,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name>lib_dir() -> string()</name> + <name name="lib_dir" arity="0"/> <fsummary>Library directory of Erlang/OTP</fsummary> <desc> <p>Returns the library directory, <c>$OTPROOT/lib</c>, where @@ -655,19 +590,16 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name>lib_dir(Name) -> string() | {error, bad_name}</name> + <name name="lib_dir" arity="1"/> <fsummary>Library directory for an application</fsummary> - <type> - <v>Name = atom()</v> - </type> <desc> <p>This function is mainly intended for finding out the path for the "library directory", the top directory, for an - application <c>Name</c> located under <c>$OTPROOT/lib</c> or + application <c><anno>Name</anno></c> located under <c>$OTPROOT/lib</c> or on a directory referred to via the <c>ERL_LIBS</c> environment variable.</p> - <p>If there is a regular directory called <c>Name</c> or - <c>Name-Vsn</c> in the code path with an <c>ebin</c> + <p>If there is a regular directory called <c><anno>Name</anno></c> or + <c><anno>Name</anno>-Vsn</c> in the code path with an <c>ebin</c> subdirectory, the path to this directory is returned (not the <c>ebin</c> directory). If the directory refers to a directory in an archive, the archive name is stripped away @@ -681,23 +613,19 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), <pre> > <input>code:lib_dir(mnesia).</input> "/usr/local/otp/lib/mnesia-4.2.2"</pre> - <p>Returns <c>{error, bad_name}</c> if <c>Name</c> + <p>Returns <c>{error, bad_name}</c> if <c><anno>Name</anno></c> is not the name of an application under <c>$OTPROOT/lib</c> or on a directory referred to via the <c>ERL_LIBS</c> environment variable. Fails with an exception if <c>Name</c> has the wrong type.</p> - <warning><p>For backward compatibility, <c>Name</c> is also allowed to + <warning><p>For backward compatibility, <c><anno>Name</anno></c> is also allowed to be a string. That will probably change in a future release.</p></warning> </desc> </func> <func> - <name>lib_dir(Name, SubDir) -> string() | {error, bad_name}</name> + <name name="lib_dir" arity="2"/> <fsummary>subdirectory for an application</fsummary> - <type> - <v>Name = atom()</v> - <v>SubDir = atom()</v> - </type> <desc> <p>Returns the path to a subdirectory directly under the top directory of an application. Normally the subdirectories @@ -711,12 +639,12 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), > <input>code:lib_dir(megaco, priv).</input> "/usr/local/otp/lib/megaco-3.9.1.1/priv"</pre> - <p>Fails with an exception if <c>Name</c> or <c>SubDir</c> has + <p>Fails with an exception if <c><anno>Name</anno></c> or <c><anno>SubDir</anno></c> has the wrong type.</p> </desc> </func> <func> - <name>compiler_dir() -> string()</name> + <name name="compiler_dir" arity="0"/> <fsummary>Library directory for the compiler</fsummary> <desc> <p>Returns the compiler library directory. Equivalent to @@ -724,21 +652,18 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name>priv_dir(Name) -> string() | {error, bad_name}</name> + <name name="priv_dir" arity="1"/> <fsummary>Priv directory for an application</fsummary> - <type> - <v>Name = atom()</v> - </type> <desc> <p>Returns the path to the <c>priv</c> directory in an - application. Equivalent to <c>code:lib_dir(Name,priv).</c>.</p> + application. Equivalent to <c>code:lib_dir(<anno>Name</anno>, priv).</c>.</p> - <warning><p>For backward compatibility, <c>Name</c> is also allowed to + <warning><p>For backward compatibility, <c><anno>Name</anno></c> is also allowed to be a string. That will probably change in a future release.</p></warning> </desc> </func> <func> - <name>objfile_extension() -> ".beam"</name> + <name name="objfile_extension" arity="0"/> <fsummary>Object code file extension</fsummary> <desc> <p>Returns the object code file extension that corresponds to @@ -746,24 +671,16 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name>stick_dir(Dir) -> ok | error</name> + <name name="stick_dir" arity="1"/> <fsummary>Mark a directory as sticky</fsummary> - <type> - <v>Dir = string()</v> - <v>What = term()</v> - </type> <desc> - <p>This function marks <c>Dir</c> as sticky.</p> + <p>This function marks <c><anno>Dir</anno></c> as sticky.</p> <p>Returns <c>ok</c> if successful or <c>error</c> if not.</p> </desc> </func> <func> - <name>unstick_dir(Dir) -> ok | error</name> + <name name="unstick_dir" arity="1"/> <fsummary>Remove a sticky directory mark</fsummary> - <type> - <v>Dir = string()</v> - <v>What = term()</v> - </type> <desc> <p>This function unsticks a directory which has been marked as sticky.</p> @@ -771,45 +688,39 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name>is_sticky(Module) -> true | false</name> + <name name="is_sticky" arity="1"/> <fsummary>Test whether a module is sticky</fsummary> - <type> - <v>Module = atom()</v> - </type> <desc> - <p>This function returns <c>true</c> if <c>Module</c> is the + <p>This function returns <c>true</c> if <c><anno>Module</anno></c> is the name of a module that has been loaded from a sticky directory (or in other words: an attempt to reload the module will fail), - or <c>false</c> if <c>Module</c> is not a loaded module or is + or <c>false</c> if <c><anno>Module</anno></c> is not a loaded module or is not sticky.</p> </desc> </func> <func> - <name>rehash() -> ok</name> + <name name="rehash" arity="0"/> <fsummary>Rehash or create code path cache</fsummary> <desc> <p>This function creates or rehashes the code path cache.</p> </desc> </func> <func> - <name>where_is_file(Filename) -> Absname | non_existing</name> + <name name="where_is_file" arity="1"/> <fsummary>Full name of a file located in the code path</fsummary> - <type> - <v>Filename = Absname = string()</v> - </type> <desc> - <p>Searches the code path for <c>Filename</c>, a file of + <p>Searches the code path for <c><anno>Filename</anno></c>, a file of arbitrary type. If found, the full name is returned. <c>non_existing</c> is returned if the file cannot be found. The function can be useful, for example, to locate application resource files. If the code path cache is used, the code server will efficiently read the full name from - the cache, provided that <c>Filename</c> is an object code + the cache, provided that <c><anno>Filename</anno></c> is an object code file or an <c>.app</c> file.</p> </desc> </func> <func> - <name>clash() -> ok</name> + <name name="clash" arity="0"/> <fsummary>Search for modules with identical names.</fsummary> <desc> <p>Searches the entire code space for module names with @@ -817,10 +728,10 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </desc> </func> <func> - <name>is_module_native(Module) -> true | false | undefined</name> + <name>is_module_native(Module) -> boolean() | undefined</name> <fsummary>Test whether a module has native code</fsummary> <type> - <v>Module = atom()</v> + <v>Module = module()</v> </type> <desc> <p>This function returns <c>true</c> if <c>Module</c> is diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index e0feaf6ee7..861c582211 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -95,9 +95,6 @@ <datatypes> <datatype> - <name name="bindings"/> - </datatype> - <datatype> <name name="deep_list"/> </datatype> <datatype> @@ -136,12 +133,6 @@ </desc> </datatype> <datatype> - <name name="date"/> - </datatype> - <datatype> - <name name="time"/> - </datatype> - <datatype> <name name="date_time"/> <desc> <p>Must denote a valid date and time.</p> @@ -1220,15 +1211,15 @@ f.txt: {person, "kalle", 25}. <item> <p>The current system access to the file.</p> </item> - <tag><c>atime = time()</c></tag> + <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso></c></tag> <item> <p>The last (local) time the file was read.</p> </item> - <tag><c>mtime = time()</c></tag> + <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso></c></tag> <item> <p>The last (local) time the file was written.</p> </item> - <tag><c>ctime = time()</c></tag> + <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso></c></tag> <item> <p>The interpretation of this time field depends on the operating system. On Unix, it is the last time @@ -1669,15 +1660,15 @@ f.txt: {person, "kalle", 25}. <p>The following fields are used from the record, if they are given.</p> <taglist> - <tag><c>atime = time()</c></tag> + <tag><c>atime = <seealso marker="#type-date_time">date_time()</seealso></c></tag> <item> <p>The last (local) time the file was read.</p> </item> - <tag><c>mtime = time()</c></tag> + <tag><c>mtime = <seealso marker="#type-date_time">date_time()</seealso></c></tag> <item> <p>The last (local) time the file was written.</p> </item> - <tag><c>ctime = time()</c></tag> + <tag><c>ctime = <seealso marker="#type-date_time">date_time()</seealso></c></tag> <item> <p>On Unix, any value give for this field will be ignored (the "ctime" for the file will be set to the current diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 56fc1834ec..e94119845a 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -126,9 +126,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> </desc> </func> <func> - <name>timestamp() -> {MegaSecs, Secs, MicroSecs}</name> + <name>timestamp() -> Timestamp</name> <fsummary>Returna a timestamp from the OS in the erlang:now/0 format</fsummary> <type> + <v>Timestamp = {MegaSecs, Secs, MicroSecs} = <seealso marker="erts:erlang#type-timestamp">erlang:timestamp()</seealso></v> <v>MegaSecs = Secs = MicroSecs = integer() >= 0</v> </type> <desc> diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index 25c88a4e1d..ac25ab958c 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.erl @@ -58,7 +58,7 @@ start_link() -> %%--Deprecated interface------------------------------------------------ -spec is_auth(Node) -> 'yes' | 'no' when - Node :: Node :: node(). + Node :: node(). is_auth(Node) -> case net_adm:ping(Node) of diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index b0f99305f2..882e9625fe 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -82,7 +82,8 @@ %% add_pathsa([Dir]) -> ok %% add_pathsz([Dir]) -> ok %% del_path(Dir) -> boolean() | {error, bad_name} -%% replace_path(Name, Dir) -> true | replace_path_error() +%% replace_path(Name, Dir) -> true | {error, bad_directory | bad_name +%% | {badarg,_}} %% load_file(Module) -> {module, Module} | {error, What :: atom()} %% load_abs(File) -> {module, Module} | {error, What :: atom()} %% load_abs(File, Module) -> {module, Module} | {error, What :: atom()} @@ -113,11 +114,16 @@ %% Some types for basic exported functions of this module %%---------------------------------------------------------------------------- --type load_error_rsn() :: 'badfile' | 'native_code' | 'nofile' | 'not_purged' - | 'sticky_directory'. % for some functions only --type load_ret() :: {'error', load_error_rsn()} | {'module', atom()}. +-type load_error_rsn() :: 'badfile' + | 'native_code' + | 'nofile' + | 'not_purged' + | 'on_load' + | 'sticky_directory'. +-type load_ret() :: {'error', What :: load_error_rsn()} + | {'module', Module :: module()}. -type loaded_ret_atoms() :: 'cover_compiled' | 'preloaded'. --type loaded_filename() :: file:filename() | loaded_ret_atoms(). +-type loaded_filename() :: (Filename :: file:filename()) | loaded_ret_atoms(). %%---------------------------------------------------------------------------- %% User interface @@ -127,55 +133,74 @@ objfile_extension() -> init:objfile_extension(). --spec load_file(Module :: atom()) -> load_ret(). +-spec load_file(Module) -> load_ret() when + Module :: module(). load_file(Mod) when is_atom(Mod) -> call({load_file,Mod}). --spec ensure_loaded(Module :: atom()) -> load_ret(). +-spec ensure_loaded(Module) -> {module, Module} | {error, What} when + Module :: module(), + What :: embedded | badfile | native_code | nofile | on_load. ensure_loaded(Mod) when is_atom(Mod) -> call({ensure_loaded,Mod}). %% XXX File as an atom is allowed only for backwards compatibility. --spec load_abs(Filename :: file:filename()) -> load_ret(). +-spec load_abs(Filename) -> load_ret() when + Filename :: file:filename(). load_abs(File) when is_list(File); is_atom(File) -> call({load_abs,File,[]}). %% XXX Filename is also an atom(), e.g. 'cover_compiled' --spec load_abs(Filename :: loaded_filename(), Module :: atom()) -> load_ret(). +-spec load_abs(Filename :: loaded_filename(), Module :: module()) -> load_ret(). load_abs(File, M) when (is_list(File) orelse is_atom(File)), is_atom(M) -> call({load_abs,File,M}). %% XXX Filename is also an atom(), e.g. 'cover_compiled' --spec load_binary(Module :: atom(), Filename :: loaded_filename(), Binary :: binary()) -> load_ret(). +-spec load_binary(Module, Filename, Binary) -> + {module, Module} | {error, What} when + Module :: module(), + Filename :: loaded_filename(), + Binary :: binary(), + What :: badarg | load_error_rsn(). load_binary(Mod, File, Bin) when is_atom(Mod), (is_list(File) orelse is_atom(File)), is_binary(Bin) -> call({load_binary,Mod,File,Bin}). --spec load_native_partial(Module :: atom(), Binary :: binary()) -> load_ret(). +-spec load_native_partial(Module :: module(), Binary :: binary()) -> load_ret(). load_native_partial(Mod, Bin) when is_atom(Mod), is_binary(Bin) -> call({load_native_partial,Mod,Bin}). --spec load_native_sticky(Module :: atom(), Binary :: binary(), WholeModule :: 'false' | binary()) -> load_ret(). +-spec load_native_sticky(Module :: module(), Binary :: binary(), WholeModule :: 'false' | binary()) -> load_ret(). load_native_sticky(Mod, Bin, WholeModule) when is_atom(Mod), is_binary(Bin), (is_binary(WholeModule) orelse WholeModule =:= false) -> call({load_native_sticky,Mod,Bin,WholeModule}). --spec delete(Module :: atom()) -> boolean(). +-spec delete(Module) -> boolean() when + Module :: module(). delete(Mod) when is_atom(Mod) -> call({delete,Mod}). --spec purge(Module :: atom()) -> boolean(). +-spec purge(Module) -> boolean() when + Module :: module(). purge(Mod) when is_atom(Mod) -> call({purge,Mod}). --spec soft_purge(Module :: atom()) -> boolean(). +-spec soft_purge(Module) -> boolean() when + Module :: module(). soft_purge(Mod) when is_atom(Mod) -> call({soft_purge,Mod}). --spec is_loaded(Module :: atom()) -> {'file', loaded_filename()} | 'false'. +-spec is_loaded(Module) -> {'file', Loaded} | false when + Module :: module(), + Loaded :: loaded_filename(). is_loaded(Mod) when is_atom(Mod) -> call({is_loaded,Mod}). --spec get_object_code(Module :: atom()) -> {atom(), binary(), file:filename()} | 'error'. +-spec get_object_code(Module) -> {Module, Binary, Filename} | error when + Module :: module(), + Binary :: binary(), + Filename :: file:filename(). get_object_code(Mod) when is_atom(Mod) -> call({get_object_code, Mod}). --spec all_loaded() -> [{atom(), loaded_filename()}]. +-spec all_loaded() -> [{Module, Loaded}] when + Module :: module(), + Loaded :: loaded_filename(). all_loaded() -> call(all_loaded). -spec stop() -> no_return(). @@ -188,65 +213,86 @@ root_dir() -> call({dir,root_dir}). lib_dir() -> call({dir,lib_dir}). %% XXX is_list() is for backwards compatibility -- take out in future version --spec lib_dir(App :: atom()) -> file:filename() | {'error', 'bad_name'}. +-spec lib_dir(Name) -> file:filename() | {'error', 'bad_name'} when + Name :: atom(). lib_dir(App) when is_atom(App) ; is_list(App) -> call({dir,{lib_dir,App}}). --spec lib_dir(App :: atom(), SubDir :: atom()) -> file:filename() | {'error', 'bad_name'}. +-spec lib_dir(Name, SubDir) -> file:filename() | {'error', 'bad_name'} when + Name :: atom(), + SubDir :: atom(). lib_dir(App, SubDir) when is_atom(App), is_atom(SubDir) -> call({dir,{lib_dir,App,SubDir}}). -spec compiler_dir() -> file:filename(). compiler_dir() -> call({dir,compiler_dir}). %% XXX is_list() is for backwards compatibility -- take out in future version --spec priv_dir(App :: atom()) -> file:filename() | {'error', 'bad_name'}. +-spec priv_dir(Name) -> file:filename() | {'error', 'bad_name'} when + Name :: atom(). priv_dir(App) when is_atom(App) ; is_list(App) -> call({dir,{priv_dir,App}}). --spec stick_dir(Directory :: file:filename()) -> 'ok' | 'error'. +-spec stick_dir(Dir) -> 'ok' | 'error' when + Dir :: file:filename(). stick_dir(Dir) when is_list(Dir) -> call({stick_dir,Dir}). --spec unstick_dir(Directory :: file:filename()) -> 'ok' | 'error'. +-spec unstick_dir(Dir) -> 'ok' | 'error' when + Dir :: file:filename(). unstick_dir(Dir) when is_list(Dir) -> call({unstick_dir,Dir}). --spec stick_mod(Module :: atom()) -> 'true'. +-spec stick_mod(Module :: module()) -> 'true'. stick_mod(Mod) when is_atom(Mod) -> call({stick_mod,Mod}). --spec unstick_mod(Module :: atom()) -> 'true'. +-spec unstick_mod(Module :: module()) -> 'true'. unstick_mod(Mod) when is_atom(Mod) -> call({unstick_mod,Mod}). --spec is_sticky(Module :: atom()) -> boolean(). +-spec is_sticky(Module) -> boolean() when + Module :: module(). is_sticky(Mod) when is_atom(Mod) -> call({is_sticky,Mod}). --spec set_path(Directories :: [file:filename()]) -> - 'true' | {'error', 'bad_directory' | 'bad_path'}. +-spec set_path(Path) -> 'true' | {'error', What} when + Path :: [Dir :: file:filename()], + What :: 'bad_directory' | 'bad_path'. set_path(PathList) when is_list(PathList) -> call({set_path,PathList}). --spec get_path() -> [file:filename()]. +-spec get_path() -> Path when + Path :: [Dir :: file:filename()]. get_path() -> call(get_path). -type add_path_ret() :: 'true' | {'error', 'bad_directory'}. --spec add_path(Directory :: file:filename()) -> add_path_ret(). +-spec add_path(Dir) -> add_path_ret() when + Dir :: file:filename(). add_path(Dir) when is_list(Dir) -> call({add_path,last,Dir}). --spec add_pathz(Directory :: file:filename()) -> add_path_ret(). +-spec add_pathz(Dir) -> add_path_ret() when + Dir :: file:filename(). add_pathz(Dir) when is_list(Dir) -> call({add_path,last,Dir}). --spec add_patha(Directory :: file:filename()) -> add_path_ret(). +-spec add_patha(Dir) -> add_path_ret() when + Dir :: file:filename(). add_patha(Dir) when is_list(Dir) -> call({add_path,first,Dir}). --spec add_paths(Directories :: [file:filename()]) -> 'ok'. +-spec add_paths(Dirs) -> 'ok' when + Dirs :: [Dir :: file:filename()]. add_paths(Dirs) when is_list(Dirs) -> call({add_paths,last,Dirs}). --spec add_pathsz(Directories :: [file:filename()]) -> 'ok'. +-spec add_pathsz(Dirs) -> 'ok' when + Dirs :: [Dir :: file:filename()]. add_pathsz(Dirs) when is_list(Dirs) -> call({add_paths,last,Dirs}). --spec add_pathsa(Directories :: [file:filename()]) -> 'ok'. +-spec add_pathsa(Dirs) -> 'ok' when + Dirs :: [Dir :: file:filename()]. add_pathsa(Dirs) when is_list(Dirs) -> call({add_paths,first,Dirs}). --spec del_path(Name :: file:filename() | atom()) -> boolean() | {'error', 'bad_name'}. +-spec del_path(NameOrDir) -> boolean() | {'error', What} when + NameOrDir :: Name | Dir, + Name :: atom(), + Dir :: file:filename(), + What :: 'bad_name'. del_path(Name) when is_list(Name) ; is_atom(Name) -> call({del_path,Name}). --type replace_path_error() :: {'error', 'bad_directory' | 'bad_name' | {'badarg',_}}. --spec replace_path(Name:: atom(), Dir :: file:filename()) -> 'true' | replace_path_error(). +-spec replace_path(Name, Dir) -> 'true' | {'error', What} when + Name:: atom(), + Dir :: file:filename(), + What :: 'bad_directory' | 'bad_name' | {'badarg',_}. replace_path(Name, Dir) when (is_atom(Name) orelse is_list(Name)), (is_atom(Dir) orelse is_list(Dir)) -> call({replace_path,Name,Dir}). @@ -351,10 +397,9 @@ get_mode(Flags) -> %% In that case return the name of the file which contains %% the loaded object code --type which_ret_atoms() :: loaded_ret_atoms() | 'non_existing'. - --spec which(Module :: atom()) -> file:filename() | which_ret_atoms(). - +-spec which(Module) -> Which when + Module :: module(), + Which :: file:filename() | loaded_ret_atoms() | non_existing. which(Module) when is_atom(Module) -> case is_loaded(Module) of false -> @@ -394,9 +439,9 @@ which(File, Base, [Directory|Tail]) -> %% Search the code path for a specific file. Try to locate %% it in the code path cache if possible. --spec where_is_file(Filename :: file:filename()) -> - 'non_existing' | file:filename(). - +-spec where_is_file(Filename) -> non_existing | Absname when + Filename :: file:filename(), + Absname :: file:filename(). where_is_file(File) when is_list(File) -> case call({is_cached,File}) of no -> diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index f1a8aa9f77..5e4e1b0ba8 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -100,15 +100,7 @@ | 'enotblk' | 'enotdir' | 'enotsup' | 'enxio' | 'eperm' | 'epipe' | 'erofs' | 'espipe' | 'esrch' | 'estale' | 'exdev'. --type bindings() :: erl_eval:binding_struct(). - --type date() :: {Year :: pos_integer(), - Month :: pos_integer(), - Day ::pos_integer()}. --type time() :: {Hour :: non_neg_integer(), - Minute :: non_neg_integer(), - Second :: non_neg_integer()}. --type date_time() :: {date(), time()}. +-type date_time() :: calendar:datetime(). -type posix_file_advise() :: 'normal' | 'sequential' | 'random' | 'no_reuse' | 'will_need' | 'dont_need'. @@ -920,7 +912,7 @@ eval(File) -> -spec eval(Filename, Bindings) -> ok | {error, Reason} when Filename :: name(), - Bindings :: bindings(), + Bindings :: erl_eval:binding_struct(), Reason :: posix() | badarg | terminated | system_limit | {Line :: integer(), Mod :: module(), Term :: term()}. @@ -948,7 +940,7 @@ path_eval(Path, File) -> {ok, FullName} | {error, Reason} when Path :: [Dir :: name()], Filename :: name(), - Bindings :: bindings(), + Bindings :: erl_eval:binding_struct(), FullName :: filename(), Reason :: posix() | badarg | terminated | system_limit | {Line :: integer(), Mod :: module(), Term :: term()}. @@ -979,7 +971,7 @@ script(File) -> -spec script(Filename, Bindings) -> {ok, Value} | {error, Reason} when Filename :: name(), - Bindings :: bindings(), + Bindings :: erl_eval:binding_struct(), Value :: term(), Reason :: posix() | badarg | terminated | system_limit | {Line :: integer(), Mod :: module(), Term :: term()}. @@ -1010,7 +1002,7 @@ path_script(Path, File) -> {ok, Value, FullName} | {error, Reason} when Path :: [Dir :: name()], Filename :: name(), - Bindings :: bindings(), + Bindings :: erl_eval:binding_struct(), Value :: term(), FullName :: filename(), Reason :: posix() | badarg | terminated | system_limit diff --git a/lib/kernel/src/inet_config.erl b/lib/kernel/src/inet_config.erl index 2458876326..1ddbdcec25 100644 --- a/lib/kernel/src/inet_config.erl +++ b/lib/kernel/src/inet_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -44,26 +44,6 @@ %% -spec init() -> 'ok'. init() -> - OsType = os:type(), - case OsType of - {ose,_} -> - case init:get_argument(loader) of - {ok,[["ose_inet"]]} -> - %% port already started by prim_loader - ok; - _Other -> - %% Setup reserved port for ose_inet driver (only OSE) - case catch erlang:open_port({spawn,"ose_inet"}, [binary]) of - {'EXIT',Why} -> - error("can't open port for ose_inet: ~p", [Why]); - OseInetPort -> - erlang:display({ose_inet_port,OseInetPort}) - end - end; - _ -> - ok - end, - set_hostname(), %% Note: In shortnames (or non-distributed) mode we don't need to know @@ -71,6 +51,7 @@ init() -> %% the user to provide it (by means of inetrc), so we need to look %% for it ourselves. + OsType = os:type(), do_load_resolv(OsType, erl_dist_mode()), case OsType of @@ -226,35 +207,6 @@ do_load_resolv(vxworks, _) -> load_resolv(Resolv, resolv) end; -do_load_resolv({ose,_Type}, _) -> - inet_db:set_lookup([file, dns]), - case os:getenv("NAMESERVER") of - false -> - case os:getenv("RESOLVFILE") of - false -> - erlang:display('Warning: No NAMESERVER or RESOLVFILE specified!'), - no_resolv; - Resolv -> - load_resolv(Resolv, resolv) - end; - Ns -> - {ok,IP} = inet_parse:address(Ns), - inet_db:add_rc_list([{nameserver,IP}]) - end, - case os:getenv("DOMAIN") of - false -> - no_domain; - D -> - ok = inet_db:add_rc_list([{domain,D}]) - end, - case os:getenv("HOSTSFILE") of - false -> - erlang:display('Warning: No HOSTSFILE specified!'), - no_hosts_file; - File -> - load_hosts(File, ose) - end; - do_load_resolv(_, _) -> inet_db:set_lookup([native]). diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index be35f99ed2..e214ffa404 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -662,9 +662,10 @@ async_call(Node, Mod, Fun, Args) -> ReplyTo ! {self(), {promise_reply, R}} %% self() is key end). --spec yield(Key) -> {value, Val} | timeout when +-spec yield(Key) -> Res | {badrpc, Reason} when Key :: key(), - Val :: (Res :: term()) | {badrpc, Reason :: term()}. + Res :: term(), + Reason :: term(). yield(Key) when is_pid(Key) -> {value,R} = do_yield(Key, infinity), diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index d61ce940c3..11e311d72d 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -90,7 +90,7 @@ Datatype - USER_INT | USER_SMALL_INT | {USER_DECIMAL, Precision, Scale} | {USER_NMERIC, Precision, Scale} | {USER_CHAR, Max} | {USER_VARCHAR, Max} | {USER_WVARCHAR, Max} | {USER_FLOAT, Precision} | USER_REAL | USER_DOUBLE | - USER_TIMESTAMP + USER_TIMESTAMP | {USER_WLONGVARCHAR, Max} Scale - integer Precision - integer Max - integer @@ -173,7 +173,7 @@ static db_result_msg encode_row_count(SQLINTEGER num_of_rows, db_state *state); static void encode_column_dyn(db_column column, int column_nr, db_state *state); -static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size, +static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, SQLSMALLINT decimal_digits, db_state *state); static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params, int i, int j); @@ -221,7 +221,7 @@ static void init_param_column(param_array *params, byte *buffer, int *index, int num_param_values, db_state* state); static void init_param_statement(int cols, - int num_param_values, + SQLLEN num_param_values, db_state *state, param_status *status); @@ -435,7 +435,7 @@ static db_result_msg db_connect(byte *args, db_state *state) diagnos diagnos; byte *connStrIn; int erl_auto_commit_mode, erl_trace_driver, - use_srollable_cursors, tuple_row_state, binary_strings; + use_srollable_cursors, tuple_row_state, binary_strings; erl_auto_commit_mode = args[0]; erl_trace_driver = args[1]; @@ -757,8 +757,9 @@ static db_result_msg db_select(byte *args, db_state *state) static db_result_msg db_param_query(byte *buffer, db_state *state) { byte *sql; - db_result_msg msg; - int i, num_param_values, ver = 0, + db_result_msg msg; + SQLLEN num_param_values; + int i, ver = 0, erl_type = 0, index = 0, size = 0, cols = 0; long long_num_param_values; param_status param_status; @@ -785,7 +786,7 @@ static db_result_msg db_param_query(byte *buffer, db_state *state) ei_decode_long(buffer, &index, &long_num_param_values); - num_param_values = (int)long_num_param_values; + num_param_values = (SQLLEN)long_num_param_values; ei_decode_list_header(buffer, &index, &cols); @@ -1002,12 +1003,16 @@ static db_result_msg encode_result(db_state *state) db_result_msg msg; int elements, update, num_of_rows = 0; char *atom; + diagnos diagnos; msg = encode_empty_message(); if(!sql_success(SQLNumResultCols(statement_handle(state), &num_of_columns))) { - DO_EXIT(EXIT_COLS); + diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state)); + msg = encode_error_message(diagnos.error_msg); + clean_state(state); + return msg; } if (num_of_columns == 0) { @@ -1021,7 +1026,10 @@ static db_result_msg encode_result(db_state *state) } if(!sql_success(SQLRowCount(statement_handle(state), &RowCountPtr))) { - DO_EXIT(EXIT_ROWS); + diagnos = get_diagnos(SQL_HANDLE_STMT, statement_handle(state)); + msg = encode_error_message(diagnos.error_msg); + clean_state(state); + return msg; } if(param_query(state) && update) { @@ -1220,7 +1228,7 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns, &nullable))) DO_EXIT(EXIT_DESC); - if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY) + if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR) size = MAXCOLSIZE; (columns(state)[i]).type.decimal_digits = dec_digits; @@ -1452,7 +1460,7 @@ static void encode_column_dyn(db_column column, int column_nr, } } -static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size, +static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, SQLSMALLINT decimal_digits, db_state *state) { switch(sql_type) { @@ -1529,6 +1537,11 @@ static void encode_data_type(SQLINTEGER sql_type, SQLINTEGER size, case SQL_LONGVARCHAR: ei_x_encode_atom(&dynamic_buffer(state), "SQL_LONGVARCHAR"); break; + case SQL_WLONGVARCHAR: + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_atom(&dynamic_buffer(state), "sql_wlongvarchar"); + ei_x_encode_long(&dynamic_buffer(state), size); + break; case SQL_VARBINARY: ei_x_encode_atom(&dynamic_buffer(state), "SQL_VARBINARY"); break; @@ -2007,7 +2020,7 @@ static void init_driver(int erl_auto_commit_mode, int erl_trace_driver, db_state *state) { - int auto_commit_mode, trace_driver; + SQLLEN auto_commit_mode, trace_driver; if(erl_auto_commit_mode == ON) { auto_commit_mode = SQL_AUTOCOMMIT_ON; @@ -2057,7 +2070,7 @@ static void init_param_column(param_array *params, byte *buffer, int *index, ei_decode_long(buffer, index, &user_type); - params->type.strlen_or_indptr = (SQLINTEGER)NULL; + params->type.strlen_or_indptr = (SQLLEN)NULL; params->type.strlen_or_indptr_array = NULL; params->type.decimal_digits = (SQLINTEGER)0; @@ -2131,10 +2144,14 @@ static void init_param_column(param_array *params, byte *buffer, int *index, break; case USER_WCHAR: case USER_WVARCHAR: - if(user_type == USER_WCHAR) { - params->type.sql = SQL_WCHAR; - } else { - params->type.sql = SQL_WVARCHAR; + case USER_WLONGVARCHAR: + switch (user_type) { + case USER_WCHAR: + params->type.sql = SQL_WCHAR; break; + case USER_WVARCHAR: + params->type.sql = SQL_WVARCHAR; break; + default: + params->type.sql = SQL_WLONGVARCHAR; break; } ei_decode_long(buffer, index, &length); /* Max string length + string terminator */ @@ -2206,7 +2223,7 @@ static void init_param_column(param_array *params, byte *buffer, int *index, } -static void init_param_statement(int cols, int num_param_values, +static void init_param_statement(int cols, SQLLEN num_param_values, db_state *state, param_status *status) { int i; @@ -2234,11 +2251,11 @@ static void init_param_statement(int cols, int num_param_values, DO_EXIT(EXIT_PARAM_ARRAY); } - /* Note the (int *) cast is correct as the API function SQLSetStmtAttr + /* Note the (SQLLEN *) cast is correct as the API function SQLSetStmtAttr takes either an interger or a pointer depending on the attribute */ if(!sql_success(SQLSetStmtAttr(statement_handle(state), SQL_ATTR_PARAMSET_SIZE, - (int *)num_param_values, + (SQLLEN *)num_param_values, 0))) { DO_EXIT(EXIT_PARAM_ARRAY); } @@ -2300,6 +2317,7 @@ static db_result_msg map_sql_2_c_column(db_column* column) break; case SQL_WCHAR: case SQL_WVARCHAR: + case SQL_WLONGVARCHAR: column -> type.len = (column -> type.col_size + 1)*sizeof(SQLWCHAR); column -> type.c = SQL_C_WCHAR; column -> type.strlen_or_indptr = SQL_NTS; @@ -2308,21 +2326,21 @@ static db_result_msg map_sql_2_c_column(db_column* column) case SQL_DECIMAL: map_dec_num_2_c_column(&(column -> type), column -> type.col_size, column -> type.decimal_digits); - column -> type.strlen_or_indptr = (SQLINTEGER)NULL; + column -> type.strlen_or_indptr = (SQLLEN)NULL; break; case SQL_TINYINT: case SQL_INTEGER: case SQL_SMALLINT: column -> type.len = sizeof(SQLINTEGER); column -> type.c = SQL_C_SLONG; - column -> type.strlen_or_indptr = (SQLINTEGER)NULL; + column -> type.strlen_or_indptr = (SQLLEN)NULL; break; case SQL_REAL: case SQL_FLOAT: case SQL_DOUBLE: column -> type.len = sizeof(double); column -> type.c = SQL_C_DOUBLE; - column -> type.strlen_or_indptr = (SQLINTEGER)NULL; + column -> type.strlen_or_indptr = (SQLLEN)NULL; break; case SQL_TYPE_DATE: case SQL_TYPE_TIME: @@ -2334,17 +2352,17 @@ static db_result_msg map_sql_2_c_column(db_column* column) case SQL_TYPE_TIMESTAMP: column -> type.len = sizeof(TIMESTAMP_STRUCT); column -> type.c = SQL_C_TYPE_TIMESTAMP; - column -> type.strlen_or_indptr = (SQLINTEGER)NULL; + column -> type.strlen_or_indptr = (SQLLEN)NULL; break; case SQL_BIGINT: column -> type.len = DEC_NUM_LENGTH; column -> type.c = SQL_C_CHAR; - column -> type.strlen_or_indptr = (SQLINTEGER)NULL; + column -> type.strlen_or_indptr = (SQLLEN)NULL; break; case SQL_BIT: column -> type.len = sizeof(byte); column -> type.c = SQL_C_BIT; - column -> type.strlen_or_indptr = (SQLINTEGER)NULL; + column -> type.strlen_or_indptr = (SQLLEN)NULL; break; case SQL_UNKNOWN_TYPE: msg = encode_error_message("Unknown column type"); diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h index 3e2b22ab7d..314fbf32c6 100644 --- a/lib/odbc/c_src/odbcserver.h +++ b/lib/odbc/c_src/odbcserver.h @@ -115,6 +115,7 @@ #define USER_WCHAR 12 #define USER_WVARCHAR 13 #define USER_TIMESTAMP 14 +#define USER_WLONGVARCHAR 15 /*------------------------ TYPDEFS ----------------------------------*/ diff --git a/lib/odbc/doc/src/databases.xml b/lib/odbc/doc/src/databases.xml index a6ba0e5245..09f5a5af5b 100644 --- a/lib/odbc/doc/src/databases.xml +++ b/lib/odbc/doc/src/databases.xml @@ -205,6 +205,10 @@ when p >= 16 </cell> <cell align="left" valign="middle">String | Binary (configurable)</cell> </row> <row> + <cell align="left" valign="middle">SQL_WLONGVARCHAR(size) </cell> + <cell align="left" valign="middle">Unicode binary encoded as UTF16 little endian.</cell> + </row> + <row> <cell align="left" valign="middle">SQL_BINARY</cell> <cell align="left" valign="middle">String | Binary (configurable)</cell> </row> diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml index 70d8cfbe22..11ca97f743 100644 --- a/lib/odbc/doc/src/odbc.xml +++ b/lib/odbc/doc/src/odbc.xml @@ -102,7 +102,7 @@ {sql_decimal, precision(), scale()} | {sql_numeric, precision(), scale()} | {sql_char, size()} | {sql_wchar, size()} | {sql_varchar, size()} | {sql_wvarchar, size()}| {sql_float, precision()} | - {sql_float, precision()} | sql_real | sql_double | sql_bit | atom() + {sql_wlongvarchar, size()} | {sql_float, precision()} | sql_real | sql_double | sql_bit | atom() </code> <code type="none"> precision() = integer() </code> diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src index 2a6667ccd3..f3a3af8c29 100644 --- a/lib/odbc/src/odbc.appup.src +++ b/lib/odbc/src/odbc.appup.src @@ -1,8 +1,10 @@ %% -*- erlang -*- {"%VSN%", [ - {"2.10.9", [{restart_application, ssl}]} + {"2.10.10", [{restart_application, odbc}]}, + {"2.10.9", [{restart_application, odbc}]} ], [ - {"2.10.9", [{restart_application, ssl}]} + {"2.10.10", [{restart_application, odbc}]}, + {"2.10.9", [{restart_application, odbc}]} ]}. diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl index 83d9f33102..68497292db 100644 --- a/lib/odbc/src/odbc.erl +++ b/lib/odbc/src/odbc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -431,7 +431,7 @@ init(Args) -> erlang:monitor(process, ClientPid), - Inet = case gen_tcp:listen(0, [inet6]) of + Inet = case gen_tcp:listen(0, [inet6, {ip, loopback}]) of {ok, Dummyport} -> gen_tcp:close(Dummyport), inet6; @@ -925,6 +925,9 @@ fix_params({{sql_wchar, Max}, InOut, Values}) -> fix_params({{sql_wvarchar, Max}, InOut, Values}) -> NewValues = string_terminate(Values), {?USER_WVARCHAR, Max, fix_inout(InOut), NewValues}; +fix_params({{sql_wlongvarchar, Max}, InOut, Values}) -> + NewValues = string_terminate(Values), + {?USER_WLONGVARCHAR, Max, fix_inout(InOut), NewValues}; fix_params({{sql_float, Precision}, InOut, Values}) -> {?USER_FLOAT, Precision, fix_inout(InOut), Values}; fix_params({sql_real, InOut, Values}) -> diff --git a/lib/odbc/src/odbc_internal.hrl b/lib/odbc/src/odbc_internal.hrl index aa60120f9a..c0e7d9657b 100644 --- a/lib/odbc/src/odbc_internal.hrl +++ b/lib/odbc/src/odbc_internal.hrl @@ -72,6 +72,7 @@ -define(USER_WCHAR, 12). -define(USER_WVARCHAR, 13). -define(USER_TIMESTAMP, 14). +-define(USER_WLONGVARCHAR, 15). %% INPUT & OUTPUT TYPE -define(IN, 0). diff --git a/lib/odbc/test/Makefile b/lib/odbc/test/Makefile index ec2bcc67b5..bc6449242e 100644 --- a/lib/odbc/test/Makefile +++ b/lib/odbc/test/Makefile @@ -34,7 +34,8 @@ MODULES= \ odbc_test_lib \ oracle \ sqlserver \ - postgres + postgres \ + mysql EBIN = . diff --git a/lib/odbc/test/mysql.erl b/lib/odbc/test/mysql.erl new file mode 100644 index 0000000000..49068c4356 --- /dev/null +++ b/lib/odbc/test/mysql.erl @@ -0,0 +1,262 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(mysql). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +%------------------------------------------------------------------------- +connection_string() -> + "DSN=MySQL;Database=odbctest;Uid=odbctest;Pwd=gurka;CHARSET=utf8;SSTMT=SET NAMES 'utf8';". + +%------------------------------------------------------------------------- +insert_result() -> + {selected,["ID","DATA"],[{1,"bar"}]}. + +update_result() -> + {selected,["ID","DATA"],[{1,"foo"}]}. + +selected_ID(N, next) -> + {selected,["ID"],[{N}]}; + +selected_ID(_, _) -> + {error, driver_does_not_support_function}. + +selected_next_N(1)-> + {selected,["ID"], + [{1}, + {2}, + {3}]}; + +selected_next_N(2)-> + {selected,["ID"], + [{4}, + {5}]}. + +selected_relative_N(_)-> + {error, driver_does_not_support_function}. + +selected_absolute_N(_)-> + {error, driver_does_not_support_function}. + +selected_list_rows() -> + {selected,["ID", "DATA"],[[1, "bar"],[2,"foo"]]}. + +first_list_rows() -> + {error, driver_does_not_support_function}. +last_list_rows() -> + {error, driver_does_not_support_function}. +prev_list_rows() -> + {error, driver_does_not_support_function}. +next_list_rows() -> + {selected,["ID","DATA"],[[1,"bar"]]}. + +multiple_select()-> + [{selected,["ID", "DATA"],[{1, "bar"},{2, "foo"}]}, + {selected,["ID"],[{"foo"}]}]. + +multiple_mix()-> + [{updated, 1},{updated, 1}, + {selected,["ID", "DATA"],[{1, "foobar"},{2, "foo"}]}, + {updated, 1}, {selected,["DATA"],[{"foo"}]}]. + +%------------------------------------------------------------------------- +var_char_min() -> + 0. +var_char_max() -> + 65535. + +create_var_char_table(Size) -> + " (FIELD varchar(" ++ integer_to_list(Size) ++ "))". + +%------------------------------------------------------------------------- +text_min() -> + 1. +text_max() -> + 2147483646. % 2147483647. %% 2^31 - 1 + +create_text_table() -> + " (FIELD text)". + +%------------------------------------------------------------------------- +create_timestamp_table() -> + " (FIELD TIMESTAMP)". + +%------------------------------------------------------------------------- +tiny_int_min() -> + -128. +tiny_int_max() -> + 127. + +create_tiny_int_table() -> + " (FIELD tinyint)". + +tiny_int_min_selected() -> + {selected,["FIELD"],[{tiny_int_min()}]}. + +tiny_int_max_selected() -> + {selected,["FIELD"], [{tiny_int_max()}]}. + +%------------------------------------------------------------------------- +small_int_min() -> + -32768. +small_int_max() -> + 32767. + +create_small_int_table() -> + " (FIELD smallint)". + +small_int_min_selected() -> + {selected,["FIELD"],[{-32768}]}. + +small_int_max_selected() -> + {selected,["FIELD"], [{32767}]}. + +%------------------------------------------------------------------------- +int_min() -> + -2147483648. +int_max() -> + 2147483647. + +create_int_table() -> + " (FIELD int)". + +int_min_selected() -> + {selected,["FIELD"],[{-2147483648}]}. + +int_max_selected() -> + {selected,["FIELD"], [{2147483647}]}. + +%------------------------------------------------------------------------- +big_int_min() -> + -9223372036854775808. + +big_int_max() -> + 9223372036854775807. + +create_big_int_table() -> + " (FIELD bigint )". + +big_int_min_selected() -> + {selected,["FIELD"], [{"-9223372036854775808"}]}. + +big_int_max_selected() -> + {selected,["FIELD"], [{"9223372036854775807"}]}. + +%------------------------------------------------------------------------- +bit_false() -> + 0. +bit_true() -> + 1. + +create_bit_table() -> + " (FIELD bit)". + +bit_false_selected() -> + {selected,["FIELD"],[{"0"}]}. + +bit_true_selected() -> + {selected,["FIELD"], [{"1"}]}. + +%------------------------------------------------------------------------- + +%% Do not test float min/max as value is only theoretical defined in +%% mysql and may vary depending on hardware. + +create_float_table() -> + " (FIELD float)". + +float_zero_selected() -> + {selected,["FIELD"],[{0.00000e+0}]}. + +%------------------------------------------------------------------------- +real_min() -> + -3.40e+38. +real_max() -> + 3.40e+38. + +real_underflow() -> + "-3.41e+38". + +real_overflow() -> + "3.41e+38". + +create_real_table() -> + " (FIELD real)". + +real_zero_selected() -> + {selected,["FIELD"],[{0.00000e+0}]}. + +%------------------------------------------------------------------------- +param_select_small_int() -> + {selected,["FIELD"],[{1}, {2}]}. + +param_select_int() -> + Int = small_int_max() + 1, + {selected,["FIELD"],[{1}, {Int}]}. + +param_select_decimal() -> + {selected,["FIELD"],[{1},{2}]}. + +param_select_numeric() -> + {selected,["FIELD"],[{1},{2}]}. + +param_select_float() -> + {selected,["FIELD"],[{1.30000},{1.20000}]}. + +param_select_real() -> + {selected,["FIELD"],[{1.30000},{1.20000}]}. + +param_select_double() -> + {selected,["FIELD"],[{1.30000},{1.20000}]}. + +param_select_mix() -> + {selected,["ID","DATA"],[{1, "foo"}, {2, "bar"}]}. + +param_update() -> + {selected,["ID","DATA"],[{1, "foobar"}, {2, "foobar"}, {3, "baz"}]}. + +param_delete() -> + {selected,["ID","DATA"],[{3, "baz"}]}. + +param_select() -> + {selected,["ID","DATA"],[{1, "foo"},{3, "foo"}]}. + +%------------------------------------------------------------------------- +describe_integer() -> + {ok,[{"myint1",sql_smallint}, + {"myint2",sql_integer}, + {"myint3",sql_integer}]}. + +describe_string() -> + {ok,[{"str1",{sql_char,10}}, + {"str2",{sql_char,10}}, + {"str3",{sql_varchar,10}}, + {"str4",{sql_varchar,10}}]}. + +describe_floating() -> + {ok,[{"f",sql_real},{"r",sql_double},{"d",sql_double}]}. +describe_dec_num() -> + {ok,[{"mydec",{sql_decimal,9,3}},{"mynum",{sql_decimal,9,2}}]}. + +describe_timestamp() -> + {ok, [{"FIELD", sql_timestamp}]}. diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl index 4772d1a6fc..e59be772e3 100644 --- a/lib/odbc/test/odbc_connect_SUITE.erl +++ b/lib/odbc/test/odbc_connect_SUITE.erl @@ -77,15 +77,18 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - %% application:start(odbc), - odbc:start(), % make sure init_per_suite fails if odbc is not built - case catch odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]) of - {ok, Ref} -> - odbc:disconnect(Ref), - [{tableName, odbc_test_lib:unique_table_name()} | Config]; - _ -> - {skip, "ODBC is not properly setup"} + case (catch odbc:start()) of + ok -> + case catch odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()) of + {ok, Ref} -> + odbc:disconnect(Ref), + [{tableName, odbc_test_lib:unique_table_name()} | Config]; + _ -> + {skip, "ODBC is not properly setup"} + end; + _ -> + {skip,"ODBC not startable"} end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -94,8 +97,7 @@ init_per_suite(Config) -> %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- end_per_suite(_Config) -> - application:stop(odbc), - ok. + application:stop(odbc). %%-------------------------------------------------------------------- %% Function: init_per_testcase(Case, Config) -> Config @@ -125,15 +127,13 @@ init_per_testcase(_TestCase, Config) -> %% Description: Cleanup after each test case %%-------------------------------------------------------------------- end_per_testcase(_TestCase, Config) -> - %% Clean up if needed Table = ?config(tableName, Config), - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), - Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), + Result = odbc:sql_query(Ref, "DROP TABLE " ++ Table), io:format("Drop table: ~p ~p~n", [Table, Result]), odbc:disconnect(Ref), Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. + test_server:timetrap_cancel(Dog). %%------------------------------------------------------------------------- %% Test cases starts here. @@ -143,13 +143,15 @@ commit(doc)-> commit(suite) -> []; commit(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), Table = ?config(tableName, Config), + TransStr = transaction_support_str(?RDBMS), + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (ID integer, DATA varchar(10))"), + " (ID integer, DATA varchar(10))" ++ TransStr), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(1,'bar')"), @@ -174,50 +176,47 @@ commit(Config) -> {'EXIT', {function_clause, _}} = (catch odbc:commit(Ref, commit, -1)), - ok = odbc:disconnect(Ref), - - ok. + ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- rollback(doc)-> ["Test the use of explicit rollback"]; rollback(suite) -> []; rollback(Config) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), Table = ?config(tableName, Config), - {updated, _} = - odbc:sql_query(Ref, + TransStr = transaction_support_str(?RDBMS), + + {updated, _} = + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (ID integer, DATA varchar(10))"), - {updated, 1} = + " (ID integer, DATA varchar(10))" ++ TransStr), + {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), ok = odbc:commit(Ref, commit), - {updated, 1} = + {updated, 1} = odbc:sql_query(Ref, "UPDATE " ++ Table ++ - " SET DATA = 'foo' WHERE ID = 1"), + " SET DATA = 'foo' WHERE ID = 1"), ok = odbc:commit(Ref, rollback), InsertResult = ?RDBMS:insert_result(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), - - {updated, 1} = + {updated, 1} = odbc:sql_query(Ref, "UPDATE " ++ Table ++ - " SET DATA = 'foo' WHERE ID = 1"), + " SET DATA = 'foo' WHERE ID = 1"), ok = odbc:commit(Ref, rollback, ?TIMEOUT), InsertResult = ?RDBMS:insert_result(), - InsertResult = + InsertResult = odbc:sql_query(Ref, "SELECT * FROM " ++ Table), + {'EXIT', {function_clause, _}} = + (catch odbc:commit(Ref, rollback, -1)), - {'EXIT', {function_clause, _}} = - (catch odbc:commit(Ref, rollback, -1)), - - ok = odbc:disconnect(Ref), - ok. + ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- not_explicit_commit(doc) -> @@ -225,20 +224,20 @@ not_explicit_commit(doc) -> not_explicit_commit(suite) -> []; not_explicit_commit(_Config) -> {ok, Ref} = - odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}]), + odbc:connect(?RDBMS:connection_string(), [{auto_commit, on}] ++ + odbc_test_lib:platform_options()), {error, _} = odbc:commit(Ref, commit), - ok = odbc:disconnect(Ref), - ok. + ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- not_exist_db(doc) -> ["Tests valid data format but invalid data in the connection parameters."]; not_exist_db(suite) -> []; not_exist_db(_Config) -> - {error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar", []), + {error, _} = odbc:connect("DSN=foo;UID=bar;PWD=foobar", + odbc_test_lib:platform_options()), %% So that the odbc control server can be stoped "in the correct way" - test_server:sleep(100), - ok. + test_server:sleep(100). %%------------------------------------------------------------------------- no_c_node(doc) -> @@ -253,7 +252,8 @@ no_c_node(_Config) -> FileName2 = filename:nativename(filename:join(Dir, "odbcsrv")), ok = file:rename(FileName1, FileName2), Result = - case catch odbc:connect(?RDBMS:connection_string(), []) of + case catch odbc:connect(?RDBMS:connection_string(), + odbc_test_lib:platform_options()) of {error, port_program_executable_not_found} -> ok; Else -> @@ -268,7 +268,7 @@ port_dies(doc) -> "Tests what happens if the port program dies"; port_dies(suite) -> []; port_dies(_Config) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), {status, _} = process_info(Ref, status), process_flag(trap_exit, true), Port = lists:last(erlang:ports()), @@ -276,26 +276,23 @@ port_dies(_Config) -> %% Wait for exit_status from port 5000 ms (will not get a exit %% status in this case), then wait a little longer to make sure %% the port and the controlprocess has had time to terminate. - test_server:sleep(7000), - undefined = process_info(Ref, status), - ok. + test_server:sleep(10000), + undefined = process_info(Ref, status). %%------------------------------------------------------------------------- control_process_dies(doc) -> "Tests what happens if the Erlang control process dies"; control_process_dies(suite) -> []; control_process_dies(_Config) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), process_flag(trap_exit, true), Port = lists:last(erlang:ports()), {connected, Ref} = erlang:port_info(Port, connected), exit(Ref, kill), - test_server:sleep(100), - undefined = erlang:port_info(Port, connected), + test_server:sleep(500), + undefined = erlang:port_info(Port, connected). %% Check for c-program still running, how? - ok. -%%------------------------------------------------------------------------- %%------------------------------------------------------------------------- client_dies_normal(doc) -> @@ -320,7 +317,7 @@ client_dies_normal(Config) when is_list(Config) -> end. client_normal(Pid) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), Pid ! {dbRef, Ref}, receive continue -> @@ -352,7 +349,7 @@ client_dies_timeout(Config) when is_list(Config) -> end. client_timeout(Pid) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), Pid ! {dbRef, Ref}, receive continue -> @@ -384,7 +381,7 @@ client_dies_error(Config) when is_list(Config) -> end. client_error(Pid) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), Pid ! {dbRef, Ref}, receive continue -> @@ -399,7 +396,10 @@ connect_timeout(doc) -> connect_timeout(suite) -> []; connect_timeout(Config) when is_list(Config) -> {'EXIT',timeout} = (catch odbc:connect(?RDBMS:connection_string(), - [{timeout, 0}])), + [{timeout, 0}] ++ + odbc_test_lib:platform_options())), + %% Need to return ok here "{'EXIT',timeout} return value" will + %% be interpreted as that the testcase has timed out. ok. %%------------------------------------------------------------------------- timeout(doc) -> @@ -412,10 +412,12 @@ timeout(Config) when is_list(Config) -> [{auto_commit, off}]), Table = ?config(tableName, Config), + TransStr = transaction_support_str(?RDBMS), + {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), @@ -447,14 +449,12 @@ timeout(Config) when is_list(Config) -> ["DATA"] = odbc_test_lib:to_upper(Fields), ok = odbc:commit(Ref, commit), - ok = odbc:disconnect(Ref), - ok. - + ok = odbc:disconnect(Ref). update_table_timeout(Table, TimeOut, Pid) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of @@ -475,15 +475,16 @@ update_table_timeout(Table, TimeOut, Pid) -> odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"), ["DATA"] = odbc_test_lib:to_upper(Fields), - {updated, 1} = odbc:sql_query(Ref, UpdateQuery, TimeOut), + %% Do not check {updated, 1} as some drivers will return 0 + %% even though the update is done, which is checked by the test + %% case when the altered message is recived. + {updated, _} = odbc:sql_query(Ref, UpdateQuery, TimeOut), ok = odbc:commit(Ref, commit), Pid ! altered, - ok = odbc:disconnect(Ref), - - ok. + ok = odbc:disconnect(Ref). %%------------------------------------------------------------------------- many_timeouts(doc) -> ["Tests that many consecutive timeouts lead to that the connection " @@ -491,14 +492,15 @@ many_timeouts(doc) -> many_timeouts(suite) -> []; many_timeouts(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), Table = ?config(tableName, Config), + TransStr = transaction_support_str(?RDBMS), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), @@ -518,22 +520,20 @@ many_timeouts(Config) when is_list(Config) -> end, ok = odbc:commit(Ref, commit), - ok = odbc:disconnect(Ref), - ok. + ok = odbc:disconnect(Ref). update_table_many_timeouts(Table, TimeOut, Pid) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", ok = loop_many_timouts(Ref, UpdateQuery, TimeOut), Pid ! many_timeouts_occurred, - ok = odbc:disconnect(Ref), - ok. + ok = odbc:disconnect(Ref). loop_many_timouts(Ref, UpdateQuery, TimeOut) -> @@ -547,18 +547,19 @@ loop_many_timouts(Ref, UpdateQuery, TimeOut) -> end. %%------------------------------------------------------------------------- timeout_reset(doc) -> - ["Check that the number of consecutive timouts is reset to 0 when " + ["Check that the number of consecutive timouts is reset to 0 when " "a successful call to the database is made."]; timeout_reset(suite) -> []; timeout_reset(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), Table = ?config(tableName, Config), + TransStr = transaction_support_str(?RDBMS), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), @@ -594,13 +595,12 @@ timeout_reset(Config) when is_list(Config) -> ["DATA"] = odbc_test_lib:to_upper(Fields), ok = odbc:commit(Ref, commit), - ok = odbc:disconnect(Ref), - ok. + ok = odbc:disconnect(Ref). update_table_timeout_reset(Table, TimeOut, Pid) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", ok = loop_timout_reset(Ref, UpdateQuery, TimeOut, @@ -617,15 +617,16 @@ update_table_timeout_reset(Table, TimeOut, Pid) -> odbc:sql_query(Ref, "SELECT DATA FROM " ++ Table ++ " WHERE ID = 2"), ["DATA"] = odbc_test_lib:to_upper(Fields), - {updated,1} = odbc:sql_query(Ref, UpdateQuery, TimeOut), + %% Do not check {updated, 1} as some drivers will return 0 + %% even though the update is done, which is checked by the test + %% case when the altered message is recived. + {updated, _} = odbc:sql_query(Ref, UpdateQuery, TimeOut), ok = odbc:commit(Ref, commit), Pid ! altered, - ok = odbc:disconnect(Ref), - - ok. + ok = odbc:disconnect(Ref). loop_timout_reset(_, _, _, 0) -> ok; @@ -649,13 +650,14 @@ disconnect_on_timeout(suite) -> []; disconnect_on_timeout(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), Table = ?config(tableName, Config), + TransStr = transaction_support_str(?RDBMS), {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (ID integer, DATA varchar(10), PRIMARY KEY(ID))"), + " (ID integer, DATA varchar(10), PRIMARY KEY(ID))" ++ TransStr), {updated, 1} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++ " VALUES(1,'bar')"), @@ -679,7 +681,7 @@ disconnect_on_timeout(Config) when is_list(Config) -> update_table_disconnect_on_timeout(Table, TimeOut, Pid) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}]), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()), UpdateQuery = "UPDATE " ++ Table ++ " SET DATA = 'foobar' WHERE ID = 1", case catch odbc:sql_query(Ref, UpdateQuery, TimeOut) of @@ -696,7 +698,7 @@ connection_closed(doc) -> " use a connection that has been closed"]; connection_closed(suite) -> []; connection_closed(Config) when is_list(Config) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), Table = ?config(tableName, Config), {updated, _} = @@ -715,8 +717,7 @@ connection_closed(Config) when is_list(Config) -> {error, connection_closed} = odbc:next(Ref), {error, connection_closed} = odbc:prev(Ref), {error, connection_closed} = odbc:select(Ref, next, 3), - {error, connection_closed} = odbc:commit(Ref, commit), - ok. + {error, connection_closed} = odbc:commit(Ref, commit). %%------------------------------------------------------------------------- disable_scrollable_cursors(doc) -> @@ -754,8 +755,7 @@ disable_scrollable_cursors(Config) when is_list(Config) -> {error, scrollable_cursors_disabled} = odbc:select(Ref, {absolute, 2}, 5), - {selected, _ColNames,[]} = odbc:select(Ref, next, 1), - ok. + {selected, _ColNames,[]} = odbc:select(Ref, next, 1). %%------------------------------------------------------------------------- return_rows_as_lists(doc)-> @@ -764,7 +764,7 @@ return_rows_as_lists(doc)-> return_rows_as_lists(suite) -> []; return_rows_as_lists(Config) when is_list(Config) -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{tuple_row, off}]), + [{tuple_row, off}] ++ odbc_test_lib:platform_options()), Table = ?config(tableName, Config), @@ -785,16 +785,21 @@ return_rows_as_lists(Config) when is_list(Config) -> {ok, _} = odbc:select_count(Ref, "SELECT * FROM " ++ Table), - First = ?RDBMS:first_list_rows(), - Last = ?RDBMS:last_list_rows(), - Prev = ?RDBMS:prev_list_rows(), - Next = ?RDBMS:next_list_rows(), - - Last = odbc:last(Ref), - Prev = odbc:prev(Ref), - First = odbc:first(Ref), - Next = odbc:next(Ref), - ok. + case proplists:get_value(scrollable_cursors, odbc_test_lib:platform_options()) of + off -> + Next = ?RDBMS:next_list_rows(), + Next = odbc:next(Ref); + _ -> + First = ?RDBMS:first_list_rows(), + Last = ?RDBMS:last_list_rows(), + Prev = ?RDBMS:prev_list_rows(), + Next = ?RDBMS:next_list_rows(), + + Last = odbc:last(Ref), + Prev = odbc:prev(Ref), + First = odbc:first(Ref), + Next = odbc:next(Ref) + end. %%------------------------------------------------------------------------- @@ -803,22 +808,27 @@ api_missuse(doc)-> api_missuse(suite) -> []; api_missuse(Config) when is_list(Config)-> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), %% Serious programming fault, connetion will be shut down gen_server:call(Ref, {self(), foobar, 10}, infinity), test_server:sleep(10), undefined = process_info(Ref, status), - {ok, Ref2} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref2} = odbc:connect(?RDBMS:connection_string(), + odbc_test_lib:platform_options()), %% Serious programming fault, connetion will be shut down gen_server:cast(Ref2, {self(), foobar, 10}), test_server:sleep(10), undefined = process_info(Ref2, status), - {ok, Ref3} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref3} = odbc:connect(?RDBMS:connection_string(), + odbc_test_lib:platform_options()), %% Could be an innocent misstake the connection lives. Ref3 ! foobar, test_server:sleep(10), - {status, _} = process_info(Ref3, status), - ok. + {status, _} = process_info(Ref3, status). +transaction_support_str(mysql) -> + "ENGINE = InnoDB"; +transaction_support_str(_) -> + "". diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl index 633ddec27f..099ae0aa7d 100644 --- a/lib/odbc/test/odbc_data_type_SUITE.erl +++ b/lib/odbc/test/odbc_data_type_SUITE.erl @@ -44,24 +44,29 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case odbc_test_lib:odbc_check() of ok -> - [{group, char}, {group, int}, {group, floats}, + [{group, char},{group, fixed_char}, {group, binary_char}, + {group, fixed_binary_char}, {group, unicode}, + {group, int}, {group, floats}, {group, dec_and_num}, timestamp]; Other -> {skip, Other} end. groups() -> [{char, [], - [char_fixed_lower_limit, char_fixed_upper_limit, - char_fixed_padding, varchar_lower_limit, + [varchar_lower_limit, varchar_upper_limit, varchar_no_padding, - text_lower_limit, text_upper_limit, unicode]}, + text_lower_limit, text_upper_limit]}, + {fixed_char, [], + [char_fixed_lower_limit, char_fixed_upper_limit, + char_fixed_padding]}, {binary_char, [], - [binary_char_fixed_lower_limit, - binary_char_fixed_upper_limit, - binary_char_fixed_padding, binary_varchar_lower_limit, + [binary_varchar_lower_limit, binary_varchar_upper_limit, binary_varchar_no_padding, - binary_text_lower_limit, binary_text_upper_limit, - unicode]}, + binary_text_lower_limit, binary_text_upper_limit]}, + {fixed_binary_char, [], [binary_char_fixed_lower_limit, + binary_char_fixed_upper_limit, + binary_char_fixed_padding]}, + {unicode, [], [utf8, nchar, nvarchar]}, {int, [], [tiny_int_lower_limit, tiny_int_upper_limit, small_int_lower_limit, small_int_upper_limit, @@ -74,14 +79,33 @@ groups() -> [dec_long, dec_double, dec_bignum, num_long, num_double, num_bignum]}]. +init_per_group(GroupName, Config) when GroupName == fixed_char; + GroupName == fixed_binary_char -> + case ?RDBMS of + mysql -> + {skip, "No supported by MYSQL"}; + _ -> + Config + end; + +init_per_group(unicode, Config) -> + %% Uses parameterized queries + case {os:type(), erlang:system_info(wordsize)} of + {{unix, _}, 4} -> + Config; + {{unix, _}, _} -> + {skip, "Not supported by driver"}; + _ -> + Config + end; + + init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. - - %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -92,9 +116,12 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - %%application:start(odbc), - odbc:start(), % make sure init_per_suite fails if odbc is not built - [{tableName, odbc_test_lib:unique_table_name()} | Config]. + case (catch odbc:start()) of + ok -> + [{tableName, odbc_test_lib:unique_table_name()} | Config]; + _ -> + {skip, "ODBC not startable"} + end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -118,8 +145,17 @@ end_per_suite(_Config) -> %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- -init_per_testcase(varchar_upper_limit, _Config) -> - {skip, "Known bug in database"}; +init_per_testcase(Case, Config) when Case == varchar_upper_limit; + Case == binary_varchar_upper_limit; + Case == varchar_no_padding; + Case == binary_varchar_no_padding -> + case is_fixed_upper_limit(?RDBMS) of + true -> + common_init_per_testcase(Case, Config); + false -> + {skip, "Upper limit is not fixed in" ++ atom_to_list(?RDBMS)} + end; + init_per_testcase(text_upper_limit, _Config) -> {skip, "Consumes too much resources"}; @@ -131,57 +167,59 @@ init_per_testcase(Case, Config) when Case == bit_true; Case == bit_false -> {skip, "Not supported by driver"} end; -init_per_testcase(Case, Config) when Case == multiple_select_result_sets; - Case == multiple_mix_result_sets; - Case == multiple_result_sets_error -> - case is_supported_multiple_resultsets(?RDBMS) of +init_per_testcase(param_insert_tiny_int = Case, Config) -> + case is_supported_tinyint(?RDBMS) of true -> common_init_per_testcase(Case, Config); false -> {skip, "Not supported by driver"} end; -init_per_testcase(param_insert_tiny_int = Case, Config) -> - case is_supported_tinyint(?RDBMS) of - true -> +init_per_testcase(Case, Config) when Case == nchar; + Case == nvarchar -> + case ?RDBMS of + sqlserver -> common_init_per_testcase(Case, Config); - false -> + _ -> {skip, "Not supported by driver"} end; + init_per_testcase(Case, Config) -> common_init_per_testcase(Case, Config). -is_supported_multiple_resultsets(sqlserver) -> - true; -is_supported_multiple_resultsets(_) -> - false. - -is_supported_tinyint(sqlserver) -> - true; -is_supported_tinyint(_) -> - false. - -is_supported_bit(sqlserver) -> - true; -is_supported_bit(_) -> - false. - common_init_per_testcase(Case, Config) -> + PlatformOptions = odbc_test_lib:platform_options(), case atom_to_list(Case) of "binary" ++ _ -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{binary_strings, on}]); - "unicode" -> + [{binary_strings, on}] ++ PlatformOptions); + LCase when LCase == "utf8"; + LCase == "nchar"; + LCase == "nvarchar" -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), - [{binary_strings, on}]); + [{binary_strings, on}] ++ PlatformOptions); _ -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []) + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), PlatformOptions) end, + odbc_test_lib:strict(Ref, ?RDBMS), Dog = test_server:timetrap(?default_timeout), Temp = lists:keydelete(connection_ref, 1, Config), NewConfig = lists:keydelete(watchdog, 1, Temp), [{watchdog, Dog}, {connection_ref, Ref} | NewConfig]. +is_fixed_upper_limit(mysql) -> + false; +is_fixed_upper_limit(_) -> + true. +is_supported_tinyint(sqlserver) -> + true; +is_supported_tinyint(_) -> + false. +is_supported_bit(sqlserver) -> + true; +is_supported_bit(_) -> + false. + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -195,7 +233,7 @@ end_per_testcase(_TestCase, Config) -> ok = odbc:disconnect(Ref), %% Clean up if needed Table = ?config(tableName, Config), - {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), []), + {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc:sql_query(NewRef, "DROP TABLE " ++ Table), odbc:disconnect(NewRef), Dog = ?config(watchdog, Config), @@ -218,18 +256,18 @@ char_fixed_lower_limit(Config) when is_list(Config) -> {error, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_fixed_char_table( + ?RDBMS:create_fixed_char_table( (?RDBMS:fixed_char_min() - 1))), %% Lower limit {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_fixed_char_table( - ?RDBMS:fixed_char_min())), + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_min())), %% Right length data {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) + "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) ++ "')"), %% Select data {selected, Fields,[{"a"}]} = @@ -240,11 +278,11 @@ char_fixed_lower_limit(Config) when is_list(Config) -> %% Too long data {error, _} = odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, - (?RDBMS:fixed_char_min() - + 1)) - ++ "')"), - ok. + "'" ++ string:chars($a, + (?RDBMS:fixed_char_min() + + 1)) + ++ "')"). + %%------------------------------------------------------------------------- char_fixed_upper_limit(doc) -> @@ -292,8 +330,7 @@ char_fixed_upper_limit(Config) when is_list(Config) -> {error, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_fixed_char_table( - (?RDBMS:fixed_char_max() + 1))), - ok + (?RDBMS:fixed_char_max() + 1))) end. %%------------------------------------------------------------------------- @@ -310,20 +347,20 @@ char_fixed_padding(Config) when is_list(Config) -> %% Data should be padded with blanks {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_fixed_char_table( - ?RDBMS:fixed_char_max())), + ?RDBMS:create_fixed_char_table( + ?RDBMS:fixed_char_max())), - {updated, _} = + {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, - ?RDBMS:fixed_char_min()) + "'" ++ string:chars($a, + ?RDBMS:fixed_char_min()) ++ "')"), {selected, Fields, [{CharStr}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = length(CharStr) == ?RDBMS:fixed_char_max(), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). + %%------------------------------------------------------------------------- varchar_lower_limit(doc) -> @@ -336,33 +373,33 @@ varchar_lower_limit(Config) when is_list(Config) -> %% Below limit {error, _} = - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_var_char_table( - ?RDBMS:var_char_min() - 1)), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_min() - 1)), %% Lower limit {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_var_char_table( - ?RDBMS:var_char_min())), + ?RDBMS:create_var_char_table( + ?RDBMS:var_char_min())), + + Str = string:chars($a, ?RDBMS:var_char_min()), %% Right length data {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, ?RDBMS:var_char_min()) - ++ "')"), + "'" ++ Str ++ "')"), %% Select data - {selected, Fields, [{"a"}]} = + {selected, Fields, [{Str}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), - %% Too long data - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, - (?RDBMS:var_char_min()+1)) - ++ "')"), - ok. + %% Too long datae + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ string:chars($a, + (?RDBMS:var_char_min()+1)) + ++ "')"). %%------------------------------------------------------------------------- @@ -438,8 +475,7 @@ varchar_no_padding(Config) when is_list(Config) -> {selected, Fields, [{CharStr}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = length(CharStr) /= ?RDBMS:var_char_max(), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------- @@ -462,8 +498,7 @@ text_lower_limit(Config) when is_list(Config) -> {selected, Fields, [{"a"}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------- @@ -493,8 +528,7 @@ text_upper_limit(Config) when is_list(Config) -> %% {error, _} = %% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ %% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) -%% ++ "')"), -%% ok. +%% ++ "')"). %%------------------------------------------------------------------------- @@ -518,13 +552,18 @@ binary_char_fixed_lower_limit(Config) when is_list(Config) -> ?RDBMS:create_fixed_char_table( ?RDBMS:fixed_char_min())), + Str = string:chars($a, ?RDBMS:fixed_char_min()), + %% Right length data {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, ?RDBMS:fixed_char_min()) + "'" ++ Str ++ "')"), + + Bin = list_to_binary(Str), + %% Select data - {selected, Fields,[{<<"a">>}]} = + {selected, Fields,[{Bin}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), @@ -535,8 +574,7 @@ binary_char_fixed_lower_limit(Config) when is_list(Config) -> "'" ++ string:chars($a, (?RDBMS:fixed_char_min() + 1)) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- binary_char_fixed_upper_limit(doc) -> @@ -614,8 +652,8 @@ binary_char_fixed_padding(Config) when is_list(Config) -> {selected, Fields, [{CharBin}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = size(CharBin) == ?RDBMS:fixed_char_max(), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). + %%------------------------------------------------------------------------- binary_varchar_lower_limit(doc) -> @@ -637,13 +675,17 @@ binary_varchar_lower_limit(Config) when is_list(Config) -> ?RDBMS:create_var_char_table( ?RDBMS:var_char_min())), + Str = string:chars($a, ?RDBMS:var_char_min()), + %% Right length data {updated, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ string:chars($a, ?RDBMS:var_char_min()) + "'" ++ Str ++ "')"), + BinStr = list_to_binary(Str), + %% Select data - {selected, Fields, [{<<"a">>}]} = + {selected, Fields, [{BinStr}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), ["FIELD"] = odbc_test_lib:to_upper(Fields), @@ -653,8 +695,7 @@ binary_varchar_lower_limit(Config) when is_list(Config) -> odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ string:chars($a, (?RDBMS:var_char_min()+1)) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- @@ -703,8 +744,7 @@ binary_varchar_upper_limit(Config) when is_list(Config) -> {error, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ ?RDBMS:create_var_char_table( - (?RDBMS:var_char_max() + 1))), - ok + (?RDBMS:var_char_max() + 1))) end. %%------------------------------------------------------------------------- @@ -730,8 +770,7 @@ binary_varchar_no_padding(Config) when is_list(Config) -> {selected, Fields, [{CharBin}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), true = size(CharBin) /= ?RDBMS:var_char_max(), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------- @@ -754,8 +793,7 @@ binary_text_lower_limit(Config) when is_list(Config) -> {selected, Fields, [{<<"a">>}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------- @@ -785,11 +823,7 @@ binary_text_upper_limit(Config) when is_list(Config) -> %% {error, _} = %% odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ %% "'" ++ string:chars($a, (?RDBMS:text_max()+1)) -%% ++ "')"), -%% ok. - - -%%------------------------------------------------------------------------- +%% ++ "')"). %%------------------------------------------------------------------------- @@ -823,8 +857,7 @@ tiny_int_lower_limit(Config) when is_list(Config) -> odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:tiny_int_min() - 1) - ++ "')"), - ok + ++ "')") end. %%------------------------------------------------------------------------- @@ -858,8 +891,7 @@ tiny_int_upper_limit(Config) when is_list(Config) -> odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:tiny_int_max() + 1) - ++ "')"), - ok + ++ "')") end. %%------------------------------------------------------------------------- @@ -889,8 +921,7 @@ small_int_lower_limit(Config) when is_list(Config) -> odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:small_int_min() - 1) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- @@ -919,8 +950,7 @@ small_int_upper_limit(Config) when is_list(Config) -> odbc:sql_query(Ref,"INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:small_int_max() + 1) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- int_lower_limit(doc) -> @@ -947,8 +977,7 @@ int_lower_limit(Config) when is_list(Config) -> {error, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:int_min() - 1) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- @@ -976,8 +1005,7 @@ int_upper_limit(Config) when is_list(Config) -> {error, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:int_max() + 1) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- @@ -1006,8 +1034,7 @@ big_int_lower_limit(Config) when is_list(Config) -> odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:big_int_min() - 1) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- @@ -1036,8 +1063,7 @@ big_int_upper_limit(Config) when is_list(Config) -> odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(?RDBMS:big_int_max() + 1) - ++ "')"), - ok. + ++ "')"). %%------------------------------------------------------------------------- bit_false(doc) -> @@ -1069,8 +1095,7 @@ bit_false(Config) when is_list(Config) -> {error, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(-1) - ++ "')"), - ok + ++ "')") end. %%------------------------------------------------------------------------- @@ -1105,14 +1130,10 @@ bit_true(Config) when is_list(Config) -> {error, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ "'" ++ integer_to_list(-1) - ++ "')"), - ok + ++ "')") end. %%------------------------------------------------------------------------- - - -%%------------------------------------------------------------------------- float_lower_limit(doc) -> [""]; float_lower_limit(suite) -> @@ -1122,44 +1143,45 @@ float_lower_limit(Config) when is_list(Config) -> Ref = ?config(connection_ref, Config), Table = ?config(tableName, Config), - {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_float_table()), - - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ float_to_list( - ?RDBMS:float_min()) - ++ "')"), - {selected,[_ColName],[{MinFloat}]} = - odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - - true = ?RDBMS:float_min() == MinFloat, - case ?RDBMS of - oracle -> - {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "DROP TABLE " ++ Table), - + mysql -> + {skip, "Not clearly defined in MYSQL"}; + _ -> {updated, _} = % Value == 0 || -1 driver dependent! odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_float_table()), + ?RDBMS:create_float_table()), {updated, _} = - odbc:sql_query(Ref, - "INSERT INTO " ++ Table ++" VALUES(" ++ - ?RDBMS:float_underflow() ++ ")"), - - SelectResult = ?RDBMS:float_zero_selected(), - SelectResult = - odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table); - _ -> - {error, _} = odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - ?RDBMS:float_underflow() ++ ")") - end, - ok. + "'" ++ float_to_list( + ?RDBMS:float_min()) + ++ "')"), + {selected,[_ColName],[{MinFloat}]} = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + true = ?RDBMS:float_min() == MinFloat, + + case ?RDBMS of + oracle -> + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "DROP TABLE " ++ Table), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_float_table()), + {updated, _} = + odbc:sql_query(Ref, + "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:float_underflow() ++ ")"), + SelectResult = ?RDBMS:float_zero_selected(), + SelectResult = + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table); + _ -> + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:float_underflow() ++ ")") + end + end. %%------------------------------------------------------------------------- float_upper_limit(doc) -> @@ -1170,26 +1192,28 @@ float_upper_limit(Config) when is_list(Config) -> Ref = ?config(connection_ref, Config), Table = ?config(tableName, Config), - {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_float_table()), - - {updated, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - "'" ++ float_to_list( - ?RDBMS:float_max()) - ++ "')"), - + case ?RDBMS of + mysql -> + {skip, "Not clearly defined in MYSQL"}; + _-> + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + ?RDBMS:create_float_table()), - {selected,[_ColName],[{MaxFloat}]} - = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), + {updated, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + "'" ++ float_to_list( + ?RDBMS:float_max()) + ++ "')"), + {selected,[_ColName],[{MaxFloat}]} + = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - true = ?RDBMS:float_max() == MaxFloat, + true = ?RDBMS:float_max() == MaxFloat, - {error, _} = - odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ - ?RDBMS:float_overflow() ++ ")"), - ok. + {error, _} = + odbc:sql_query(Ref, "INSERT INTO " ++ Table ++" VALUES(" ++ + ?RDBMS:float_overflow() ++ ")") + end. %%------------------------------------------------------------------------- float_zero(doc) -> @@ -1209,8 +1233,7 @@ float_zero(Config) when is_list(Config) -> SelectResult = ?RDBMS:float_zero_selected(), SelectResult = - odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ok. + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table). %%------------------------------------------------------------------------- real_zero(doc) -> ["Test the real value zero."]; @@ -1234,10 +1257,8 @@ real_zero(Config) when is_list(Config) -> SelectResult = ?RDBMS:real_zero_selected(), SelectResult = - odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ok + odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table) end. -%%------------------------------------------------------------------------- %%------------------------------------------------------------------------ dec_long(doc) -> [""]; @@ -1256,8 +1277,7 @@ dec_long(Config) when is_list(Config) -> {selected, Fields, [{2}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------ dec_double(doc) -> [""]; @@ -1304,8 +1324,7 @@ dec_double(Config) when is_list(Config) -> {selected, Fields2, [{1.60000}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields2), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields2). %%------------------------------------------------------------------------ dec_bignum(doc) -> @@ -1338,8 +1357,7 @@ dec_bignum(Config) when is_list(Config) -> {selected, Fields1, [{"1.6"}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields1), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ num_long(doc) -> [""]; @@ -1358,8 +1376,7 @@ num_long(Config) when is_list(Config) -> {selected, Fields, [{2}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields). %%------------------------------------------------------------------------ num_double(doc) -> [""]; @@ -1405,8 +1422,7 @@ num_double(Config) when is_list(Config) -> {selected, Fields2, [{1.6000}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields2), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields2). %%------------------------------------------------------------------------ num_bignum(doc) -> [""]; @@ -1438,21 +1454,19 @@ num_bignum(Config) when is_list(Config) -> {selected, Fields1, [{"1.6"}]} = odbc:sql_query(Ref,"SELECT FIELD FROM " ++ Table), - ["FIELD"] = odbc_test_lib:to_upper(Fields1), - ok. + ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ -unicode(doc) -> +utf8(doc) -> ["Test unicode support"]; -unicode(suit) -> +utf8(suit) -> []; -unicode(Config) when is_list(Config) -> +utf8(Config) when is_list(Config) -> Ref = ?config(connection_ref, Config), Table = ?config(tableName, Config), - {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_unicode_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"), + Latin1Data = ["���������", "testasdf", "Row 3", @@ -1465,39 +1479,7 @@ unicode(Config) when is_list(Config) -> "Row 10", "Row 11", "Row 12"], - - case ?RDBMS of - sqlserver -> - w_char_support_win(Ref, Table, Latin1Data); - postgres -> - direct_utf8(Ref, Table, Latin1Data); - oracle -> - {skip, "not currently supported"} - end. - -w_char_support_win(Ref, Table, Latin1Data) -> - UnicodeIn = lists:map(fun(S) -> - unicode:characters_to_binary(S,latin1,{utf16,little}) - end, - Latin1Data), - - test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), - {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)", - [{{sql_wvarchar,50},UnicodeIn}]), - - {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), - - test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), - - Result = lists:map(fun({Unicode}) -> - unicode:characters_to_list(Unicode,{utf16,little}) - end, - UnicodeOut), - Latin1Data = Result. - - -direct_utf8(Ref, Table, Latin1Data) -> UnicodeIn = lists:map(fun(String) -> unicode:characters_to_binary(String,latin1,utf8) end, @@ -1518,6 +1500,37 @@ direct_utf8(Ref, Table, Latin1Data) -> test_server:format("Result: ~p ~n", [Result]), Latin1Data = Result. +%%------------------------------------------------------------------------ + +nchar(doc) -> + ["Test unicode nchar support in sqlserver"]; +nchar(suit) -> + []; +nchar(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD nchar(50))"), + + w_char_support(Ref, Table, sql_wvarchar, 50). + +%%------------------------------------------------------------------------ + +nvarchar(doc) -> + ["Test 'unicode' nvarchar support"]; +nvarchar(suit) -> + []; +nvarchar(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD nvarchar(50))"), + + w_char_support(Ref, Table, sql_wlongvarchar, 50). %%------------------------------------------------------------------------ timestamp(doc) -> @@ -1546,3 +1559,43 @@ timestamp(Config) when is_list(Config) -> TimeStamps = lists:map(fun(Value) -> {Value} end, Data), {selected,_, TimeStamps} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table). +%%------------------------------------------------------------------------ + +w_char_support(Ref, Table, CharType, Size) -> + Latin1Data = ["���������", + "testasdf", + "Row 3", + "Row 4", + "Row 5", + "Row 6", + "Row 7", + "Row 8", + "Row 9", + "Row 10", + "Row 11", + "Row 12"], + + UnicodeIn = lists:map(fun(S) -> + unicode:characters_to_binary(S,latin1,{utf16,little}) + end, + Latin1Data), + + test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), + + {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)", + [{{CharType, Size},UnicodeIn}]), + + {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), + + test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + + PadResult = lists:map(fun({Unicode}) -> + unicode:characters_to_list(Unicode,{utf16,little}) + end, + UnicodeOut), + + test_server:format("Result: ~p~n", [PadResult]), + + Result = lists:map(fun(Str) -> string:strip(Str) end, PadResult), + + Latin1Data = Result. diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl index 61106fbf84..76a214d553 100644 --- a/lib/odbc/test/odbc_query_SUITE.erl +++ b/lib/odbc/test/odbc_query_SUITE.erl @@ -43,19 +43,22 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> case odbc_test_lib:odbc_check() of ok -> - [sql_query, first, last, next, prev, select_count, + [sql_query, next, {group, scrollable_cursors}, select_count, select_next, select_relative, select_absolute, create_table_twice, delete_table_twice, duplicate_key, not_connection_owner, no_result_set, query_error, - multiple_select_result_sets, multiple_mix_result_sets, - multiple_result_sets_error, + {group, multiple_result_sets}, {group, parameterized_queries}, {group, describe_table}, delete_nonexisting_row]; Other -> {skip, Other} end. groups() -> - [{parameterized_queries, [], + [{multiple_result_sets, [], [multiple_select_result_sets, + multiple_mix_result_sets, + multiple_result_sets_error]}, + {scrollable_cursors, [], [first, last, prev]}, + {parameterized_queries, [], [{group, param_integers}, param_insert_decimal, param_insert_numeric, {group, param_insert_string}, param_insert_float, param_insert_real, @@ -72,7 +75,21 @@ groups() -> [describe_integer, describe_string, describe_floating, describe_dec_num, describe_no_such_table]}]. -init_per_group(_GroupName, Config) -> +init_per_group(multiple_result_sets, Config) -> + case is_supported_multiple_resultsets(?RDBMS) of + true -> + Config; + false -> + {skip, "Not supported by " ++ atom_to_list(?RDBMS) ++ "driver"} + end; +init_per_group(scrollable_cursors, Config) -> + case proplists:get_value(scrollable_cursors, odbc_test_lib:platform_options()) of + off -> + {skip, "Not supported by driver"}; + _ -> + Config + end; +init_per_group(_,Config) -> Config. end_per_group(_GroupName, Config) -> @@ -88,9 +105,12 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) when is_list(Config) -> - %% application:start(odbc), - odbc:start(), % make sure init_per_suite fails if odbc is not built - [{tableName, odbc_test_lib:unique_table_name()}| Config]. + case (catch odbc:start()) of + ok -> + [{tableName, odbc_test_lib:unique_table_name()}| Config]; + _ -> + {skip, "ODBC not startable"} + end. %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ @@ -115,7 +135,8 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_testcase(_Case, Config) -> - {ok, Ref} = odbc:connect(?RDBMS:connection_string(), []), + {ok, Ref} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), + odbc_test_lib:strict(Ref, ?RDBMS), Dog = test_server:timetrap(?default_timeout), Temp = lists:keydelete(connection_ref, 1, Config), NewConfig = lists:keydelete(watchdog, 1, Temp), @@ -134,7 +155,7 @@ end_per_testcase(_Case, Config) -> ok = odbc:disconnect(Ref), %% Clean up if needed Table = ?config(tableName, Config), - {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), []), + {ok, NewRef} = odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()), odbc:sql_query(NewRef, "DROP TABLE " ++ Table), odbc:disconnect(NewRef), Dog = ?config(watchdog, Config), @@ -661,9 +682,6 @@ multiple_result_sets_error(Config) when is_list(Config) -> end. %%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- -%%------------------------------------------------------------------------- param_insert_tiny_int(doc)-> ["Test insertion of tiny ints by parameterized queries."]; param_insert_tiny_int(suite) -> @@ -899,8 +917,6 @@ param_insert_numeric(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- param_insert_char(doc)-> ["Test insertion of fixed length string by parameterized queries."]; param_insert_char(suite) -> @@ -1323,8 +1339,6 @@ param_select(Config) when is_list(Config) -> ok. %%------------------------------------------------------------------------- - -%%------------------------------------------------------------------------- describe_integer(doc) -> ["Test describe_table/[2,3] for integer columns."]; describe_integer(suite) -> @@ -1336,7 +1350,7 @@ describe_integer(Config) when is_list(Config) -> {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (int1 SMALLINT, int2 INT, int3 INTEGER)"), + " (myint1 SMALLINT, myint2 INT, myint3 INTEGER)"), Decs = ?RDBMS:describe_integer(), %% Make sure to test timeout clause @@ -1397,7 +1411,7 @@ describe_dec_num(Config) when is_list(Config) -> {updated, _} = odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - " (dec DECIMAL(9,3), num NUMERIC(9,2))"), + " (mydec DECIMAL(9,3), mynum NUMERIC(9,2))"), Decs = ?RDBMS:describe_dec_num(), @@ -1449,3 +1463,7 @@ is_driver_error(Error) -> false -> test_server:fail(Error) end. +is_supported_multiple_resultsets(sqlserver) -> + true; +is_supported_multiple_resultsets(_) -> + false. diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl index 65b990133f..440c0ca921 100644 --- a/lib/odbc/test/odbc_start_SUITE.erl +++ b/lib/odbc/test/odbc_start_SUITE.erl @@ -125,14 +125,16 @@ start(doc) -> start(suite) -> []; start(Config) when is_list(Config) -> - {error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(), []), + PlatformOptions = odbc_test_lib:platform_options(), + {error,odbc_not_started} = odbc:connect(?RDBMS:connection_string(), + PlatformOptions), odbc:start(), - case odbc:connect(?RDBMS:connection_string(), []) of + case odbc:connect(?RDBMS:connection_string(), PlatformOptions) of {ok, Ref0} -> ok = odbc:disconnect(Ref0), odbc:stop(), {error,odbc_not_started} = - odbc:connect(?RDBMS:connection_string(), []), + odbc:connect(?RDBMS:connection_string(), PlatformOptions), start_odbc(transient), start_odbc(permanent); {error, odbc_not_started} -> @@ -144,7 +146,7 @@ start(Config) when is_list(Config) -> start_odbc(Type) -> ok = odbc:start(Type), - case odbc:connect(?RDBMS:connection_string(), []) of + case odbc:connect(?RDBMS:connection_string(), odbc_test_lib:platform_options()) of {ok, Ref} -> ok = odbc:disconnect(Ref), odbc:stop(); diff --git a/lib/odbc/test/odbc_test.hrl b/lib/odbc/test/odbc_test.hrl index 87f50043db..397d04756b 100644 --- a/lib/odbc/test/odbc_test.hrl +++ b/lib/odbc/test/odbc_test.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,7 +27,12 @@ {unix, sunos} -> postgres; {unix,linux} -> - postgres; + case erlang:system_info(wordsize) of + 4 -> + mysql; + _ -> + postgres + end; {win32, _} -> sqlserver end). diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl index 012eb96e43..3e78105cf3 100644 --- a/lib/odbc/test/odbc_test_lib.erl +++ b/lib/odbc/test/odbc_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,18 +38,7 @@ match_float(Float, Match, Delta) -> odbc_check() -> case erlang:system_info(wordsize) of 4 -> - case test_server:os_type() of - {unix, sunos} -> - ok; - {unix, linux} -> - ok; - {win32, _} -> - ok; - Other -> - lists:flatten( - io_lib:format("Platform not supported: ~w", - [Other])) - end; + ok; Other -> case os:type() of {unix, linux} -> @@ -75,3 +64,16 @@ check_row_count(Expected, Count) -> to_upper(List) -> lists:map(fun(Str) -> string:to_upper(Str) end, List). + +strict(Ref, mysql) -> + odbc:sql_query(Ref, "SET sql_mode='STRICT_ALL_TABLES,STRICT_TRANS_TABLES';"); +strict(_,_) -> + ok. + +platform_options() -> + case os:type() of + {unix, sunos} -> + [{scrollable_cursors, off}]; + _ -> + [] + end. diff --git a/lib/odbc/test/oracle.erl b/lib/odbc/test/oracle.erl index ebf6dbb6bf..d74863d8c1 100644 --- a/lib/odbc/test/oracle.erl +++ b/lib/odbc/test/oracle.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -108,10 +108,6 @@ create_text_table() -> " (FIELD long)". %Oracle long is variable length char data %------------------------------------------------------------------------- -create_unicode_table() -> - " (FIELD nvarchar(50))". - -%------------------------------------------------------------------------- create_timestamp_table() -> " (FIELD DATETIME)". @@ -231,8 +227,8 @@ param_select() -> %------------------------------------------------------------------------- describe_integer() -> - {ok,[{"INT1",{sql_decimal,38,0}},{"INT2",{sql_decimal,38,0}}, - {"INT3",{sql_decimal,38,0}}]}. + {ok,[{"MYINT1",{sql_decimal,38,0}},{"MYINT2",{sql_decimal,38,0}}, + {"MYINT3",{sql_decimal,38,0}}]}. describe_string() -> {ok,[{"STR1",{sql_char,10}}, @@ -243,4 +239,4 @@ describe_string() -> describe_floating() -> {ok,[{"F",sql_double},{"R",sql_double},{"D",sql_double}]}. describe_dec_num() -> - {ok,[{"DEC",{sql_decimal,9,3}},{"NUM",{sql_decimal,9,2}}]}. + {ok,[{"MYDEC",{sql_decimal,9,3}},{"MYNUM",{sql_decimal,9,2}}]}. diff --git a/lib/odbc/test/postgres.erl b/lib/odbc/test/postgres.erl index 169ca26e43..26a2913d46 100644 --- a/lib/odbc/test/postgres.erl +++ b/lib/odbc/test/postgres.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -135,10 +135,6 @@ create_text_table() -> " (FIELD text)". %------------------------------------------------------------------------- -create_unicode_table() -> - " (FIELD text)". - -%------------------------------------------------------------------------- create_timestamp_table() -> " (FIELD TIMESTAMP)". @@ -275,9 +271,9 @@ param_select() -> %------------------------------------------------------------------------- describe_integer() -> - {ok,[{"int1",sql_smallint}, - {"int2",sql_integer}, - {"int3",sql_integer}]}. + {ok,[{"myint1",sql_smallint}, + {"myint2",sql_integer}, + {"myint3",sql_integer}]}. describe_string() -> {ok,[{"str1",{sql_char,10}}, @@ -288,7 +284,7 @@ describe_string() -> describe_floating() -> {ok,[{"f",sql_real},{"r",sql_real},{"d",{sql_float,15}}]}. describe_dec_num() -> - {ok,[{"dec",{sql_numeric,9,3}},{"num",{sql_numeric,9,2}}]}. + {ok,[{"mydec",{sql_numeric,9,3}},{"mynum",{sql_numeric,9,2}}]}. describe_timestamp() -> {ok, [{"field", sql_timestamp}]}. diff --git a/lib/odbc/test/sqlserver.erl b/lib/odbc/test/sqlserver.erl index e3fe30e0bc..59252d4276 100644 --- a/lib/odbc/test/sqlserver.erl +++ b/lib/odbc/test/sqlserver.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -123,10 +123,6 @@ create_text_table() -> " (FIELD text)". %------------------------------------------------------------------------- -create_unicode_table() -> - " (FIELD nvarchar(50))". - -%------------------------------------------------------------------------- create_timestamp_table() -> " (FIELD DATETIME)". @@ -279,8 +275,8 @@ param_select() -> %------------------------------------------------------------------------- describe_integer() -> - {ok,[{"int1", sql_smallint},{"int2", sql_integer}, - {"int3", sql_integer}]}. + {ok,[{"myint1", sql_smallint},{"myint2", sql_integer}, + {"myint3", sql_integer}]}. describe_string() -> {ok,[{"str1",{sql_char,10}}, @@ -292,7 +288,7 @@ describe_floating() -> {ok,[{"f", sql_real},{"r", sql_real}, {"d", {sql_float, 53}}]}. describe_dec_num() -> - {ok,[{"dec",{sql_decimal,9,3}},{"num",{sql_numeric,9,2}}]}. + {ok,[{"mydec",{sql_decimal,9,3}},{"mynum",{sql_numeric,9,2}}]}. describe_timestamp() -> {ok, [{"field", sql_timestamp}]}. diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk index 42a51be33e..120ed9ee3d 100644 --- a/lib/odbc/vsn.mk +++ b/lib/odbc/vsn.mk @@ -1 +1 @@ -ODBC_VSN = 2.10.10 +ODBC_VSN = 2.10.11 diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl index e49032ebe4..942e9928b1 100644 --- a/lib/parsetools/src/leex.erl +++ b/lib/parsetools/src/leex.erl @@ -35,7 +35,7 @@ -export([compile/3,file/1,file/2,format_error/1]). -import(lists, [member/2,reverse/1,sort/1,delete/2, - keysort/2,keydelete/3,keyfind/3, + keysort/2,keydelete/3, map/2,foldl/3,foreach/2,flatmap/2]). -import(string, [substr/2,substr/3,span/2]). -import(ordsets, [is_element/2,add_element/2,union/2]). @@ -58,7 +58,7 @@ gfile=[], % Graph file module, % Module name opts=[], % Options - posix=false, % POSIX regular expressions + % posix=false, % POSIX regular expressions errors=[], warnings=[] }). @@ -136,8 +136,8 @@ format_error({regexp,E})-> "unterminated " ++ Cs; {illegal_char,Cs} -> "illegal character " ++ Cs; - {posix_cc,What} -> - ["illegal POSIX character class ",io_lib:write_string(What)]; +%% {posix_cc,What} -> +%% ["illegal POSIX character class ",io_lib:write_string(What)]; {char_class,What} -> ["illegal character class ",io_lib:write_string(What)] end, @@ -314,6 +314,7 @@ report_warnings(St) -> end, sort(St#leex.warnings)) end, report_warnings, St#leex.opts). +-spec add_error(_, #leex{}) -> no_return(). add_error(E, St) -> add_error(St#leex.xfile, E, St). @@ -662,14 +663,14 @@ re_repeat1([$*|Cs], Sn, S, St) -> re_repeat1(Cs, Sn, {kclosure,S}, St); re_repeat1([$+|Cs], Sn, S, St) -> re_repeat1(Cs, Sn, {pclosure,S}, St); re_repeat1([$?|Cs], Sn, S, St) -> re_repeat1(Cs, Sn, {optional,S}, St); %% { only starts interval when ere is true, otherwise normal character. -re_repeat1([${|Cs0], Sn, S, #leex{posix=true}=St) -> % $} - case re_interval_range(Cs0) of - {Min,Max,[$}|Cs1]} when is_integer(Min), is_integer(Max), Min =< Max -> - re_repeat1(Cs1, Sn, {interval,S,Min,Max}, St); - {Min,Max,[$}|Cs1]} when is_integer(Min), is_atom(Max) -> - re_repeat1(Cs1, Sn, {interval,S,Min,Max}, St); - {_,_,Cs1} -> parse_error({interval_range,string_between([${|Cs0], Cs1)}) - end; +%% re_repeat1([${|Cs0], Sn, S, #leex{posix=true}=St) -> % $} +%% case re_interval_range(Cs0) of +%% {Min,Max,[$}|Cs1]} when is_integer(Min), is_integer(Max), Min =< Max -> +%% re_repeat1(Cs1, Sn, {interval,S,Min,Max}, St); +%% {Min,Max,[$}|Cs1]} when is_integer(Min), is_atom(Max) -> +%% re_repeat1(Cs1, Sn, {interval,S,Min,Max}, St); +%% {_,_,Cs1} -> parse_error({interval_range,string_between([${|Cs0], Cs1)}) +%% end; re_repeat1(Cs, Sn, S, _) -> {S,Sn,Cs}. %% re_single(Chars, SubNumber, State) -> {RegExp,SubNumber,Chars}. @@ -751,7 +752,7 @@ special_char($|, _) -> true; special_char($*, _) -> true; special_char($+, _) -> true; special_char($?, _) -> true; -special_char(${, #leex{posix=true}) -> true; % Only when POSIX set +%% special_char(${, #leex{posix=true}) -> true; % Only when POSIX set special_char($\\, _) -> true; special_char(_, _) -> false. @@ -762,12 +763,12 @@ re_char_class([$]|Cs], St) -> % Must special case this. re_char_class(Cs, [$]], St); re_char_class(Cs, St) -> re_char_class(Cs, [], St). -re_char_class("[:" ++ Cs0, Cc, #leex{posix=true}=St) -> - %% POSIX char class only. - case posix_cc(Cs0) of - {Pcl,":]" ++ Cs1} -> re_char_class(Cs1, [{posix,Pcl}|Cc], St); - {_,Cs1} -> parse_error({posix_cc,string_between(Cs0, Cs1)}) - end; +%% re_char_class("[:" ++ Cs0, Cc, #leex{posix=true}=St) -> +%% %% POSIX char class only. +%% case posix_cc(Cs0) of +%% {Pcl,":]" ++ Cs1} -> re_char_class(Cs1, [{posix,Pcl}|Cc], St); +%% {_,Cs1} -> parse_error({posix_cc,string_between(Cs0, Cs1)}) +%% end; re_char_class([C1|Cs0], Cc, St) when C1 =/= $] -> case re_char(C1, Cs0) of {Cf,[$-,C2|Cs1]} when C2 =/= $] -> @@ -784,19 +785,19 @@ re_char_class(Cs, Cc, _) -> {reverse(Cc),Cs}. % Preserve order %% posix_cc(String) -> {PosixClass,RestString}. %% Handle POSIX character classes. -posix_cc("alnum" ++ Cs) -> {alnum,Cs}; -posix_cc("alpha" ++ Cs) -> {alpha,Cs}; -posix_cc("blank" ++ Cs) -> {blank,Cs}; -posix_cc("cntrl" ++ Cs) -> {cntrl,Cs}; -posix_cc("digit" ++ Cs) -> {digit,Cs}; -posix_cc("graph" ++ Cs) -> {graph,Cs}; -posix_cc("lower" ++ Cs) -> {lower,Cs}; -posix_cc("print" ++ Cs) -> {print,Cs}; -posix_cc("punct" ++ Cs) -> {punct,Cs}; -posix_cc("space" ++ Cs) -> {space,Cs}; -posix_cc("upper" ++ Cs) -> {upper,Cs}; -posix_cc("xdigit" ++ Cs) -> {xdigit,Cs}; -posix_cc(Cs) -> parse_error({posix_cc,substr(Cs, 1, 5)}). +%% posix_cc("alnum" ++ Cs) -> {alnum,Cs}; +%% posix_cc("alpha" ++ Cs) -> {alpha,Cs}; +%% posix_cc("blank" ++ Cs) -> {blank,Cs}; +%% posix_cc("cntrl" ++ Cs) -> {cntrl,Cs}; +%% posix_cc("digit" ++ Cs) -> {digit,Cs}; +%% posix_cc("graph" ++ Cs) -> {graph,Cs}; +%% posix_cc("lower" ++ Cs) -> {lower,Cs}; +%% posix_cc("print" ++ Cs) -> {print,Cs}; +%% posix_cc("punct" ++ Cs) -> {punct,Cs}; +%% posix_cc("space" ++ Cs) -> {space,Cs}; +%% posix_cc("upper" ++ Cs) -> {upper,Cs}; +%% posix_cc("xdigit" ++ Cs) -> {xdigit,Cs}; +%% posix_cc(Cs) -> parse_error({posix_cc,substr(Cs, 1, 5)}). escape_char($n) -> $\n; % \n = LF escape_char($r) -> $\r; % \r = CR @@ -815,24 +816,24 @@ escape_char(C) -> C. % Pass it straight through %% Int, -> Int,any %% Int1,Int2 -> Int1,Int2 -re_interval_range(Cs0) -> - case re_number(Cs0) of - {none,Cs1} -> {none,none,Cs1}; - {N,[$,|Cs1]} -> - case re_number(Cs1) of - {none,Cs2} -> {N,any,Cs2}; - {M,Cs2} -> {N,M,Cs2} - end; - {N,Cs1} -> {N,none,Cs1} - end. +%% re_interval_range(Cs0) -> +%% case re_number(Cs0) of +%% {none,Cs1} -> {none,none,Cs1}; +%% {N,[$,|Cs1]} -> +%% case re_number(Cs1) of +%% {none,Cs2} -> {N,any,Cs2}; +%% {M,Cs2} -> {N,M,Cs2} +%% end; +%% {N,Cs1} -> {N,none,Cs1} +%% end. -re_number([C|Cs]) when C >= $0, C =< $9 -> - re_number(Cs, C - $0); -re_number(Cs) -> {none,Cs}. +%% re_number([C|Cs]) when C >= $0, C =< $9 -> +%% re_number(Cs, C - $0); +%% re_number(Cs) -> {none,Cs}. -re_number([C|Cs], Acc) when C >= $0, C =< $9 -> - re_number(Cs, 10*Acc + (C - $0)); -re_number(Cs, Acc) -> {Acc,Cs}. +%% re_number([C|Cs], Acc) when C >= $0, C =< $9 -> +%% re_number(Cs, 10*Acc + (C - $0)); +%% re_number(Cs, Acc) -> {Acc,Cs}. string_between(Cs1, Cs2) -> substr(Cs1, 1, length(Cs1)-length(Cs2)). diff --git a/lib/sasl/src/erlsrv.erl b/lib/sasl/src/erlsrv.erl index f9804c41dc..086dc7c651 100644 --- a/lib/sasl/src/erlsrv.erl +++ b/lib/sasl/src/erlsrv.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -75,14 +75,21 @@ write_all_data(Port,[H|T]) -> write_all_data(Port,T). read_all_data(Port) -> + lists:reverse(read_all_data(Port,[],[])). +read_all_data(Port,Line,Lines) -> receive + {Port, {data, {noeol,Data}}} -> + read_all_data(Port,Line++Data,Lines); {Port, {data, {eol,Data}}} -> - [ Data | read_all_data(Port)]; - _ -> + read_all_data(Port,[],[Line++Data|Lines]); + {Port,_Other} -> Port ! {self(), close}, receive {Port, closed} -> - [] + case Line of + [] -> Lines; + _ -> [Line|Lines] + end end end. @@ -208,7 +215,7 @@ store_service(EmulatorVersion,Service) -> false -> {error, no_servicename}; {value, {_,Name}} -> - {Action,Service1} = case get_service(Name) of + {Action,Service1} = case get_service(EmulatorVersion,Name) of {error, no_such_service} -> {"add",Service}; _ -> @@ -377,8 +384,14 @@ pick_argument(_,[],Acc) -> {Acc, ""}; pick_argument(normal,[$ |T],Acc) -> {Acc,T}; +pick_argument(normal,[$\\|T],Acc) -> + pick_argument(normal_escaped,T,[$\\|Acc]); pick_argument(normal,[$"|T],Acc) -> pick_argument(quoted,T,[$"|Acc]); +pick_argument(normal_escaped,[$"|T],Acc) -> + pick_argument(bquoted,T,[$"|Acc]); +pick_argument(normal_escaped,[A|T],Acc) -> + pick_argument(normal,T,[A|Acc]); pick_argument(quoted_escaped,[H|T],Acc) -> pick_argument(quoted,T,[H|Acc]); pick_argument(quoted,[$"|T],Acc) -> @@ -387,6 +400,14 @@ pick_argument(quoted,[$\\|T],Acc) -> pick_argument(quoted_escaped,T,[$\\|Acc]); pick_argument(quoted,[H|T],Acc) -> pick_argument(quoted,T,[H|Acc]); +pick_argument(bquoted_escaped,[$"|T],Acc) -> + pick_argument(normal,T,[$"|Acc]); +pick_argument(bquoted_escaped,[H|T],Acc) -> + pick_argument(bquoted,T,[H|Acc]); +pick_argument(bquoted,[$\\|T],Acc) -> + pick_argument(bquoted_escaped,T,[$\\|Acc]); +pick_argument(bquoted,[H|T],Acc) -> + pick_argument(bquoted,T,[H|Acc]); pick_argument(normal,[H|T],Acc) -> pick_argument(normal,T,[H|Acc]). diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl index b60aa847df..eb29787103 100644 --- a/lib/sasl/src/release_handler.erl +++ b/lib/sasl/src/release_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -890,6 +890,7 @@ do_check_install_release(RelDir, Vsn, Releases, Masters) -> end. do_install_release(#state{start_prg = StartPrg, + root = RootDir, rel_dir = RelDir, releases = Releases, masters = Masters, static_emulator = Static}, @@ -926,8 +927,8 @@ do_install_release(#state{start_prg = StartPrg, NReleases = set_status(Vsn, current, Releases), NReleases2 = set_status(Vsn,tmp_current,NReleases), write_releases(RelDir, NReleases2, Masters), - prepare_restart_new_emulator(StartPrg, RelDir, - Release, + prepare_restart_new_emulator(StartPrg, RootDir, + RelDir, Release, PermanentRelease, Masters), {restart_new_emulator, CurrentVsn, Descr}; @@ -997,7 +998,7 @@ do_make_services_permanent(PermanentVsn,Vsn, PermanentEVsn, EVsn) -> throw(Error4) end end. - + do_make_permanent(#state{releases = Releases, rel_dir = RelDir, unpurged = Unpurged, masters = Masters, @@ -1409,8 +1410,8 @@ prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn}, FutureServiceName = hd(string:tokens(atom_to_list(node()),"@")) ++ "_" ++ Vsn, CurrentService = case erlsrv:get_service(PermEVsn,CurrentServiceName) of - {error, Reason} -> - throw({error, Reason}); + {error, _} = Error1 -> + throw(Error1); CS -> CS end, @@ -1425,37 +1426,33 @@ prepare_restart_nt(#release{erts_vsn = EVsn, vsn = Vsn}, CurrentServiceName), case erlsrv:store_service(EVsn, FutureService) of - {error, Rison} -> - throw({error,Rison}); - _ -> + {error, _} = Error2 -> + throw(Error2); + _X -> erlsrv:disable_service(EVsn, FutureServiceName), ErlSrv = filename:nativename(erlsrv:erlsrv(EVsn)), - case heart:set_cmd(ErlSrv ++ " enable " ++ FutureServiceName ++ - " & " ++ ErlSrv ++ " start " ++ - FutureServiceName ++ - " & " ++ ErlSrv ++ " disable " ++ - FutureServiceName) of + StartDisabled = ErlSrv ++ " start_disabled " ++ FutureServiceName, + case heart:set_cmd(StartDisabled) of ok -> ok; - Error -> - throw({error, {'heart:set_cmd() error', Error}}) + Error3 -> + throw({error, {'heart:set_cmd() error', Error3}}) end end. - %%----------------------------------------------------------------- %% Set things up for restarting the new emulator. The actual %% restart is performed by calling init:reboot() higher up. %%----------------------------------------------------------------- -prepare_restart_new_emulator(StartPrg, RelDir, - Release, PRelease, - Masters) -> +prepare_restart_new_emulator(StartPrg, RootDir, RelDir, + Release, PRelease, Masters) -> #release{erts_vsn = EVsn, vsn = Vsn} = Release, Data = EVsn ++ " " ++ Vsn, DataFile = write_new_start_erl(Data, RelDir, Masters), %% Tell heart to use DataFile instead of start_erl.data case os:type() of {win32,nt} -> + write_ini_file(RootDir,EVsn,Masters), prepare_restart_nt(Release,PRelease,DataFile); {unix,_} -> StartP = check_start_prg(StartPrg, Masters), @@ -1832,50 +1829,10 @@ write_start(File, Data, false) -> end; write_start(File, Data, Masters) -> all_masters(Masters), - write_start_m(File, Data, Masters). + safe_write_file_m(File, Data, Masters). %%----------------------------------------------------------------- -%% Write the "start_erl.data" file at all master nodes. -%% 1. Save "start_erl.backup" at all nodes. -%% 2. Write the "start_erl.change" file at all nodes. -%% 3. Move "start_erl.change" to "start_erl.data". -%% 4. Remove "start_erl.backup" at all nodes. -%% -%% If one of the steps above fails, all steps is recovered from -%% (as long as possible), except for 4 which is allowed to fail. -%%----------------------------------------------------------------- -write_start_m(File, Data, Masters) -> - Dir = filename:dirname(File), - Backup = filename:join(Dir, "start_erl.backup"), - Change = filename:join(Dir, "start_erl.change"), - case at_all_masters(Masters, ?MODULE, do_copy_files, - [File, [Backup]]) of - ok -> - case at_all_masters(Masters, ?MODULE, do_write_file, - [Change, Data]) of - ok -> - case at_all_masters(Masters, file, rename, - [Change, File]) of - ok -> - remove_files(all, [Backup, Change], Masters), - ok; - {error, {Master, R}} -> - takewhile(Master, Masters, file, rename, - [Backup, File]), - remove_files(all, [Backup, Change], Masters), - throw({error, {Master, R, move_start_erl}}) - end; - {error, {Master, R}} -> - remove_files(all, [Backup, Change], Masters), - throw({error, {Master, R, write_start_erl}}) - end; - {error, {Master, R}} -> - remove_files(Master, [Backup], Masters), - throw({error, {Master, R, backup_start_erl}}) - end. - -%%----------------------------------------------------------------- %% Copy the "start.boot" and "sys.config" from SrcDir to DestDir at all %% master nodes. %% 1. Save DestDir/"start.backup" and DestDir/"sys.backup" at all nodes. @@ -1917,3 +1874,75 @@ set_static_files(SrcDir, DestDir, Masters) -> remove_files(Master, [BackupBoot, BackupConf], Masters), throw({error, {Master, R, backup_start_config}}) end. + +%%----------------------------------------------------------------- +%% Write erl.ini +%% Writes the erl.ini file used by erl.exe when (re)starting the erlang node. +%% At first installation, this is done by Install.exe, which means that if +%% the format of this file for some reason is changed, then Install.c must +%% also be updated (and probably some other c-files which read erl.ini) +%%----------------------------------------------------------------- +write_ini_file(RootDir,EVsn,Masters) -> + BinDir = filename:join([RootDir,"erts-"++EVsn,"bin"]), + Str0 = io_lib:format("[erlang]~n" + "Bindir=~s~n" + "Progname=erl~n" + "Rootdir=~s~n", + [filename:nativename(BinDir), + filename:nativename(RootDir)]), + Str = re:replace(Str0,"\\\\","\\\\\\\\",[{return,list},global]), + IniFile = filename:join(BinDir,"erl.ini"), + do_write_ini_file(IniFile,Str,Masters). + +do_write_ini_file(File,Data,false) -> + case do_write_file(File, Data) of + ok -> ok; + Error -> throw(Error) + end; +do_write_ini_file(File,Data,Masters) -> + all_masters(Masters), + safe_write_file_m(File, Data, Masters). + + +%%----------------------------------------------------------------- +%% Write the given file at all master nodes. +%% 1. Save <File>.backup at all nodes. +%% 2. Write <File>.change at all nodes. +%% 3. Move <File>.change to <File> +%% 4. Remove <File>.backup at all nodes. +%% +%% If one of the steps above fails, all steps are recovered from +%% (as long as possible), except for 4 which is allowed to fail. +%%----------------------------------------------------------------- +safe_write_file_m(File, Data, Masters) -> + Backup = File ++ ".backup", + Change = File ++ ".change", + case at_all_masters(Masters, ?MODULE, do_copy_files, + [File, [Backup]]) of + ok -> + case at_all_masters(Masters, ?MODULE, do_write_file, + [Change, Data]) of + ok -> + case at_all_masters(Masters, file, rename, + [Change, File]) of + ok -> + remove_files(all, [Backup, Change], Masters), + ok; + {error, {Master, R}} -> + takewhile(Master, Masters, file, rename, + [Backup, File]), + remove_files(all, [Backup, Change], Masters), + throw({error, {Master, R, rename, + filename:basename(Change), + filename:basename(File)}}) + end; + {error, {Master, R}} -> + remove_files(all, [Backup, Change], Masters), + throw({error, {Master, R, write, filename:basename(Change)}}) + end; + {error, {Master, R}} -> + remove_files(Master, [Backup], Masters), + throw({error, {Master, R, backup, + filename:basename(File), + filename:basename(Backup)}}) + end. diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile index ad08c8136b..0bdb79a06a 100644 --- a/lib/sasl/test/Makefile +++ b/lib/sasl/test/Makefile @@ -31,7 +31,8 @@ MODULES= \ systools_SUITE \ systools_rc_SUITE \ overload_SUITE \ - rb_SUITE + rb_SUITE \ + rh_test_lib ERL_FILES= $(MODULES:%=%.erl) diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl index a114c4b5c9..f5ceab0dc4 100644 --- a/lib/sasl/test/installer.erl +++ b/lib/sasl/test/installer.erl @@ -119,6 +119,7 @@ install_3(TestNode,PrivDir) -> ?print(["install_3 unpack_release P2A ok"]), ?check_release("P2A",unpacked,["a-1.1"]), {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ?print(["install_3 check_install_release P2A ok"]), ok = release_handler:make_permanent("P1I"), ?print(["install_3 make_permanent P1I ok"]), ?check_release("P1I",permanent,["a-1.1"]), @@ -268,23 +269,30 @@ client1_1(TestNode,PrivDir,MasterDir,ClientSname) -> erl_boot_server:start([IP]), ok = net_kernel:monitor_nodes(true), - Node = start_client(TestNode,ClientSname), + Node = start_client(TestNode,client1,ClientSname), trace_disallowed_calls(Node), %% Check env var for SASL on client node SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + ?print([{client1_1,sasl_env},SaslEnv]), {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), {_,[Master]} = lists:keyfind(masters,1,SaslEnv), {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), - Root = code:root_dir(), - true = (CliDir =:= filename:join([Root,"clients","type1",Node])), - true = (StartCli =:= filename:join([CliDir,"bin","start"])), + NodeStr = atom_to_list(Node), + [NodeStr,"type1","clients"|_] = lists:reverse(filename:split(CliDir)), true = (Master =:= node()), + case os:type() of + {unix,_} -> + true = (StartCli =:= filename:join([CliDir,"bin","start"])); + _ -> + ok + end, %% Unpack P1H on master {ok, "P1H"} = unpack_release(PrivDir,"rel1"), %% Unpack and install P1H on client + Root = code:root_dir(), P1HDir = filename:join([Root, "releases", "P1H"]), %% The AppDirs argument (last arg to set_unpacked) below is really @@ -339,6 +347,7 @@ client1_2(TestNode,PrivDir,Node) -> ?check_running_app_client(Node,a,"1.0"), ok = rpc:call(Node, release_handler, make_permanent, ["P1H"]), + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), check_disallowed_calls(), reboot(TestNode,Node), @@ -584,7 +593,7 @@ trace_disallowed_calls(Node) -> MasterProc = self(), rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]), rpc:call(Node,dbg,p,[all,call]), - rpc:call(Node,dbg,tp,[file,[]]). + rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]). check_disallowed_calls() -> receive @@ -594,13 +603,12 @@ check_disallowed_calls() -> ok end. -start_client(TestNode,Client) -> - {Start, Node} = do_start_client(Client,test_host()), - Cmd = lists:concat(["env NODENAME=",Client," ", - filename:join(code:root_dir(), Start)]), - ?print([{start_client,Client},Cmd]), - Res = os:cmd(Cmd), - ?print([{start_client,result},Res]), +start_client(TestNode,Client,Sname) -> + Node = list_to_atom(lists:concat([Sname,"@",test_host()])), + case os:type() of + {unix,_} -> start_client_unix(TestNode,Sname,Node); + {win32,_} -> start_client_win32(TestNode,Client,Sname) + end, receive {nodeup, Node} -> wait_started(TestNode,Node) @@ -609,10 +617,34 @@ start_client(TestNode,Client) -> ?fail({"can not start", Node}) end. -do_start_client(Client, Host) -> - Node = list_to_atom(lists:concat([Client,"@",Host])), +start_client_unix(TestNode,Sname,Node) -> Start = filename:join(["clients", "type1", Node, "bin", "start"]), - {Start, Node}. + Cmd = lists:concat(["env NODENAME=",Sname," ", + filename:join(code:root_dir(), Start)]), + ?print([{start_client,Sname},Cmd]), + Res = os:cmd(Cmd), + ?print([{start_client,result},Res]). + +start_client_win32(TestNode,Client,ClientSname) -> + Name = atom_to_list(ClientSname) ++ "_P1G", + RootDir = code:root_dir(), + ErtsBinDir = filename:join(RootDir,"erts-4.4/bin"), + + {ClientArgs,RelClientDir} = rh_test_lib:get_client_args(Client,ClientSname, + RootDir), + StartErlArgs = rh_test_lib:get_start_erl_args(RootDir,RelClientDir, + ClientArgs), + ServiceArgs = rh_test_lib:get_service_args(RootDir, RelClientDir, + ClientSname, StartErlArgs), + + ?print([{start_client,ClientSname},ServiceArgs]), + Erlsrv = filename:nativename(filename:join(ErtsBinDir,"erlsrv")), + rh_test_lib:erlsrv(Erlsrv,stop,Name), + rh_test_lib:erlsrv(Erlsrv,remove,Name), + ok = rh_test_lib:erlsrv(Erlsrv,add,Name,ServiceArgs), + ok = rh_test_lib:erlsrv(Erlsrv,start,Name), + ?print([{start_client,result},ok]), + ok. reboot(TestNode,Node) -> cover_client(TestNode,Node,stop_cover), @@ -628,7 +660,7 @@ check_reboot(TestNode,Node) -> receive {nodeup, Node} -> wait_started(TestNode,Node) after 30000 -> - ?fail({Node, "not rebooted",net_adm:ping(Node)}) + ?fail({Node, "not rebooted",net_adm:ping(Node)}) end after 30000 -> ?fail({Node, "not closing down",net_adm:ping(Node)}) @@ -678,22 +710,28 @@ client2(TestNode,PrivDir,ClientSname) -> release_handler:remove_release("P1H"), ok = net_kernel:monitor_nodes(true), - Node = start_client(TestNode,ClientSname), + Node = start_client(TestNode,client2,ClientSname), %% Check env var for SASL on client node - ?print([{sasl_env, Node}, rpc:call(Node, application, get_all_env, [sasl])]), SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + ?print([{client1_1,sasl_env},SaslEnv]), {_,CliDir} = lists:keyfind(client_directory,1,SaslEnv), {_,[Master,Master2]} = lists:keyfind(masters,1,SaslEnv), {_,StartCli} = lists:keyfind(start_prg,1,SaslEnv), - Root = code:root_dir(), - true = (CliDir =:= filename:join([Root,"clients","type1",Node])), - true = (StartCli =:= filename:join([CliDir,"bin","start"])), + NodeStr = atom_to_list(Node), + [NodeStr,"type1","clients"|_] = lists:reverse(filename:split(CliDir)), true = (Master =:= node()), true = (Master2 =:= list_to_atom("master2@"++TestHost)), + case os:type() of + {unix,_} -> + true = (StartCli =:= filename:join([CliDir,"bin","start"])); + _ -> + ok + end, {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + Root = code:root_dir(), {error,{bad_masters,[Master2]}} = rpc:call(Node, release_handler, set_unpacked, [filename:join([Root, "releases", "P1H", "rel1.rel"]),[]]), diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl index efa775f344..16267ba0d4 100644 --- a/lib/sasl/test/release_handler_SUITE.erl +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -51,7 +51,7 @@ unix_cases() -> [target_system] ++ RunErlCases ++ cases(). win32_cases() -> - cases(). + [{group,release} | cases()]. %% Cases that can be run on all platforms cases() -> @@ -148,6 +148,10 @@ init_per_group(release_gg, Config0) -> end_per_group(release, Config) -> Dog = ?t:timetrap(?default_timeout), stop_print_proc(), + case os:type() of + {win32,_} -> delete_all_services(); + _ -> ok + end, delete_release(Config), ?t:timetrap_cancel(Dog), Config; @@ -169,6 +173,10 @@ end_per_testcase(Case, Config) -> Dog=?config(watchdog, Config), test_server:timetrap_cancel(Dog), + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + %% DEBUG case ?config(tc_status,Config) of ok -> @@ -206,10 +214,6 @@ end_per_testcase(Case, Config) -> %% immediately restarted by heart and the test cases wait until %% the node is actually up and running -- see wait_nodes_up/2) file:delete("sasl_erl_crash.dump"), - - try apply(?MODULE,Case,[cleanup,Config]) - catch error:undef -> ok - end, ok. gg_node_snames(Config) -> @@ -224,7 +228,10 @@ gg_node_snames(Config) -> no_run_erl(Config) when is_list(Config) -> {comment, "No run_erl program"}. - +break(Config) -> + erlang:display(test_break), + ?t:break(priv_dir(Config)), + ok. %% Test upgrade and downgrade of erts upgrade(Conf) when is_list(Conf) -> @@ -323,7 +330,7 @@ client1(Conf) when is_list(Conf) -> %% Copy the P1G release to a directory for use in this testcase ok = copy_installed(Conf,p1g_install,[Master]), - ok = copy_client(Conf,Master,Client,"start_cli1"), + ok = copy_client(Conf,Master,Client,client1), %% start the master node [TestNode] = start_nodes(Conf,[Master],"client1"), @@ -348,7 +355,7 @@ client2(Conf) when is_list(Conf) -> %% Copy the P1G release to a directory for use in this testcase ok = copy_installed(Conf,p1g_install,[Master]), - ok = copy_client(Conf,Master,Client,"start_cli2"), + ok = copy_client(Conf,Master,Client,client2), %% start the master node [TestNode] = start_nodes(Conf,[Master],"client2"), @@ -983,19 +990,16 @@ stop_node(Node) -> ?t:stop_node(Node). -copy_client(Conf,Master,Sname,StartScript) -> +copy_client(Conf,Master,Sname,Client) -> io:format("copy_client(Conf)"), DataDir = ?config(data_dir, Conf), MasterDir = filename:join(priv_dir(Conf),Master), - {ok,Host} = inet:gethostname(), - {ok,IpTuple} = inet:getaddr(Host,inet), - IpAddr = inet_parse:ntoa(IpTuple), - - CliNode = node_name(Sname), + {ClientArgs,RelCliDir} = rh_test_lib:get_client_args(Client,Sname,MasterDir, + node_name(Master)), - Cli = filename:join([MasterDir, "clients", "type1", CliNode]), + Cli = filename:join([MasterDir, RelCliDir]), ok = filelib:ensure_dir(filename:join([Cli,"bin","."])), ok = filelib:ensure_dir(filename:join([Cli,"releases","."])), ok = filelib:ensure_dir(filename:join([Cli,"log","."])), @@ -1003,12 +1007,16 @@ copy_client(Conf,Master,Sname,StartScript) -> P1GOrig = filename:join([MasterDir, "releases", "P1G"]), ok = copy_tree(Conf,P1GOrig,filename:join(Cli,"releases")), - ok = subst_file(filename:join([DataDir, "clients", StartScript]), - filename:join([Cli,"bin","start"]), - [{"ROOT",MasterDir}, - {"MASTER",atom_to_list(Master)}, - {"IPADDR",IpAddr}], - [{chmod,8#0755}]), + case os:type() of + {unix,_} -> + ok = subst_file(filename:join([DataDir, "start_client"]), + filename:join([Cli,"bin","start"]), + [{"ROOT",MasterDir}, + {"CLIENTARGS",ClientArgs}], + [{chmod,8#0755}]); + _ -> + ok + end, StartErlData = filename:join([MasterDir, "releases", "start_erl.data"]), CliRelDir = filename:join([Cli, "releases"]), @@ -1030,21 +1038,32 @@ delete_release(Conf) -> {ok, Dirs} = file:list_dir(PrivDir), ?t:format("======== deleting ~p~n",[Dirs]), - ok = delete_release_os(Dirs), - ?t:format("======== remaining ~p~n",[file:list_dir(PrivDir)]), + ok = delete_release_os(Dirs--["save"]), + {ok,Remaining} = file:list_dir(PrivDir), + ?t:format("======== remaining ~p~n",[Remaining]), + + case Remaining of + [] -> + ok; + _ -> + delete_release_os(Remaining), + Remaining2 = file:list_dir(PrivDir), + ?t:format("======== remaining after second try ~p~n",[Remaining2]) + end, + ok = file:set_cwd(OrigWd), ok. delete_release_os(Dirs) -> case os:type() of - {unix, _} -> - delete_release_unix(Dirs); - {win32, _} -> - delete_release_win32(Dirs); - Os -> - test_server:fail({error, {not_yet_implemented_os, Os}}) - end. + {unix, _} -> + delete_release_unix(Dirs); + {win32, _} -> + delete_release_win32(Dirs); + Os -> + test_server:fail({error, {not_yet_implemented_os, Os}}) + end. delete_release_unix([]) -> @@ -1075,7 +1094,14 @@ delete_release_win32([]) -> delete_release_win32(["save"|Dirs]) -> delete_release_win32(Dirs); delete_release_win32([Dir|Dirs]) -> - Rm = string:concat("rmdir /s ", Dir), + Rm = + case filelib:is_dir(Dir) of + true -> + string:concat("rmdir /s /q ", Dir); + false -> + string:concat("del /q ", Dir) + end, + ?t:format("============== COMMAND ~p~n",[Rm]), [] = os:cmd(Rm), delete_release_win32(Dirs). @@ -1200,7 +1226,12 @@ subst_var([], Vars, Result, VarAcc) -> priv_dir(Conf) -> - filename:absname(?config(priv_dir, Conf)). % Get rid of trailing slash +%% filename:absname(?config(priv_dir, Conf)). % Get rid of trailing slash + %% Due to problem with long paths on windows => creating a new + %% priv_dir under data_dir + Dir = filename:absname(filename:join(?config(data_dir, Conf),priv_dir)), + filelib:ensure_dir(filename:join(Dir,"*")), + Dir. latest_version(Dir) -> List = filelib:wildcard(Dir ++ "*"), @@ -1256,12 +1287,28 @@ do_create_p1g(Conf,TargetDir) -> ErtsLatest = latest_version(filename:join(code:root_dir(),"erts")), ok = copy_tree(Conf, ErtsLatest, ErtsDir, TargetDir), ErtsBinDir = filename:join([TargetDir,ErtsDir,bin]), - copy_file(filename:join([ErtsBinDir, "epmd"]), BinDir, [preserve]), - copy_file(filename:join([ErtsBinDir, "run_erl"]), BinDir, [preserve]), - copy_file(filename:join([ErtsBinDir, "to_erl"]), BinDir, [preserve]), + + case os:type() of + {unix, _} -> + copy_file(filename:join([ErtsBinDir, "epmd"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), BinDir, [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), BinDir, [preserve]), + + %% Create the start_erl shell script + ok = subst_file(filename:join([ErtsBinDir,"start_erl.src"]), + filename:join([BinDir,"start_erl"]), + [{"EMU","beam"}], + [{chmod,8#0755}]); + {win32,_} -> + %% Add a batch file to use as HEART_COMMAND + ok = copy_file(filename:join(DataDir, "heart_restart.bat"), + ErtsBinDir,[preserve]) + end, copy_file(filename:join(DataDir, "../installer.beam"), filename:join([DataDir,lib,"installer-1.0",ebin])), + copy_file(filename:join(DataDir, "../rh_test_lib.beam"), + filename:join([DataDir,lib,"installer-1.0",ebin])), %% Create .rel, .script and .boot files RelName = "rel0", @@ -1272,7 +1319,7 @@ do_create_p1g(Conf,TargetDir) -> ok = filelib:ensure_dir(RelFile), LibPath = filename:join([DataDir,lib,"*",ebin]), - TarFile = create_basic_release(RelFile, RelVsn, {ErtsVsn,false}, + TarFile = create_basic_release(Conf, RelFile, RelVsn, {ErtsVsn,false}, LibPath, [], [], [], []), %% Extract tar file in target directory (i.e. same directory as erts etc.) @@ -1286,20 +1333,6 @@ do_create_p1g(Conf,TargetDir) -> %% Create RELEASES ok = release_handler:create_RELEASES(TargetDir,ReleasesDir,RelFile,[]), - %% Create start_erl - ok = subst_file(filename:join([ErtsBinDir,"start_erl.src"]), - filename:join([BinDir,"start_erl"]), - [{"EMU","beam"}], - [{chmod,8#0755}]), - - %% Create start script - %% Using a customized start script from DataDir where some options - %% (heart and nodename) are added compared to the start.src in the - %% erlang distribution. - ok = subst_file(filename:join(DataDir, "start"), - filename:join([BinDir, "start"]), - [{"ROOT",TargetDir}], - [preserve]), ok. %% Create version P1H - which is P1G + a-1.0 @@ -1336,12 +1369,12 @@ create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,{UpFromName,Descr}) UpFrom = [{filename:join([PrivDir,UpFromName,UpFromName]),Descr}], - create_basic_release(RelFile, RelVsn, Erts, LibPath, + create_basic_release(Conf, RelFile, RelVsn, Erts, LibPath, Apps, Config, UpFrom, []), ok. %% Create .rel, .script, .boot, sys.config and tar -create_basic_release(RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Config,UpFrom,DownTo) -> +create_basic_release(Conf, RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Config,UpFrom,DownTo) -> RelDir = filename:dirname(RelFile), RelFileName = filename:rootname(RelFile), @@ -1370,7 +1403,14 @@ create_basic_release(RelFile,RelVsn,{ErtsVsn,ErtsDir},LibPath,ExtraApps,Config,U _ -> [{erts,ErtsDir}] end]), - RelFileName ++ ".tar.gz". + TarFileName = RelFileName ++ ".tar.gz", + + case os:type() of + {win32,_} when ErtsDir=/=false -> modify_tar_win32(Conf, TarFileName); + _ -> ok + end, + + TarFileName. %% Create a .rel file create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps) -> @@ -1470,21 +1510,70 @@ permanent_p1h(Node) -> copy_installed(Conf,FromNode,ToNodes) -> PrivDir = priv_dir(Conf), DataDir = ?config(data_dir,Conf), + + %% Instead of using copy_tree on the complete node directory, I'm + %% splitting this in separate tar files per subdirectory so the + %% log directory can be completely skipped. The reason for this is + %% that the tar file might become faulty if the node is alive and + %% writing to the log while the tar is created. + FromDir = filename:join(PrivDir,FromNode), + {ok,FromDirNames} = file:list_dir(FromDir), + TempTarFiles = + [begin + TempTarFile = filename:join(PrivDir,"temp_" ++ FDN ++ ".tar"), + {ok,Tar} = erl_tar:open(TempTarFile,[write]), + ok = erl_tar:add(Tar,filename:join(FromDir,FDN),FDN,[]), + ok = erl_tar:close(Tar), + TempTarFile + end || FDN <- FromDirNames, FDN=/="log"], lists:foreach( fun(Node) -> - ok = copy_tree(Conf,filename:join(PrivDir,FromNode),Node,PrivDir), NodeDir = filename:join(PrivDir,Node), - ok = subst_file(filename:join(DataDir, "start"), - filename:join([NodeDir, "bin", "start"]), - [{"ROOT",NodeDir}]), - LogDir = filename:join(NodeDir,log), - {ok,Logs} = file:list_dir(LogDir), - lists:foreach(fun(Log) -> - file:delete(filename:join(LogDir,Log)) - end, - Logs) + ok = filelib:ensure_dir(filename:join([NodeDir,"log","*"])), + lists:foreach( + fun(TempTarFile) -> + ok = erl_tar:extract(TempTarFile,[{cwd,NodeDir}]) + end, TempTarFiles), + case os:type() of + {unix,_} -> + %% Create start script + %% Using a customized start script from DataDir + %% where some options (heart and nodename) are + %% added compared to the start.src in the erlang + %% distribution. + ok = subst_file(filename:join(DataDir, "start"), + filename:join([NodeDir, "bin", "start"]), + [{"ROOT",NodeDir}], + [preserve]); + {win32,_} -> + %% Write erl.ini + ErtsDirs = + filelib:wildcard(filename:join(NodeDir,"erts-*")), + lists:foreach( + fun(ErtsDir) -> + ok = subst_file( + filename:join(DataDir, "erl.ini.src"), + filename:join([ErtsDir, "bin", "erl.ini"]), + [{"ROOTDIR",NodeDir}, + {"BINDIR",filename:join(ErtsDir,"bin")}]) + end, + ErtsDirs), + + %% The service on windows runs as local + %% administrator (not otptest user), so we need + %% to chmod the release in order to allow the + %% executing node to install releases, write + %% logs etc. + chmod_release_win32(NodeDir) + end end, - ToNodes). + ToNodes), + + lists:foreach(fun(TempTarFile) -> file:delete(TempTarFile) end, TempTarFiles), + ok. + +chmod_release_win32(Dir) -> + os:cmd("echo y|cacls " ++ Dir ++ " /T /E /G Administrators:F"). start_nodes(Conf,Snames,Tag) -> PrivDir = priv_dir(Conf), @@ -1493,19 +1582,42 @@ start_nodes(Conf,Snames,Tag) -> fun(Sname) -> NodeDir = filename:join(PrivDir,Sname), Node = node_name(Sname), - - Script = filename:join([NodeDir,"bin","start"]), - Cmd = "env NODENAME="++atom_to_list(Sname) ++ " " ++ Script, - %% {ok,StartFile} = file:read_file(Cmd), - %% io:format("~s:\n~s~n~n",[Start,binary_to_list(StartFile)]), - Res = os:cmd(Cmd), - io:format("Start ~p: ~p~n=>\t~p~n", [Sname,Cmd,Res]), + + case os:type() of + {unix,_} -> + start_node_unix(Sname,NodeDir); + {win32,_} -> + start_node_win32(Sname,NodeDir) + end, Node end, Snames), wait_nodes_up(Nodes,Tag), Nodes. +start_node_unix(Sname,NodeDir) -> + Script = filename:join([NodeDir,"bin","start"]), + Cmd = "env NODENAME="++atom_to_list(Sname) ++ " " ++ Script, + %% {ok,StartFile} = file:read_file(Cmd), + %% io:format("~s:\n~s~n~n",[Start,binary_to_list(StartFile)]), + Res = os:cmd(Cmd), + io:format("Start ~p: ~p~n=>\t~p~n", [Sname,Cmd,Res]). + +start_node_win32(Sname,NodeDir) -> + Name = atom_to_list(Sname) ++ "_P1G", + ErtsBinDir = filename:join(NodeDir,"erts-4.4/bin"), + + StartErlArgs = rh_test_lib:get_start_erl_args(NodeDir), + ServiceArgs = rh_test_lib:get_service_args(NodeDir, Sname, StartErlArgs), + + Erlsrv = filename:nativename(filename:join(ErtsBinDir,"erlsrv")), + rh_test_lib:erlsrv(Erlsrv,stop,Name), + rh_test_lib:erlsrv(Erlsrv,remove,Name), + ok = rh_test_lib:erlsrv(Erlsrv,add,Name,ServiceArgs), + ok = rh_test_lib:erlsrv(Erlsrv,start,Name), + ok. + +%% Create a unique node name for each test case tc_sname(Config) -> tc_sname(Config,""). tc_sname(Config,Fix) when is_atom(Fix) -> @@ -1649,3 +1761,32 @@ create_fake_release(Dir,RelName,RelVsn,AppDirs) -> rpc_inst(Node,Func,Args) -> rpc:call(Node,installer,Func,[node()|Args]). + +delete_all_services() -> + ErlSrv = erlsrv:erlsrv(erlang:system_info(version)), + [_|Serviceinfo] = string:tokens(os:cmd(ErlSrv ++ " list"),"\n"), + Services = + [lists:takewhile(fun($\t) -> false; (_) -> true end,S) + || S <- Serviceinfo], + ?t:format("Services to remove: ~p~n",[Services]), + lists:foreach(fun(S) -> + rh_test_lib:erlsrv(ErlSrv,stop,S), + rh_test_lib:erlsrv(ErlSrv,remove,S) + end, + Services). + +modify_tar_win32(Conf, TarFileName) -> + DataDir = ?config(data_dir,Conf), + PrivDir = priv_dir(Conf), + TmpDir = filename:join(PrivDir,"tmp_modify_tar_win32"), + ok = erl_tar:extract(TarFileName,[{cwd,TmpDir},compressed]), + + ErtsBinDir = filelib:wildcard(filename:join([TmpDir,"erts-*","bin"])), + ok = copy_file(filename:join(DataDir, "heart_restart.bat"), + ErtsBinDir,[preserve]), + + {ok,Fs} = file:list_dir(TmpDir), + {ok,T} = erl_tar:open(TarFileName,[write,compressed]), + [ok = erl_tar:add(T,filename:join(TmpDir,F),F,[]) || F <- Fs], + ok = erl_tar:close(T), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 deleted file mode 100755 index ee3d8c97cf..0000000000 --- a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh -# -# This program invokes the erlang emulator by calling run_erl. -# It should only be used at an embedded target system. -# It should be modified to give the correct flags to erl (via start_erl), -# e.g -mode embedded -sname XXX -# -# Usage: start [Data] -# - -if [ "x${NODENAME}" = "x" ] -then - echo "ERROR: Variable \$NODENAME is not set!!" - exit 1 -fi - -TESTHOST=`hostname | sed 's/[.].*//'` -IPADDR=%IPADDR% - -ROOTDIR=%ROOT% -CLIENTDIR=$ROOTDIR/clients/type1/$NODENAME@$TESTHOST - -RELDIR=$CLIENTDIR/releases - -# Note that this scripts is modified an copied to $CLIENTDIR/bin/start -# in release_handler_SUITE:copy_client - therefore HEART_COMMAND is as follows: -HEART_COMMAND=$CLIENTDIR/bin/start -HW_WD_DISABLE=true -export HW_WD_DISABLE HEART_COMMAND - -START_ERL_DATA=${1:-$RELDIR/start_erl.data} - -if [ ! -d /tmp/$NODENAME@$TESTHOST ] -then - mkdir /tmp/$NODENAME@$TESTHOST -fi - -$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\" -loader inet -id $NODENAME -hosts $IPADDR" > $CLIENTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/erl.ini.src b/lib/sasl/test/release_handler_SUITE_data/erl.ini.src new file mode 100644 index 0000000000..b8791e75a5 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/erl.ini.src @@ -0,0 +1,4 @@ +[erlang] +Bindir=%BINDIR% +Progname=erl +Rootdir=%ROOTDIR% diff --git a/lib/sasl/test/release_handler_SUITE_data/heart_restart.bat b/lib/sasl/test/release_handler_SUITE_data/heart_restart.bat new file mode 100755 index 0000000000..ede1ad4ff3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/heart_restart.bat @@ -0,0 +1,3 @@ +@echo off +%ERLSRV_EXECUTABLE% stop %ERLSRV_SERVICE_NAME% +%ERLSRV_EXECUTABLE% start %ERLSRV_SERVICE_NAME%
\ No newline at end of file diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app index 6f77317f6a..e1391c0605 100644 --- a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app @@ -1,6 +1,6 @@ {application, installer, [{description, "Installer application"}, {vsn, "1.0"}, - {modules, [{installer, 1}]}, + {modules, [installer,rh_test_lib]}, {registered, []}, {applications, [kernel, stdlib, sasl]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 b/lib/sasl/test/release_handler_SUITE_data/start_client index 88912cf884..5ea94d6f7c 100755 --- a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 +++ b/lib/sasl/test/release_handler_SUITE_data/start_client @@ -34,4 +34,4 @@ then mkdir /tmp/$NODENAME@$TESTHOST fi -$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME -sasl start_prg \\\"$CLIENTDIR/bin/start\\\" masters \[\\'%MASTER%@$TESTHOST\\',\\'master2@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\"" > /dev/null 2>&1 & +$ROOTDIR/bin/run_erl /tmp/$NODENAME@$TESTHOST/ $CLIENTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME %CLIENTARGS%" > $CLIENTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/rh_test_lib.erl b/lib/sasl/test/rh_test_lib.erl new file mode 100644 index 0000000000..99a7f919a7 --- /dev/null +++ b/lib/sasl/test/rh_test_lib.erl @@ -0,0 +1,100 @@ +-module(rh_test_lib). + +-export([erlsrv/3, + erlsrv/4]). +-export([get_service_args/3, + get_service_args/4, + get_start_erl_args/1, + get_start_erl_args/3, + get_client_args/3, + get_client_args/4]). + + +erlsrv(Erlsrv,Action,Name) -> + erlsrv(Erlsrv,Action,Name,""). +erlsrv(Erlsrv,Action,Name,Rest) -> + Cmd = Erlsrv ++ " " ++ atom_to_list(Action) ++ " " ++ Name ++ " " ++ Rest, + io:format("erlsrv cmd: ~p~n",[Cmd]), + Port = open_port({spawn, Cmd}, [stream, {line, 100}, eof, in]), + Res = recv_prog_output(Port), + case Res of + [] -> + failed; + _Y -> + io:format("erlsrv res: ~p~n",[_Y]), + ok + end. + +recv_prog_output(Port) -> + receive + {Port, {data, {eol,Data}}} -> + %%io:format("Got data: ~s~n", [Data]), + [ Data, "\n" | recv_prog_output(Port)]; + {Port, {data, {noeol,Data}}} -> + %%io:format("Got data: ~s~n", [Data]), + [ Data | recv_prog_output(Port)]; + {Port, _Other} -> + %%io:format("Got ~p from port~n", [_Other]), + Port ! {self(), close}, + receive + {Port,closed} -> + [] + end + end. + +get_service_args(RootDir, Sname, StartErlArgs) -> + get_service_args(RootDir, "", Sname, StartErlArgs). +get_service_args(RootDir, RelClientDir, Sname, StartErlArgs) -> + LogDir = filename:nativename(filename:join([RootDir,RelClientDir,"log"])), + %% start_erl.exe will be found since it is in the same directory as erlsrv.exe + %% And heart_restart.bat will be found since the erts bin dir is + %% always in the path for the erlang virtual machine. + " -machine start_erl.exe -workdir " ++ LogDir ++ + " -debugtype new -sname " ++ atom_to_list(Sname) ++ + " -env HEART_COMMAND=heart_restart.bat -args \"" ++ StartErlArgs ++ "\"". + +get_start_erl_args(RootDir) -> + get_start_erl_args(RootDir,"",""). +get_start_erl_args(RootDir,RelClientDir,ExtraArgs) -> + Cookie = atom_to_list(erlang:get_cookie()), + RelDir = filename:join([RootDir,RelClientDir,"releases"]), + ExtraArgs ++ " -setcookie " ++ Cookie ++ + " -heart ++ -rootdir " ++ filename:nativename(RootDir) ++ + " -reldir " ++ filename:nativename(RelDir). + +%% Must be called on the master node +get_client_args(Client,Sname,RootDir) -> + get_client_args(Client,Sname,RootDir,node()). +get_client_args(Client,Sname,RootDir,Master) -> + {ok,Host} = inet:gethostname(), + Node = atom_to_list(Sname) ++ "@" ++ Host, + RelClientDir = filename:join(["clients","type1",Node]), + ClientDir = filename:join([RootDir,RelClientDir]), + StartPrg = filename:join([ClientDir,"bin","start"]), + {" -sasl start_prg \\\\\\\"" ++ StartPrg ++ "\\\\\\\" masters \[" ++ + single_quote() ++ atom_to_list(Master) ++ single_quote() ++ + get_client_extra_master(Client,Host) ++ + "\] client_directory \\\\\\\"" ++ ClientDir ++ "\\\\\\\"" ++ + get_client_loader_args(Client,Sname,Host), + RelClientDir}. + +get_client_loader_args(client1,Sname,Host) -> + {ok,IpTuple} = inet:getaddr(Host,inet), + IpAddr = inet_parse:ntoa(IpTuple), + " -loader inet -id " ++ + atom_to_list(Sname) ++ " -hosts " ++ IpAddr; +get_client_loader_args(_,_,_) -> + "". + +get_client_extra_master(client2,Host) -> + "," ++ single_quote() ++ "master2@" ++ Host ++ single_quote(); +get_client_extra_master(_,_) -> + "". + +single_quote() -> + case os:type() of + {win32,_} -> + "\'"; + _ -> + "\\'" + end. diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 0da6bbee5b..566068beaf 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -480,7 +480,6 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | </func> <func> - <name>getopts(Socket) -> </name> <name>getopts(Socket, OptionNames) -> {ok, [socketoption()]} | {error, Reason}</name> <fsummary>Get the value of the specified options.</fsummary> @@ -489,8 +488,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <v>OptionNames = [atom()]</v> </type> <desc> - <p>Get the value of the specified socket options, if no - options are specified all options are returned. + <p>Get the value of the specified socket options. </p> </desc> </func> diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index a5e8e7e5c2..a0aedbbbee 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -112,7 +112,7 @@ connect(Socket, SslOptions) when is_port(Socket) -> connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> EmulatedOptions = emulated_options(), {ok, InetValues} = inet:getopts(Socket, EmulatedOptions), - inet:setopts(Socket, internal_inet_values()), + ok = inet:setopts(Socket, internal_inet_values()), try handle_options(SslOptions0 ++ InetValues, client) of {ok, #config{cb=CbInfo, ssl=SslOptions, emulated=EmOpts}} -> case inet:peername(Socket) of @@ -238,7 +238,7 @@ ssl_accept(#sslsocket{} = Socket, Timeout) -> ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> EmulatedOptions = emulated_options(), {ok, InetValues} = inet:getopts(Socket, EmulatedOptions), - inet:setopts(Socket, internal_inet_values()), + ok = inet:setopts(Socket, internal_inet_values()), try handle_options(SslOptions ++ InetValues, server) of {ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} -> {ok, Port} = inet:port(Socket), @@ -406,25 +406,51 @@ cipher_suites(openssl) -> %% %% Description: Gets options %%-------------------------------------------------------------------- -getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptTags) when is_pid(Pid) -> - ssl_connection:get_opts(Pid, OptTags); -getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) -> - inet:getopts(ListenSocket, OptTags); -getopts(#sslsocket{} = Socket, Options) -> +getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) -> + ssl_connection:get_opts(Pid, OptionTags); +getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptionTags) when is_list(OptionTags) -> + try inet:getopts(ListenSocket, OptionTags) of + {ok, _} = Result -> + Result; + {error, InetError} -> + {error, {eoptions, {inet_options, OptionTags, InetError}}} + catch + _:_ -> + {error, {eoptions, {inet_options, OptionTags}}} + end; +getopts(#sslsocket{fd = new_ssl}, OptionTags) -> + {error, {eoptions, {inet_options, OptionTags}}}; +getopts(#sslsocket{} = Socket, OptionTags) -> ensure_old_ssl_started(), - ssl_broker:getopts(Socket, Options). + ssl_broker:getopts(Socket, OptionTags). %%-------------------------------------------------------------------- -spec setopts(#sslsocket{}, [proplists:property()]) -> ok | {error, reason()}. %% %% Description: Sets options %%-------------------------------------------------------------------- -setopts(#sslsocket{fd = new_ssl, pid = Pid}, Opts0) when is_pid(Pid) -> - Opts = proplists:expand([{binary, [{mode, binary}]}, - {list, [{mode, list}]}], Opts0), - ssl_connection:set_opts(Pid, Opts); -setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) -> - inet:setopts(ListenSocket, OptTags); +setopts(#sslsocket{fd = new_ssl, pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) -> + try proplists:expand([{binary, [{mode, binary}]}, + {list, [{mode, list}]}], Options0) of + Options -> + ssl_connection:set_opts(Pid, Options) + catch + _:_ -> + {error, {eoptions, {not_a_proplist, Options0}}} + end; + +setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, Options) when is_list(Options) -> + try inet:setopts(ListenSocket, Options) of + ok -> + ok; + {error, InetError} -> + {error, {eoptions, {inet_options, Options, InetError}}} + catch + _:Error -> + {error, {eoptions, {inet_options, Options, Error}}} + end; +setopts(#sslsocket{fd = new_ssl}, Options) -> + {error, {eoptions,{not_a_proplist, Options}}}; setopts(#sslsocket{} = Socket, Options) -> ensure_old_ssl_started(), ssl_broker:setopts(Socket, Options). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 8c0c2bfa5d..422ea6404b 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -30,9 +30,9 @@ -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([trusted_cert_and_path/2, - certificate_chain/2, - file_to_certificats/1, +-export([trusted_cert_and_path/3, + certificate_chain/3, + file_to_certificats/2, validate_extension/3, is_valid_extkey_usage/2, is_valid_key_usage/2, @@ -46,14 +46,14 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec trusted_cert_and_path([der_cert()], certdb_ref()) -> +-spec trusted_cert_and_path([der_cert()], db_handle(), certdb_ref()) -> {der_cert() | unknown_ca, [der_cert()]}. %% %% Description: Extracts the root cert (if not presents tries to %% look it up, if not found {bad_cert, unknown_ca} will be added verification %% errors. Returns {RootCert, Path, VerifyErrors} %%-------------------------------------------------------------------- -trusted_cert_and_path(CertChain, CertDbRef) -> +trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef) -> Path = [Cert | _] = lists:reverse(CertChain), OtpCert = public_key:pkix_decode_cert(Cert, otp), SignedAndIssuerID = @@ -66,7 +66,7 @@ trusted_cert_and_path(CertChain, CertDbRef) -> {ok, IssuerId} -> {other, IssuerId}; {error, issuer_not_found} -> - case find_issuer(OtpCert, no_candidate) of + case find_issuer(OtpCert, no_candidate, CertDbHandle) of {ok, IssuerId} -> {other, IssuerId}; Other -> @@ -82,7 +82,7 @@ trusted_cert_and_path(CertChain, CertDbRef) -> {self, _} when length(Path) == 1 -> {selfsigned_peer, Path}; {_ ,{SerialNr, Issuer}} -> - case ssl_manager:lookup_trusted_cert(CertDbRef, SerialNr, Issuer) of + case ssl_manager:lookup_trusted_cert(CertDbHandle, CertDbRef, SerialNr, Issuer) of {ok, {BinCert,_}} -> {BinCert, Path}; _ -> @@ -92,23 +92,23 @@ trusted_cert_and_path(CertChain, CertDbRef) -> end. %%-------------------------------------------------------------------- --spec certificate_chain(undefined | binary(), certdb_ref()) -> +-spec certificate_chain(undefined | binary(), db_handle(), certdb_ref()) -> {error, no_cert} | {ok, [der_cert()]}. %% %% Description: Return the certificate chain to send to peer. %%-------------------------------------------------------------------- -certificate_chain(undefined, _CertsDbRef) -> +certificate_chain(undefined, _, _) -> {error, no_cert}; -certificate_chain(OwnCert, CertsDbRef) -> +certificate_chain(OwnCert, CertDbHandle, CertsDbRef) -> ErlCert = public_key:pkix_decode_cert(OwnCert, otp), - certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]). + certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]). %%-------------------------------------------------------------------- --spec file_to_certificats(string()) -> [der_cert()]. +-spec file_to_certificats(string(), term()) -> [der_cert()]. %% %% Description: Return list of DER encoded certificates. %%-------------------------------------------------------------------- -file_to_certificats(File) -> - {ok, List} = ssl_manager:cache_pem_file(File), +file_to_certificats(File, DbHandle) -> + {ok, List} = ssl_manager:cache_pem_file(File, DbHandle), [Bin || {'Certificate', Bin, not_encrypted} <- List]. %%-------------------------------------------------------------------- -spec validate_extension(term(), #'Extension'{} | {bad_cert, atom()} | valid, @@ -180,7 +180,7 @@ signature_type(?'id-dsa-with-sha1') -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) -> +certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) -> IssuerAndSelfSigned = case public_key:pkix_is_self_signed(OtpCert) of true -> @@ -191,11 +191,11 @@ certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) -> case IssuerAndSelfSigned of {_, true = SelfSigned} -> - certificate_chain(CertsDbRef, Chain, ignore, ignore, SelfSigned); + certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned); {{error, issuer_not_found}, SelfSigned} -> - case find_issuer(OtpCert, no_candidate) of + case find_issuer(OtpCert, no_candidate, CertDbHandle) of {ok, {SerialNr, Issuer}} -> - certificate_chain(CertsDbRef, Chain, + certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned); _ -> %% Guess the the issuer must be the root @@ -205,19 +205,19 @@ certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) -> {ok, lists:reverse(Chain)} end; {{ok, {SerialNr, Issuer}}, SelfSigned} -> - certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, SelfSigned) + certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned) end. -certificate_chain(_CertsDbRef, Chain, _SerialNr, _Issuer, true) -> +certificate_chain(_,_, Chain, _SerialNr, _Issuer, true) -> {ok, lists:reverse(Chain)}; -certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> - case ssl_manager:lookup_trusted_cert(CertsDbRef, +certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> + case ssl_manager:lookup_trusted_cert(CertDbHandle, CertsDbRef, SerialNr, Issuer) of {ok, {IssuerCert, ErlCert}} -> ErlCert = public_key:pkix_decode_cert(IssuerCert, otp), certificate_chain(ErlCert, IssuerCert, - CertsDbRef, [IssuerCert | Chain]); + CertDbHandle, CertsDbRef, [IssuerCert | Chain]); _ -> %% The trusted cert may be obmitted from the chain as the %% counter part needs to have it anyway to be able to @@ -227,8 +227,8 @@ certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> {ok, lists:reverse(Chain)} end. -find_issuer(OtpCert, PrevCandidateKey) -> - case ssl_manager:issuer_candidate(PrevCandidateKey) of +find_issuer(OtpCert, PrevCandidateKey, CertDbHandle) -> + case ssl_manager:issuer_candidate(PrevCandidateKey, CertDbHandle) of no_more_candidates -> {error, issuer_not_found}; {Key, {_Cert, ErlCertCandidate}} -> @@ -236,7 +236,7 @@ find_issuer(OtpCert, PrevCandidateKey) -> true -> public_key:pkix_issuer_id(ErlCertCandidate, self); false -> - find_issuer(OtpCert, Key) + find_issuer(OtpCert, Key, CertDbHandle) end end. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index 3eceefa304..0560a02110 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -26,8 +26,8 @@ -include_lib("public_key/include/public_key.hrl"). -export([create/0, remove/1, add_trusted_certs/3, - remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1, - lookup_cached_certs/1, cache_pem_file/4, uncache_pem_file/2, lookup/2]). + remove_trusted_certs/2, lookup_trusted_cert/4, issuer_candidate/2, + lookup_cached_certs/2, cache_pem_file/4, uncache_pem_file/2, lookup/2]). -type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. @@ -36,19 +36,19 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec create() -> certdb_ref(). +-spec create() -> [db_handle()]. %% %% Description: Creates a new certificate db. -%% Note: lookup_trusted_cert/3 may be called from any process but only +%% Note: lookup_trusted_cert/4 may be called from any process but only %% the process that called create may call the other functions. %%-------------------------------------------------------------------- create() -> - [ets:new(certificate_db_name(), [named_table, set, protected]), - ets:new(ssl_file_to_ref, [named_table, set, protected]), + [ets:new(ssl_otp_certificate_db, [set, protected]), + ets:new(ssl_file_to_ref, [set, protected]), ets:new(ssl_pid_to_file, [bag, private])]. %%-------------------------------------------------------------------- --spec remove(certdb_ref()) -> term(). +-spec remove([db_handle()]) -> term(). %% %% Description: Removes database db %%-------------------------------------------------------------------- @@ -56,7 +56,7 @@ remove(Dbs) -> lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). %%-------------------------------------------------------------------- --spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> +-spec lookup_trusted_cert(db_handle(), certdb_ref(), serialnumber(), issuer()) -> undefined | {ok, {der_cert(), #'OTPCertificate'{}}}. %% @@ -64,19 +64,19 @@ remove(Dbs) -> %% <SerialNumber, Issuer>. Ref is used as it is specified %% for each connection which certificates are trusted. %%-------------------------------------------------------------------- -lookup_trusted_cert(Ref, SerialNumber, Issuer) -> - case lookup({Ref, SerialNumber, Issuer}, certificate_db_name()) of +lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> + case lookup({Ref, SerialNumber, Issuer}, DbHandle) of undefined -> undefined; [Certs] -> {ok, Certs} end. -lookup_cached_certs(File) -> - ets:lookup(certificate_db_name(), {file, File}). +lookup_cached_certs(DbHandle, File) -> + ets:lookup(DbHandle, {file, File}). %%-------------------------------------------------------------------- --spec add_trusted_certs(pid(), string() | {der, list()}, certdb_ref()) -> {ok, certdb_ref()}. +-spec add_trusted_certs(pid(), string() | {der, list()}, [db_handle()]) -> {ok, [db_handle()]}. %% %% Description: Adds the trusted certificates from file <File> to the %% runtime database. Returns Ref that should be handed to lookup_trusted_cert @@ -100,7 +100,7 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> insert(Pid, File, PidToFileDb), {ok, Ref}. %%-------------------------------------------------------------------- --spec cache_pem_file(pid(), string(), time(), certdb_ref()) -> term(). +-spec cache_pem_file(pid(), string(), time(), [db_handle()]) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- @@ -112,7 +112,7 @@ cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) -> {ok, Content}. %-------------------------------------------------------------------- --spec uncache_pem_file(string(), certdb_ref()) -> no_return(). +-spec uncache_pem_file(string(), [db_handle()]) -> no_return(). %% %% Description: If a cached file is no longer valid (changed on disk) %% we must terminate the connections using the old file content, and @@ -130,7 +130,7 @@ uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) -> %%-------------------------------------------------------------------- --spec remove_trusted_certs(pid(), certdb_ref()) -> term(). +-spec remove_trusted_certs(pid(), [db_handle()]) -> term(). %% %% Description: Removes trusted certs originating from @@ -161,7 +161,7 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> end. %%-------------------------------------------------------------------- --spec issuer_candidate(no_candidate | cert_key() | {file, term()}) -> +-spec issuer_candidate(no_candidate | cert_key() | {file, term()}, term()) -> {cert_key(),{der_cert(), #'OTPCertificate'{}}} | no_more_candidates. %% %% Description: If a certificat does not define its issuer through @@ -169,32 +169,30 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> %% try to find the issuer in the database over known %% certificates. %%-------------------------------------------------------------------- -issuer_candidate(no_candidate) -> - Db = certificate_db_name(), +issuer_candidate(no_candidate, Db) -> case ets:first(Db) of '$end_of_table' -> no_more_candidates; {file, _} = Key -> - issuer_candidate(Key); + issuer_candidate(Key, Db); Key -> [Cert] = lookup(Key, Db), {Key, Cert} end; -issuer_candidate(PrevCandidateKey) -> - Db = certificate_db_name(), +issuer_candidate(PrevCandidateKey, Db) -> case ets:next(Db, PrevCandidateKey) of '$end_of_table' -> no_more_candidates; {file, _} = Key -> - issuer_candidate(Key); + issuer_candidate(Key, Db); Key -> [Cert] = lookup(Key, Db), {Key, Cert} end. %%-------------------------------------------------------------------- --spec lookup(term(), term()) -> term() | undefined. +-spec lookup(term(), db_handle()) -> term() | undefined. %% %% Description: Looks up an element in a certificat <Db>. %%-------------------------------------------------------------------- @@ -212,9 +210,6 @@ lookup(Key, Db) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -certificate_db_name() -> - ssl_otp_certificate_db. - insert(Key, Data, Db) -> true = ets:insert(Db, {Key, Data}). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 2c452837f8..21b021afb0 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -70,6 +70,7 @@ %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) tls_handshake_hashes, % see above tls_cipher_texts, % list() received but not deciphered yet + cert_db, % session, % #session{} from ssl_handshake.hrl session_cache, % session_cache_cb, % @@ -305,12 +306,13 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, Hashes0 = ssl_handshake:init_hashes(), try ssl_init(SSLOpts0, Role) of - {ok, Ref, CacheRef, OwnCert, Key, DHParams} -> + {ok, Ref, CertDbHandle, CacheHandle, OwnCert, Key, DHParams} -> Session = State0#state.session, State = State0#state{tls_handshake_hashes = Hashes0, session = Session#session{own_certificate = OwnCert}, cert_db_ref = Ref, - session_cache = CacheRef, + cert_db = CertDbHandle, + session_cache = CacheHandle, private_key = Key, diffie_hellman_params = DHParams}, {ok, hello, State, get_timeout(State)} @@ -500,9 +502,10 @@ certify(#certificate{asn1_certificates = []}, certify(#certificate{} = Cert, #state{negotiated_version = Version, role = Role, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, ssl_options = Opts} = State) -> - case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth, + case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth, Opts#ssl_options.verify, Opts#ssl_options.verify_fun, Role) of {PeerCert, PublicKeyInfo} -> @@ -859,23 +862,23 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, #state{socket_options = Opts1, socket = Socket, user_data_buffer = Buffer} = State0) -> - Opts = set_socket_opts(Socket, Opts0, Opts1, []), + {Reply, Opts} = set_socket_opts(Socket, Opts0, Opts1, []), State1 = State0#state{socket_options = Opts}, if Opts#socket_options.active =:= false -> - {reply, ok, StateName, State1, get_timeout(State1)}; + {reply, Reply, StateName, State1, get_timeout(State1)}; Buffer =:= <<>>, Opts1#socket_options.active =:= false -> %% Need data, set active once {Record, State2} = next_record_if_active(State1), case next_state(StateName, Record, State2) of {next_state, StateName, State, Timeout} -> - {reply, ok, StateName, State, Timeout}; + {reply, Reply, StateName, State, Timeout}; {stop, Reason, State} -> {stop, Reason, State} end; Buffer =:= <<>> -> %% Active once already set - {reply, ok, StateName, State1, get_timeout(State1)}; + {reply, Reply, StateName, State1, get_timeout(State1)}; true -> case application_data(<<>>, State1) of Stop = {stop,_,_} -> @@ -883,7 +886,7 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, {Record, State2} -> case next_state(StateName, Record, State2) of {next_state, StateName, State, Timeout} -> - {reply, ok, StateName, State, Timeout}; + {reply, Reply, StateName, State, Timeout}; {stop, Reason, State} -> {stop, Reason, State} end @@ -1044,19 +1047,19 @@ start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo, end. ssl_init(SslOpts, Role) -> - {ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role), + {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = - init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, + init_private_key(CertDbHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, SslOpts#ssl_options.password, Role), - DHParams = init_diffie_hellman(SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), - {ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}. + DHParams = init_diffie_hellman(CertDbHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), + {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert, PrivateKey, DHParams}. init_certificates(#ssl_options{cacerts = CaCerts, cacertfile = CACertFile, certfile = CertFile, cert = Cert}, Role) -> - {ok, CertDbRef, CacheRef} = + {ok, CertDbRef, CertDbHandle, CacheHandle} = try Certs = case CaCerts of undefined -> @@ -1064,44 +1067,44 @@ init_certificates(#ssl_options{cacerts = CaCerts, _ -> {der, CaCerts} end, - {ok, _, _} = ssl_manager:connection_init(Certs, Role) + {ok, _, _, _} = ssl_manager:connection_init(Certs, Role) catch Error:Reason -> handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile, erlang:get_stacktrace()) end, - init_certificates(Cert, CertDbRef, CacheRef, CertFile, Role). + init_certificates(Cert, CertDbRef, CertDbHandle, CacheHandle, CertFile, Role). -init_certificates(undefined, CertDbRef, CacheRef, "", _) -> - {ok, CertDbRef, CacheRef, undefined}; +init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, "", _) -> + {ok, CertDbRef, CertDbHandle, CacheHandle, undefined}; -init_certificates(undefined, CertDbRef, CacheRef, CertFile, client) -> +init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, CertFile, client) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile), - {ok, CertDbRef, CacheRef, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), + {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} catch _Error:_Reason -> - {ok, CertDbRef, CacheRef, undefined} + {ok, CertDbRef, CertDbHandle, CacheHandle, undefined} end; -init_certificates(undefined, CertDbRef, CacheRef, CertFile, server) -> +init_certificates(undefined, CertDbRef, CertDbHandle, CacheRef, CertFile, server) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile), - {ok, CertDbRef, CacheRef, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), + {ok, CertDbRef, CertDbHandle, CacheRef, OwnCert} catch Error:Reason -> handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, erlang:get_stacktrace()) end; -init_certificates(Cert, CertDbRef, CacheRef, _, _) -> - {ok, CertDbRef, CacheRef, Cert}. +init_certificates(Cert, CertDbRef, CertDbHandle, CacheRef, _, _) -> + {ok, CertDbRef, CertDbHandle, CacheRef, Cert}. -init_private_key(undefined, "", _Password, _Client) -> +init_private_key(_, undefined, "", _Password, _Client) -> undefined; -init_private_key(undefined, KeyFile, Password, _) -> +init_private_key(DbHandle, undefined, KeyFile, Password, _) -> try - {ok, List} = ssl_manager:cache_pem_file(KeyFile), + {ok, List} = ssl_manager:cache_pem_file(KeyFile, DbHandle), [PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List, - PKey =:= 'RSAPrivateKey' orelse + PKey =:= 'RSAPrivateKey' orelse PKey =:= 'DSAPrivateKey'], public_key:pem_entry_decode(PemEntry, Password) catch @@ -1110,9 +1113,9 @@ init_private_key(undefined, KeyFile, Password, _) -> erlang:get_stacktrace()) end; -init_private_key({rsa, PrivateKey}, _, _,_) -> +init_private_key(_,{rsa, PrivateKey}, _, _,_) -> public_key:der_decode('RSAPrivateKey', PrivateKey); -init_private_key({dsa, PrivateKey},_,_,_) -> +init_private_key(_,{dsa, PrivateKey},_,_,_) -> public_key:der_decode('DSAPrivateKey', PrivateKey). -spec(handle_file_error(_,_,_,_,_,_) -> no_return()). @@ -1128,15 +1131,15 @@ file_error(Line, Error, Reason, File, Throw, Stack) -> error_logger:error_report(Report), throw(Throw). -init_diffie_hellman(Params, _,_) when is_binary(Params)-> +init_diffie_hellman(_,Params, _,_) when is_binary(Params)-> public_key:der_decode('DHParameter', Params); -init_diffie_hellman(_,_, client) -> +init_diffie_hellman(_,_,_, client) -> undefined; -init_diffie_hellman(_,undefined, _) -> +init_diffie_hellman(_,_,undefined, _) -> ?DEFAULT_DIFFIE_HELLMAN_PARAMS; -init_diffie_hellman(_, DHParamFile, server) -> +init_diffie_hellman(DbHandle,_, DHParamFile, server) -> try - {ok, List} = ssl_manager:cache_pem_file(DHParamFile), + {ok, List} = ssl_manager:cache_pem_file(DHParamFile,DbHandle), case [Entry || Entry = {'DHParameter', _ , _} <- List] of [Entry] -> public_key:pem_entry_decode(Entry); @@ -1180,11 +1183,12 @@ certify_client(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, transport_cb = Transport, negotiated_version = Version, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}, socket = Socket, tls_handshake_hashes = Hashes0} = State) -> - Certificate = ssl_handshake:certificate(OwnCert, CertDbRef, client), + Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), {BinCert, ConnectionStates1, Hashes1} = encode_handshake(Certificate, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinCert), @@ -1365,9 +1369,10 @@ certify_server(#state{transport_cb = Transport, negotiated_version = Version, connection_states = ConnectionStates, tls_handshake_hashes = Hashes, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}} = State) -> - case ssl_handshake:certificate(OwnCert, CertDbRef, server) of + case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of CertMsg = #certificate{} -> {BinCertMsg, NewConnectionStates, NewHashes} = encode_handshake(CertMsg, Version, ConnectionStates, Hashes), @@ -1454,12 +1459,13 @@ rsa_key_exchange(_, _) -> request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, tls_handshake_hashes = Hashes0, negotiated_version = Version, socket = Socket, transport_cb = Transport} = State) -> - Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbRef), + Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), @@ -2040,31 +2046,67 @@ get_socket_opts(Socket, [active | Tags], SockOpts, Acc) -> get_socket_opts(Socket, Tags, SockOpts, [{active, SockOpts#socket_options.active} | Acc]); get_socket_opts(Socket, [Tag | Tags], SockOpts, Acc) -> - case inet:getopts(Socket, [Tag]) of + try inet:getopts(Socket, [Tag]) of {ok, [Opt]} -> get_socket_opts(Socket, Tags, SockOpts, [Opt | Acc]); {error, Error} -> - {error, Error} - end. + {error, {eoptions, {inet_option, Tag, Error}}} + catch + %% So that inet behavior does not crash our process + _:Error -> {error, {eoptions, {inet_option, Tag, Error}}} + end; +get_socket_opts(_,Opts, _,_) -> + {error, {eoptions, {inet_option, Opts, function_clause}}}. set_socket_opts(_, [], SockOpts, []) -> - SockOpts; + {ok, SockOpts}; set_socket_opts(Socket, [], SockOpts, Other) -> %% Set non emulated options - inet:setopts(Socket, Other), - SockOpts; -set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) -> + try inet:setopts(Socket, Other) of + ok -> + {ok, SockOpts}; + {error, InetError} -> + {{error, {eoptions, {inet_options, Other, InetError}}}, SockOpts} + catch + _:Error -> + %% So that inet behavior does not crash our process + {{error, {eoptions, {inet_options, Other, Error}}}, SockOpts} + end; + +set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary -> set_socket_opts(Socket, Opts, SockOpts#socket_options{mode = Mode}, Other); -set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) -> +set_socket_opts(_, [{mode, _} = Opt| _], SockOpts, _) -> + {{error, {eoptions, {inet_opt, Opt}}}, SockOpts}; +set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw; + Packet == 0; + Packet == 1; + Packet == 2; + Packet == 4; + Packet == asn1; + Packet == cdr; + Packet == sunrm; + Packet == fcgi; + Packet == tpkt; + Packet == line; + Packet == http; + Packet == http_bin -> set_socket_opts(Socket, Opts, SockOpts#socket_options{packet = Packet}, Other); -set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) -> +set_socket_opts(_, [{packet, _} = Opt| _], SockOpts, _) -> + {{error, {eoptions, {inet_opt, Opt}}}, SockOpts}; +set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) -> set_socket_opts(Socket, Opts, SockOpts#socket_options{header = Header}, Other); -set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) -> +set_socket_opts(_, [{header, _} = Opt| _], SockOpts, _) -> + {{error,{eoptions, {inet_opt, Opt}}}, SockOpts}; +set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once; + Active == true; + Active == false -> set_socket_opts(Socket, Opts, SockOpts#socket_options{active = Active}, Other); +set_socket_opts(_, [{active, _} = Opt| _], SockOpts, _) -> + {{error, {eoptions, {inet_opt, Opt}} }, SockOpts}; set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) -> set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 1f4c44d115..4e74aec4ac 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -31,9 +31,9 @@ -include_lib("public_key/include/public_key.hrl"). -export([master_secret/4, client_hello/6, server_hello/4, hello/4, - hello_request/0, certify/6, certificate/3, + hello_request/0, certify/7, certificate/4, client_certificate_verify/5, certificate_verify/5, - certificate_request/2, key_exchange/2, server_key_exchange_hash/2, + certificate_request/3, key_exchange/2, server_key_exchange_hash/2, finished/4, verify_connection/5, get_tls_handshake/2, decode_client_key/3, server_hello_done/0, encode_handshake/2, init_hashes/0, update_hashes/2, @@ -106,7 +106,7 @@ hello_request() -> %%-------------------------------------------------------------------- -spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, - #connection_states{} | {port_num(), #session{}, cache_ref(), + #connection_states{} | {port_num(), #session{}, db_handle(), atom(), #connection_states{}, binary()}, boolean()) -> {tls_version(), session_id(), #connection_states{}}| {tls_version(), {resumed | new, #session{}}, @@ -173,13 +173,13 @@ hello(#client_hello{client_version = ClientVersion, random = Random, end. %%-------------------------------------------------------------------- --spec certify(#certificate{}, term(), integer() | nolimit, +-spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit, verify_peer | verify_none, {fun(), term}, client | server) -> {der_cert(), public_key_info()} | #alert{}. %% %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- -certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, +certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, MaxPathLen, _Verify, VerifyFunAndState, Role) -> [PeerCert | _] = ASN1Certs, @@ -208,7 +208,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, end, {TrustedErlCert, CertPath} = - ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef), + ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef), case public_key:pkix_path_validation(TrustedErlCert, CertPath, @@ -222,13 +222,13 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, end. %%-------------------------------------------------------------------- --spec certificate(der_cert(), term(), client | server) -> #certificate{} | #alert{}. +-spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}. %% %% Description: Creates a certificate message. %%-------------------------------------------------------------------- -certificate(OwnCert, CertDbRef, client) -> +certificate(OwnCert, CertDbHandle, CertDbRef, client) -> Chain = - case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of + case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, CertChain} -> CertChain; {error, _} -> @@ -239,8 +239,8 @@ certificate(OwnCert, CertDbRef, client) -> end, #certificate{asn1_certificates = Chain}; -certificate(OwnCert, CertDbRef, server) -> - case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of +certificate(OwnCert, CertDbHandle, CertDbRef, server) -> + case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, Chain} -> #certificate{asn1_certificates = Chain}; {error, _} -> @@ -302,17 +302,17 @@ certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams %%-------------------------------------------------------------------- --spec certificate_request(#connection_states{}, certdb_ref()) -> +-spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) -> #certificate_request{}. %% %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- -certificate_request(ConnectionStates, CertDbRef) -> +certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> #connection_state{security_parameters = #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates, read), Types = certificate_types(CipherSuite), - Authorities = certificate_authorities(CertDbRef), + Authorities = certificate_authorities(CertDbHandle, CertDbRef), #certificate_request{ certificate_types = Types, certificate_authorities = Authorities @@ -1071,8 +1071,8 @@ certificate_types({KeyExchange, _, _, _}) certificate_types(_) -> <<?BYTE(?RSA_SIGN)>>. -certificate_authorities(CertDbRef) -> - Authorities = certificate_authorities_from_db(CertDbRef), +certificate_authorities(CertDbHandle, CertDbRef) -> + Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef), Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> OTPSubj = TBSCert#'OTPTBSCertificate'.subject, DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp), @@ -1084,18 +1084,18 @@ certificate_authorities(CertDbRef) -> end, list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]). -certificate_authorities_from_db(CertDbRef) -> - certificate_authorities_from_db(CertDbRef, no_candidate, []). +certificate_authorities_from_db(CertDbHandle, CertDbRef) -> + certificate_authorities_from_db(CertDbHandle, CertDbRef, no_candidate, []). -certificate_authorities_from_db(CertDbRef, PrevKey, Acc) -> - case ssl_manager:issuer_candidate(PrevKey) of +certificate_authorities_from_db(CertDbHandle,CertDbRef, PrevKey, Acc) -> + case ssl_manager:issuer_candidate(PrevKey, CertDbHandle) of no_more_candidates -> lists:reverse(Acc); {{CertDbRef, _, _} = Key, Cert} -> - certificate_authorities_from_db(CertDbRef, Key, [Cert|Acc]); + certificate_authorities_from_db(CertDbHandle, CertDbRef, Key, [Cert|Acc]); {Key, _Cert} -> %% skip certs not from this ssl connection - certificate_authorities_from_db(CertDbRef, Key, Acc) + certificate_authorities_from_db(CertDbHandle, CertDbRef, Key, Acc) end. digitally_signed(Hash, #'RSAPrivateKey'{} = Key) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index c28daa271e..cc66246068 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -33,8 +33,8 @@ -type session_id() :: 0 | binary(). -type tls_version() :: {integer(), integer()}. -type tls_atom_version() :: sslv3 | tlsv1. --type cache_ref() :: term(). --type certdb_ref() :: term(). +-type certdb_ref() :: reference(). +-type db_handle() :: term(). -type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. -type der_cert() :: binary(). -type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 541ca1e918..b02815bfd8 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -28,8 +28,8 @@ %% Internal application API -export([start_link/1, - connection_init/2, cache_pem_file/1, - lookup_trusted_cert/3, issuer_candidate/1, client_session_id/4, + connection_init/2, cache_pem_file/2, + lookup_trusted_cert/4, issuer_candidate/2, client_session_id/4, server_session_id/4, register_session/2, register_session/3, invalidate_session/2, invalidate_session/3]). @@ -50,7 +50,8 @@ session_cache_cb, session_lifetime, certificate_db, - session_validation_timer + session_validation_timer, + last_delay_timer %% Keep for testing purposes }). -define('24H_in_msec', 8640000). @@ -72,45 +73,45 @@ start_link(Opts) -> %%-------------------------------------------------------------------- -spec connection_init(string()| {der, list()}, client | server) -> - {ok, reference(), cache_ref()}. + {ok, certdb_ref(), db_handle(), db_handle()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init(Trustedcerts, Role) -> call({connection_init, Trustedcerts, Role}). %%-------------------------------------------------------------------- --spec cache_pem_file(string()) -> {ok, term()} | {error, reason()}. +-spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}. %% %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- -cache_pem_file(File) -> +cache_pem_file(File, DbHandle) -> try file:read_file_info(File) of {ok, #file_info{mtime = LastWrite}} -> - cache_pem_file(File, LastWrite) + cache_pem_file(File, LastWrite, DbHandle) catch _:Reason -> {error, Reason} end. %%-------------------------------------------------------------------- --spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> +-spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) -> undefined | {ok, {der_cert(), #'OTPCertificate'{}}}. %% %% Description: Lookup the trusted cert with Key = {reference(), %% serialnumber(), issuer()}. %% -------------------------------------------------------------------- -lookup_trusted_cert(Ref, SerialNumber, Issuer) -> - ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). +lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> + ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- --spec issuer_candidate(cert_key() | no_candidate) -> +-spec issuer_candidate(cert_key() | no_candidate, term()) -> {cert_key(), {der_cert(), #'OTPCertificate'{}}} | no_more_candidates. %% %% Description: Return next issuer candidate. %%-------------------------------------------------------------------- -issuer_candidate(PrevCandidateKey) -> - ssl_certificate_db:issuer_candidate(PrevCandidateKey). +issuer_candidate(PrevCandidateKey, DbHandle) -> + ssl_certificate_db:issuer_candidate(PrevCandidateKey, DbHandle). %%-------------------------------------------------------------------- -spec client_session_id(host(), port_num(), #ssl_options{}, der_cert() | undefined) -> session_id(). @@ -192,19 +193,20 @@ init([Opts]) -> %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call({{connection_init, "", _Role}, Pid}, _From, - #state{session_cache = Cache} = State) -> + #state{certificate_db = [CertDb |_], + session_cache = Cache} = State) -> erlang:monitor(process, Pid), - Result = {ok, make_ref(), Cache}, + Result = {ok, make_ref(),CertDb, Cache}, {reply, Result, State}; handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, - #state{certificate_db = Db, + #state{certificate_db = [CertDb|_] =Db, session_cache = Cache} = State) -> erlang:monitor(process, Pid), Result = try {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db), - {ok, Ref, Cache} + {ok, Ref, CertDb, Cache} catch _:Reason -> {error, Reason} @@ -273,15 +275,17 @@ handle_cast({invalidate_session, Host, Port, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> CacheCb:update(Cache, {{Host, Port}, ID}, Session#session{is_resumable = false}), - timer:send_after(delay_time(), self(), {delayed_clean_session, {{Host, Port}, ID}}), - {noreply, State}; + TRef = + erlang:send_after(delay_time(), self(), {delayed_clean_session, {{Host, Port}, ID}}), + {noreply, State#state{last_delay_timer = TRef}}; handle_cast({invalidate_session, Port, #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> CacheCb:update(Cache, {Port, ID}, Session#session{is_resumable = false}), - timer:send_after(delay_time(), self(), {delayed_clean_session, {Port, ID}}), - {noreply, State}; + TRef = + erlang:send_after(delay_time(), self(), {delayed_clean_session, {Port, ID}}), + {noreply, State#state{last_delay_timer = TRef}}; handle_cast({recache_pem, File, LastWrite, Pid, From}, #state{certificate_db = [_, FileToRefDb, _]} = State0) -> @@ -408,8 +412,8 @@ session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. -cache_pem_file(File, LastWrite) -> - case ssl_certificate_db:lookup_cached_certs(File) of +cache_pem_file(File, LastWrite, DbHandle) -> + case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of [{_, {Mtime, Content}}] -> case LastWrite of Mtime -> diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index dc4b7a711c..85c9fcb61c 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -48,7 +48,7 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom(), +-spec id({host(), port_num(), #ssl_options{}}, db_handle(), atom(), undefined | binary()) -> binary(). %% %% Description: Should be called by the client side to get an id @@ -63,7 +63,7 @@ id(ClientInfo, Cache, CacheCb, OwnCert) -> end. %%-------------------------------------------------------------------- --spec id(port_num(), binary(), #ssl_options{}, cache_ref(), +-spec id(port_num(), binary(), #ssl_options{}, db_handle(), atom(), seconds(), binary()) -> binary(). %% %% Description: Should be called by the server side to get an id diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index ae7c67bb98..66610817be 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -31,15 +31,15 @@ -type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}. %%-------------------------------------------------------------------- --spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque) +-spec init(list()) -> db_handle(). %% Returns reference to the cache (opaque) %% %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- init(_) -> - ets:new(cache_name(), [named_table, set, protected]). + ets:new(cache_name(), [set, protected]). %%-------------------------------------------------------------------- --spec terminate(cache_ref()) -> any(). %% +-spec terminate(db_handle()) -> any(). %% %% Description: Handles cache table at termination of ssl manager. %%-------------------------------------------------------------------- @@ -47,7 +47,7 @@ terminate(Cache) -> ets:delete(Cache). %%-------------------------------------------------------------------- --spec lookup(cache_ref(), key()) -> #session{} | undefined. +-spec lookup(db_handle(), key()) -> #session{} | undefined. %% %% Description: Looks up a cach entry. Should be callable from any %% process. @@ -61,7 +61,7 @@ lookup(Cache, Key) -> end. %%-------------------------------------------------------------------- --spec update(cache_ref(), key(), #session{}) -> any(). +-spec update(db_handle(), key(), #session{}) -> any(). %% %% Description: Caches a new session or updates a already cached one. %% Will only be called from the ssl_manager process. @@ -70,7 +70,7 @@ update(Cache, Key, Session) -> ets:insert(Cache, {Key, Session}). %%-------------------------------------------------------------------- --spec delete(cache_ref(), key()) -> any(). +-spec delete(db_handle(), key()) -> any(). %% %% Description: Delets a cache entry. %% Will only be called from the ssl_manager process. @@ -79,7 +79,7 @@ delete(Cache, Key) -> ets:delete(Cache, Key). %%-------------------------------------------------------------------- --spec foldl(fun(), term(), cache_ref()) -> term(). +-spec foldl(fun(), term(), db_handle()) -> term(). %% %% Description: Calls Fun(Elem, AccIn) on successive elements of the %% cache, starting with AccIn == Acc0. Fun/2 must return a new @@ -91,7 +91,7 @@ foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). %%-------------------------------------------------------------------- --spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}]. +-spec select_session(db_handle(), {host(), port_num()} | port_num()) -> [#session{}]. %% %% Description: Selects a session that could be reused. Should be callable %% from any process. diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index ecb5228a8b..d9f4a76d80 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -25,7 +25,6 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). -include_lib("public_key/include/public_key.hrl"). -include("ssl_alert.hrl"). @@ -210,6 +209,10 @@ all() -> empty_protocol_versions, controlling_process, controller_dies, client_closes_socket, peercert, connect_dist, peername, sockname, socket_options, + invalid_inet_get_option, invalid_inet_get_option_not_list, + invalid_inet_get_option_improper_list, + invalid_inet_set_option, invalid_inet_set_option_not_list, + invalid_inet_set_option_improper_list, misc_ssl_options, versions, cipher_suites, upgrade, upgrade_with_timeout, tcp_connect, tcp_connect_big, ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown, @@ -810,8 +813,218 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]), ssl:setopts(Socket, [{nodelay, true}]), {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), + {ok, All} = ssl:getopts(Socket, []), + test_server:format("All opts ~p~n", [All]), ok. + + +%%-------------------------------------------------------------------- +invalid_inet_get_option(doc) -> + ["Test handling of invalid inet options in getopts"]; + +invalid_inet_get_option(suite) -> + []; + +invalid_inet_get_option(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +get_invalid_inet_option(Socket) -> + {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_get_option_not_list(doc) -> + ["Test handling of invalid type in getopts"]; + +invalid_inet_get_option_not_list(suite) -> + []; + +invalid_inet_get_option_not_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option_not_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +get_invalid_inet_option_not_list(Socket) -> + {error, {eoptions, {inet_options, some_invalid_atom_here}}} + = ssl:getopts(Socket, some_invalid_atom_here), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_get_option_improper_list(doc) -> + ["Test handling of invalid type in getopts"]; + +invalid_inet_get_option_improper_list(suite) -> + []; + +invalid_inet_get_option_improper_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option_improper_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +get_invalid_inet_option_improper_list(Socket) -> + {error, {eoptions, {inet_option, foo,_}}} = ssl:getopts(Socket, [packet | foo]), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_set_option(doc) -> + ["Test handling of invalid inet options in setopts"]; + +invalid_inet_set_option(suite) -> + []; + +invalid_inet_set_option(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +set_invalid_inet_option(Socket) -> + {error, {eoptions, {inet_opt, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]), + {error, {eoptions, {inet_opt, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]), + {error, {eoptions, {inet_opt, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]), + {error, {eoptions, {inet_opt, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]), + ok. +%%-------------------------------------------------------------------- +invalid_inet_set_option_not_list(doc) -> + ["Test handling of invalid type in setopts"]; + +invalid_inet_set_option_not_list(suite) -> + []; + +invalid_inet_set_option_not_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option_not_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +set_invalid_inet_option_not_list(Socket) -> + {error, {eoptions, {not_a_proplist, some_invalid_atom_here}}} + = ssl:setopts(Socket, some_invalid_atom_here), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_set_option_improper_list(doc) -> + ["Test handling of invalid tye in setopts"]; + +invalid_inet_set_option_improper_list(suite) -> + []; + +invalid_inet_set_option_improper_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option_improper_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +set_invalid_inet_option_improper_list(Socket) -> + {error, {eoptions, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} = + ssl:setopts(Socket, [{packet, 0} | {foo, 2}]), + ok. + %%-------------------------------------------------------------------- misc_ssl_options(doc) -> ["Test what happens when we give valid options"]; @@ -3338,6 +3551,7 @@ reuseaddr(Config) when is_list(Config) -> {options, [{active, false} | ClientOpts]}]), test_server:sleep(?SLEEP), ssl_test_lib:close(Server), + ssl_test_lib:close(Client), Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, @@ -3472,7 +3686,7 @@ session_cache_process_mnesia(suite) -> session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). -session_cache_process(Type,Config) when is_list(Config) -> +session_cache_process(_Type,Config) when is_list(Config) -> reuse_session(Config). init([Type]) -> diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 62d404092f..f80ac3c1a9 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("common_test/include/ct.hrl"). +-define(DELAY, 500). -define(SLEEP, 500). -define(TIMEOUT, 60000). -define(LONG_TIMEOUT, 600000). @@ -102,7 +103,7 @@ init_per_testcase(session_cleanup, Config0) -> ssl:stop(), application:load(ssl), application:set_env(ssl, session_lifetime, 5), - application:set_env(ssl, session_delay_cleanup_time, ?SLEEP), + application:set_env(ssl, session_delay_cleanup_time, ?DELAY), ssl:start(), [{watchdog, Dog} | Config]; @@ -178,7 +179,7 @@ end_per_group(_GroupName, Config) -> %%-------------------------------------------------------------------- session_cleanup(doc) -> ["Test that sessions are cleand up eventually, so that the session table " - "does grow and grow ..."]; + "does not grow and grow ..."]; session_cleanup(suite) -> []; session_cleanup(Config)when is_list(Config) -> @@ -207,24 +208,51 @@ session_cleanup(Config)when is_list(Config) -> %% Make sure session is registered test_server:sleep(?SLEEP), + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = state(Prop), + Cache = element(2, State), + SessionTimer = element(6, State), + Id = proplists:get_value(session_id, SessionInfo), - CSession = ssl_session_cache:lookup(ssl_otp_session_cache, {{Hostname, Port}, Id}), - SSession = ssl_session_cache:lookup(ssl_otp_session_cache, {Port, Id}), + CSession = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}), + SSession = ssl_session_cache:lookup(Cache, {Port, Id}), true = CSession =/= undefined, true = SSession =/= undefined, %% Make sure session has expired and been cleaned up - test_server:sleep(5000), %% Expire time - test_server:sleep(?SLEEP *4), %% Clean up delay + check_timer(SessionTimer), + test_server:sleep(?DELAY), %% Delay time + some extra time + + {status, _, _, StatusInfo1} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop1] = StatusInfo1, + State1 = state(Prop1), + DelayTimer = element(7, State1), - undefined = ssl_session_cache:lookup(ssl_otp_session_cache, {{Hostname, Port}, Id}), - undefined = ssl_session_cache:lookup(ssl_otp_session_cache, {Port, Id}), + check_timer(DelayTimer), + + undefined = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}), + undefined = ssl_session_cache:lookup(Cache, {Port, Id}), process_flag(trap_exit, false), ssl_test_lib:close(Server), ssl_test_lib:close(Client). +state([{data,[{"State", State}]} | _]) -> + State; +state([_ | Rest]) -> + state(Rest). + +check_timer(Timer) -> + case erlang:read_timer(Timer) of + false -> + {status, _, _, _} = sys:get_status(whereis(ssl_manager)), + ok; + Int -> + test_server:sleep(Int), + check_timer(Timer) + end. %%-------------------------------------------------------------------- session_cache_process_list(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -242,7 +270,6 @@ session_cache_process_mnesia(suite) -> session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). - %%-------------------------------------------------------------------- %%% Session cache API callbacks %%-------------------------------------------------------------------- diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml index 4876b37127..f8db48e00c 100644 --- a/lib/stdlib/doc/src/calendar.xml +++ b/lib/stdlib/doc/src/calendar.xml @@ -75,13 +75,13 @@ <datatypes> <datatype> - <name name="t_datetime"/> + <name name="datetime"/> </datatype> <datatype> - <name name="t_datetime1970"/> + <name name="datetime1970"/> </datatype> <datatype> - <name name="t_date"/> + <name name="date"/> </datatype> <datatype> <name name="year"/> @@ -100,7 +100,7 @@ <name name="day"/> </datatype> <datatype> - <name name="t_time"/> + <name name="time"/> </datatype> <datatype> <name name="hour"/> @@ -118,12 +118,7 @@ <name name="ldom"/> </datatype> <datatype> - <name name="t_now"/> - <desc><p>See <seealso marker="erts:erlang#now/0">erlang:now/0</seealso>.</p> - </desc> - </datatype> - <datatype> - <name name="t_yearweeknum"/> + <name name="yearweeknum"/> </datatype> <datatype> <name name="weeknum"/> diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index af9c75d546..667d758e29 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -439,7 +439,7 @@ ok</pre> <item> <p>Prints the argument with the <c>string</c> syntax. The argument is, if no Unicode translation modifier is present, an - <seealso marker="erts:erlang#iolist_definition">I/O list</seealso>, a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters + iolist(), a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is unicode:chardata(), meaning that binaries are in UTF-8. The characters are printed without quotes. The string is first truncated by the given precision and then padded and justified to the given field width. The default precision is the field width.</p> diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl index 8d1071209e..0320e0cd0e 100644 --- a/lib/stdlib/src/calendar.erl +++ b/lib/stdlib/src/calendar.erl @@ -63,7 +63,7 @@ %% Types %%---------------------------------------------------------------------- --export_type([t_now/0]). +-export_type([date/0, time/0, datetime/0, datetime1970/0]). -type year() :: non_neg_integer(). -type year1970() :: 1970..10000. % should probably be 1970.. @@ -76,15 +76,11 @@ -type ldom() :: 28 | 29 | 30 | 31. % last day of month -type weeknum() :: 1..53. --type t_now() :: {MegaSecs :: non_neg_integer(), - Secs :: non_neg_integer(), - MicroSecs :: non_neg_integer()}. - --type t_date() :: {year(),month(),day()}. --type t_time() :: {hour(),minute(),second()}. --type t_datetime() :: {t_date(),t_time()}. --type t_datetime1970() :: {{year1970(),month(),day()},t_time()}. --type t_yearweeknum() :: {year(),weeknum()}. +-type date() :: {year(),month(),day()}. +-type time() :: {hour(),minute(),second()}. +-type datetime() :: {date(),time()}. +-type datetime1970() :: {{year1970(),month(),day()},time()}. +-type yearweeknum() :: {year(),weeknum()}. %%---------------------------------------------------------------------- @@ -123,7 +119,7 @@ date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> end. -spec date_to_gregorian_days(Date) -> Days when - Date :: t_date(), + Date :: date(), Days :: non_neg_integer(). date_to_gregorian_days({Year, Month, Day}) -> date_to_gregorian_days(Year, Month, Day). @@ -135,7 +131,7 @@ date_to_gregorian_days({Year, Month, Day}) -> %% January 1st. %% -spec datetime_to_gregorian_seconds(DateTime) -> Seconds when - DateTime :: t_datetime(), + DateTime :: datetime(), Seconds :: non_neg_integer(). datetime_to_gregorian_seconds({Date, Time}) -> ?SECONDS_PER_DAY*date_to_gregorian_days(Date) + @@ -155,14 +151,14 @@ day_of_the_week(Year, Month, Day) -> (date_to_gregorian_days(Year, Month, Day) + 5) rem 7 + 1. -spec day_of_the_week(Date) -> daynum() when - Date:: t_date(). + Date:: date(). day_of_the_week({Year, Month, Day}) -> day_of_the_week(Year, Month, Day). %% gregorian_days_to_date(Days) = {Year, Month, Day} %% --spec gregorian_days_to_date(Days) -> t_date() when +-spec gregorian_days_to_date(Days) -> date() when Days :: non_neg_integer(). gregorian_days_to_date(Days) -> {Year, DayOfYear} = day_to_year(Days), @@ -172,7 +168,7 @@ gregorian_days_to_date(Days) -> %% gregorian_seconds_to_datetime(Secs) %% --spec gregorian_seconds_to_datetime(Seconds) -> t_datetime() when +-spec gregorian_seconds_to_datetime(Seconds) -> datetime() when Seconds :: non_neg_integer(). gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> Days = Secs div ?SECONDS_PER_DAY, @@ -198,7 +194,7 @@ is_leap_year1(_) -> false. %% %% Calculates the iso week number for the current date. %% --spec iso_week_number() -> t_yearweeknum(). +-spec iso_week_number() -> yearweeknum(). iso_week_number() -> {Date, _} = local_time(), iso_week_number(Date). @@ -207,8 +203,8 @@ iso_week_number() -> %% %% Calculates the iso week number for the given date. %% --spec iso_week_number(Date) -> t_yearweeknum() when - Date :: t_date(). +-spec iso_week_number(Date) -> yearweeknum() when + Date :: date(). iso_week_number({Year, Month, Day}) -> D = date_to_gregorian_days({Year, Month, Day}), W01_1_Year = gregorian_days_of_iso_w01_1(Year), @@ -260,7 +256,7 @@ last_day_of_the_month1(_, M) when is_integer(M), M > 0, M < 13 -> %% local_time() %% %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. --spec local_time() -> t_datetime(). +-spec local_time() -> datetime(). local_time() -> erlang:localtime(). @@ -268,20 +264,20 @@ local_time() -> %% local_time_to_universal_time(DateTime) %% -spec local_time_to_universal_time(DateTime1) -> DateTime2 when - DateTime1 :: t_datetime1970(), - DateTime2 :: t_datetime1970(). + DateTime1 :: datetime1970(), + DateTime2 :: datetime1970(). local_time_to_universal_time(DateTime) -> erlang:localtime_to_universaltime(DateTime). --spec local_time_to_universal_time(t_datetime1970(), +-spec local_time_to_universal_time(datetime1970(), 'true' | 'false' | 'undefined') -> - t_datetime1970(). + datetime1970(). local_time_to_universal_time(DateTime, IsDst) -> erlang:localtime_to_universaltime(DateTime, IsDst). -spec local_time_to_universal_time_dst(DateTime1) -> [DateTime] when - DateTime1 :: t_datetime1970(), - DateTime :: t_datetime1970(). + DateTime1 :: datetime1970(), + DateTime :: datetime1970(). local_time_to_universal_time_dst(DateTime) -> UtDst = erlang:localtime_to_universaltime(DateTime, true), Ut = erlang:localtime_to_universaltime(DateTime, false), @@ -309,14 +305,14 @@ local_time_to_universal_time_dst(DateTime) -> %% = MilliSec = integer() %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. %% --spec now_to_datetime(Now) -> t_datetime1970() when - Now :: t_now(). +-spec now_to_datetime(Now) -> datetime1970() when + Now :: erlang:timestamp(). now_to_datetime({MSec, Sec, _uSec}) -> Sec0 = MSec*1000000 + Sec + ?DAYS_FROM_0_TO_1970*?SECONDS_PER_DAY, gregorian_seconds_to_datetime(Sec0). --spec now_to_universal_time(Now) -> t_datetime1970() when - Now :: t_now(). +-spec now_to_universal_time(Now) -> datetime1970() when + Now :: erlang:timestamp(). now_to_universal_time(Now) -> now_to_datetime(Now). @@ -325,8 +321,8 @@ now_to_universal_time(Now) -> %% %% Args: Now = now() %% --spec now_to_local_time(Now) -> t_datetime1970() when - Now :: t_now(). +-spec now_to_local_time(Now) -> datetime1970() when + Now :: erlang:timestamp(). now_to_local_time({MSec, Sec, _uSec}) -> erlang:universaltime_to_localtime( now_to_universal_time({MSec, Sec, _uSec})). @@ -338,7 +334,7 @@ now_to_local_time({MSec, Sec, _uSec}) -> -spec seconds_to_daystime(Seconds) -> {Days, Time} when Seconds :: integer(), Days :: integer(), - Time :: t_time(). + Time :: time(). seconds_to_daystime(Secs) -> Days0 = Secs div ?SECONDS_PER_DAY, Secs0 = Secs rem ?SECONDS_PER_DAY, @@ -356,7 +352,7 @@ seconds_to_daystime(Secs) -> %% Wraps. %% -type secs_per_day() :: 0..?SECONDS_PER_DAY. --spec seconds_to_time(Seconds) -> t_time() when +-spec seconds_to_time(Seconds) -> time() when Seconds :: secs_per_day(). seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> Secs0 = Secs rem ?SECONDS_PER_DAY, @@ -375,10 +371,10 @@ seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> %% Year = Month = Day = Hour = Minute = Sec = integer() %% -spec time_difference(T1, T2) -> {Days, Time} when - T1 :: t_datetime(), - T2 :: t_datetime(), + T1 :: datetime(), + T2 :: datetime(), Days :: integer(), - Time :: t_time(). + Time :: time(). time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, {{Y2, Mo2, D2}, {H2, Mi2, S2}}) -> Secs = datetime_to_gregorian_seconds({{Y2, Mo2, D2}, {H2, Mi2, S2}}) - @@ -390,7 +386,7 @@ time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, %% time_to_seconds(Time) %% -spec time_to_seconds(Time) -> secs_per_day() when - Time :: t_time(). + Time :: time(). time_to_seconds({H, M, S}) when is_integer(H), is_integer(M), is_integer(S) -> H * ?SECONDS_PER_HOUR + M * ?SECONDS_PER_MINUTE + S. @@ -399,15 +395,15 @@ time_to_seconds({H, M, S}) when is_integer(H), is_integer(M), is_integer(S) -> %% universal_time() %% %% Returns: {date(), time()}, date() = {Y, M, D}, time() = {H, M, S}. --spec universal_time() -> t_datetime(). +-spec universal_time() -> datetime(). universal_time() -> erlang:universaltime(). %% universal_time_to_local_time(DateTime) %% --spec universal_time_to_local_time(DateTime) -> t_datetime() when - DateTime :: t_datetime1970(). +-spec universal_time_to_local_time(DateTime) -> datetime() when + DateTime :: datetime1970(). universal_time_to_local_time(DateTime) -> erlang:universaltime_to_localtime(DateTime). @@ -429,7 +425,7 @@ valid_date1(_, _, _) -> false. -spec valid_date(Date) -> boolean() when - Date :: t_date(). + Date :: date(). valid_date({Y, M, D}) -> valid_date(Y, M, D). diff --git a/lib/stdlib/src/erl_tar.erl b/lib/stdlib/src/erl_tar.erl index fd85c7aef5..306834e845 100644 --- a/lib/stdlib/src/erl_tar.erl +++ b/lib/stdlib/src/erl_tar.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -798,30 +798,10 @@ set_extracted_file_info(Name, #tar_header{mode=Mode, mtime=Mtime}) -> %% Makes all directories leading up to the file. -make_dirs(Name, Type) -> - make_dirs1(filename:split(Name), Type). - -make_dirs1([Dir, Next|Rest], Type) -> - case file:read_file_info(Dir) of - {ok, #file_info{type=directory}} -> - make_dirs1([filename:join(Dir, Next)|Rest], Type); - {ok, #file_info{}} -> - throw({error, enotdir}); - {error, _} -> - case file:make_dir(Dir) of - ok -> - make_dirs1([filename:join(Dir, Next)|Rest], Type); - {error, Reason} -> - throw({error, Reason}) - end - end; -make_dirs1([_], file) -> ok; -make_dirs1([Dir], dir) -> - file:make_dir(Dir); -make_dirs1([], _) -> - %% There must be something wrong here. The list was not supposed - %% to be empty. - throw({error, enoent}). +make_dirs(Name, file) -> + filelib:ensure_dir(Name); +make_dirs(Name, dir) -> + filelib:ensure_dir(filename:join(Name,"*")). %% Prints the message on if the verbose option is given (for reading). diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index 89fae05e4f..e3d6c905b6 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -200,8 +200,8 @@ tc(M, F, A) -> %% erlang:now() timestamps, T2-T1. %% -spec now_diff(T1, T2) -> Tdiff when - T1 :: calendar:t_now(), - T2 :: calendar:t_now(), + T1 :: erlang:timestamp(), + T2 :: erlang:timestamp(), Tdiff :: integer(). now_diff({A2, B2, C2}, {A1, B1, C1}) -> ((A2-A1)*1000000 + B2-B1)*1000000 + C2-C1. diff --git a/make/otp.mk.in b/make/otp.mk.in index a4e9dad46f..1ba74a3048 100644 --- a/make/otp.mk.in +++ b/make/otp.mk.in @@ -239,7 +239,9 @@ FOP = @FOP@ DOCGEN=$(ERL_TOP)/lib/erl_docgen +ifneq (,$(findstring $(origin SPECS_ESRC),$(DUBIOUS_ORIGINS))) SPECS_ESRC = ../../src +endif SPECS_EXTRACTOR=$(DOCGEN)/priv/bin/specs_gen.escript # Extract specifications and types from Erlang source files (-spec, -type) $(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl |