From f9ec3cbca0f05fd9640bbd5cd3e21942c4512d3d Mon Sep 17 00:00:00 2001 From: Filipe David Manana Date: Sun, 26 Sep 2010 11:58:45 +0100 Subject: httpc: allow streaming of PUT and POST request bodies This is a must when uploading large bodies that are to large to store in a string or binary. Besides a string or binary, a body can now be a function and an accumulator. Example: -module(httpc_post_stream_test). -compile(export_all). -define(LEN, 1024 * 1024). prepare_data() -> {ok, Fd} = file:open("test_data.dat", [binary, write]), ok = file:write(Fd, lists:duplicate(?LEN, "1")), ok = file:close(Fd). test() -> inets:start(), ok = prepare_data(), {ok, Fd1} = file:open("test_data.dat", [binary, read]), BodyFun = fun(Fd) -> case file:read(Fd, 512) of eof -> eof; {ok, Data} -> {ok, Data, Fd} end end, {ok, {{_,200,_}, _, _}} = httpc:request(post, {"http://localhost:8888", [{"content-length", integer_to_list(?LEN)}], "text/plain", {BodyFun, Fd1}}, [], []), ok = file:close(Fd1). --- lib/inets/doc/src/httpc.xml | 4 +- lib/inets/src/http_client/httpc.erl | 4 +- lib/inets/src/http_client/httpc_request.erl | 64 ++++++++++++++++++++++------- lib/inets/test/httpc_SUITE.erl | 40 ++++++++++++++++++ 4 files changed, 96 insertions(+), 16 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 9c8df28fec..df333074cd 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -89,7 +89,9 @@ headers() = [header()] header() = {field(), value()} field() = string() value() = string() -body() = string() | binary() +body() = string() | binary() | {fun(acc()) -> send_fun_result(), acc()} +send_fun_result() = eof | {ok, iolist(), acc()} +acc() = term() filename() = string() ]]> diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 851364001c..b82a9db4c9 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -126,7 +126,9 @@ request(Url, Profile) -> %% Header = {Field, Value} %% Field = string() %% Value = string() -%% Body = string() | binary() - HTLM-code +%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code +%% SendFunResult = eof | {ok, iolist(), NewSendAcc} +%% SendAcc = NewSendAcc = term() %% %% Description: Sends a HTTP-request. The function can be both %% syncronus and asynchronous in the later case the function will diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index d4df97ad40..5386d1eb4a 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -101,15 +101,41 @@ send(SendAddr, Socket, SocketType, end, Version = HttpOptions#http_options.version, - Message = [method(Method), " ", Uri, " ", - version(Version), ?CRLF, - headers(FinalHeaders, Version), ?CRLF, Body], + do_send_body(SocketType, Socket, Method, Uri, Version, FinalHeaders, Body). + +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, {DataFun, Acc}) + when is_function(DataFun, 1) -> + case do_send_body(SocketType, Socket, Method, Uri, Version, Headers, []) of + ok -> + data_fun_loop(SocketType, Socket, DataFun, Acc); + Error -> + Error + end; + +do_send_body(SocketType, Socket, Method, Uri, Version, Headers, Body) -> + Message = [method(Method), " ", Uri, " ", + version(Version), ?CRLF, + headers(Headers, Version), ?CRLF, Body], ?hcrd("send", [{message, Message}]), - http_transport:send(SocketType, Socket, lists:append(Message)). +data_fun_loop(SocketType, Socket, DataFun, Acc) -> + case DataFun(Acc) of + eof -> + ok; + {ok, Data, NewAcc} -> + DataBin = iolist_to_binary(Data), + ?hcrd("send", [{message, DataBin}]), + case http_transport:send(SocketType, Socket, DataBin) of + ok -> + data_fun_loop(SocketType, Socket, DataFun, NewAcc); + Error -> + Error + end + end. + %%------------------------------------------------------------------------- %% is_idempotent(Method) -> @@ -161,7 +187,6 @@ is_client_closing(Headers) -> %%%======================================================================== post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) when (Method =:= post) orelse (Method =:= put) -> - ContentLength = body_length(Body), NewBody = case Headers#http_request_h.expect of "100-continue" -> ""; @@ -170,14 +195,22 @@ post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) end, NewHeaders = case HeadersAsIs of - [] -> - Headers#http_request_h{'content-type' = - ContentType, - 'content-length' = - ContentLength}; - _ -> - HeadersAsIs - end, + [] -> + Headers#http_request_h{ + 'content-type' = ContentType, + 'content-length' = case body_length(Body) of + undefined -> + % on upload streaming the caller must give a + % value to the Content-Length header + % (or use chunked Transfer-Encoding) + Headers#http_request_h.'content-length'; + Len when is_list(Len) -> + Len + end + }; + _ -> + HeadersAsIs + end, {NewHeaders, NewBody}; @@ -190,7 +223,10 @@ body_length(Body) when is_binary(Body) -> integer_to_list(size(Body)); body_length(Body) when is_list(Body) -> - integer_to_list(length(Body)). + integer_to_list(length(Body)); + +body_length({DataFun, _Acc}) when is_function(DataFun, 1) -> + undefined. method(Method) -> http_util:to_upper(atom_to_list(Method)). diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 902e440c80..6947f75b3d 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -77,6 +77,7 @@ all(suite) -> http_head, http_get, http_post, + http_post_streaming, http_dummy_pipe, http_inets_pipe, http_trace, @@ -423,6 +424,45 @@ http_post(Config) when is_list(Config) -> {skip, "Failed to start local http-server"} end. +%%------------------------------------------------------------------------- +http_post_streaming(doc) -> + ["Test streaming http post request against local server. We" + " only care about the client side of the the post. The server" + " script will not actually use the post data."]; +http_post_streaming(suite) -> + []; +http_post_streaming(Config) when is_list(Config) -> + case ?config(local_server, Config) of + ok -> + Port = ?config(local_port, Config), + URL = case test_server:os_type() of + {win32, _} -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo.exe"; + _ -> + ?URL_START ++ integer_to_list(Port) ++ + "/cgi-bin/cgi_echo" + end, + %% Cgi-script expects the body length to be 100 + BodyFun = fun(0) -> + eof; + (LenLeft) -> + {ok, lists:duplicate(10, "1"), LenLeft - 10} + end, + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, {"content-length", "100"}], + "text/plain", {BodyFun, 100}}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(post, {URL, + [{"expect", "100-continue"}, {"content-length", "10"}], + "text/plain", {BodyFun, 10}}, [], []); + _ -> + {skip, "Failed to start local http-server"} + end. + %%------------------------------------------------------------------------- http_emulate_lower_versions(doc) -> ["Perform request as 0.9 and 1.0 clients."]; -- cgit v1.2.3 From 6951ed1075b8c36d5b6f51e5e5df7bd14602c1d8 Mon Sep 17 00:00:00 2001 From: Filipe David Manana Date: Tue, 5 Oct 2010 00:26:33 +0100 Subject: httpc: add option to do automatic chunked transfer-encoding This is specially useful when a client doesn't know in advance the length of the payload (so that it can't set the Content-Length header). Example: -module(httpc_post_stream_test). -compile(export_all). prepare_data() -> crypto:start(), {ok, Fd} = file:open("test_data.dat", [binary, write]), ok = file:write(Fd, lists:duplicate(crypto:rand_uniform(8182, 32768), "1")), ok = file:close(Fd). test() -> inets:start(), ok = prepare_data(), {ok, Fd1} = file:open("test_data.dat", [binary, read]), BodyFun = fun(Fd) -> case file:read(Fd, 512) of eof -> eof; {ok, Data} -> {ok, Data, Fd} end end, %% header 'Transfer-Encoding: chunked' is added by httpc {ok, {{_,200,_}, _, _}} = httpc:request(post, {"http://localhost:8888", [], "text/plain", {chunkify, BodyFun, Fd1}}, [], []), ok = file:close(Fd1). --- lib/inets/doc/src/httpc.xml | 4 +++- lib/inets/src/http_client/httpc.erl | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index df333074cd..8b04b4c7f3 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -89,7 +89,9 @@ headers() = [header()] header() = {field(), value()} field() = string() value() = string() -body() = string() | binary() | {fun(acc()) -> send_fun_result(), acc()} +body() = string() | binary() | + {fun(acc()) -> send_fun_result(), acc()} | + {chunkify, fun(acc()) -> send_fun_result(), acc()} send_fun_result() = eof | {ok, iolist(), acc()} acc() = term() filename() = string() diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index b82a9db4c9..8cf82df809 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -126,7 +126,8 @@ request(Url, Profile) -> %% Header = {Field, Value} %% Field = string() %% Value = string() -%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code +%% Body = string() | binary() | {fun(SendAcc) -> SendFunResult, SendAcc} | +%% {chunkify, fun(SendAcc) -> SendFunResult, SendAcc} - HTLM-code %% SendFunResult = eof | {ok, iolist(), NewSendAcc} %% SendAcc = NewSendAcc = term() %% @@ -428,11 +429,20 @@ service_info(Pid) -> handle_request(Method, Url, {Scheme, UserInfo, Host, Port, Path, Query}, - Headers, ContentType, Body, + Headers0, ContentType, Body0, HTTPOptions0, Options0, Profile) -> Started = http_util:timestamp(), - NewHeaders = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers], + NewHeaders0 = [{http_util:to_lower(Key), Val} || {Key, Val} <- Headers0], + + {NewHeaders, Body} = case Body0 of + {chunkify, BodyFun, Acc} -> + NewHeaders1 = lists:keystore("transfer-encoding", 1, + NewHeaders0, {"transfer-encoding", "chunked"}), + {NewHeaders1, {chunkify_fun(BodyFun), Acc}}; + _ -> + {NewHeaders0, Body0} + end, try begin @@ -456,7 +466,7 @@ handle_request(Method, Url, abs_uri = Url, userinfo = UserInfo, stream = Stream, - headers_as_is = headers_as_is(Headers, Options), + headers_as_is = headers_as_is(Headers0, Options), socket_opts = SocketOpts, started = Started}, case httpc_manager:request(Request, profile_name(Profile)) of @@ -473,6 +483,22 @@ handle_request(Method, Url, Error end. +chunkify_fun(BodyFun) -> + fun(eof_body_fun) -> + eof; + (Acc) -> + case BodyFun(Acc) of + eof -> + {ok, <<"0\r\n\r\n">>, eof_body_fun}; + {ok, Data, NewAcc} -> + Bin = iolist_to_binary(Data), + Chunk = [hex_size(Bin), "\r\n", Bin, "\r\n"], + {ok, iolist_to_binary(Chunk), NewAcc} + end + end. + +hex_size(Bin) -> + hd(io_lib:format("~.16B", [size(Bin)])). handle_answer(RequestId, false, _) -> {ok, RequestId}; -- cgit v1.2.3 From 4827d5db5fb2ca10772f70fbb6ad7f7f99285d96 Mon Sep 17 00:00:00 2001 From: Michael Santos Date: Mon, 21 Feb 2011 13:53:56 -0500 Subject: inets: prevent XSS in error pages Prevent user controlled input from being interpreted as HTML in error pages by encoding the reserved HTML characters. The reserved character set should be safe for displaying data within the body of HTML pages as outlined here: http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet Previously, weird URLs were URI encoded in the error page. This worked quite well but the URL would be displayed in the HTML in percent encoded format. There was also a check for URIs that were already escaped (by the browser) that would fail if the browser sent an URI containing a "%", e.g.: w3m "http://localhost:8080/foo?%" Also encode the HTTP method and version, since it's possible they may be manipulated: FOO /index.html HTTP/1.0 GET /index.html foo/1.0 Encode the static messages to prevent characters from being interpreted as HTML such as "heavy load (>~w processes)". --- lib/inets/src/http_lib/http_util.erl | 18 ++++++++++++++- lib/inets/src/http_server/httpd_util.erl | 38 ++++++++++++++++---------------- lib/inets/test/httpd_basic_SUITE.erl | 11 ++++----- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/lib/inets/src/http_lib/http_util.erl b/lib/inets/src/http_lib/http_util.erl index 4f1147176c..5e6b69ac5e 100644 --- a/lib/inets/src/http_lib/http_util.erl +++ b/lib/inets/src/http_lib/http_util.erl @@ -25,7 +25,8 @@ hexlist_to_integer/1, integer_to_hexlist/1, convert_month/1, is_hostname/1, - timestamp/0, timeout/2 + timestamp/0, timeout/2, + html_encode/1 ]). @@ -187,6 +188,13 @@ timeout(Timeout, Started) -> end. +html_encode(Chars) -> + Reserved = sets:from_list([$&, $<, $>, $\", $', $/]), + lists:append(lists:map(fun(Char) -> + char_to_html_entity(Char, Reserved) + end, Chars)). + + %%%======================================================================== %%% Internal functions %%%======================================================================== @@ -235,3 +243,11 @@ convert_to_ascii([Num | Reversed], Number) convert_to_ascii([Num | Reversed], Number) when (Num > 9) andalso (Num < 16) -> convert_to_ascii(Reversed, [Num + 55 | Number]). + +char_to_html_entity(Char, Reserved) -> + case sets:is_element(Char, Reserved) of + true -> + "&#" ++ integer_to_list(Char) ++ ";"; + false -> + [Char] + end. diff --git a/lib/inets/src/http_server/httpd_util.erl b/lib/inets/src/http_server/httpd_util.erl index 789f12652b..c1aff65d5e 100644 --- a/lib/inets/src/http_server/httpd_util.erl +++ b/lib/inets/src/http_server/httpd_util.erl @@ -181,7 +181,7 @@ message(304, _URL,_) -> message(400,none,_) -> "Your browser sent a query that this server could not understand."; message(400,Msg,_) -> - "Your browser sent a query that this server could not understand. "++ maybe_encode(Msg); + "Your browser sent a query that this server could not understand. "++ http_util:html_encode(Msg); message(401,none,_) -> "This server could not verify that you are authorized to access the document you @@ -190,48 +190,48 @@ credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required."; message(403,RequestURI,_) -> - "You don't have permission to access "++ maybe_encode(RequestURI) ++" on this server."; + "You don't have permission to access "++ http_util:html_encode(RequestURI) ++" on this server."; message(404,RequestURI,_) -> - "The requested URL " ++ maybe_encode(RequestURI) ++ " was not found on this server."; + "The requested URL " ++ http_util:html_encode(RequestURI) ++ " was not found on this server."; message(408, Timeout, _) -> Timeout; message(412,none,_) -> - "The requested preconditions where false"; + "The requested preconditions were false"; message(413, Reason,_) -> - "Entity: " ++ Reason; + "Entity: " ++ http_util:html_encode(Reason); message(414,ReasonPhrase,_) -> - "Message "++ ReasonPhrase ++"."; + "Message "++ http_util:html_encode(ReasonPhrase) ++"."; message(416,ReasonPhrase,_) -> - ReasonPhrase; + http_util:html_encode(ReasonPhrase); message(500,_,ConfigDB) -> ServerAdmin=lookup(ConfigDB,server_admin,"unknown@unknown"), "The server encountered an internal error or " "misconfiguration and was unable to complete " "your request.

Please contact the server administrator " - ++ ServerAdmin ++ ", and inform them of the time the error occurred " + ++ http_util:html_encode(ServerAdmin) ++ ", and inform them of the time the error occurred " "and anything you might have done that may have caused the error."; message(501,{Method, RequestURI, HTTPVersion}, _ConfigDB) -> if is_atom(Method) -> - atom_to_list(Method)++ - " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported."; + http_util:html_encode(atom_to_list(Method))++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported."; is_list(Method) -> - Method++ - " to "++ maybe_encode(RequestURI)++" ("++HTTPVersion++") not supported." + http_util:html_encode(Method)++ + " to "++ http_util:html_encode(RequestURI)++" ("++ http_util:html_encode(HTTPVersion)++") not supported." end; message(503, String, _ConfigDB) -> - "This service in unavailable due to: "++String. + "This service in unavailable due to: "++ http_util:html_encode(String). maybe_encode(URI) -> - case lists:member($%, URI) of - true -> - URI; - false -> - http_uri:encode(URI) - end. + Decoded = try http_uri:decode(URI) of + N -> N + catch + error:_ -> URI + end, + http_uri:encode(Decoded). %%convert_rfc_date(Date)->{{YYYY,MM,DD},{HH,MIN,SEC}} diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index dcea200a1a..1cb07c2f5b 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -151,12 +151,13 @@ escaped_url_in_error_body(Config) when is_list(Config) -> URL = ?URL_START ++ integer_to_list(Port) ++ Path, EscapedPath = http_uri:encode(Path), {ok, {404, Body}} = httpc:request(get, {URL, []}, - [{url_encode, true}], - [{version, "HTTP/1.0"}, {full_result, false}]), + [{url_encode, true}, {version, "HTTP/1.0"}], + [{full_result, false}]), EscapedPath = find_URL_path(string:tokens(Body, " ")), - {ok, {404, Body1}} = httpc:request(get, {URL, []}, [], - [{version, "HTTP/1.0"}, {full_result, false}]), - EscapedPath = find_URL_path(string:tokens(Body1, " ")), + {ok, {404, Body1}} = httpc:request(get, {URL, []}, + [{version, "HTTP/1.0"}], [{full_result, false}]), + HTMLEncodedPath = http_util:html_encode(Path), + HTMLEncodedPath = find_URL_path(string:tokens(Body1, " ")), inets:stop(httpd, Pid). find_URL_path([]) -> -- cgit v1.2.3 From 73f261d2f44a58fda92e3d6e035051c11c3e4521 Mon Sep 17 00:00:00 2001 From: Bernard Duggan Date: Wed, 23 Feb 2011 15:55:37 +1100 Subject: Modify mod_esi:deliver/2 to accept binary data This change allows for more efficient delivery of large amounts of data through the mod_esi interface when the handling process has that data in binary format. It avoids the need to convert to list and the extra memory involved in passing that list between processes. --- lib/inets/doc/src/mod_esi.xml | 6 ++++-- lib/inets/src/http_server/mod_esi.erl | 4 ++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index 3c473d3f94..e063088eb4 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -41,7 +41,7 @@ Sends Data back to client. SessionID = term() - Data = string() | io_list() + Data = string() | io_list() | binary() Reason = term() @@ -54,7 +54,9 @@

Note that if any HTTP-header fields should be added by the script they must be in the first call to deliver/2 and the - data in the call must be a string. Do not + data in the call must be a string. Calls after the headers + are complete may contain binary data to reduce copying + overhead. Do not assume anything about the data type of SessionID, the SessionID must be the value given as input to the esi call back function that you implemented.

diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 929185a67a..b85c479693 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -452,6 +452,10 @@ handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) -> ?hdrt("handle_body - send chunk", [{timeout, Timeout}, {size, Size}]), httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend), receive + {esi_data, Data} when is_binary(Data) -> + ?hdrt("handle_body - received binary data (esi)", []), + handle_body(Pid, ModData, Data, Timeout, Size + byte_size(Data), + IsDisableChunkedSend); {esi_data, Data} -> ?hdrt("handle_body - received data (esi)", []), handle_body(Pid, ModData, Data, Timeout, Size + length(Data), -- cgit v1.2.3 From 7f26c62a2f705cca26227411547c31288df289c5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 2 Mar 2011 16:55:02 +0100 Subject: Updated version, preliminary appup and release notes. --- lib/snmp/doc/src/notes.xml | 49 +++++++++++++++++++++++++++++++++++++++++ lib/snmp/src/app/snmp.appup.src | 8 +++++++ lib/snmp/vsn.mk | 2 +- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 2efeb8ae3f..8677d5ab61 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -32,6 +32,55 @@ notes.xml +
+ SNMP Development Toolkit 4.20 +

Version 4.20 supports code replacement in runtime from/to + version 4,19 and 4.18.

+ +
+ Improvements and new features + + + +

[agent] Added support for sending traps to IPv6 targets.

+

Own Id: OTP-9088

+

Aux Id: Seq 11790

+
+ +
+
+ +
+ Fixed Bugs and Malfunctions +

-

+ +
+ + +
+ Incompatibilities +

-

+
+ +
+
SNMP Development Toolkit 4.19

Version 4.19 supports code replacement in runtime from/to diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index de0e5d6e14..5687863522 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,6 +22,10 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.19", + [ + ] + }, {"4.18", [ {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -53,6 +57,10 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.19", + [ + ] + }, {"4.18", [ {load_module, snmp_misc, soft_purge, soft_purge, []}, diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index e70c97dcb8..29228fc59b 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -17,6 +17,6 @@ # # %CopyrightEnd% -SNMP_VSN = 4.19 +SNMP_VSN = 4.20 PRE_VSN = APP_VSN = "snmp-$(SNMP_VSN)$(PRE_VSN)" -- cgit v1.2.3 From 91355baf90ac2e708799233a98244af532ea82cc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 2 Mar 2011 17:07:36 +0100 Subject: Added new mib file (TRANSPORT-ADDRESS-MIB) and fixed makefile accordingly. --- lib/snmp/mibs/Makefile.in | 3 +- lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib | 417 ++++++++++++++++++++++++++++++++ 2 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib diff --git a/lib/snmp/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in index 7aefb0ea34..3af74eca75 100644 --- a/lib/snmp/mibs/Makefile.in +++ b/lib/snmp/mibs/Makefile.in @@ -61,7 +61,8 @@ MIBS_A = \ SNMP-USER-BASED-SM-MIB \ SNMP-VIEW-BASED-ACM-MIB \ SNMP-USM-AES-MIB \ - INET-ADDRESS-MIB + INET-ADDRESS-MIB \ + TRANSPORT-ADDRESS-MIB MIBS_B = OTP-SNMPEA-MIB diff --git a/lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib b/lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib new file mode 100644 index 0000000000..7d450fbc2a --- /dev/null +++ b/lib/snmp/mibs/TRANSPORT-ADDRESS-MIB.mib @@ -0,0 +1,417 @@ +TRANSPORT-ADDRESS-MIB DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-IDENTITY, mib-2 FROM SNMPv2-SMI + TEXTUAL-CONVENTION FROM SNMPv2-TC; + +transportAddressMIB MODULE-IDENTITY + LAST-UPDATED "200211010000Z" + ORGANIZATION + "IETF Operations and Management Area" + CONTACT-INFO + "Juergen Schoenwaelder (Editor) + TU Braunschweig + Bueltenweg 74/75 + 38106 Braunschweig, Germany + Phone: +49 531 391-3289 + EMail: schoenw@ibr.cs.tu-bs.de + + Send comments to ." + DESCRIPTION + "This MIB module provides commonly used transport + address definitions. + + Copyright (C) The Internet Society (2002). This version of + this MIB module is part of RFC 3419; see the RFC itself for + full legal notices." + + -- Revision log + + REVISION "200211010000Z" + DESCRIPTION + "Initial version, published as RFC 3419." + ::= { mib-2 100 } + + +transportDomains OBJECT IDENTIFIER ::= { transportAddressMIB 1 } + +transportDomainUdpIpv4 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4 for + global IPv4 addresses." + ::= { transportDomains 1 } + +transportDomainUdpIpv6 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6 for + global IPv6 addresses." + ::= { transportDomains 2 } + +transportDomainUdpIpv4z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4z for + scoped IPv4 addresses with a zone index." + ::= { transportDomains 3 } + +transportDomainUdpIpv6z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6z for + scoped IPv6 addresses with a zone index." + ::= { transportDomains 4 } + +transportDomainTcpIpv4 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4 for + global IPv4 addresses." + ::= { transportDomains 5 } + +transportDomainTcpIpv6 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6 for + global IPv6 addresses." + ::= { transportDomains 6 } + +transportDomainTcpIpv4z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4z for + scoped IPv4 addresses with a zone index." + ::= { transportDomains 7 } + +transportDomainTcpIpv6z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6z for + scoped IPv6 addresses with a zone index." + ::= { transportDomains 8 } + +transportDomainSctpIpv4 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4 for + global IPv4 addresses. This transport domain usually + represents the primary address on multihomed SCTP + endpoints." + ::= { transportDomains 9 } + +transportDomainSctpIpv6 OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6 for + global IPv6 addresses. This transport domain usually + represents the primary address on multihomed SCTP + endpoints." + ::= { transportDomains 10 } + +transportDomainSctpIpv4z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv4 transport domain. The corresponding + transport address is of type TransportAddressIPv4z for + scoped IPv4 addresses with a zone index. This transport + domain usually represents the primary address on + multihomed SCTP endpoints." + ::= { transportDomains 11 } + +transportDomainSctpIpv6z OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP over IPv6 transport domain. The corresponding + transport address is of type TransportAddressIPv6z for + scoped IPv6 addresses with a zone index. This transport + domain usually represents the primary address on + multihomed SCTP endpoints." + ::= { transportDomains 12 } + +transportDomainLocal OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The Posix Local IPC transport domain. The corresponding + transport address is of type TransportAddressLocal. + + The Posix Local IPC transport domain incorporates the + well-known UNIX domain sockets." + ::= { transportDomains 13 } + +transportDomainUdpDns OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The UDP transport domain using fully qualified domain + names. The corresponding transport address is of type + TransportAddressDns." + ::= { transportDomains 14 } + +transportDomainTcpDns OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The TCP transport domain using fully qualified domain + names. The corresponding transport address is of type + TransportAddressDns." + ::= { transportDomains 15 } + +transportDomainSctpDns OBJECT-IDENTITY + STATUS current + DESCRIPTION + "The SCTP transport domain using fully qualified domain + names. The corresponding transport address is of type + TransportAddressDns." + ::= { transportDomains 16 } + +TransportDomain ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A value that represents a transport domain. + + Some possible values, such as transportDomainUdpIpv4, are + defined in this module. Other possible values can be + defined in other MIB modules." + SYNTAX OBJECT IDENTIFIER + +-- +-- The enumerated values of the textual convention below should +-- be identical to the last sub-identifier of the OID registered +-- for the same domain. +-- + +TransportAddressType ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "A value that represents a transport domain. This is the + enumerated version of the transport domain registrations + in this MIB module. The enumerated values have the + following meaning: + + unknown(0) unknown transport address type + udpIpv4(1) transportDomainUdpIpv4 + udpIpv6(2) transportDomainUdpIpv6 + udpIpv4z(3) transportDomainUdpIpv4z + udpIpv6z(4) transportDomainUdpIpv6z + tcpIpv4(5) transportDomainTcpIpv4 + tcpIpv6(6) transportDomainTcpIpv6 + tcpIpv4z(7) transportDomainTcpIpv4z + tcpIpv6z(8) transportDomainTcpIpv6z + sctpIpv4(9) transportDomainSctpIpv4 + sctpIpv6(10) transportDomainSctpIpv6 + sctpIpv4z(11) transportDomainSctpIpv4z + sctpIpv6z(12) transportDomainSctpIpv6z + local(13) transportDomainLocal + udpDns(14) transportDomainUdpDns + tcpDns(15) transportDomainTcpDns + sctpDns(16) transportDomainSctpDns + + This textual convention can be used to represent transport + domains in situations where a syntax of TransportDomain is + unwieldy (for example, when used as an index). + + The usage of this textual convention implies that additional + transport domains can only be supported by updating this MIB + module. This extensibility restriction does not apply for the + TransportDomain textual convention which allows MIB authors + to define additional transport domains independently in + other MIB modules." + SYNTAX INTEGER { + unknown(0), + udpIpv4(1), + udpIpv6(2), + udpIpv4z(3), + udpIpv6z(4), + tcpIpv4(5), + tcpIpv6(6), + tcpIpv4z(7), + tcpIpv6z(8), + sctpIpv4(9), + sctpIpv6(10), + sctpIpv4z(11), + sctpIpv6z(12), + local(13), + udpDns(14), + tcpDns(15), + sctpDns(16) + } + +TransportAddress ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "Denotes a generic transport address. + + A TransportAddress value is always interpreted within the + context of a TransportAddressType or TransportDomain value. + Every usage of the TransportAddress textual convention MUST + specify the TransportAddressType or TransportDomain object + which provides the context. Furthermore, MIB authors SHOULD + define a separate TransportAddressType or TransportDomain + object for each TransportAddress object. It is suggested that + the TransportAddressType or TransportDomain is logically + registered before the object(s) which use the + TransportAddress textual convention if they appear in the + same logical row. + + The value of a TransportAddress object must always be + consistent with the value of the associated + TransportAddressType or TransportDomain object. Attempts + to set a TransportAddress object to a value which is + inconsistent with the associated TransportAddressType or + TransportDomain must fail with an inconsistentValue error. + + When this textual convention is used as a syntax of an + index object, there may be issues with the limit of 128 + sub-identifiers specified in SMIv2, STD 58. In this case, + the OBJECT-TYPE declaration MUST include a 'SIZE' clause + to limit the number of potential instance sub-identifiers." + SYNTAX OCTET STRING (SIZE (0..255)) + +TransportAddressIPv4 ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1d.1d.1d.1d:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv4 + address and a port number (as used for example by UDP, + TCP and SCTP): + + octets contents encoding + 1-4 IPv4 address network-byte order + 5-6 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (6)) + +TransportAddressIPv6 ::= TEXTUAL-CONVENTION + DISPLAY-HINT "0a[2x:2x:2x:2x:2x:2x:2x:2x]0a:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv6 + address and a port number (as used for example by UDP, + TCP and SCTP): + + octets contents encoding + 1-16 IPv6 address network-byte order + 17-18 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (18)) + +TransportAddressIPv4z ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1d.1d.1d.1d%4d:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv4 + address, a zone index and a port number (as used for + example by UDP, TCP and SCTP): + + octets contents encoding + 1-4 IPv4 address network-byte order + 5-8 zone index network-byte order + 9-10 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (10)) + +TransportAddressIPv6z ::= TEXTUAL-CONVENTION + DISPLAY-HINT "0a[2x:2x:2x:2x:2x:2x:2x:2x%4d]0a:2d" + STATUS current + DESCRIPTION + "Represents a transport address consisting of an IPv6 + address, a zone index and a port number (as used for + example by UDP, TCP and SCTP): + + octets contents encoding + 1-16 IPv6 address network-byte order + 17-20 zone index network-byte order + 21-22 port number network-byte order + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair." + SYNTAX OCTET STRING (SIZE (22)) + +TransportAddressLocal ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1a" + STATUS current + DESCRIPTION + "Represents a POSIX Local IPC transport address: + + octets contents encoding + all POSIX Local IPC address string + + The Posix Local IPC transport domain subsumes UNIX domain + sockets. + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair. + + When this textual convention is used as a syntax of an + index object, there may be issues with the limit of 128 + sub-identifiers specified in SMIv2, STD 58. In this case, + the OBJECT-TYPE declaration MUST include a 'SIZE' clause + to limit the number of potential instance sub-identifiers." + REFERENCE + "Protocol Independent Interfaces (IEEE POSIX 1003.1g)" + SYNTAX OCTET STRING (SIZE (1..255)) + +TransportAddressDns ::= TEXTUAL-CONVENTION + DISPLAY-HINT "1a" + STATUS current + DESCRIPTION + "Represents a DNS domain name followed by a colon ':' + (ASCII character 0x3A) and a port number in ASCII. + The name SHOULD be fully qualified whenever possible. + + Values of this textual convention are not directly useable as + transport-layer addressing information, and require runtime + resolution. As such, applications that write them must be + prepared for handling errors if such values are not + supported, or cannot be resolved (if resolution occurs at the + time of the management operation). + + The DESCRIPTION clause of TransportAddress objects that may + have TransportAddressDns values must fully describe how (and + when) such names are to be resolved to IP addresses and vice + versa. + + This textual convention SHOULD NOT be used directly in object + definitions since it restricts addresses to a specific format. + However, if it is used, it MAY be used either on its own or + in conjunction with TransportAddressType or TransportDomain + as a pair. + + When this textual convention is used as a syntax of an + index object, there may be issues with the limit of 128 + sub-identifiers specified in SMIv2, STD 58. In this case, + the OBJECT-TYPE declaration MUST include a 'SIZE' clause + to limit the number of potential instance sub-identifiers." + SYNTAX OCTET STRING (SIZE (1..255)) + +END -- cgit v1.2.3 From 92893c6df0f01c6c1d16f9d52a135f44393e9b01 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 2 Mar 2011 17:34:54 +0100 Subject: Added check functions for domain and tdomain. Also updated check functions for ip and taddress. --- lib/snmp/src/misc/snmp_conf.erl | 150 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 9 deletions(-) diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 4d2f5d8f92..5490d60ac2 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -37,7 +37,10 @@ check_timer/1, - check_ip/1, check_taddress/1, + check_domain/1, + check_tdomain/1, + check_ip/1, + check_taddress/1, check_taddress/2, check_packet_size/1, @@ -52,8 +55,10 @@ -define(SNMP_USE_V3, true). --include("snmp_types.hrl"). --include("SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/snmp_types.hrl"). +-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl"). +-include_lib("snmp/include/SNMPv2-TM.hrl"). -define(VMODULE,"CONF"). -include("snmp_verbosity.hrl"). @@ -338,15 +343,82 @@ check_sec_level(BadSecLevel) -> %% --------- -check_taddress(X) when is_list(X) andalso (length(X) =:= 6) -> +check_tdomain(TDomain) -> + SupportedTDomains = + [ + ?snmpUDPDomain, + ?transportDomainUdpIpv4, + ?transportDomainUdpIpv6 + ], + AllTDomains = + [ + ?transportDomainUdpIpv4, + ?transportDomainUdpIpv6, + ?transportDomainUdpIpv4z, + ?transportDomainUdpIpv6z, + ?transportDomainTcpIpv4, + ?transportDomainTcpIpv6, + ?transportDomainTcpIpv4z, + ?transportDomainTcpIpv6z, + ?transportDomainSctpIpv4, + ?transportDomainSctpIpv6, + ?transportDomainSctpIpv4z, + ?transportDomainSctpIpv6z, + ?transportDomainLocal, + ?transportDomainUdpDns, + ?transportDomainTcpDns, + ?transportDomainSctpDns + ], + case lists:member(TDomain, SupportedTDomains) of + true -> + ok; + false -> + case lists:member(TDomain, AllTDomains) of + true -> + error({unsupported_tdomain, TDomain}); + false -> + error({unknown_tdomain, TDomain}) + end + end. + + +%% --------- + +check_taddress(X) -> + check_taddress(snmpUDPDomain, X). + +check_taddress(snmpUDPDomain, X) -> + check_taddress(transportDomainUdpIpv4, X); + +check_taddress(transportDomainUdpIpv4, X) + when is_list(X) andalso (length(X) =:= 6) -> case (catch all_integer(X)) of true -> ok; false -> error({invalid_taddress, X}) end; -check_taddress(X) -> - error({invalid_taddress, X}). +check_taddress(transportDomainUdpIpv4, X) -> + error({invalid_taddress, X}); +check_taddress(?transportDomainUdpIpv4, X) -> + check_taddress(transportDomainUdpIpv4, X); + +check_taddress(transportDomainUdpIpv6, X) + when is_list(X) andalso (length(X) =:= 10) -> + case (catch all_integer(X)) of + true -> + ok; + false -> + error({invalid_taddress, X}) + end; +check_taddress(transportDomainUdpIpv6, X) -> + error({invalid_taddress, X}); +check_taddress(?transportDomainUdpIpv6, X) -> + check_taddress(transportDomainUdpIpv6, X); + +check_taddress(BadDomain, _X) -> + error({invalid_tdomain, BadDomain}). + %% --------- @@ -385,15 +457,75 @@ do_check_timer(WaitFor, Factor, Incr, Retry) -> %% --------- -check_ip(X) when is_list(X) andalso (length(X) =:= 4) -> +check_domain(Domain) -> + SupportedDomains = + [ + snmpUDPDomain, + transportDomainUdpIpv4, + transportDomainUdpIpv6 + ], + AllDomains = + [ + transportDomainUdpIpv4, + transportDomainUdpIpv6, + transportDomainUdpIpv4z, + transportDomainUdpIpv6z, + transportDomainTcpIpv4, + transportDomainTcpIpv6, + transportDomainTcpIpv4z, + transportDomainTcpIpv6z, + transportDomainSctpIpv4, + transportDomainSctpIpv6, + transportDomainSctpIpv4z, + transportDomainSctpIpv6z, + transportDomainLocal, + transportDomainUdpDns, + transportDomainTcpDns, + transportDomainSctpDns + ], + case lists:member(Domain, SupportedDomains) of + true -> + ok; + false -> + case lists:member(Domain, AllDomains) of + true -> + error({unsupported_domain, Domain}); + false -> + error({unknown_domain, Domain}) + end + end. + + +%% --------- + +check_ip(X) -> + check_ip(snmpUDPDomain, X). + +check_ip(snmpUDPDomain, X) -> + check_ip(transportDomainUdpIpv4, X); +check_ip(transportDomainUdpIpv4, X) when is_list(X) andalso (length(X) =:= 4) -> case (catch all_integer(X)) of true -> ok; false -> error({invalid_ip_address, X}) end; -check_ip(X) -> - error({invalid_ip_address, X}). +check_ip(transportDomainUdpIpv4, X) -> + error({invalid_ip_address, X}); + +check_ip(transportDomainUdpIpv6, X) when is_list(X) andalso (length(X) =:= 8) -> + case (catch all_integer(X)) of + true -> + ok; + false -> + error({invalid_ip_address, X}) + end; +check_ip(transportDomainUdpIpv6, X) -> + error({invalid_ip_address, X}); + +check_ip(BadDomain, _X) -> + error({invalid_domain, BadDomain}). + %% --------- -- cgit v1.2.3 From 8e312df7663c203ab936c9555cc0f61bbf83cb2f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 2 Mar 2011 17:47:37 +0100 Subject: Added taddress and tdomain create functions. --- lib/snmp/src/misc/snmp_conf.erl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 5490d60ac2..6fc261b07e 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -39,8 +39,10 @@ check_domain/1, check_tdomain/1, + mk_tdomain/1, check_ip/1, check_taddress/1, check_taddress/2, + mk_taddress/3, check_packet_size/1, @@ -382,6 +384,18 @@ check_tdomain(TDomain) -> end. +%% --------- + +mk_tdomain(snmpUDPDomain) -> + ?snmpUDPDomain; +mk_tdomain(transportDomainUdpIpv4) -> + ?transportDomainUdpIpv4; +mk_tdomain(transportDomainUdpIpv6) -> + ?transportDomainUdpIpv6; +mk_tdomain(BadDomain) -> + error({bad_domain, BadDomain}). + + %% --------- check_taddress(X) -> @@ -496,6 +510,20 @@ check_domain(Domain) -> end. +%% --------- + +%% The values of Ip and Port has both been checked at this +%% point, so we dont need to do that again. +mk_taddress(snmpUDPDomain, Ip, Port) -> + mk_taddress(transportDomainUdpIpv4, Ip, Port); +mk_taddress(transportDomainUdpIpv4, Ip, Port) -> + Ip ++ [Port div 256, Port rem 256]; +mk_taddress(transportDomainUdpIpv6, Ip, Port) -> + Ip ++ [Port div 256, Port rem 256]; +mk_taddress(BadDomain, _Ip, _Port) -> + error({bad_domain, BadDomain}). + + %% --------- check_ip(X) -> -- cgit v1.2.3 From d711ad48d29ea87398880e2655db72455334bf07 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 2 Mar 2011 18:21:15 +0100 Subject: Preliminary support for multiple domains (e.g. IPv6). --- lib/snmp/src/agent/snmp_target_mib.erl | 157 ++++++++++++++++++++++----------- 1 file changed, 106 insertions(+), 51 deletions(-) diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index 270a5fd5b6..c76e4bb173 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -30,12 +30,13 @@ add_params/5, delete_params/1]). -export([check_target_addr/1, check_target_params/1]). --include("snmp_types.hrl"). --include("snmp_tables.hrl"). --include("SNMP-TARGET-MIB.hrl"). --include("SNMPv2-TC.hrl"). --include("SNMPv2-TM.hrl"). --include("SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/snmp_types.hrl"). +-include_lib("snmp/include/snmp_tables.hrl"). +-include_lib("snmp/include/SNMP-TARGET-MIB.hrl"). +-include_lib("snmp/include/SNMPv2-TC.hrl"). +-include_lib("snmp/include/SNMPv2-TM.hrl"). +-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl"). -define(VMODULE,"TARGET-MIB"). -include("snmp_verbosity.hrl"). @@ -139,39 +140,53 @@ read_target_config_files(Dir) -> %% {Name, Ip, Udp, Timeout, RetryCount, TagList, Params, EngineId, %% TMask, MMS} %%----------------------------------------------------------------- -check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, - Params, EngineId, TMask, MMS}) -> +default_domain() -> + snmpUDPDomain. + +check_target_addr({Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, + Params, EngineId, TMask, MMS}) -> ?vtrace("check target address with:" - "~n Name: ~s" - "~n Ip: ~p" - "~n Udp: ~p" - "~n Timeout: ~p" - "~n RetryCount: ~p" - "~n TagList: ~p" - "~n Params: ~p" - "~n EngineId: ~p" - "~n TMask: ~p" - "~n MMS: ~p", - [Name,Ip,Udp,Timeout,RetryCount, - TagList,Params,EngineId,TMask,MMS]), + "~n Name: ~s" + "~n Domain: ~p" + "~n Ip: ~p" + "~n Udp: ~p" + "~n Timeout: ~p" + "~n RetryCount: ~p" + "~n TagList: ~p" + "~n Params: ~p" + "~n EngineId: ~p" + "~n TMask: ~p" + "~n MMS: ~p", + [Name, + Domain, Ip, Udp, + Timeout, RetryCount, + TagList, Params, EngineId, TMask, MMS]), snmp_conf:check_string(Name,{gt,0}), - snmp_conf:check_ip(Ip), + snmp_conf:check_domain(Domain), + snmp_conf:check_ip(Domain, Ip), snmp_conf:check_integer(Udp, {gt, 0}), snmp_conf:check_integer(Timeout, {gte, 0}), snmp_conf:check_integer(RetryCount, {gte,0}), snmp_conf:check_string(TagList), snmp_conf:check_string(Params), check_engine_id(EngineId), - TAddr = Ip ++ [Udp div 256, Udp rem 256], - check_mask(TMask, TAddr), + TAddress = snmp_conf:mk_taddress(Domain, Ip, Udp), + TDomain = snmp_conf:mk_tdomain(Domain), + check_mask(TDomain, TMask, TAddress), snmp_conf:check_packet_size(MMS), ?vtrace("check target address done",[]), - - Addr = {Name, ?snmpUDPDomain, TAddr, Timeout, + Addr = {Name, TDomain, TAddress, Timeout, RetryCount, TagList, Params, ?'StorageType_nonVolatile', ?'RowStatus_active', EngineId, TMask, MMS}, % Values for Augmenting table in SNMP-COMMUNITY-MIB {ok, Addr}; +check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, + Params, EngineId, TMask, MMS}) -> + Domain = default_domain(), + check_target_addr({Name, + Domain, Ip, Udp, + Timeout, RetryCount, TagList, + Params, EngineId, TMask, MMS}); check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, Params, EngineId}) -> check_target_addr({Name, Ip, Udp, Timeout, RetryCount, TagList, @@ -194,11 +209,12 @@ check_engine_id(discovery) -> check_engine_id(EngineId) -> snmp_conf:check_string(EngineId). -check_mask([], _TAddr) -> + +check_mask(_TDomain, [], _TAddress) -> ok; -check_mask(TMask, TAddr) when length(TMask) == length(TAddr) -> - snmp_conf:check_taddress(TMask); -check_mask(TMask, _TAddr) -> +check_mask(TDomain, TMask, TAddress) when length(TMask) =:= length(TAddress) -> + snmp_conf:check_taddress(TDomain, TMask); +check_mask(_TDomain, TMask, _TAddr) -> throw({error, {invalid_mask, TMask}}). @@ -261,7 +277,13 @@ table_del_row(Tab, Key) -> add_addr(Name, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> - Addr = {Name, Ip, Port, Timeout, Retry, TagList, + Domain = default_domain(), + add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, + Params, EngineId, TMask, MMS). + +add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, + Params, EngineId, TMask, MMS) -> + Addr = {Name, Domain, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS}, case (catch check_target_addr(Addr)) of {ok, Row} -> @@ -341,8 +363,11 @@ maybe_create_var(Var) -> init_var(Var) -> ets:insert(snmp_agent_table, {Var, 0}). vars() -> - [snmpUnavailableContexts, - snmpUnknownContexts]. + [ + snmpUnavailableContexts, + snmpUnknownContexts + ]. + %%----------------------------------------------------------------- %% API functions @@ -562,6 +587,8 @@ snmpTargetAddrTable(print) -> Prefix, element(?snmpTargetAddrTDomain, Row), case element(?snmpTargetAddrTDomain, Row) of ?snmpUDPDomain -> udp; + ?transportDomainUdpIpv4 -> udpIpv4; + ?transportDomainUdpIpv6 -> udpIpv6; _ -> undefined end, Prefix, element(?snmpTargetAddrTAddress, Row), @@ -611,13 +638,13 @@ snmpTargetAddrTable(get_next, RowIndex, Cols) -> next(snmpTargetAddrTable, RowIndex, Cols); snmpTargetAddrTable(set, RowIndex, Cols0) -> %% BMK BMK BMK - case (catch verify_targetAddrTable_cols(Cols0, [])) of + case (catch verify_targetAddrTable_cols(Cols0)) of {ok, Cols} -> snmp_notification_mib:invalidate_cache(), %% Add columns for augmenting table snmpTargetAddrExtTable and for - %% target engine ID. Target engine ID is set to "". The function + %% target engine ID. Target engine ID is set to "". The function %% get_target_engine_id will return "" unless a value is set using - %% set_target_engine_id. If it is "" Informs can't be sent to the + %% set_target_engine_id. If it is "" Informs can't be sent to the %% target. NCols = Cols ++ [{?snmpTargetAddrEngineId, ""}, {?snmpTargetAddrTMask, []}, @@ -628,12 +655,12 @@ snmpTargetAddrTable(set, RowIndex, Cols0) -> Error end; snmpTargetAddrTable(is_set_ok, RowIndex, Cols0) -> - case (catch verify_targetAddrTable_cols(Cols0, [])) of + case (catch verify_targetAddrTable_cols(Cols0)) of {ok, Cols} -> %% Add columns for augmenting table snmpTargetAddrExtTable and for - %% target engine ID. Target engine ID is set to "". The function + %% target engine ID. Target engine ID is set to "". The function %% get_target_engine_id will return "" unless a value is set using - %% set_target_engine_id. If it is "" Informs can't be sent to the + %% set_target_engine_id. If it is "" Informs can't be sent to the %% target. NCols = Cols ++ [{?snmpTargetAddrEngineId, ""}, {?snmpTargetAddrTMask, []}, @@ -647,55 +674,83 @@ snmpTargetAddrTable(Op, Arg1, Arg2) -> Db = db(snmpTargetAddrTable), snmp_generic:table_func(Op, Arg1, Arg2, Db). +verify_targetAddrTable_cols(Cols) -> + ValidCols0 = verify_targetAddrTable_cols(Cols, []), + %% Make a last pass to verify TDomain and TAddress. + ValidCols0. + verify_targetAddrTable_cols([], Cols) -> {ok, lists:reverse(Cols)}; -verify_targetAddrTable_cols([{Col, Val0}|Cols], Acc) -> - Val = verify_targetAddrTable_col(Col, Val0), - verify_targetAddrTable_cols(Cols, [{Col, Val}|Acc]). +verify_targetAddrTable_cols([{Col, Val0}|Cols], ValidCols) -> + Val = verify_targetAddrTable_col(Col, Val0, ValidCols), + verify_targetAddrTable_cols(Cols, [{Col, Val}|ValidCols]). -verify_targetAddrTable_col(?snmpTargetAddrName, Name) -> +verify_targetAddrTable_col(?snmpTargetAddrName, Name, _) -> case (catch snmp_conf:check_string(Name)) of ok -> Name; _ -> wrongValue(?snmpTargetAddrName) end; -verify_targetAddrTable_col(?snmpTargetAddrTAddress, TAddr) -> - case (catch snmp_conf:check_taddress(TAddr)) of +verify_targetAddrTable_col(?snmpTargetAddrTDomain, TDomain, _) -> + case (catch snmp_conf:check_tdomain(TDomain)) of ok -> - TAddr; + TDomain; _ -> - wrongValue(?snmpTargetAddrTAddress) + wrongValue(?snmpTargetAddrTDomain) + end; +%% In order to (properly) validate the TAddress, +%% the TDomain must already have been validated +%% (the format of TAddress depends on TDomain). +verify_targetAddrTable_col(?snmpTargetAddrTAddress, TAddress, ValidCols) -> + case lists:keysearch(?snmpTargetAddrTDomain, 1, ValidCols) of + {value, {?snmpTargetAddrTDomain, TDomain}} -> + case (catch snmp_conf:check_taddress(TDomain, TAddress)) of + ok -> + TAddress; + _ -> + wrongValue(?snmpTargetAddrTAddress) + end; + false -> + %% The user did not provide us with a TDomain, which + %% must mean that he/she intends to use the old domain. + TDomain = snmp_misc:mk_tdomain(default_domain()), + case (catch snmp_conf:check_taddress(TDomain, TAddress)) of + ok -> + TAddress; + _ -> + wrongValue(?snmpTargetAddrTAddress) + end end; -verify_targetAddrTable_col(?snmpTargetAddrTimeout, Timeout) -> +verify_targetAddrTable_col(?snmpTargetAddrTimeout, Timeout, _) -> case (catch snmp_conf:check_integer(Timeout)) of ok when Timeout >= 0 -> Timeout; _ -> wrongValue(?snmpTargetAddrTimeout) end; -verify_targetAddrTable_col(?snmpTargetAddrRetryCount, Retry) -> +verify_targetAddrTable_col(?snmpTargetAddrRetryCount, Retry, _) -> case (catch snmp_conf:check_integer(Retry)) of ok when Retry >= 0 -> Retry; _ -> wrongValue(?snmpTargetAddrRetryCount) end; -verify_targetAddrTable_col(?snmpTargetAddrTagList, TagList) -> +verify_targetAddrTable_col(?snmpTargetAddrTagList, TagList, _) -> case (catch snmp_conf:check_string(TagList)) of ok -> TagList; _ -> wrongValue(?snmpTargetAddrTagList) end; -verify_targetAddrTable_col(?snmpTargetAddrParams, Params) -> +verify_targetAddrTable_col(?snmpTargetAddrParams, Params, _) -> case (catch snmp_conf:check_string(Params)) of ok -> Params; _ -> wrongValue(?snmpTargetAddrParams) end; -verify_targetAddrTable_col(_, Val) -> +verify_targetAddrTable_col(_, Val, _) -> Val. -- cgit v1.2.3 From 4526ec71fc913993ec6c5f0a9c0e11192240a60a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 3 Mar 2011 14:27:32 +0100 Subject: Handling target address configuration files (with new Domain). Also added some proper documentation. --- lib/snmp/doc/src/notes.xml | 4 ++ lib/snmp/doc/src/snmp_agent_config_files.xml | 60 ++++++++++++---------------- lib/snmp/doc/src/snmp_target_mib.xml | 24 ++++++++--- lib/snmp/doc/src/snmpa_conf.xml | 25 +++++++++--- lib/snmp/src/agent/snmp_target_mib.erl | 21 ++++++---- lib/snmp/src/agent/snmpa_conf.erl | 47 ++++++++++++++++------ 6 files changed, 115 insertions(+), 66 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 8677d5ab61..8dd5bcb2c0 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -45,6 +45,10 @@

[agent] Added support for sending traps to IPv6 targets.

+

See the + target address config file, + the target_addr_entry/11 function or + add_addr/11 for more info.

Own Id: OTP-9088

Aux Id: Seq 11790

diff --git a/lib/snmp/doc/src/snmp_agent_config_files.xml b/lib/snmp/doc/src/snmp_agent_config_files.xml index b62269d506..bd5c537522 100644 --- a/lib/snmp/doc/src/snmp_agent_config_files.xml +++ b/lib/snmp/doc/src/snmp_agent_config_files.xml @@ -1,4 +1,4 @@ - + @@ -367,56 +367,50 @@ Target Address Definitions

The information about Target Address Definitions should be - stored in a file called - target_addr.conf. -

+ stored in a file called target_addr.conf.

The corresponding tables are snmpTargetAddrTable in the - SNMP-TARGET-MIB and snmpTargetAddrExtTable in the SNMP-COMMUNITY-MIB. -

-

Each entry is a term: -

-

{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId}. or

-{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.

+ SNMP-TARGET-MIB and snmpTargetAddrExtTable in the + SNMP-COMMUNITY-MIB.

+

Each entry is a term:

+

{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId}.

or

+{TargetName, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.

or

+{TargetName, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}.

TargetName is a unique non-empty string.

-

Ip is a list of four integers. -

+

Domain is one of the atoms: + transportDomainUdpIpv4 | transportDomainUdpIpv6.

-

Udp is an integer. -

+

Ip is a list of four or eight integers.

-

Timeout is an integer. -

+

Udp is an integer.

-

RetryCount is an integer. -

+

Timeout is an integer.

-

TagList is a string. -

+

RetryCount is an integer.

-

ParamsName is a string. -

+

TagList is a string.

-

EngineId is a string or the atom discovery. -

+

ParamsName is a string.

-

TMask is a string of size 0, or size 6 (default: []). -

+

EngineId is a string or the atom discovery.

-

MaxMessageSize is an integer (default: 2048). -

+

TMask is a list of integer() of size 0, + size 6 or size 10 (default: []).

+
+ +

MaxMessageSize is an integer (default: 2048).

Note that if EngineId has the value discovery, @@ -429,14 +423,10 @@ Target Parameters Definitions

The information about Target Parameters Definitions should be - stored in a file called - target_params.conf. -

+ stored in a file called target_params.conf.

The corresponding table is snmpTargetParamsTable in the - SNMP-TARGET-MIB. -

-

Each entry is a term: -

+ SNMP-TARGET-MIB.

+

Each entry is a term:

{ParamsName, MPModel, SecurityModel, SecurityName, SecurityLevel}.

diff --git a/lib/snmp/doc/src/snmp_target_mib.xml b/lib/snmp/doc/src/snmp_target_mib.xml index 4a36be19a3..d5151d41de 100644 --- a/lib/snmp/doc/src/snmp_target_mib.xml +++ b/lib/snmp/doc/src/snmp_target_mib.xml @@ -1,10 +1,10 @@ - +
- 19982009 + 19982011 Ericsson AB. All Rights Reserved. @@ -39,9 +39,21 @@ and functions for configuring the database.

The configuration files are described in the SNMP User's Manual.

- + +
+ DATA TYPES + + + +
+ configure(ConfDir) -> void() @@ -118,17 +130,19 @@ add_addr(Name, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret + add_addr(Name, Domain, Ip, Port, Timeout, Retry, TagList, Params, EngineId, TMask, MMS) -> Ret Add one target address definition Name = string() - Ip = [integer()], length 4 + Domain = transportDomain() + Ip = transportAddressIPv4() | transportAddressIPv6() (depends on the value of Domain) Port = integer() Timeout = integer() Retry = integer() TagList = string() ParamsName = string() EngineId = string() - TMask = string(), length 0 or 6 + TMask = transportAddressMask() (depends on Domain) MMS = integer() Ret = {ok, Key} | {error, Reason} Key = term() diff --git a/lib/snmp/doc/src/snmpa_conf.xml b/lib/snmp/doc/src/snmpa_conf.xml index d873574c6e..a533c179ee 100644 --- a/lib/snmp/doc/src/snmpa_conf.xml +++ b/lib/snmp/doc/src/snmpa_conf.xml @@ -1,10 +1,10 @@ - +
- 20062010 + 20062011 Ericsson AB. All Rights Reserved. @@ -38,8 +38,21 @@ used for manipulating (write/append/read) the config files of the SNMP agent.

- + + +
+ DATA TYPES + + + +
+ agent_entry(Tag, Val) -> agent_entry() @@ -381,17 +394,19 @@ target_addr_entry(Name, Ip, TagList, ParamsName, EngineId, TMask) -> target_addr_entry() target_addr_entry(Name, Ip, Udp, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry() target_addr_entry(Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry() + target_addr_entry(Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize) -> target_addr_entry() Create an target_addr entry Name = string() - Ip = string() + Domain = transportDomain() + Ip = transportAddressIPv4() | transportAddressIPv6() (depends on Domain) Udp = integer() Timeout = integer() RetryCount = integer() TagList = string() ParamsName = string() EngineId = string() - TMask = string() + TMask = transportAddressMask() (depends on Domain) MaxMessageSize = integer() target_addr_entry() = term() diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index c76e4bb173..c7fc449bd2 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -29,6 +29,7 @@ -export([add_addr/10, delete_addr/1, add_params/5, delete_params/1]). -export([check_target_addr/1, check_target_params/1]). +-export([default_domain/0]). -include_lib("snmp/include/snmp_types.hrl"). -include_lib("snmp/include/snmp_tables.hrl"). @@ -49,6 +50,12 @@ -define(snmpTargetAddrMMS, 12). +%%----------------------------------------------------------------- + +default_domain() -> + snmpUDPDomain. + + %%----------------------------------------------------------------- %% Func: configure/1 %% Args: Dir is the directory where the configuration files are found. @@ -140,8 +147,6 @@ read_target_config_files(Dir) -> %% {Name, Ip, Udp, Timeout, RetryCount, TagList, Params, EngineId, %% TMask, MMS} %%----------------------------------------------------------------- -default_domain() -> - snmpUDPDomain. check_target_addr({Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, Params, EngineId, TMask, MMS}) -> @@ -172,7 +177,7 @@ check_target_addr({Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, check_engine_id(EngineId), TAddress = snmp_conf:mk_taddress(Domain, Ip, Udp), TDomain = snmp_conf:mk_tdomain(Domain), - check_mask(TDomain, TMask, TAddress), + check_tmask(TDomain, TMask, TAddress), snmp_conf:check_packet_size(MMS), ?vtrace("check target address done",[]), Addr = {Name, TDomain, TAddress, Timeout, @@ -210,12 +215,12 @@ check_engine_id(EngineId) -> snmp_conf:check_string(EngineId). -check_mask(_TDomain, [], _TAddress) -> +check_tmask(_TDomain, [], _TAddress) -> ok; -check_mask(TDomain, TMask, TAddress) when length(TMask) =:= length(TAddress) -> +check_tmask(TDomain, TMask, TAddress) when length(TMask) =:= length(TAddress) -> snmp_conf:check_taddress(TDomain, TMask); -check_mask(_TDomain, TMask, _TAddr) -> - throw({error, {invalid_mask, TMask}}). +check_tmask(_TDomain, TMask, _TAddr) -> + throw({error, {invalid_tmask, TMask}}). %%----------------------------------------------------------------- @@ -637,7 +642,7 @@ snmpTargetAddrTable(get, RowIndex, Cols) -> snmpTargetAddrTable(get_next, RowIndex, Cols) -> next(snmpTargetAddrTable, RowIndex, Cols); snmpTargetAddrTable(set, RowIndex, Cols0) -> - %% BMK BMK BMK + %% BMK BMK case (catch verify_targetAddrTable_cols(Cols0)) of {ok, Cols} -> snmp_notification_mib:invalidate_cache(), diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl index b4fc716b3e..c17256b258 100644 --- a/lib/snmp/src/agent/snmpa_conf.erl +++ b/lib/snmp/src/agent/snmpa_conf.erl @@ -48,7 +48,7 @@ %% target_addr.conf target_addr_entry/5, target_addr_entry/6, - target_addr_entry/8, target_addr_entry/10, + target_addr_entry/8, target_addr_entry/10, target_addr_entry/11, write_target_addr_config/2, write_target_addr_config/3, append_target_addr_config/2, read_target_addr_config/1, @@ -447,7 +447,23 @@ target_addr_entry(Name, EngineId, TMask, MaxMessageSize) -> + target_addr_entry(Name, snmp_target_mib:default_domain(), Ip, Udp, + Timeout, RetryCount, TagList, ParamsName, EngineId, + TMask, MaxMessageSize). + +target_addr_entry(Name, + Domain, + Ip, + Udp, + Timeout, + RetryCount, + TagList, + ParamsName, + EngineId, + TMask, + MaxMessageSize) -> {Name, + Domain, Ip, Udp, Timeout, @@ -465,9 +481,13 @@ write_target_addr_config(Dir, Conf) -> "%% The data is inserted into the snmpTargetAddrTable defined\n" "%% in SNMP-TARGET-MIB, and in the snmpTargetAddrExtTable defined\n" "%% in SNMP-COMMUNITY-MIB.\n" -"%% Each row is a 10-tuple:\n" -"%% {Name, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId,\n" -"%% TMask, MaxMessageSize}.\n" +"%% Each row is a 10 or 11-tuple (Domain is optional):\n" +"%% {Name, \n" +"%% Domain, Ip, Udp, \n" +"%% Timeout, RetryCount, TagList, ParamsName, EngineId,\n" +"%% TMask, MaxMessageSize}.\n" +"%% The value of Domain deside the format of the Ip and TMask values. \n" +"%% If not present, classic Ipv4 is assumed. \n" "%% The EngineId value is only used if Inform-Requests are sent to this\n" "%% target. If Informs are not sent, this value is ignored, and can be\n" "%% e.g. an empty string. However, if Informs are sent, it is essential\n" @@ -524,13 +544,14 @@ write_target_addr_conf(Fd, Conf) -> lists:foreach(Fun, Conf). do_write_target_addr_conf(Fd, - {Name, Ip, Udp, + {Name, + Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize}) -> io:format(Fd, - "{\"~s\", ~w, ~w, ~w, ~w, \"~s\", \"~s\", \"~s\", ~w, ~w}.~n", - [Name, Ip, Udp, Timeout, RetryCount, TagList, + "{\"~s\", ~w, ~w, ~w, ~w, ~w, \"~s\", \"~s\", \"~s\", ~w, ~w}.~n", + [Name, Domain, Ip, Udp, Timeout, RetryCount, TagList, ParamsName, EngineId, TMask, MaxMessageSize]); do_write_target_addr_conf(_Fd, Crap) -> error({bad_target_addr_config, Crap}). @@ -546,13 +567,13 @@ target_params_entry(Name, Vsn) -> target_params_entry(Name, Vsn, SecName, SecLevel). target_params_entry(Name, Vsn, SecName, SecLevel) -> - MPModel = if Vsn == v1 -> v1; - Vsn == v2 -> v2c; - Vsn == v3 -> v3 + MPModel = if Vsn =:= v1 -> v1; + Vsn =:= v2 -> v2c; + Vsn =:= v3 -> v3 end, - SecModel = if Vsn == v1 -> v1; - Vsn == v2 -> v2c; - Vsn == v3 -> usm + SecModel = if Vsn =:= v1 -> v1; + Vsn =:= v2 -> v2c; + Vsn =:= v3 -> usm end, target_params_entry(Name, MPModel, SecModel, SecName, SecLevel). -- cgit v1.2.3 From 1191b33a100185862929a4175436e946022a8789 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 3 Mar 2011 14:58:49 +0100 Subject: Trap sending module now handles the new domains. --- lib/snmp/src/agent/snmpa_trap.erl | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 450cb2e9f4..786512e0c9 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -33,13 +33,14 @@ send_inform/6]). -export([init_discovery_inform/12, send_discovery_inform/5]). --include("snmp_types.hrl"). --include("snmpa_internal.hrl"). --include("SNMPv2-MIB.hrl"). --include("SNMPv2-TM.hrl"). --include("SNMPv2-TC.hrl"). --include("SNMP-FRAMEWORK-MIB.hrl"). --include("SNMP-TARGET-MIB.hrl"). +-include_lib("snmp/include/snmp_types.hrl"). +-include_lib("snmp/src/agent/snmpa_internal.hrl"). +-include_lib("snmp/include/SNMPv2-MIB.hrl"). +-include_lib("snmp/include/SNMPv2-TM.hrl"). +-include_lib("snmp/include/SNMPv2-TC.hrl"). +-include_lib("snmp/include/SNMP-FRAMEWORK-MIB.hrl"). +-include_lib("snmp/include/SNMP-TARGET-MIB.hrl"). +-include_lib("snmp/include/TRANSPORT-ADDRESS-MIB.hrl"). -define(enterpriseSpecific, 6). @@ -1001,9 +1002,27 @@ transform_taddr({?snmpUDPDomain, [A1, A2, A3, A4, P1, P2]}) -> % v2 Addr = {A1, A2, A3, A4}, Port = P1 bsl 8 + P2, {Addr, Port}; +transform_taddr({?transportDomainUdpIpv4, [A1, A2, A3, A4, P1, P2]}) -> % v2 + Addr = {A1, A2, A3, A4}, + Port = P1 bsl 8 + P2, + {Addr, Port}; +transform_taddr({?transportDomainUdpIpv6, + [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]}) -> % v2 + Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, + Port = P1 bsl 8 + P2, + {Addr, Port}; transform_taddr({{?snmpUDPDomain, [A1, A2, A3, A4, P1, P2]}, _MsgData}) -> % v3 Addr = {A1, A2, A3, A4}, Port = P1 bsl 8 + P2, + {Addr, Port}; +transform_taddr({{?transportDomainUdpIpv4, [A1, A2, A3, A4, P1, P2]}, _MsgData}) -> % v3 + Addr = {A1, A2, A3, A4}, + Port = P1 bsl 8 + P2, + {Addr, Port}; +transform_taddr({{?transportDomainUdpIpv6, + [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]}, _MsgData}) -> % v3 + Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, + Port = P1 bsl 8 + P2, {Addr, Port}. @@ -1060,6 +1079,7 @@ mic([{Addr, Comm} | T], CurComm, AddrList, Res) -> mic([], CurComm, AddrList, Res) -> [{CurComm, AddrList} | Res]. + %%----------------------------------------------------------------- %% Convert the SecurityLevel into a flag value used by snmpa_mpd %%----------------------------------------------------------------- -- cgit v1.2.3 From 2ff2b4ee1b50eb621e436f7c448808d67d6690df Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 3 Mar 2011 18:17:43 +0100 Subject: Backup... --- lib/snmp/src/agent/snmpa_net_if.erl | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/snmp/src/agent/snmpa_net_if.erl b/lib/snmp/src/agent/snmpa_net_if.erl index 97a7a63dee..d07829bd7e 100644 --- a/lib/snmp/src/agent/snmpa_net_if.erl +++ b/lib/snmp/src/agent/snmpa_net_if.erl @@ -636,7 +636,6 @@ process_taddrs([{{_Domain, AddrAndPort}, _SecData}|T], Acc) -> process_taddrs([{_Domain, AddrAndPort}|T], Acc) -> process_taddrs(T, [AddrAndPort|Acc]). - merge_taddrs(To1, To2) -> merge_taddrs(To1, To2, []). -- cgit v1.2.3 From db29f9ede14ff5b8d747230fcad8ffa1b157f1e1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 7 Mar 2011 14:52:30 +0100 Subject: Adding missing "send loop" for raw sending. Also fixed some of the documentation (types). --- lib/inets/doc/src/httpc.xml | 52 ++++--- lib/inets/doc/src/notes.xml | 85 +++++++---- lib/inets/src/http_client/httpc.erl | 85 +++++++---- lib/inets/src/http_client/httpc_handler.erl | 18 +++ lib/inets/src/http_client/httpc_request.erl | 52 ++++--- lib/inets/test/httpc_SUITE.erl | 229 ++++++++++------------------ 6 files changed, 269 insertions(+), 252 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 6dcf2d6d17..12f4fa535e 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -76,25 +76,29 @@ socket_opt() = See the Options used by gen_tcp(3) and

For more information about HTTP see rfc 2616

send_fun_result(), acc()} | - {chunkify, fun(acc()) -> send_fun_result(), acc()} -send_fun_result() = eof | {ok, iolist(), acc()} -acc() = term() -filename() = string() +method() = head | get | put | post | trace | options | delete +request() = {url(), headers()} | + {url(), headers(), content_type(), body()} +url() = string() - Syntax according to the URI definition in rfc 2396, ex: "http://www.erlang.org" +status_line() = {http_version(), status_code(), reason_phrase()} +http_version() = string() ex: "HTTP/1.1" +status_code() = integer() +reason_phrase() = string() +content_type() = string() +headers() = [header()] +header() = {field(), value()} +field() = string() +value() = string() +body() = string() | + binary() | + {fun(accumulator()) -> body_processing_result(), + accumulator()} | + {chunkify, + fun(accumulator()) -> body_processing_result(), + accumulator()} +body_processing_result() = eof | {ok, iolist(), accumulator()} +accumulator() = term() +filename() = string() ]]>
@@ -146,8 +150,9 @@ ssl_options() = {verify, code()} | Sends a get HTTP-request Url = url() - Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() + Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() + Body = string() | binary() Profile = profile() Reason = term() @@ -195,8 +200,9 @@ ssl_options() = {verify, code()} | Function = atom() Args = list() body_format() = string | binary - Result = {status_line(), headers(), body()} | - {status_code(), body()} | request_id() + Result = {status_line(), headers(), Body} | + {status_code(), Body} | request_id() + Body = string() | binary() Profile = profile() Reason = {connect_failed, term()} | {send_failed, term()} | term() diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 11b0af4310..8c0d683a90 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -1,4 +1,4 @@ - + @@ -32,50 +32,80 @@ notes.xml -
Inets 5.5.1 +
Inets 5.6 -
Fixed Bugs and Malfunctions +
Improvements and New Features + -

Fix format_man_pages so it handles all man sections - and remove warnings/errors in various man pages.

-

- Own Id: OTP-8600

+

[httpc] Add support for upload body streaming (PUT and POST).

+

For more info, + see the definition of the Body argument of the + request/4,5 + function.

+

Filipe David Manana

+

Own Id: OTP-9094

+
+
+ +
Fixed Bugs and Malfunctions +

-

+ +
+
+ + +
Inets 5.5.1 +
Improvements and New Features -

- Miscellaneous inet6 related problems.

-

- Own Id: OTP-8927

+

Miscellaneous inet6 related problems.

+

Own Id: OTP-8927

-

- Updated http-server to make sure URLs in error-messages - are URL-encoded. Added support in http-client to use - URL-encoding. Also added the missing include directory - for the inets application.

-

- Own Id: OTP-8940 Aux Id: seq11735

+

Updated http-server to make sure URLs in error-messages + are URL-encoded. Added support in http-client to use + URL-encoding. Also added the missing include directory + for the inets application.

+

Own Id: OTP-8940, Aux Id: seq11735

-
+
Fixed Bugs and Malfunctions + + +

Fix format_man_pages so it handles all man sections + and remove warnings/errors in various man pages.

+

Own Id: OTP-8600

+
+ +

[httpc] Pipelined and queued requests not processed when + connection closed remotelly.

+

Own Id: OTP-8906

+
+
+
+ +
+ -
Inets 5.5 +
Inets 5.5
Fixed Bugs and Malfunctions @@ -120,9 +150,10 @@
-
+
+ -
Inets 5.4 +
Inets 5.4
Improvements and New Features + + +

[agent] To be able to handle multiple engine-id(s) when + sending trap(s), the function + add_community/6 + has been added.

+

Own Id: OTP-9119 Aux Id: Seq 11792

+
+ +
+
+ +
+ Fixed Bugs and Malfunctions +

-

+ +
+ + +
+ Incompatibilities +

-

+
+ +
+ +
SNMP Development Toolkit 4.19

Version 4.19 supports code replacement in runtime from/to @@ -126,6 +179,7 @@ snmp_view_basec_acm_mib:vacmAccessTable(set, RowIndex, Cols).

+
SNMP Development Toolkit 4.18

Version 4.18 supports code replacement in runtime from/to diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index de0e5d6e14..07e696ce88 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,6 +22,10 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.19", + [ + ] + }, {"4.18", [ {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -53,6 +57,10 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.19", + [ + ] + }, {"4.18", [ {load_module, snmp_misc, soft_purge, soft_purge, []}, diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index e70c97dcb8..29228fc59b 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -17,6 +17,6 @@ # # %CopyrightEnd% -SNMP_VSN = 4.19 +SNMP_VSN = 4.20 PRE_VSN = APP_VSN = "snmp-$(SNMP_VSN)$(PRE_VSN)" -- cgit v1.2.3 From 4f42486c2f94860c1aa8152562760b6a2aa7c5cb Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 9 Mar 2011 17:41:05 +0100 Subject: Added (type) specs for all "public" functions in the ftp module. --- lib/inets/doc/src/notes.xml | 38 +++++ lib/inets/src/ftp/ftp.erl | 259 ++++++++++++++++++++++++++++++-- lib/inets/src/inets_app/inets.appup.src | 12 ++ lib/inets/test/ftp_SUITE.erl | 55 ++++--- lib/inets/vsn.mk | 2 +- 5 files changed, 328 insertions(+), 38 deletions(-) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..72807a27fa 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,44 @@ notes.xml +

Inets 5.6 + +
Improvements and New Features + + + + +

[ftp] Added (type) spec for all exported functions.

+

Own Id: OTP-9114 Aux Id: seq11799

+
+
+ +
+ +
Fixed Bugs and Malfunctions +

-

+ + +
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 5ad74851c8..fe6cb0c191 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.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 @@ -92,6 +92,12 @@ }). +-type shortage_reason() :: 'etnospc' | 'epnospc'. +-type restriction_reason() :: 'epath' | 'efnamena' | 'elogin' | 'enotbinary'. +-type common_reason() :: 'econn' | 'eclosed' | term(). +-type file_write_error_reason() :: term(). % See file:write for more info + + %%%========================================================================= %%% API - CLIENT FUNCTIONS %%%========================================================================= @@ -106,6 +112,9 @@ %% Description: Start an ftp client and connect to a host. %%-------------------------------------------------------------------------- +-spec open(Host :: string() | inet:ip_address()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% open({option_list, Options}) when is_list(Options) -> try @@ -126,6 +135,9 @@ open({option_list, Options}) when is_list(Options) -> open(Host) -> open(Host, []). +-spec open(Host :: string() | inet:ip_address(), Opts :: list()) -> + {'ok', Pid :: pid()} | {'error', Reason :: 'ehost' | term()}. + %% open(Host, Port) when is_integer(Port) -> open(Host, [{port, Port}]); @@ -161,12 +173,24 @@ open(Host, Opts) when is_list(Opts) -> %% %% Description: Login with or without a supplied account name. %%-------------------------------------------------------------------------- +-spec user(Pid :: pid(), + User :: string(), + Pass :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass) -> call(Pid, {user, User, Pass}, atom). +-spec user(Pid :: pid(), + User :: string(), + Pass :: string(), + Acc :: string()) -> + 'ok' | {'error', Reason :: 'euser' | common_reason()}. + user(Pid, User, Pass, Acc) -> call(Pid, {user, User, Pass, Acc}, atom). + %%-------------------------------------------------------------------------- %% account(Pid, Acc) -> ok | {error, eacct} %% Pid = pid() @@ -174,9 +198,14 @@ user(Pid, User, Pass, Acc) -> %% %% Description: Set a user Account. %%-------------------------------------------------------------------------- + +-spec account(Pid :: pid(), Acc :: string()) -> + 'ok' | {'error', Reason :: 'eacct' | common_reason()}. + account(Pid, Acc) -> call(Pid, {account, Acc}, atom). + %%-------------------------------------------------------------------------- %% pwd(Pid) -> {ok, Dir} | {error, elogin} | {error, econn} %% Pid = pid() @@ -184,19 +213,30 @@ account(Pid, Acc) -> %% %% Description: Get the current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec pwd(Pid :: pid()) -> + {'ok', Dir :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + pwd(Pid) -> call(Pid, pwd, ctrl). + %%-------------------------------------------------------------------------- -%% lpwd(Pid) -> {ok, Dir} | {error, elogin} +%% lpwd(Pid) -> {ok, Dir} %% Pid = pid() %% Dir = string() %% %% Description: Get the current working directory at local server. %%-------------------------------------------------------------------------- + +-spec lpwd(Pid :: pid()) -> + {'ok', Dir :: string()}. + lpwd(Pid) -> call(Pid, lpwd, string). + %%-------------------------------------------------------------------------- %% cd(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid() @@ -204,9 +244,14 @@ lpwd(Pid) -> %% %% Description: Change current working directory at remote server. %%-------------------------------------------------------------------------- + +-spec cd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + cd(Pid, Dir) -> call(Pid, {cd, Dir}, atom). + %%-------------------------------------------------------------------------- %% lcd(Pid, Dir) -> ok | {error, epath} %% Pid = pid() @@ -214,9 +259,14 @@ cd(Pid, Dir) -> %% %% Description: Change current working directory for the local client. %%-------------------------------------------------------------------------- + +-spec lcd(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason()}. + lcd(Pid, Dir) -> call(Pid, {lcd, Dir}, string). + %%-------------------------------------------------------------------------- %% ls(Pid) -> Result %% ls(Pid, ) -> Result @@ -229,11 +279,22 @@ lcd(Pid, Dir) -> %% %% Description: Returns a list of files in long format. %%-------------------------------------------------------------------------- + +-spec ls(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid) -> ls(Pid, ""). + +-spec ls(Pid :: pid(), Dir :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + ls(Pid, Dir) -> call(Pid, {dir, long, Dir}, string). + %%-------------------------------------------------------------------------- %% nlist(Pid) -> Result %% nlist(Pid, Pathname) -> Result @@ -246,21 +307,37 @@ ls(Pid, Dir) -> %% %% Description: Returns a list of files in short format %%-------------------------------------------------------------------------- + +-spec nlist(Pid :: pid()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid) -> nlist(Pid, ""). + +-spec nlist(Pid :: pid(), Pathname :: string()) -> + {'ok', Listing :: string()} | + {'error', Reason :: restriction_reason() | common_reason()}. + nlist(Pid, Dir) -> call(Pid, {dir, short, Dir}, string). + %%-------------------------------------------------------------------------- -%% rename(Pid, CurrFile, NewFile) -> ok | {error, epath} | {error, elogin} -%% | {error, econn} +%% rename(Pid, Old, New) -> ok | {error, epath} | {error, elogin} +%% | {error, econn} %% Pid = pid() %% CurrFile = NewFile = string() %% %% Description: Rename a file at remote server. %%-------------------------------------------------------------------------- -rename(Pid, CurrFile, NewFile) -> - call(Pid, {rename, CurrFile, NewFile}, string). + +-spec rename(Pid :: pid(), Old :: string(), New :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + +rename(Pid, Old, New) -> + call(Pid, {rename, Old, New}, string). + %%-------------------------------------------------------------------------- %% delete(Pid, File) -> ok | {error, epath} | {error, elogin} | @@ -270,9 +347,14 @@ rename(Pid, CurrFile, NewFile) -> %% %% Description: Remove file at remote server. %%-------------------------------------------------------------------------- + +-spec delete(Pid :: pid(), File :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + delete(Pid, File) -> call(Pid, {delete, File}, string). + %%-------------------------------------------------------------------------- %% mkdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -280,9 +362,14 @@ delete(Pid, File) -> %% %% Description: Make directory at remote server. %%-------------------------------------------------------------------------- + +-spec mkdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + mkdir(Pid, Dir) -> call(Pid, {mkdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% rmdir(Pid, Dir) -> ok | {error, epath} | {error, elogin} | {error, econn} %% Pid = pid(), @@ -290,9 +377,14 @@ mkdir(Pid, Dir) -> %% %% Description: Remove directory at remote server. %%-------------------------------------------------------------------------- + +-spec rmdir(Pid :: pid(), Dir :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + rmdir(Pid, Dir) -> call(Pid, {rmdir, Dir}, atom). + %%-------------------------------------------------------------------------- %% type(Pid, Type) -> ok | {error, etype} | {error, elogin} | {error, econn} %% Pid = pid() @@ -300,23 +392,41 @@ rmdir(Pid, Dir) -> %% %% Description: Set transfer type. %%-------------------------------------------------------------------------- + +-spec type(Pid :: pid(), Type :: ascii | binary) -> + 'ok' | + {'error', Reason :: 'etype' | restriction_reason() | common_reason()}. + type(Pid, Type) -> call(Pid, {type, Type}, atom). + %%-------------------------------------------------------------------------- -%% recv(Pid, RemoteFileName ) -> ok | {error, epath} | +%% recv(Pid, RemoteFileName [, LocalFileName]) -> ok | {error, epath} | %% {error, elogin} | {error, econn} %% Pid = pid() %% RemoteFileName = LocalFileName = string() %% %% Description: Transfer file from remote server. %%-------------------------------------------------------------------------- + +-spec recv(Pid :: pid(), RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | + common_reason() | + file_write_error_reason()}. + recv(Pid, RemotFileName) -> recv(Pid, RemotFileName, RemotFileName). +-spec recv(Pid :: pid(), + RemoteFileName :: string(), + LocalFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + recv(Pid, RemotFileName, LocalFileName) -> call(Pid, {recv, RemotFileName, LocalFileName}, atom). + %%-------------------------------------------------------------------------- %% recv_bin(Pid, RemoteFile) -> {ok, Bin} | {error, epath} | {error, elogin} %% | {error, econn} @@ -326,9 +436,16 @@ recv(Pid, RemotFileName, LocalFileName) -> %% %% Description: Transfer file from remote server into binary. %%-------------------------------------------------------------------------- + +-spec recv_bin(Pid :: pid(), + RemoteFile :: string()) -> + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_bin(Pid, RemoteFile) -> call(Pid, {recv_bin, RemoteFile}, bin). + %%-------------------------------------------------------------------------- %% recv_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -337,9 +454,15 @@ recv_bin(Pid, RemoteFile) -> %% %% Description: Start receive of chunks of remote file. %%-------------------------------------------------------------------------- + +-spec recv_chunk_start(Pid :: pid(), + RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk_start(Pid, RemoteFile) -> call(Pid, {recv_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% recv_chunk(Pid, RemoteFile) -> ok | {ok, Bin} | {error, Reason} %% Pid = pid() @@ -347,24 +470,47 @@ recv_chunk_start(Pid, RemoteFile) -> %% %% Description: Transfer file from remote server into binary in chunks %%-------------------------------------------------------------------------- + +-spec recv_chunk(Pid :: pid()) -> + 'ok' | + {'ok', Bin :: binary()} | + {'error', Reason :: restriction_reason() | common_reason()}. + recv_chunk(Pid) -> call(Pid, recv_chunk, atom). + %%-------------------------------------------------------------------------- -%% send(Pid, LocalFileName ) -> ok | {error, epath} -%% | {error, elogin} -%% | {error, econn} +%% send(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Transfer file to remote server. %%-------------------------------------------------------------------------- + +-spec send(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName) -> send(Pid, LocalFileName, LocalFileName). +-spec send(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send(Pid, LocalFileName, RemotFileName) -> call(Pid, {send, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% send_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -374,11 +520,19 @@ send(Pid, LocalFileName, RemotFileName) -> %% %% Description: Transfer a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec send_bin(Pid :: pid(), Bin :: binary(), RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {send_bin, Bin, RemoteFile}, atom); send_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | {error, epath} %% | {error, econn} @@ -387,9 +541,14 @@ send_bin(_Pid, _Bin, _RemoteFile) -> %% %% Description: Start transfer of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: restriction_reason() | common_reason()}. + send_chunk_start(Pid, RemoteFile) -> call(Pid, {send_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% append_chunk_start(Pid, RemoteFile) -> ok | {error, elogin} | %% {error, epath} | {error, econn} @@ -398,9 +557,14 @@ send_chunk_start(Pid, RemoteFile) -> %% %% Description: Start append chunks of data to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_start(Pid :: pid(), RemoteFile :: string()) -> + 'ok' | {'error', Reason :: term()}. + append_chunk_start(Pid, RemoteFile) -> call(Pid, {append_chunk_start, RemoteFile}, atom). + %%-------------------------------------------------------------------------- %% send_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -409,11 +573,19 @@ append_chunk_start(Pid, RemoteFile) -> %% %% Purpose: Send chunk to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + send_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); send_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% append_chunk(Pid, Bin) -> ok | {error, elogin} | {error, enotbinary} %% | {error, echunk} | {error, econn} @@ -422,11 +594,19 @@ send_chunk(_Pid, _Bin) -> %% %% Description: Append chunk to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk(Pid :: pid(), Bin :: binary()) -> + 'ok' | + {'error', Reason :: 'echunk' | + restriction_reason() | + common_reason()}. + append_chunk(Pid, Bin) when is_binary(Bin) -> call(Pid, {transfer_chunk, Bin}, atom); append_chunk(_Pid, _Bin) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- %% send_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -434,9 +614,17 @@ append_chunk(_Pid, _Bin) -> %% %% Description: End sending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec send_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + send_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- %% append_chunk_end(Pid) -> ok | {error, elogin} | {error, echunk} %% | {error, econn} @@ -444,23 +632,47 @@ send_chunk_end(Pid) -> %% %% Description: End appending of chunks to remote file. %%-------------------------------------------------------------------------- + +-spec append_chunk_end(Pid :: pid()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_chunk_end(Pid) -> call(Pid, chunk_end, atom). + %%-------------------------------------------------------------------------- -%% append(Pid, LocalFileName, RemotFileName) -> ok | {error, epath} -%% | {error, elogin} | {error, econn} +%% append(Pid, LocalFileName [, RemotFileName]) -> ok | {error, epath} +%% | {error, elogin} +%% | {error, econn} %% Pid = pid() %% LocalFileName = RemotFileName = string() %% %% Description: Append the local file to the remote file %%-------------------------------------------------------------------------- + +-spec append(Pid :: pid(), LocalFileName :: string()) -> + 'ok' | + {'error', Reason :: 'epath' | + 'elogin' | + 'etnospc' | + 'epnospc' | + 'efnamena' | common_reason()}. + append(Pid, LocalFileName) -> append(Pid, LocalFileName, LocalFileName). +-spec append(Pid :: pid(), + LocalFileName :: string(), + RemoteFileName :: string()) -> + 'ok' | {'error', Reason :: term()}. + append(Pid, LocalFileName, RemotFileName) -> call(Pid, {append, LocalFileName, RemotFileName}, atom). + %%-------------------------------------------------------------------------- %% append_bin(Pid, Bin, RemoteFile) -> ok | {error, epath} | {error, elogin} %% | {error, enotbinary} | {error, econn} @@ -470,27 +682,44 @@ append(Pid, LocalFileName, RemotFileName) -> %% %% Purpose: Append a binary to a remote file. %%-------------------------------------------------------------------------- + +-spec append_bin(Pid :: pid(), + Bin :: binary(), + RemoteFile :: string()) -> + 'ok' | + {'error', Reason :: restriction_reason() | + common_reason() | + shortage_reason()}. + append_bin(Pid, Bin, RemoteFile) when is_binary(Bin) -> call(Pid, {append_bin, Bin, RemoteFile}, atom); append_bin(_Pid, _Bin, _RemoteFile) -> {error, enotbinary}. + %%-------------------------------------------------------------------------- -%% quote(Pid, Cmd) -> ok +%% quote(Pid, Cmd) -> list() %% Pid = pid() %% Cmd = string() %% %% Description: Send arbitrary ftp command. %%-------------------------------------------------------------------------- + +-spec quote(Pid :: pid(), Cmd :: string()) -> list(). + quote(Pid, Cmd) when is_list(Cmd) -> call(Pid, {quote, Cmd}, atom). + %%-------------------------------------------------------------------------- %% close(Pid) -> ok %% Pid = pid() %% %% Description: End the ftp session. %%-------------------------------------------------------------------------- + +-spec close(Pid :: pid()) -> 'ok'. + close(Pid) -> cast(Pid, close), ok. @@ -502,9 +731,13 @@ close(Pid) -> %% %% Description: Return diagnostics. %%-------------------------------------------------------------------------- + +-spec formaterror(Tag :: term()) -> string(). + formaterror(Tag) -> ftp_response:error_string(Tag). + info(Pid) -> call(Pid, info, list). diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..6f32a31c1c 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,8 +18,14 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, ftp, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ + {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []} ] }, @@ -35,8 +41,14 @@ } ], [ + {"5.5.2", + [ + {load_module, ftp, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ + {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []} ] }, diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index 7059bb12cf..17e5f6777e 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -57,35 +57,42 @@ %% Description: Returns documentation/test cases in this test suite %% or a skip tuple if the platform is not supported. %%-------------------------------------------------------------------- -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> [{ct_hooks, [ts_install_cth]}]. all() -> - [{group, solaris8_test}, {group, solaris9_test}, - {group, solaris10_test}, {group, linux_x86_test}, - {group, linux_ppc_test}, {group, macosx_x86_test}, - {group, macosx_ppc_test}, {group, openbsd_test}, - {group, freebsd_test}, {group, netbsd_test}, + [ + {group, solaris8_test}, + {group, solaris9_test}, + {group, solaris10_test}, + {group, linux_x86_test}, + {group, linux_ppc_test}, + {group, macosx_x86_test}, + {group, macosx_ppc_test}, + {group, openbsd_test}, + {group, freebsd_test}, + {group, netbsd_test}, {group, windows_xp_test}, {group, windows_2003_server_test}, - {group, ticket_tests}]. + {group, ticket_tests} + ]. groups() -> - [{solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, - {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, - {solaris10_test, [], - [{ftp_solaris10_sparc_test, all}, - {ftp_solaris10_x86_test, all}]}, - {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, - {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, - {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, - {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, - {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, - {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, - {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, - {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, - {windows_2003_server_test, [], - [{ftp_windows_2003_server_test, all}]}, - {ticket_tests, [], [{ftp_ticket_test, all}]}]. + [ + {solaris8_test, [], [{ftp_solaris8_sparc_test, all}]}, + {solaris9_test, [], [{ftp_solaris9_sparc_test, all}]}, + {solaris10_test, [], [{ftp_solaris10_sparc_test, all}, + {ftp_solaris10_x86_test, all}]}, + {linux_x86_test, [], [{ftp_linux_x86_test, all}]}, + {linux_ppc_test, [], [{ftp_linux_ppc_test, all}]}, + {macosx_x86_test, [], [{ftp_macosx_x86_test, all}]}, + {macosx_ppc_test, [], [{ftp_macosx_ppc_test, all}]}, + {openbsd_test, [], [{ftp_openbsd_x86_test, all}]}, + {freebsd_test, [], [{ftp_freebsd_x86_test, all}]}, + {netbsd_test, [], [{ftp_netbsd_x86_test, all}]}, + {windows_xp_test, [], [{ftp_windows_xp_test, all}]}, + {windows_2003_server_test, [], [{ftp_windows_2003_server_test, all}]}, + {ticket_tests, [], [{ftp_ticket_test, all}]} + ]. init_per_group(_GroupName, Config) -> Config. diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From ed30d7b09dbdc23be7facd602a07f5a373da565e Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 9 Mar 2011 17:45:53 +0100 Subject: Added handling of muiltiple engine-id(s). --- lib/snmp/src/agent/snmp_community_mib.erl | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/snmp/src/agent/snmp_community_mib.erl b/lib/snmp/src/agent/snmp_community_mib.erl index 5644a43345..3debe0a30e 100644 --- a/lib/snmp/src/agent/snmp_community_mib.erl +++ b/lib/snmp/src/agent/snmp_community_mib.erl @@ -25,7 +25,7 @@ snmpTargetAddrExtTable/3, community2vacm/2, vacm2community/2, get_target_addr_ext_mms/2]). --export([add_community/5, delete_community/1]). +-export([add_community/5, add_community/6, delete_community/1]). -export([check_community/1]). -include("SNMP-COMMUNITY-MIB.hrl"). @@ -128,12 +128,16 @@ read_community_config_files(Dir) -> Comms. check_community({Index, CommunityName, SecName, CtxName, TransportTag}) -> + EngineID = get_engine_id(), + check_community({Index, CommunityName, SecName, + EngineID, CtxName, TransportTag}); +check_community({Index, CommunityName, SecName, + EngineID, CtxName, TransportTag}) -> snmp_conf:check_string(Index,{gt,0}), snmp_conf:check_string(CommunityName), snmp_conf:check_string(SecName), snmp_conf:check_string(CtxName), snmp_conf:check_string(TransportTag), - EngineID = get_engine_id(), Comm = {Index, CommunityName, SecName, EngineID, CtxName, TransportTag, ?'StorageType_nonVolatile', ?'RowStatus_active'}, {ok, Comm}; @@ -173,6 +177,13 @@ table_del_row(Tab, Key) -> %% FIXME: does not work with mnesia add_community(Idx, CommName, SecName, CtxName, TransportTag) -> Community = {Idx, CommName, SecName, CtxName, TransportTag}, + do_add_community(Community). + +add_community(Idx, CommName, SecName, EngineId, CtxName, TransportTag) -> + Community = {Idx, CommName, SecName, EngineId, CtxName, TransportTag}, + do_add_community(Community). + +do_add_community(Community) -> case (catch check_community(Community)) of {ok, Row} -> Key = element(1, Row), -- cgit v1.2.3 From 52b0134e8f7b871051af9be6cb7354553a0f72cd Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 11:20:16 +0100 Subject: Added proper release notes, appup and version. --- lib/inets/doc/src/mod_esi.xml | 29 +++++++++++++----------- lib/inets/doc/src/notes.xml | 39 +++++++++++++++++++++++++++++++++ lib/inets/src/inets_app/inets.appup.src | 12 +++++++++- lib/inets/vsn.mk | 2 +- 4 files changed, 67 insertions(+), 15 deletions(-) diff --git a/lib/inets/doc/src/mod_esi.xml b/lib/inets/doc/src/mod_esi.xml index e063088eb4..7b1e93e313 100644 --- a/lib/inets/doc/src/mod_esi.xml +++ b/lib/inets/doc/src/mod_esi.xml @@ -1,10 +1,10 @@ - +
- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -32,9 +32,12 @@ Erlang Server Interface

This module defines the API - Erlang Server Interface (ESI). - Which is a more efficient way of writing erlang scripts - for your Inets web server than writing them as common CGI scripts.

+ Which is a more efficient way of writing erlang scripts + for your Inets web server than writing them as common CGI scripts.

+ +
+ deliver(SessionID, Data) -> ok | {error, Reason} @@ -51,15 +54,15 @@ parts of the content to the user.

Sends data from a Erl Scheme script back to the client.

-

Note - that if any HTTP-header fields should be added by the - script they must be in the first call to deliver/2 and the - data in the call must be a string. Calls after the headers - are complete may contain binary data to reduce copying - overhead. Do not - assume anything about the data type of SessionID, the - SessionID must be the value given as input to the esi - call back function that you implemented.

+ +

Note that if any HTTP-header fields should be added by the + script they must be in the first call to deliver/2 and the + data in the call must be a string. Calls after the headers + are complete may contain binary data to reduce copying + overhead. Do not assume anything about the data type of + SessionID, the SessionID must be the value given as input to + the esi call back function that you implemented.

+
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..8e1acba9b8 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,45 @@ notes.xml
+
Inets 5.6 + +
Improvements and New Features + + + + +

[httpd] + mod_esi:deliver/2 + made to accept binary data.

+

Own Id: OTP-9123

+
+
+ +
+ +
Fixed Bugs and Malfunctions +

-

+ +
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..b75277706f 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,6 +18,11 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, mod_esi, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} @@ -34,7 +39,12 @@ ] } ], - [ + [ + {"5.5.2", + [ + {load_module, mod_esi, soft_purge, soft_purge, []} + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From 06b742fd9fcdbff2cec092565d920462335cfbc0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 11:24:15 +0100 Subject: Removed email address. --- lib/inets/test/httpd_mod.erl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index f2c1fd6a65..1754cec7bc 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-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 @@ -19,7 +19,6 @@ %% -module(httpd_mod). --author('ingela@erix.ericsson.se'). -include("test_server.hrl"). -include("test_server_line.hrl"). @@ -815,6 +814,8 @@ esi(Type, Port, Host, Node) -> [{statuscode, 302}, {version, "HTTP/1.0"}]), ok. + + %%-------------------------------------------------------------------- get(Type, Port, Host, Node) -> ok = httpd_test_lib:verify_request(Type, Host, Port, Node, -- cgit v1.2.3 From 0422eb9016dd7bea2dff4004895ed45815ef0f48 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 16:15:35 +0100 Subject: Added proper release notes, appup and version. --- lib/inets/doc/src/notes.xml | 41 +++++++++++++++++++++++++++++++++ lib/inets/src/inets_app/inets.appup.src | 13 ++++++++++- lib/inets/vsn.mk | 2 +- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..5dbe761988 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,47 @@ notes.xml +
Inets 5.6 + +
Improvements and New Features + + + + +

[httpd] Prevent XSS in error pages. + Prevent user controlled input from being interpreted + as HTML in error pages by encoding the reserved HTML + characters.

+

Michael Santos

+

Own Id: OTP-9124

+
+
+ +
+ +
Fixed Bugs and Malfunctions +

-

+ +
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..7e3785e240 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,6 +18,12 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]} + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} @@ -34,7 +40,12 @@ ] } ], - [ + [ + {"5.5.2", + [ + {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, httpd_util, soft_purge, soft_purge, [http_util]} + ] {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From a5d14f14e04ca7ca5fd34a7811cefbccdd024ce4 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 16:19:20 +0100 Subject: Add original auther (Bernard Duggan) in release notes. --- lib/inets/doc/src/notes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 8e1acba9b8..50dbb7d3da 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -44,6 +44,7 @@

[httpd] mod_esi:deliver/2 made to accept binary data.

+

Bernard Duggan

Own Id: OTP-9123

-- cgit v1.2.3 From 143cc965e88ea2486c1e6b804f2ae033e870de2d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 10 Mar 2011 18:16:33 +0100 Subject: Added export of snmp_target_mib:add_addr/11. --- lib/snmp/src/agent/snmp_target_mib.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index a2820269d0..b2f2417b02 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -26,7 +26,7 @@ snmpTargetParamsTable/1, snmpTargetParamsTable/3, get_target_addrs/0, get_target_engine_id/1, set_target_engine_id/2, is_valid_tag/3, get/3, table_next/2]). --export([add_addr/10, delete_addr/1, +-export([add_addr/10, add_addr/11, delete_addr/1, add_params/5, delete_params/1]). -export([check_target_addr/1, check_target_params/1]). -export([default_domain/0]). -- cgit v1.2.3 From bf6a8c53023a284b9f2de8d13538db1ab3ff8dee Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 11 Mar 2011 12:27:47 +0100 Subject: [httpd] Prevent XSS in error pages. Prevent user controlled input from being interpreted as HTML in error pages by encoding the reserved HTML characters. --- lib/inets/src/inets_app/inets.appup.src | 1 + lib/inets/test/httpd_basic_SUITE.erl | 55 +++++++++++++++++++++++++-------- 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 7e3785e240..b89ce0fbb2 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -46,6 +46,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]} ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 1cb07c2f5b..ced0d3d6d0 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -29,7 +29,11 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [uri_too_long_414, header_too_long_413, escaped_url_in_error_body]. + [ + uri_too_long_414, + header_too_long_413, + escaped_url_in_error_body + ]. groups() -> []. @@ -40,6 +44,7 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -50,6 +55,8 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + tsp("init_per_suite -> entry with" + "~n Config: ~p", [Config]), ok = inets:start(), PrivDir = ?config(priv_dir, Config), HttpdConf = [{port, 0}, {ipfamily, inet}, @@ -64,6 +71,8 @@ init_per_suite(Config) -> %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- end_per_suite(_Config) -> + tsp("end_per_suite -> entry with" + "~n Config: ~p", [_Config]), inets:stop(), ok. @@ -79,9 +88,12 @@ 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(_Case, Config) -> +init_per_testcase(Case, Config) -> + tsp("init_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -90,9 +102,12 @@ init_per_testcase(_Case, Config) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(_, Config) -> +end_per_testcase(Case, Config) -> + tsp("end_per_testcase(~w) -> entry with" + "~n Config: ~p", [Case, Config]), Config. + %%------------------------------------------------------------------------- %% Test cases starts here. %%------------------------------------------------------------------------- @@ -142,23 +157,30 @@ escaped_url_in_error_body(doc) -> escaped_url_in_error_body(suite) -> []; escaped_url_in_error_body(Config) when is_list(Config) -> - HttpdConf = ?config(httpd_conf, Config), + tsp("escaped_url_in_error_body -> entry with" + "~n Config: ~p", [Config]), + HttpdConf = ?config(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), Info = httpd:info(Pid), Port = proplists:get_value(port, Info), - Address = proplists:get_value(bind_address, Info), - Path = "/this_is_bold", + _Address = proplists:get_value(bind_address, Info), + Path = "/this_is_bold", URL = ?URL_START ++ integer_to_list(Port) ++ Path, EscapedPath = http_uri:encode(Path), - {ok, {404, Body}} = httpc:request(get, {URL, []}, - [{url_encode, true}, {version, "HTTP/1.0"}], - [{full_result, false}]), - EscapedPath = find_URL_path(string:tokens(Body, " ")), {ok, {404, Body1}} = httpc:request(get, {URL, []}, - [{version, "HTTP/1.0"}], [{full_result, false}]), + [{url_encode, true}, + {version, "HTTP/1.0"}], + [{full_result, false}]), + EscapedPath = find_URL_path(string:tokens(Body1, " ")), + {ok, {404, Body2}} = httpc:request(get, {URL, []}, + [{url_encode, false}, + {version, "HTTP/1.0"}], + [{full_result, false}]), HTMLEncodedPath = http_util:html_encode(Path), - HTMLEncodedPath = find_URL_path(string:tokens(Body1, " ")), - inets:stop(httpd, Pid). + HTMLEncodedPath = find_URL_path(string:tokens(Body2, " ")), + inets:stop(httpd, Pid), + tsp("escaped_url_in_error_body -> done"), + ok. find_URL_path([]) -> ""; @@ -166,3 +188,10 @@ find_URL_path(["URL", URL | _]) -> URL; find_URL_path([_ | Rest]) -> find_URL_path(Rest). + + +tsp(F) -> + tsp(F, []). +tsp(F, A) -> + test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + -- cgit v1.2.3 From 61c6577254568abd92c2d8d00c948cf230e602ca Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 14 Mar 2011 12:17:12 +0100 Subject: Used the wrong security property names in documentation. --- lib/inets/doc/src/httpd.xml | 12 +++++------ lib/inets/doc/src/mod_security.xml | 4 ++-- lib/inets/doc/src/notes.xml | 37 +++++++++++++++++++++++++++++++++ lib/inets/src/inets_app/inets.appup.src | 14 ++++++++++--- lib/inets/vsn.mk | 2 +- 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index 62f4e18f82..91a55f6920 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -4,7 +4,7 @@
- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -793,14 +793,14 @@ bytes

Here follows the valid properties for security directories

- {security_data_file, path()} + {data_file, path()} Name of the security data file. The filename can either absolute or relative to the server_root. This file is used to store persistent data for the mod_security module. - {security_max_retries, integer()} + {max_retries, integer()} Specifies the maximum number of tries to authenticate a user has before the user is blocked out. If a user @@ -810,13 +810,13 @@ bytes server will return 401 (Unauthorized), for security reasons. Defaults to 3 may also be set to infinity. - {security_block_time, integer()} + {block_time, integer()} Specifies the number of minutes a user is blocked. After this amount of time, he automatically regains access. Defaults to 60 - {security_fail_expire_time, integer()} + {fail_expire_time, integer()} Specifies the number of minutes a failed user authentication @@ -824,7 +824,7 @@ bytes time, his previous failed authentications are forgotten. Defaults to 30 - {security_auth_timeout, integer()} + {auth_timeout, integer()} Specifies the number of seconds a successful user diff --git a/lib/inets/doc/src/mod_security.xml b/lib/inets/doc/src/mod_security.xml index 2a871d29d8..a3c91dca5b 100644 --- a/lib/inets/doc/src/mod_security.xml +++ b/lib/inets/doc/src/mod_security.xml @@ -1,10 +1,10 @@ - +
- 19982010 + 19982011 Ericsson AB. All Rights Reserved. diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 5da9d98002..b7899400fa 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,43 @@ notes.xml
+
Inets 5.6 + +
Improvements and New Features +

-

+ + + +
+ +
Fixed Bugs and Malfunctions + + +

[httpd] Wrong + security property + names used in documentation.

+

security_data_file used instead of data_file. + security_max_retries instead of max_retries. + security_block_time instead of block_time. + security_fail_expire_time instead of fail_expire_time. + security_auth_timeout instead of auth_timeout.

+

Garrett Smith

+

Own Id: OTP-9131

+
+
+
+ +
+ +
Inets 5.5.2
Improvements and New Features diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..e8a9601f4f 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,11 +18,15 @@ {"%VSN%", [ - {"5.5.1", + {"5.5.2", [ - {load_module, http_chunk, soft_purge, soft_purge, []} ] }, + {"5.5.1", + [ + {load_module, http_chunk, soft_purge, soft_purge, []} + ] + }, {"5.5", [ {restart_application, inets} @@ -34,7 +38,11 @@ ] } ], - [ + [ + {"5.5.2", + [ + ] + }, {"5.5.1", [ {load_module, http_chunk, soft_purge, soft_purge, []} diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From cbe8b2e034a8b2cd6052df5e27f1b229ded9748e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Catalinas=20Jim=C3=A9nez?= Date: Thu, 17 Mar 2011 01:30:51 +0100 Subject: Fix log messages formating in httpd --- lib/inets/src/http_server/httpd_file.erl | 15 +++++++-------- lib/inets/src/http_server/httpd_log.erl | 2 +- lib/inets/src/http_server/httpd_request_handler.erl | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/lib/inets/src/http_server/httpd_file.erl b/lib/inets/src/http_server/httpd_file.erl index 7e21d9e158..dc1dae8bc9 100644 --- a/lib/inets/src/http_server/httpd_file.erl +++ b/lib/inets/src/http_server/httpd_file.erl @@ -26,22 +26,21 @@ handle_error(eacces, Op, ModData, Path) -> - handle_error(403, Op, ModData, Path,"Forbidden"); + handle_error(403, Op, ModData, Path, ": Forbidden"); handle_error(enoent, Op, ModData, Path) -> - handle_error(404, Op, ModData, Path,"File not found"); + handle_error(404, Op, ModData, Path, ": File not found"); handle_error(enotdir, Op, ModData, Path) -> handle_error(404, Op, ModData, Path, - ": A component of the file name is not a directory"); + ": A component of the file name is not a directory"); handle_error(emfile, Op, _ModData, Path) -> handle_error(500, Op, none, Path, ": To many open files"); handle_error({enfile,_}, Op, _ModData, Path) -> handle_error(500, Op, none, Path, ": File table overflow"); handle_error(_Reason, Op, ModData, Path) -> - handle_error(404, Op, ModData, Path, "File not found"). - -handle_error(StatusCode, Op, none, Path, Reason) -> - {StatusCode, none, ?NICE("Can't " ++ Op ++ Path ++ Reason)}; + handle_error(404, Op, ModData, Path, ": File not found"). +handle_error(StatusCode, Op, none, Path, Reason) -> + {StatusCode, none, ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}; handle_error(StatusCode, Op, ModData, Path, Reason) -> {StatusCode, ModData#mod.request_uri, - ?NICE("Can't " ++ Op ++ Path ++ Reason)}. + ?NICE("Can't " ++ Op ++ " " ++ Path ++ Reason)}. diff --git a/lib/inets/src/http_server/httpd_log.erl b/lib/inets/src/http_server/httpd_log.erl index f3ea3aa0e2..4b0f49d560 100644 --- a/lib/inets/src/http_server/httpd_log.erl +++ b/lib/inets/src/http_server/httpd_log.erl @@ -113,7 +113,7 @@ do_error_entry(ConfigDB, RemoteHost, undefined, Date, Reason) -> do_error_entry(ConfigDB, RemoteHost, URI, Date, Reason) -> case httpd_util:lookup(ConfigDB, error_log_format, pretty) of pretty -> - io_lib:format("[~s] access to ~s failed for ~s reason: ~n~p~n", + io_lib:format("[~s] access to ~s failed for ~s, reason: ~n~p~n", [Date, URI, RemoteHost, Reason]); compact -> io_lib:format( "[~s] access to ~s failed for ~s, reason: ~w~n", diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl index a9db6e2058..31ec416b2d 100644 --- a/lib/inets/src/http_server/httpd_request_handler.erl +++ b/lib/inets/src/http_server/httpd_request_handler.erl @@ -240,13 +240,13 @@ handle_info({ssl_error, _, _} = Reason, State) -> %% Timeouts handle_info(timeout, #state{mod = ModData, mfa = {_, parse, _}} = State) -> - error_log("No request received on keep-alive connection" + error_log("No request received on keep-alive connection " "before server side timeout", ModData), %% No response should be sent! {stop, normal, State#state{response_sent = true}}; handle_info(timeout, #state{mod = ModData} = State) -> httpd_response:send_status(ModData, 408, "Request timeout"), - error_log("The client did not send the whole request before the" + error_log("The client did not send the whole request before the " "server side timeout", ModData), {stop, normal, State#state{response_sent = true}}; -- cgit v1.2.3 From eea0a7bdf77179c98e41f867398253c4c6f0811f Mon Sep 17 00:00:00 2001 From: Bernard Duggan Date: Thu, 17 Mar 2011 12:18:29 +1100 Subject: Fix timeout message generated by mod_esi When a mod_esi request times out, the code to send a timeout response was incorrect and generated an internal server error as well as an invalid response line. --- lib/inets/src/http_server/mod_esi.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/src/http_server/mod_esi.erl b/lib/inets/src/http_server/mod_esi.erl index 929185a67a..a8e3b4ab47 100644 --- a/lib/inets/src/http_server/mod_esi.erl +++ b/lib/inets/src/http_server/mod_esi.erl @@ -415,7 +415,7 @@ deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) -> end; timeout -> ?hdrv("deliver_webpage_chunk - timeout", []), - send_headers(ModData, {504, "Timeout"},[{"connection", "close"}]), + send_headers(ModData, 504, [{"connection", "close"}]), httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket), process_flag(trap_exit,false), {proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]} -- cgit v1.2.3 From f10de199e5aad1aecbff03a5e7af9e1c5d017301 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 18 Mar 2011 10:20:16 +0100 Subject: Fixed debug macro. Fixed appup file (not updated). Fixed application version. --- lib/inets/src/http_client/httpc.erl | 4 ++-- lib/inets/src/inets_app/inets.appup.src | 28 +++++++++++++++++++++++++--- lib/inets/vsn.mk | 2 +- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index c7f8a02a0b..b70b16f57f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -494,10 +494,10 @@ handle_request(Method, Url, end catch error:{noproc, _} -> - ?hcrl("noproc", [{profile, Profile}]), + ?hcrv("noproc", [{profile, Profile}]), {error, {not_started, Profile}}; throw:Error -> - ?hcrl("throw", [{error, Error}]), + ?hcrv("throw", [{error, Error}]), Error end. diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 07da8ca961..65ce0cf0eb 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,9 +18,20 @@ {"%VSN%", [ + {"5.5.2", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {load_module, http_chunk, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, + [httpc_request,http_chunk]} ] }, {"5.5", @@ -34,10 +45,21 @@ ] } ], - [ + [ + {"5.5.2", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + ] + }, {"5.5.1", [ - {load_module, http_chunk, soft_purge, soft_purge, []} + {load_module, http_chunk, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, + {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpc_handler, soft, soft_purge, soft_purge, + [httpc_request,http_chunk]} ] }, {"5.5", diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index b1de3fef43..c0e25a30e3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.5.2 +INETS_VSN = 5.6 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" -- cgit v1.2.3 From b1fa2355c461fd0feb11a41d4424594f5590373a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 25 Mar 2011 21:39:03 +0100 Subject: Checking an taddress with domain snmpUDPDomain giv as a OID not handled. --- lib/snmp/src/misc/snmp_conf.erl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 4fffb0fe38..cb5b3bbfbd 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -402,9 +402,13 @@ mk_tdomain(BadDomain) -> check_taddress(X) -> check_taddress(snmpUDPDomain, X). +check_taddress(?snmpUDPDomain, X) -> + check_taddress(transportDomainUdpIpv4, X); check_taddress(snmpUDPDomain, X) -> check_taddress(transportDomainUdpIpv4, X); +check_taddress(?transportDomainUdpIpv4, X) -> + check_taddress(transportDomainUdpIpv4, X); check_taddress(transportDomainUdpIpv4, X) when is_list(X) andalso (length(X) =:= 6) -> case (catch all_integer(X)) of @@ -415,9 +419,9 @@ check_taddress(transportDomainUdpIpv4, X) end; check_taddress(transportDomainUdpIpv4, X) -> error({invalid_taddress, X}); -check_taddress(?transportDomainUdpIpv4, X) -> - check_taddress(transportDomainUdpIpv4, X); +check_taddress(?transportDomainUdpIpv6, X) -> + check_taddress(transportDomainUdpIpv6, X); check_taddress(transportDomainUdpIpv6, X) when is_list(X) andalso (length(X) =:= 10) -> case (catch all_integer(X)) of @@ -428,8 +432,6 @@ check_taddress(transportDomainUdpIpv6, X) end; check_taddress(transportDomainUdpIpv6, X) -> error({invalid_taddress, X}); -check_taddress(?transportDomainUdpIpv6, X) -> - check_taddress(transportDomainUdpIpv6, X); check_taddress(BadDomain, _X) -> error({invalid_tdomain, BadDomain}). -- cgit v1.2.3 From a0ab2e0fa31a2599d0532ef87c944837e96153c0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 28 Mar 2011 14:07:44 +0200 Subject: A slightly more usefull debug printout (including httpd services). --- lib/inets/src/http_server/httpd_sup.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index f94e5459c1..b248c9bcf0 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -90,7 +90,7 @@ id(Address, Port) -> %%% Supervisor callback %%%========================================================================= init([HttpdServices]) -> - ?hdrd("starting", []), + ?hdrd("starting", [{httpd_service, HttpdServices}]), RestartStrategy = one_for_one, MaxR = 10, MaxT = 3600, -- cgit v1.2.3 From 6ebe1ea0e4b9bf58f6a27bac75c7c5e700044682 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 28 Mar 2011 14:10:52 +0200 Subject: A (hopefully) temporary skip of some of the httpc proxy test cases. --- lib/inets/test/httpc_SUITE.erl | 21 +++++++++++++++++++-- lib/inets/test/inets_test_lib.erl | 11 +++++++++-- lib/inets/test/inets_test_lib.hrl | 2 +- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index c1a36d23de..d8848412c5 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -28,6 +28,7 @@ -include("test_server_line.hrl"). -include_lib("kernel/include/file.hrl"). +-include("inets_test_lib.hrl"). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -228,8 +229,8 @@ init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> [{local_ssl_server, Server} | Config2]. init_per_testcase(Case, Timeout, Config) -> - io:format(user, "~n~n*** INIT ~w:[~w][~w] ***~n~n", - [?MODULE, Timeout, Case]), + io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n", + [?MODULE, Case, Timeout]), PrivDir = ?config(priv_dir, Config), tsp("init_per_testcase -> stop inets"), application:stop(inets), @@ -1348,6 +1349,10 @@ proxy_options(doc) -> proxy_options(suite) -> []; proxy_options(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets, which + %% do not implement "options". + ?SKIP(server_does_not_implement_options), case ?config(skip, Config) of undefined -> case httpc:request(options, {?PROXY_URL, []}, [], []) of @@ -1372,6 +1377,9 @@ proxy_head(doc) -> proxy_head(suite) -> []; proxy_head(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(head, {?PROXY_URL, []}, [], []) of @@ -1467,6 +1475,9 @@ proxy_post(doc) -> proxy_post(suite) -> []; proxy_post(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(post, {?PROXY_URL, [], @@ -1489,6 +1500,9 @@ proxy_put(doc) -> proxy_put(suite) -> []; proxy_put(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(put, {"http://www.erlang.org/foobar.html", [], @@ -1513,6 +1527,9 @@ proxy_delete(doc) -> proxy_delete(suite) -> []; proxy_delete(Config) when is_list(Config) -> + %% As of 2011-03-24, erlang.org (which is used as server) + %% does no longer run Apache, but instead runs inets. + ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> URL = ?PROXY_URL ++ "/foobar.html", diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index c56a714f5a..c837326bb5 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -32,7 +32,7 @@ -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). -export([oscmd/1]). --export([non_pc_tc_maybe_skip/4, os_based_skip/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]). @@ -395,6 +395,13 @@ sleep(MSecs) -> skip(Reason, File, Line) -> exit({skipped, {Reason, File, Line}}). +fail(Reason, File, Line) -> + String = lists:flatten(io_lib:format("Failure ~p(~p): ~p~n", + [File, Line, Reason])), + tsf(String). + + + flush() -> receive Msg -> @@ -407,7 +414,7 @@ flush() -> tsp(F) -> tsp(F, []). tsp(F, A) -> - test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + test_server:format("~p ~p ~p:" ++ F ++ "~n", [node(), self(), ?MODULE | A]). tsf(Reason) -> test_server:fail(Reason). diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index 0cdb04139c..cc83a309b5 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -72,7 +72,7 @@ %% - Test case macros - --define(SKIP(Reason), inets_test_lib:skip(Reason)). +-define(SKIP(Reason), inets_test_lib:skip(Reason, ?MODULE, ?LINE)). -define(FAIL(Reason), inets_test_lib:fail(Reason, ?MODULE, ?LINE)). -- cgit v1.2.3 From 5f4c56191dd2ff41768b3ac2873a61da934298fc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 28 Mar 2011 18:29:04 +0200 Subject: Cosmetic prep for handling of domains in the config tool. --- lib/snmp/src/misc/snmp_config.erl | 17 +++++++++++++++-- lib/snmp/src/misc/snmp_misc.erl | 9 +++++++-- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index 91a6550b0a..813942225e 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -491,6 +491,9 @@ config_agent_snmp(Dir, Vsns) -> Host = host(), AgentIP = ask("5. IP address for the agent (only used as id ~n" " when sending traps)", Host, fun verify_address/1), + %% We intentionally skip TDomain... + %% If the user wish to use IPv6, the user must create an dummy entry here + %% and then manually edit these entries later. ManagerIP = ask("6. IP address for the manager (only this manager ~n" " will have access to the agent, traps are sent ~n" " to this one)", Host, fun verify_address/1), @@ -1062,9 +1065,19 @@ verify_sec_type(ST) -> {error, "invalid security type: " ++ ST}. verify_address(A) -> - case (catch snmp_misc:ip(A)) of + verify_address(A, snmpUDPDomain). + +verify_address(A, snmpUDPDomain = _Domain) -> + do_verify_address(A, inet); +verify_address(A, transportDomainUdpIpv4 = _Domain) -> + do_verify_address(A, inet); +verify_address(A, transportDomainUdpIpv6 = _Domain) -> + do_verify_address(A, inet6). + +do_verify_address(A, Family) -> + case (catch snmp_misc:ip(A, Family)) of {ok, IP} -> - {ok, tuple_to_list(IP)}; + {ok, tuple_to_list(IP)}; {error, _} -> {error, "invalid address: " ++ A}; _E -> diff --git a/lib/snmp/src/misc/snmp_misc.erl b/lib/snmp/src/misc/snmp_misc.erl index 1b535743a4..2dce76992b 100644 --- a/lib/snmp/src/misc/snmp_misc.erl +++ b/lib/snmp/src/misc/snmp_misc.erl @@ -347,10 +347,15 @@ bits_to_int([Kibble|Ks],Kibbles,Res) -> %%---------------------------------------------------------------------- -%% Returns: {ok, {int(),int(),int(),int()}} | {error, Reason} +%% Returns: {ok, {int(),int(),int(),int()}} | +%% {ok, {int(),int(),int(),int()},int(),int(),int(),int()} | +%% {error, Reason} %%---------------------------------------------------------------------- ip(Host) -> - inet:getaddr(Host, inet). + ip(Host, inet). + +ip(Host, Family) -> + inet:getaddr(Host, Family). ensure_trailing_dir_delimiter([]) -> "/"; ensure_trailing_dir_delimiter(DirSuggestion) -> -- cgit v1.2.3 From 7e9ed39a425c3869f74dd69e6d3f48cf38a95f41 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 28 Mar 2011 18:37:05 +0200 Subject: Updated the appup also... --- lib/snmp/src/app/snmp.appup.src | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index b8aea85cbe..db2b3ededb 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -25,6 +25,7 @@ {"4.19", [ {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_trap, soft_purge, soft_purge, @@ -94,6 +95,7 @@ {"4.19", [ {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_trap, soft_purge, soft_purge, -- cgit v1.2.3 From fe83491166fd1a394f768c33234150c67de98289 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 Mar 2011 11:35:40 +0200 Subject: Improved (httpc proxy) "test case skipping". --- lib/inets/test/httpc_SUITE.erl | 44 +++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index d8848412c5..81e9c2b230 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -254,9 +254,10 @@ init_per_testcase(Case, Timeout, Config) -> [$e, $s, $s, $l | _] -> init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); - "proxy" ++ Rest -> + "proxy_" ++ Rest -> + io:format("init_per_testcase -> Rest: ~p~n", [Rest]), case Rest of - "_https_not_supported" -> + "https_not_supported" -> tsp("init_per_testcase -> [proxy case] start inets"), inets:start(), tsp("init_per_testcase -> [proxy case] start ssl"), @@ -270,13 +271,39 @@ init_per_testcase(Case, Timeout, Config) -> | TmpConfig] end; _ -> + %% We use erlang.org for the proxy tests + %% and after the switch to erlang-web, many + %% of the test cases no longer work (erlang.org + %% previously run on Apache). + %% Until we have had time to update inets + %% (and updated erlang.org to use that inets) + %% and the test cases, we simply skip the + %% problematic test cases. + %% This is not ideal, but I am busy.... case is_proxy_available(?PROXY, ?PROXY_PORT) of true -> - inets:start(), - [{watchdog, Dog} | TmpConfig]; + BadCases = + [ + "delete", + "get", + "head", + "not_modified_otp_6821", + "options", + "page_does_not_exist", + "post", + "put", + "stream" + ], + case lists:member(Rest, BadCases) of + true -> + [{skip, "TC and server not compatible"}| + TmpConfig]; + false -> + inets:start(), + [{watchdog, Dog} | TmpConfig] + end; false -> - [{skip, "Failed to contact proxy"} | - TmpConfig] + [{skip, "proxy not responding"} | TmpConfig] end end; _ -> @@ -1352,7 +1379,6 @@ proxy_options(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets, which %% do not implement "options". - ?SKIP(server_does_not_implement_options), case ?config(skip, Config) of undefined -> case httpc:request(options, {?PROXY_URL, []}, [], []) of @@ -1379,7 +1405,6 @@ proxy_head(suite) -> proxy_head(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(head, {?PROXY_URL, []}, [], []) of @@ -1477,7 +1502,6 @@ proxy_post(suite) -> proxy_post(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(post, {?PROXY_URL, [], @@ -1502,7 +1526,6 @@ proxy_put(suite) -> proxy_put(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> case httpc:request(put, {"http://www.erlang.org/foobar.html", [], @@ -1529,7 +1552,6 @@ proxy_delete(suite) -> proxy_delete(Config) when is_list(Config) -> %% As of 2011-03-24, erlang.org (which is used as server) %% does no longer run Apache, but instead runs inets. - ?SKIP(tc_and_server_not_compatible), case ?config(skip, Config) of undefined -> URL = ?PROXY_URL ++ "/foobar.html", -- cgit v1.2.3 From 49737813349f164b35737eb3cd59491930128f58 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 31 Mar 2011 17:46:17 +0200 Subject: Deprecated old manager API (set and get) request functions. --- lib/snmp/doc/src/notes.xml | 46 ++++++++++++++ lib/snmp/src/app/snmp.appup.src | 12 ++++ lib/snmp/src/manager/snmpm.erl | 135 +++++++++++----------------------------- lib/snmp/vsn.mk | 2 +- lib/stdlib/src/otp_internal.erl | 96 +++++++++++++++++++++++++++- 5 files changed, 191 insertions(+), 100 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 2efeb8ae3f..87dcd46725 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -32,6 +32,52 @@ notes.xml
+
+ SNMP Development Toolkit 4.20 +

Version 4.20 supports code replacement in runtime from/to + version 4.19 and 4.18.

+ +
+ Improvements and new features + + + +

[manager] The old API functions (for get and set + requests) are now officially deprecated. + They will be removed as of R16B.

+

Own Id: OTP-9174

+
+ +
+
+ +
+ Fixed Bugs and Malfunctions +

-

+ +
+ + +
+ Incompatibilities +

-

+
+ +
+
SNMP Development Toolkit 4.19

Version 4.19 supports code replacement in runtime from/to diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index de0e5d6e14..4e86e12465 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,8 +22,14 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.19", + [ + {load_module, snmpm, soft_purge, soft_purge, []} + ] + }, {"4.18", [ + {load_module, snmpm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa, soft_purge, soft_purge, @@ -53,8 +59,14 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.19", + [ + {load_module, snmpm, soft_purge, soft_purge, []} + ] + }, {"4.18", [ + {load_module, snmpm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa, soft_purge, soft_purge, diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 5b6321b4c3..36b4901e9a 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -92,6 +92,43 @@ -export([format_reason/1, format_reason/2]). %% Backward compatibillity exports +-deprecated({agent_info, 3}). +-deprecated({update_agent_info, 5}). +-deprecated({g, 3}). +-deprecated({g, 4}). +-deprecated({g, 5}). +-deprecated({g, 6}). +-deprecated({g, 7}). +-deprecated({ag, 3}). +-deprecated({ag, 4}). +-deprecated({ag, 5}). +-deprecated({ag, 6}). +-deprecated({ag, 7}). +-deprecated({gn, 3}). +-deprecated({gn, 4}). +-deprecated({gn, 5}). +-deprecated({gn, 6}). +-deprecated({gn, 7}). +-deprecated({agn, 3}). +-deprecated({agn, 4}). +-deprecated({agn, 5}). +-deprecated({agn, 6}). +-deprecated({agn, 7}). +-deprecated({gb, 5}). +-deprecated({gb, 6}). +-deprecated({gb, 7}). +-deprecated({gb, 8}). +-deprecated({gb, 9}). +-deprecated({s, 3}). +-deprecated({s, 4}). +-deprecated({s, 5}). +-deprecated({s, 6}). +-deprecated({s, 7}). +-deprecated({as, 3}). +-deprecated({as, 4}). +-deprecated({as, 5}). +-deprecated({as, 6}). +-deprecated({as, 7}). -export([ agent_info/3, update_agent_info/5, g/3, g/4, g/5, g/6, g/7, @@ -393,24 +430,12 @@ agent_info(Addr, Port, Item) -> end. update_agent_info(UserId, TargetName, Item, Val) -> -%% p("update_agent_info -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Item: ~p" -%% "~n Val: ~p", [UserId, TargetName, Item, Val]), snmpm_config:update_agent_info(UserId, TargetName, Item, Val). %% Backward compatibillity functions update_agent_info(UserId, Addr, Port, Item, Val) -> -%% p("update_agent_info -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n Item: ~p" -%% "~n Val: ~p", [UserId, Addr, Port, Item, Val]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("update_agent_info -> TargetName: ~p", [TargetName]), update_agent_info(UserId, TargetName, Item, Val); Error -> Error @@ -473,93 +498,39 @@ which_usm_users(EngineID) when is_list(EngineID) -> %% sync_get(UserId, TargetName, Oids) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Oids: ~p", [UserId, TargetName, Oids]), sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). sync_get(UserId, TargetName, Context, Oids) when is_list(Oids) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p", [UserId, TargetName, Context, Oids]), snmpm_server:sync_get(UserId, TargetName, Context, Oids); sync_get(UserId, TargetName, Oids, Timeout) when is_integer(Timeout) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", [UserId, TargetName, Oids, Timeout]), sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout). sync_get(UserId, TargetName, Context, Oids, Timeout) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", [UserId, TargetName, Context, Oids, Timeout]), snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout). sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> -%% p("sync_get -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p" -%% "~n ExtraInfo: ~p", -%% [UserId, TargetName, Context, Oids, Timeout, ExtraInfo]), snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo). g(UserId, Addr, Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Oids: ~p", [UserId, Addr, Oids]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). g(UserId, Addr, CtxName, Oids) when is_list(CtxName) andalso is_list(Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p", [UserId, Addr, CtxName, Oids]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids); g(UserId, Addr, Port, Oids) when is_integer(Port) andalso is_list(Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n Oids: ~p", [UserId, Addr, Port, Oids]), g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids); g(UserId, Addr, Oids, Timeout) when is_list(Oids) andalso is_integer(Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", [UserId, Addr, Oids, Timeout]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids, Timeout). g(UserId, Addr, Port, CtxName, Oids) when is_integer(Port) andalso is_list(CtxName) andalso is_list(Oids) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n Context: ~p" -%% "~n Oids: ~p", [UserId, Addr, Port, CtxName, Oids]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("g -> TargetName: ~p", [TargetName]), sync_get(UserId, TargetName, CtxName, Oids); Error -> Error @@ -567,55 +538,23 @@ g(UserId, Addr, Port, CtxName, Oids) g(UserId, Addr, Port, Oids, Timeout) when is_integer(Port) andalso is_list(Oids) andalso is_integer(Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", -%% [UserId, Addr, Oids, Timeout]), g(UserId, Addr, Port, ?DEFAULT_CONTEXT, Oids, Timeout); g(UserId, Addr, CtxName, Oids, Timeout) when is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", -%% [UserId, Addr, CtxName, Oids, Timeout]), g(UserId, Addr, ?DEFAULT_AGENT_PORT, CtxName, Oids, Timeout). g(UserId, Addr, Port, CtxName, Oids, Timeout) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p", -%% [UserId, Addr, Port, CtxName, Oids, Timeout]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("g -> TargetName: ~p", [TargetName]), sync_get(UserId, TargetName, CtxName, Oids, Timeout); Error -> Error end. g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> -%% p("g -> entry with" -%% "~n UserId: ~p" -%% "~n Addr: ~p" -%% "~n Port: ~p" -%% "~n CtxName: ~p" -%% "~n Oids: ~p" -%% "~n Timeout: ~p" -%% "~n ExtraInfo: ~p", -%% [UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo]), case target_name(Addr, Port) of {ok, TargetName} -> -%% p("g -> TargetName: ~p", [TargetName]), sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo); Error -> Error diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index e70c97dcb8..29228fc59b 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -17,6 +17,6 @@ # # %CopyrightEnd% -SNMP_VSN = 4.19 +SNMP_VSN = 4.20 PRE_VSN = APP_VSN = "snmp-$(SNMP_VSN)$(PRE_VSN)" diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index 5c52dfcbf0..39d017d430 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.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 @@ -180,6 +180,9 @@ obsolete_1(calendar, local_time_to_universal_time, 1) -> obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 -> {deprecated, {rpc, multi_server_call, A}}; + +%% *** SNMP *** + obsolete_1(snmp, N, A) -> case is_snmp_agent_function(N, A) of false -> @@ -189,9 +192,100 @@ obsolete_1(snmp, N, A) -> integer_to_list(A)++" instead"} end; +obsolete_1(snmpm, agent_info, 3) -> + {deprecated, {snmpm, agent_info, 2}, "R16B"}; +obsolete_1(snmpm, update_agent_info, 5) -> + {deprecated, {snmpm, update_agent_info, 4}, "R16B"}; +obsolete_1(snmpm, g, 3) -> + {deprecated, {snmpm, sync_get, 3}, "R16B"}; +obsolete_1(snmpm, g, 4) -> + {deprecated, {snmpm, sync_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, g, 5) -> + {deprecated, {snmpm, sync_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, g, 6) -> + {deprecated, {snmpm, sync_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, g, 7) -> + {deprecated, {snmpm, sync_get, 6}, "R16B"}; +obsolete_1(snmpm, ag, 3) -> + {deprecated, {snmpm, async_get, 3}, "R16B"}; +obsolete_1(snmpm, ag, 4) -> + {deprecated, {snmpm, async_get, [3,4]}, "R16B"}; +obsolete_1(snmpm, ag, 5) -> + {deprecated, {snmpm, async_get, [4,5]}, "R16B"}; +obsolete_1(snmpm, ag, 6) -> + {deprecated, {snmpm, async_get, [5,6]}, "R16B"}; +obsolete_1(snmpm, ag, 7) -> + {deprecated, {snmpm, async_get, 6}, "R16B"}; +obsolete_1(snmpm, gn, 3) -> + {deprecated, {snmpm, sync_get_next, 3}, "R16B"}; +obsolete_1(snmpm, gn, 4) -> + {deprecated, {snmpm, sync_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, gn, 5) -> + {deprecated, {snmpm, sync_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, gn, 6) -> + {deprecated, {snmpm, sync_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, gn, 7) -> + {deprecated, {snmpm, sync_get_next, 6}, "R16B"}; +obsolete_1(snmpm, agn, 3) -> + {deprecated, {snmpm, async_get_next, 3}, "R16B"}; +obsolete_1(snmpm, agn, 4) -> + {deprecated, {snmpm, async_get_next, [3,4]}, "R16B"}; +obsolete_1(snmpm, agn, 5) -> + {deprecated, {snmpm, async_get_next, [4,5]}, "R16B"}; +obsolete_1(snmpm, agn, 6) -> + {deprecated, {snmpm, async_get_next, [5,6]}, "R16B"}; +obsolete_1(snmpm, agn, 7) -> + {deprecated, {snmpm, async_get_next, 6}, "R16B"}; +obsolete_1(snmpm, s, 3) -> + {deprecated, {snmpm, sync_set, 3}, "R16B"}; +obsolete_1(snmpm, s, 4) -> + {deprecated, {snmpm, sync_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, s, 5) -> + {deprecated, {snmpm, sync_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, s, 6) -> + {deprecated, {snmpm, sync_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, s, 7) -> + {deprecated, {snmpm, sync_set, 6}, "R16B"}; +obsolete_1(snmpm, as, 3) -> + {deprecated, {snmpm, async_set, 3}, "R16B"}; +obsolete_1(snmpm, as, 4) -> + {deprecated, {snmpm, async_set, [3,4]}, "R16B"}; +obsolete_1(snmpm, as, 5) -> + {deprecated, {snmpm, async_set, [4,5]}, "R16B"}; +obsolete_1(snmpm, as, 6) -> + {deprecated, {snmpm, async_set, [5,6]}, "R16B"}; +obsolete_1(snmpm, as, 7) -> + {deprecated, {snmpm, async_set, 6}, "R16B"}; +obsolete_1(snmpm, gb, 5) -> + {deprecated, {snmpm, sync_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, gb, 6) -> + {deprecated, {snmpm, sync_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, gb, 7) -> + {deprecated, {snmpm, sync_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, gb, 8) -> + {deprecated, {snmpm, sync_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, gb, 9) -> + {deprecated, {snmpm, sync_get_bulk, 8}, "R16B"}; +obsolete_1(snmpm, agb, 5) -> + {deprecated, {snmpm, async_get_bulk, 5}, "R16B"}; +obsolete_1(snmpm, agb, 6) -> + {deprecated, {snmpm, async_get_bulk, [5,6]}, "R16B"}; +obsolete_1(snmpm, agb, 7) -> + {deprecated, {snmpm, async_get_bulk, [6,7]}, "R16B"}; +obsolete_1(snmpm, agb, 8) -> + {deprecated, {snmpm, async_get_bulk, [7,8]}, "R16B"}; +obsolete_1(snmpm, agb, 9) -> + {deprecated, {snmpm, async_get_bulk, 8}, "R16B"}; + + +%% *** MEGACO *** + obsolete_1(megaco, format_versions, 1) -> {deprecated, "Deprecated; use megaco:print_version_info/0,1 instead"}; + +%% *** OS-MON-MIB *** + obsolete_1(os_mon_mib, init, 1) -> {deprecated, {os_mon_mib, load, 1}}; obsolete_1(os_mon_mib, stop, 1) -> -- cgit v1.2.3 From 3bc0fccb951ffb0909d2824b65d58ad31ad16cc5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 1 Apr 2011 11:51:58 +0200 Subject: Added crypto-start-check to undef_funcs test case. --- lib/inets/test/inets_app_test.erl | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/inets/test/inets_app_test.erl b/lib/inets/test/inets_app_test.erl index 11b507fa26..49ea18501f 100644 --- a/lib/inets/test/inets_app_test.erl +++ b/lib/inets/test/inets_app_test.erl @@ -241,6 +241,20 @@ undef_funcs(suite) -> undef_funcs(doc) -> []; undef_funcs(Config) when is_list(Config) -> + %% We need to check if there is a point to run this test. + %% On some platforms, crypto will not build, which in turn + %% causes ssl to not to not build (at this time, this will + %% change in the future). + %% So, we first check if we can start crypto, and if not, + %% we skip this test case! + case (catch crypto:start()) of + ok -> + ok; + {error, {already_started, crypto}} -> + ok; + _ -> + ?SKIP(crypto_start_check_failed) + end, App = inets, AppFile = key1search(app_file, Config), Mods = key1search(modules, AppFile), -- cgit v1.2.3 From 51e89f67ba97074ec87275d50021749a18d004e9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 1 Apr 2011 12:09:26 +0200 Subject: Added export of new (ip-) verification function. --- lib/snmp/src/misc/snmp_misc.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/snmp/src/misc/snmp_misc.erl b/lib/snmp/src/misc/snmp_misc.erl index 2dce76992b..6adef06ab9 100644 --- a/lib/snmp/src/misc/snmp_misc.erl +++ b/lib/snmp/src/misc/snmp_misc.erl @@ -40,7 +40,7 @@ get_option/2, get_option/3, get_sec_level/1, - ip/1, + ip/1, ip/2, is_auth/1, is_BitString/1, is_oid/1, -- cgit v1.2.3 From 7354ffb0e5cde599fcad776febd33cdb234f30a7 Mon Sep 17 00:00:00 2001 From: Kostis Sagonas Date: Sun, 3 Apr 2011 19:10:06 +0300 Subject: Add specs for functions that do not return --- lib/snmp/src/agent/snmpa_usm.erl | 17 +++++++---------- lib/snmp/src/manager/snmpm_usm.erl | 5 +++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_usm.erl b/lib/snmp/src/agent/snmpa_usm.erl index f35d1f1916..6f54307f9f 100644 --- a/lib/snmp/src/agent/snmpa_usm.erl +++ b/lib/snmp/src/agent/snmpa_usm.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 @@ -330,7 +330,6 @@ non_authoritative(SecName, end end. - is_auth(?usmNoAuthProtocol, _, _, _, SecName, _, _, _, _) -> % 3.2.5 error(usmStatsUnsupportedSecLevels, ?usmStatsUnsupportedSecLevels_instance, SecName); % OTP-5464 @@ -613,8 +612,7 @@ authenticate_outgoing(Message, UsmSecParams, end, ?vtrace("authenticate_outgoing -> encode message only",[]), snmp_pdus:enc_message_only(Message2). - - + %%----------------------------------------------------------------- %% Auth and priv algorithms @@ -753,14 +751,19 @@ set_engine_latest_time(SnmpEngineID, EngineTime) -> %%----------------------------------------------------------------- %% Utility functions %%----------------------------------------------------------------- +-spec error(term()) -> no_return(). error(Reason) -> throw({error, Reason}). +-spec error(term(), term()) -> no_return(). error(Reason, ErrorInfo) -> throw({error, Reason, ErrorInfo}). +-spec error(term(), term(), term()) -> no_return(). error(Variable, Oid, SecName) -> error(Variable, Oid, SecName, []). + +-spec error(term(), term(), term(), [term()]) -> no_return(). error(Variable, Oid, SecName, Opts) -> Val = inc(Variable), ErrorInfo = {#varbind{oid = Oid, @@ -772,7 +775,6 @@ error(Variable, Oid, SecName, Opts) -> inc(Name) -> ets:update_counter(snmp_agent_table, Name, 1). - get_counter(Name) -> case (catch ets:lookup(snmp_agent_table, Name)) of [{_, Val}] -> @@ -780,8 +782,3 @@ get_counter(Name) -> _ -> 0 end. - - - - - diff --git a/lib/snmp/src/manager/snmpm_usm.erl b/lib/snmp/src/manager/snmpm_usm.erl index 449127844a..ef2070a90e 100644 --- a/lib/snmp/src/manager/snmpm_usm.erl +++ b/lib/snmp/src/manager/snmpm_usm.erl @@ -476,14 +476,19 @@ set_engine_latest_time(SnmpEngineID, EngineTime) -> %%----------------------------------------------------------------- %% Utility functions %%----------------------------------------------------------------- +-spec error(term()) -> no_return(). error(Reason) -> throw({error, Reason}). +-spec error(term(), term()) -> no_return(). error(Reason, ErrorInfo) -> throw({error, Reason, ErrorInfo}). +-spec error(term(), term(), term()) -> no_return(). error(Variable, Oid, SecName) -> error(Variable, Oid, SecName, []). + +-spec error(term(), term(), term(), [term()]) -> no_return(). error(Variable, Oid, SecName, Opts) -> Val = inc(Variable), ErrorInfo = {#varbind{oid = Oid, -- cgit v1.2.3 From 245061870d1be138819c5a3df235da8faef90899 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 4 Apr 2011 16:09:50 +0200 Subject: Added extra-info as an argument to be provided when sending trap/notification and passed through to the net-if process. First version. --- lib/snmp/src/agent/snmpa.erl | 72 +++++++-- lib/snmp/src/agent/snmpa_agent.erl | 295 ++++++++++++++++++++++++++++------ lib/snmp/src/agent/snmpa_internal.hrl | 1 + lib/snmp/src/agent/snmpa_net_if.erl | 18 +++ lib/snmp/src/agent/snmpa_trap.erl | 140 +++++++++------- 5 files changed, 415 insertions(+), 111 deletions(-) diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index 22fbd33add..4cdc9d0064 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -108,8 +108,9 @@ -export([print_mib_info/0, print_mib_tables/0, print_mib_variables/0]). -include("snmpa_atl.hrl"). +-include("snmpa_internal.hrl"). --define(EXTRA_INFO, undefined). +-define(DISCO_EXTRA_INFO, undefined). %%----------------------------------------------------------------- @@ -596,22 +597,67 @@ set_request_limit(Agent, NewLimit) -> %% - +send_notification2(Agent, Notification, SendOpts) -> + snmpa_agent:send_notification(Agent, Notification, SendOpts). + +send_notification(Agent, Notification) -> + SendOpts = + [ + {receiver, no_receiver}, + {varbinds, []}, + {name, ""}, + {context, ""}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). + send_notification(Agent, Notification, Recv) -> - send_notification(Agent, Notification, Recv, "", "", []). + SendOpts = + [ + {receiver, Recv}, + {varbinds, []}, + {name, ""}, + {context, ""}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, Varbinds) -> - send_notification(Agent, Notification, Recv, "", "", Varbinds). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, ""}, + {context, ""}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, NotifyName, Varbinds) -> - send_notification(Agent, Notification, Recv, NotifyName, "", Varbinds). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, ""}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, NotifyName, ContextName, Varbinds) when (is_list(NotifyName) andalso is_list(ContextName) andalso is_list(Varbinds)) -> - snmpa_agent:send_trap(Agent, Notification, NotifyName, - ContextName, Recv, Varbinds). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, ContextName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification2(Agent, Notification, SendOpts). send_notification(Agent, Notification, Recv, NotifyName, ContextName, Varbinds, LocalEngineID) @@ -619,8 +665,16 @@ send_notification(Agent, Notification, Recv, is_list(ContextName) andalso is_list(Varbinds) andalso is_list(LocalEngineID)) -> - snmpa_agent:send_trap(Agent, Notification, NotifyName, - ContextName, Recv, Varbinds, LocalEngineID). + SendOpts = + [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, ContextName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO}, + {local_enging_id, LocalEngineID} + ], + send_notification2(Agent, Notification, SendOpts). %% Kept for backwards compatibility send_trap(Agent, Trap, Community) -> @@ -655,7 +709,7 @@ discovery(TargetName, Notification, Varbinds, DiscoHandler) discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler). discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler) -> - ExtraInfo = ?EXTRA_INFO, + ExtraInfo = ?DISCO_EXTRA_INFO, discovery(TargetName, Notification, ContextName, Varbinds, DiscoHandler, ExtraInfo). diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index d9a0438b56..0a28e753ce 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -30,7 +30,7 @@ -export([subagent_set/2, load_mibs/2, unload_mibs/2, which_mibs/1, whereis_mib/2, info/1, register_subagent/3, unregister_subagent/2, - send_trap/6, send_trap/7, + send_notification/3, register_notification_filter/5, unregister_notification_filter/2, which_notification_filter/1, @@ -62,10 +62,16 @@ -export([increment_counter/3]). -export([restart_worker/1, restart_set_worker/1]). +%% For backward compatibillity +-export([send_trap/6, send_trap/7]). + %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3, tr_var/2, tr_varbind/1, - handle_pdu/7, worker/2, worker_loop/1, do_send_trap/7]). + handle_pdu/7, worker/2, worker_loop/1, + do_send_trap/7, do_send_trap/8]). + +-include("snmpa_internal.hrl"). -ifndef(default_verbosity). -define(default_verbosity,silence). @@ -527,6 +533,11 @@ which_notification_filter(Agent) -> call(Agent, which_notification_filter). +send_notification(Agent, Notification, SendOpts) -> + Msg = {send_notif, Notification, SendOpts}, + maybe_call(Agent, Msg). + +%% send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) -> ?d("send_trap -> entry with" "~n self(): ~p" @@ -538,13 +549,33 @@ send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) -> "~n Varbinds: ~p", [self(), Agent, wis(Agent), Trap, NotifyName, CtxName, Recv, Varbinds]), - Msg = {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds}, - case (wis(Agent) =:= self()) of - false -> - call(Agent, Msg); - true -> - Agent ! Msg - end. + SendOpts = [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, CtxName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO} + ], + send_notification(Agent, Trap, SendOpts). + +%% send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds) -> +%% ?d("send_trap -> entry with" +%% "~n self(): ~p" +%% "~n Agent: ~p [~p]" +%% "~n Trap: ~p" +%% "~n NotifyName: ~p" +%% "~n CtxName: ~p" +%% "~n Recv: ~p" +%% "~n Varbinds: ~p", +%% [self(), Agent, wis(Agent), +%% Trap, NotifyName, CtxName, Recv, Varbinds]), +%% Msg = {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds}, +%% case (wis(Agent) =:= self()) of +%% false -> +%% call(Agent, Msg); +%% true -> +%% Agent ! Msg +%% end. send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) -> ?d("send_trap -> entry with" @@ -558,14 +589,38 @@ send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) -> "~n LocalEngineID: ~p", [self(), Agent, wis(Agent), Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID]), - Msg = - {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID}, - case (wis(Agent) =:= self()) of - false -> - call(Agent, Msg); - true -> - Agent ! Msg - end. + SendOpts = [ + {receiver, Recv}, + {varbinds, Varbinds}, + {name, NotifyName}, + {context, CtxName}, + {extra, ?DEFAULT_NOTIF_EXTRA_INFO}, + {local_engine_id, LocalEngineID} + ], + send_notification(Agent, Trap, SendOpts). + +%% send_trap(Agent, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID) -> +%% ?d("send_trap -> entry with" +%% "~n self(): ~p" +%% "~n Agent: ~p [~p]" +%% "~n Trap: ~p" +%% "~n NotifyName: ~p" +%% "~n CtxName: ~p" +%% "~n Recv: ~p" +%% "~n Varbinds: ~p" +%% "~n LocalEngineID: ~p", +%% [self(), Agent, wis(Agent), +%% Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID]), +%% Msg = +%% {send_trap, Trap, NotifyName, CtxName, Recv, Varbinds, LocalEngineID}, +%% case (wis(Agent) =:= self()) of +%% false -> +%% call(Agent, Msg); +%% true -> +%% Agent ! Msg +%% end. + +%% %% -- Discovery functions -- @@ -655,7 +710,14 @@ wis(Atom) when is_atom(Atom) -> forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds) -> - Agent ! {forward_trap, TrapRecord, NotifyName, CtxName, Recv, Varbinds}. + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds, + ExtraInfo). + +forward_trap(Agent, TrapRecord, NotifyName, CtxName, Recv, Varbinds, + ExtraInfo) -> + Agent ! {forward_trap, TrapRecord, NotifyName, CtxName, Recv, Varbinds, + ExtraInfo}. %%----------------------------------------------------------------- @@ -745,6 +807,22 @@ handle_info(worker_available, S) -> ?vdebug("worker available",[]), {noreply, S#state{worker_state = ready}}; +handle_info({send_notif, Notification, SendOpts}, S) -> + ?vlog("[handle_info] send trap request:" + "~n Notification: ~p" + "~n SendOpts: ~p", + [Notification, SendOpts]), + case (catch handle_send_trap(cast, S, Notification, SendOpts)) of + {ok, NewS} -> + {noreply, NewS}; + {'EXIT', R} -> + ?vinfo("Trap not sent:~n ~p", [R]), + {noreply, S}; + _ -> + {noreply, S} + end; + +%% handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, S) -> ?vlog("[handle_info] send trap request:" "~n Trap: ~p" @@ -753,9 +831,10 @@ handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, S) -> "~n Recv: ~p" "~n Varbinds: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, case (catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {noreply, NewS}; {'EXIT', R} -> @@ -775,8 +854,9 @@ handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds, "~n Varbinds: ~p" "~n LocalEngineID: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, case (catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {noreply, NewS}; {'EXIT', R} -> @@ -785,7 +865,30 @@ handle_info({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds, _ -> {noreply, S} end; +%% +handle_info({forward_trap, TrapRecord, NotifyName, ContextName, + Recv, Varbinds, ExtraInfo}, S) -> + ?vlog("[handle_info] forward trap request:" + "~n TrapRecord: ~p" + "~n NotifyName: ~p" + "~n ContextName: ~p" + "~n Recv: ~p" + "~n Varbinds: ~p", + [TrapRecord, NotifyName, ContextName, Recv, Varbinds]), + LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, + case (catch maybe_send_trap(S, TrapRecord, NotifyName, ContextName, + Recv, Varbinds, LocalEngineID, ExtraInfo)) of + {ok, NewS} -> + {noreply, NewS}; + {'EXIT', R} -> + ?vinfo("Trap not sent:~n ~p", [R]), + {noreply, S}; + _ -> + {noreply, S} + end; + +%% handle_info({forward_trap, TrapRecord, NotifyName, ContextName, Recv, Varbinds}, S) -> ?vlog("[handle_info] forward trap request:" @@ -795,9 +898,10 @@ handle_info({forward_trap, TrapRecord, NotifyName, ContextName, "~n Recv: ~p" "~n Varbinds: ~p", [TrapRecord, NotifyName, ContextName, Recv, Varbinds]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, case (catch maybe_send_trap(S, TrapRecord, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {noreply, NewS}; {'EXIT', R} -> @@ -806,6 +910,7 @@ handle_info({forward_trap, TrapRecord, NotifyName, ContextName, _ -> {noreply, S} end; +%% handle_info({backup_done, Reply}, #state{backup = {_, From}} = S) -> ?vlog("[handle_info] backup done:" @@ -908,6 +1013,23 @@ handle_call(restart_set_worker, _From, #state{set_worker = Pid} = S) -> end, {reply, ok, S}; +handle_call({send_notif, Notification, SendOpts}, _From, S) -> + ?vlog("[handle_info] send trap request:" + "~n Notification: ~p" + "~n SendOpts: ~p", + [Notification, SendOpts]), + case (catch handle_send_trap(call, S, Notification, SendOpts)) of + {ok, NewS} -> + {reply, ok, NewS}; + {'EXIT', Reason} -> + ?vinfo("Trap not sent:~n ~p", [Reason]), + {reply, {error, {send_failed, Reason}}, S}; + _ -> + ?vinfo("Trap not sent", []), + {reply, {error, send_failed}, S} + end; + +%% handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, _From, S) -> ?vlog("[handle_call] send trap request:" @@ -917,6 +1039,7 @@ handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, "~n Recv: ~p" "~n Varbinds: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = case S#state.type of master_agent -> @@ -929,7 +1052,7 @@ handle_call({send_trap, Trap, NotifyName, ContextName, Recv, Varbinds}, ignore end, case (catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {reply, ok, NewS}; {'EXIT', Reason} -> @@ -951,8 +1074,9 @@ handle_call({send_trap, Trap, NotifyName, "~n Varbinds: ~p" "~n LocalEngineID: ~p", [Trap, NotifyName, ContextName, Recv, Varbinds, LocalEngineID]), + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, case (catch handle_send_trap(S, Trap, NotifyName, ContextName, - Recv, Varbinds, LocalEngineID)) of + Recv, Varbinds, LocalEngineID, ExtraInfo)) of {ok, NewS} -> {reply, ok, NewS}; {'EXIT', Reason} -> @@ -962,6 +1086,7 @@ handle_call({send_trap, Trap, NotifyName, ?vinfo("Trap not sent", []), {reply, {error, send_failed}, S} end; +%% handle_call({discovery, TargetName, Notification, ContextName, Vbs, DiscoHandler, @@ -1526,19 +1651,24 @@ spawn_thread(Vsn, Pdu, PduMS, ACMData, Address, Extra) -> proc_lib:spawn_link(?MODULE, handle_pdu, Args). spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID) -> + LocalEngineID, ExtraInfo) -> Dict = get(), proc_lib:spawn_link(?MODULE, do_send_trap, [TrapRec, NotifyName, ContextName, - Recv, Vbs, LocalEngineID, Dict]). + Recv, Vbs, LocalEngineID, ExtraInfo, Dict]). do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, Dict) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, Dict). +do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, Dict) -> lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict), put(sname,trap_sender_short_name(get(sname))), ?vlog("starting",[]), snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, get(net_if)). + LocalEngineID, ExtraInfo, get(net_if)). worker(Master, Dict) -> lists:foreach(fun({Key, Val}) -> put(Key, Val) end, Dict), @@ -1553,21 +1683,34 @@ worker_loop(Master) -> handle_pdu(Vsn, Pdu, PduMS, ACMData, Address, Extra), Master ! worker_available; - %% We don't trap exits! + %% We don't trap EXITs! {TrapRec, NotifyName, ContextName, Recv, Vbs} -> ?vtrace("worker_loop -> send trap:" "~n ~p", [TrapRec]), snmpa_trap:send_trap(TrapRec, NotifyName, - ContextName, Recv, Vbs, get(net_if)), + ContextName, Recv, Vbs, + ?DEFAULT_NOTIF_EXTRA_INFO, + get(net_if)), Master ! worker_available; - %% We don't trap exits! + %% We don't trap EXITs! {send_trap, TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID} -> ?vtrace("worker_loop -> send trap:" "~n ~p", [TrapRec]), snmpa_trap:send_trap(TrapRec, NotifyName, - ContextName, Recv, Vbs, LocalEngineID, + ContextName, Recv, Vbs, + LocalEngineID, ?DEFAULT_NOTIF_EXTRA_INFO, + get(net_if)), + Master ! worker_available; + + {send_trap, + TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, ExtraInfo} -> + ?vtrace("worker_loop -> send trap:" + "~n ~p", [TrapRec]), + snmpa_trap:send_trap(TrapRec, NotifyName, + ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, get(net_if)), Master ! worker_available; @@ -1716,34 +1859,79 @@ handle_acm_error(Vsn, Reason, Pdu, ACMData, Address, Extra) -> ok end. +get_opt(Key, Default, SendOpts) -> + case lists:keysearch(Key, 1, SendOpts) of + {value, {Key, Value}} -> + Value; + false -> + Default + end. -handle_send_trap(S, TrapName, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) -> +handle_send_trap(call, #state{type = master_agent} = S, + Notification, SendOpts) -> + SendOpts2 = + case lists:keymember(local_engine_id, 1, SendOpts) of + true -> + SendOpts; + false -> + [{local_engine_id, ?DEFAULT_LOCAL_ENGINE_ID}|SendOpts] + end, + handle_send_trap(S, Notification, SendOpts2); +handle_send_trap(call, S, Notification, SendOpts) -> + SendOpts2 = + case lists:keymember(local_engine_id, 1, SendOpts) of + true -> + SendOpts; + false -> + %% subagent - + %% we don't need this now, eventually the trap send + %% request will reach the master-agent and then it + %% will look up the proper engine id. + [{local_engine_id, ignore}|SendOpts] + end, + handle_send_trap(S, Notification, SendOpts2); +handle_send_trap(_, S, Notification, SendOpts) -> + handle_send_trap(S, Notification, SendOpts). + +handle_send_trap(S, Notification, SendOpts) -> + NotifyName = get_opt(name, "", SendOpts), + ContextName = get_opt(context, "", SendOpts), + Recv = get_opt(receiver, no_receiver, SendOpts), + Varbinds = get_opt(varbinds, [], SendOpts), + ExtraInfo = get_opt(extra, ?DEFAULT_NOTIF_EXTRA_INFO, SendOpts), + LocalEngineID = + get_opt(local_engine_id, ?DEFAULT_LOCAL_ENGINE_ID, SendOpts), + handle_send_trap(S, Notification, NotifyName, ContextName, Recv, Varbinds, + LocalEngineID, ExtraInfo). + +handle_send_trap(#state{type = Type} = S, + Notification, NotifyName, ContextName, Recv, Varbinds, + LocalEngineID, ExtraInfo) -> ?vtrace("handle_send_trap -> entry with" "~n Agent type: ~p" "~n TrapName: ~p" "~n NotifyName: ~p" "~n ContextName: ~p" "~n LocalEngineID: ~p", - [S#state.type, TrapName, NotifyName, ContextName, LocalEngineID]), - case snmpa_trap:construct_trap(TrapName, Varbinds) of + [Type, Notification, NotifyName, ContextName, LocalEngineID]), + case snmpa_trap:construct_trap(Notification, Varbinds) of {ok, TrapRecord, VarList} -> ?vtrace("handle_send_trap -> construction complete: " "~n TrapRecord: ~p" "~n VarList: ~p", [TrapRecord, VarList]), - case S#state.type of + case Type of subagent -> ?vtrace("handle_send_trap -> [sub] forward trap",[]), maybe_forward_trap(S, TrapRecord, NotifyName, - ContextName, Recv, VarList), + ContextName, Recv, VarList, ExtraInfo), {ok, S}; master_agent -> ?vtrace("handle_send_trap -> " "[master] handle send trap",[]), maybe_send_trap(S, TrapRecord, NotifyName, ContextName, Recv, VarList, - LocalEngineID) + LocalEngineID, ExtraInfo) end; error -> error @@ -1751,7 +1939,7 @@ handle_send_trap(S, TrapName, NotifyName, ContextName, Recv, Varbinds, maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S, - TrapRec, NotifyName, ContextName, Recv, V) -> + TrapRec, NotifyName, ContextName, Recv, V, ExtraInfo) -> ?vtrace("maybe_forward_trap -> entry with" "~n NFs: ~p", [NFs]), case filter_notification(NFs, [], TrapRec) of @@ -1767,13 +1955,15 @@ maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S, {send, [], TrapRec2} -> ?vtrace("maybe_forward_trap -> forward trap:" "~n ~p", [TrapRec2]), - forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V), + forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V, + ExtraInfo), {ok, S}; {send, Removed, TrapRec2} -> ?vtrace("maybe_forward_trap -> forward trap:" "~n ~p", [TrapRec2]), - forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V), + forward_trap(Parent, TrapRec2, NotifyName, ContextName, Recv, V, + ExtraInfo), NFs2 = del_notification_filter(Removed, NFs), {ok, S#state{nfilters = NFs2}} end. @@ -1781,7 +1971,7 @@ maybe_forward_trap(#state{parent = Parent, nfilters = NFs} = S, maybe_send_trap(#state{nfilters = NFs} = S, TrapRec, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) -> + LocalEngineID, ExtraInfo) -> ?vtrace("maybe_send_trap -> entry with" "~n NFs: ~p", [NFs]), case filter_notification(NFs, [], TrapRec) of @@ -1799,7 +1989,7 @@ maybe_send_trap(#state{nfilters = NFs} = S, "~n ~p", [TrapRec2]), do_handle_send_trap(S, TrapRec2, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID); + LocalEngineID, ExtraInfo); {send, Removed, TrapRec2} -> ?vtrace("maybe_send_trap -> send trap:" @@ -1807,36 +1997,37 @@ maybe_send_trap(#state{nfilters = NFs} = S, NFs2 = del_notification_filter(Removed, NFs), do_handle_send_trap(S#state{nfilters = NFs2}, TrapRec2, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) + LocalEngineID, ExtraInfo) end. do_handle_send_trap(S, TrapRec, NotifyName, ContextName, Recv, Varbinds, - LocalEngineID) -> + LocalEngineID, ExtraInfo) -> Vbs = snmpa_trap:try_initialise_vars(get(mibserver), Varbinds), case S#state.type of subagent -> forward_trap(S#state.parent, TrapRec, NotifyName, ContextName, - Recv, Vbs), + Recv, Vbs, ExtraInfo), {ok, S}; master_agent when S#state.multi_threaded =:= false -> ?vtrace("do_handle_send_trap -> send trap:" "~n ~p", [TrapRec]), snmpa_trap:send_trap(TrapRec, NotifyName, ContextName, - Recv, Vbs, LocalEngineID, get(net_if)), + Recv, Vbs, LocalEngineID, ExtraInfo, + get(net_if)), {ok, S}; master_agent when S#state.worker_state =:= busy -> %% Main worker busy => create new worker ?vtrace("do_handle_send_trap -> main worker busy: " "spawn a trap sender", []), spawn_trap_thread(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID), + LocalEngineID, ExtraInfo), {ok, S}; master_agent -> %% Send to main worker ?vtrace("do_handle_send_trap -> send to main worker",[]), S#state.worker ! {send_trap, TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID}, + LocalEngineID, ExtraInfo}, {ok, S#state{worker_state = busy}} end. @@ -3974,6 +4165,14 @@ user_err(F, A) -> %% --------------------------------------------------------------------- +maybe_call(Server, Req) -> + case (wis(Server) =:= self()) of + false -> + call(Server, Req); + true -> + Server ! Req + end. + call(Server, Req) -> gen_server:call(Server, Req, infinity). diff --git a/lib/snmp/src/agent/snmpa_internal.hrl b/lib/snmp/src/agent/snmpa_internal.hrl index 9fa874f119..fba800175b 100644 --- a/lib/snmp/src/agent/snmpa_internal.hrl +++ b/lib/snmp/src/agent/snmpa_internal.hrl @@ -23,6 +23,7 @@ -include_lib("snmp/src/app/snmp_internal.hrl"). -define(DEFAULT_LOCAL_ENGINE_ID, snmp_framework_mib:get_engine_id()). +-define(DEFAULT_NOTIF_EXTRA_INFO, undefined). -define(snmpa_info(F, A), ?snmp_info("agent", F, A)). -define(snmpa_warning(F, A), ?snmp_warning("agent", F, A)). diff --git a/lib/snmp/src/agent/snmpa_net_if.erl b/lib/snmp/src/agent/snmpa_net_if.erl index d4bb5bdf9f..bbc43c3da7 100644 --- a/lib/snmp/src/agent/snmpa_net_if.erl +++ b/lib/snmp/src/agent/snmpa_net_if.erl @@ -314,6 +314,14 @@ loop(S) -> NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, undefined), loop(NewS); + %% We dont use the extra-info at this time, ... + {send_pdu, Vsn, Pdu, MsgData, To, _ExtraInfo} -> + ?vdebug("send pdu: " + "~n Pdu: ~p" + "~n To: ~p", [Pdu, To]), + NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, undefined), + loop(NewS); + %% Informs {send_pdu_req, Vsn, Pdu, MsgData, To, From} -> ?vdebug("send pdu request: " @@ -324,6 +332,16 @@ loop(S) -> NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, From), loop(NewS); + %% We dont use the extra-info at this time, ... + {send_pdu_req, Vsn, Pdu, MsgData, To, From, _ExtraInfo} -> + ?vdebug("send pdu request: " + "~n Pdu: ~p" + "~n To: ~p" + "~n From: ~p", + [Pdu, To, toname(From)]), + NewS = maybe_handle_send_pdu(S, Vsn, Pdu, MsgData, To, From), + loop(NewS); + %% Discovery Inform {send_discovery, Pdu, MsgData, To, From} -> ?vdebug("received send discovery request: " diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 09ecb5228b..0f6a5f8984 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -24,12 +24,12 @@ %% External exports -export([construct_trap/2, try_initialise_vars/2, - send_trap/6, send_trap/7]). + send_trap/6, send_trap/7, send_trap/8]). -export([send_discovery/5]). %% Internal exports --export([init_v2_inform/9, - init_v3_inform/9, init_v3_inform/10, +-export([init_v2_inform/9, init_v2_inform/10, + init_v3_inform/9, init_v3_inform/10, init_v3_inform/11, send_inform/6]). -export([init_discovery_inform/12, send_discovery_inform/5]). @@ -46,6 +46,7 @@ -define(VMODULE,"TRAP"). -include("snmp_verbosity.hrl"). +-include("snmpa_internal.hrl"). %%----------------------------------------------------------------- @@ -336,20 +337,27 @@ make_varbind_list(Varbinds) -> %% SnmpTargetAddrTable (using the Tag). %%----------------------------------------------------------------- send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, NetIf) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, NetIf). + LocalEngineID, ExtraInfo, NetIf). -send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, NetIf) -> +send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, ExtraInfo, NetIf) -> + LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, + send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, + LocalEngineID, ExtraInfo, NetIf). + +send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, + ExtraInfo, NetIf) -> (catch do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, NetIf)). + LocalEngineID, ExtraInfo, NetIf)). do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, - LocalEngineID, NetIf) -> + LocalEngineID, ExtraInfo, NetIf) -> VarbindList = make_varbind_list(Vbs), Dests = find_dests(NotifyName), send_trap_pdus(Dests, ContextName, {TrapRec, VarbindList}, [], [], [], - Recv, LocalEngineID, NetIf). + Recv, LocalEngineID, ExtraInfo, NetIf). send_discovery(TargetName, Record, ContextName, Vbs, NetIf) -> case find_dest(TargetName) of @@ -635,7 +643,7 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, Type} | T], ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) -> + LocalEngineID, ExtraInfo, NetIf) -> ?vdebug("send trap pdus: " "~n Destination address: ~p" "~n Target name: ~p" @@ -661,13 +669,13 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, send_trap_pdus(T, ContextName, {TrapRec, Vbs}, [{DestAddr, Community} | V1Res], V2Res, V3Res, Recv, - LocalEngineID, NetIf); + LocalEngineID, ExtraInfo, NetIf); undefined -> ?vdebug("No community found for v1 dest: ~p", [element(2, DestAddr)]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) + LocalEngineID, ExtraInfo, NetIf) end; true when MpModel =:= ?MP_V2C -> ?vtrace("send_trap_pdus -> v2c mp model",[]), @@ -682,13 +690,14 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, [{DestAddr, Community, Type}|V2Res], - V3Res, Recv, LocalEngineID, NetIf); + V3Res, Recv, + LocalEngineID, ExtraInfo, NetIf); undefined -> ?vdebug("No community found for v2c dest: ~p", [element(2, DestAddr)]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) + LocalEngineID, ExtraInfo, NetIf) end; true when MpModel =:= ?MP_V3 -> ?vtrace("send_trap_pdus -> v3 mp model",[]), @@ -697,20 +706,20 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, [{DestAddr, MsgData, Type} | V3Res], - Recv, LocalEngineID, NetIf); + Recv, LocalEngineID, ExtraInfo, NetIf); true -> ?vlog("bad MpModel ~p for dest ~p", [MpModel, element(2, DestAddr)]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf); + LocalEngineID, ExtraInfo, NetIf); _ -> ?vlog("no access for dest: " "~n ~p in target ~p", [element(2, DestAddr), TargetName]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, - LocalEngineID, NetIf) + LocalEngineID, ExtraInfo, NetIf) end; {discarded, Reason} -> ?vlog("mib view error ~p for" @@ -718,24 +727,27 @@ send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, "~n SecName: ~w", [Reason, element(2, DestAddr), SecName]), send_trap_pdus(T, ContextName, {TrapRec, Vbs}, - V1Res, V2Res, V3Res, Recv, LocalEngineID, NetIf) + V1Res, V2Res, V3Res, Recv, + LocalEngineID, ExtraInfo, NetIf) end; -send_trap_pdus([], ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, - Recv, LocalEngineID, NetIf) -> +send_trap_pdus([], ContextName, {TrapRec, Vbs}, + V1Res, V2Res, V3Res, Recv, + LocalEngineID, ExtraInfo, + NetIf) -> SysUpTime = snmp_standard_mib:sys_up_time(), ?vdebug("send trap pdus with sysUpTime ~p", [SysUpTime]), InformRecvs = get_inform_recvs(V2Res ++ V3Res), InformTargets = [Addr || {Addr, _, _, _} <- InformRecvs], deliver_recv(Recv, snmp_targets, InformTargets), - send_v1_trap(TrapRec, V1Res, Vbs, NetIf, SysUpTime), - send_v2_trap(TrapRec, V2Res, Vbs, Recv, NetIf, SysUpTime), - send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, NetIf, + send_v1_trap(TrapRec, V1Res, Vbs, ExtraInfo, NetIf, SysUpTime), + send_v2_trap(TrapRec, V2Res, Vbs, Recv, ExtraInfo, NetIf, SysUpTime), + send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf, SysUpTime, ContextName). -send_v1_trap(_TrapRec, [], _Vbs, _NetIf, _SysUpTime) -> +send_v1_trap(_TrapRec, [], _Vbs, _ExtraInfo, _NetIf, _SysUpTime) -> ok; send_v1_trap(#trap{enterpriseoid = Enter, specificcode = Spec}, - V1Res, Vbs, NetIf, SysUpTime) -> + V1Res, Vbs, ExtraInfo, NetIf, SysUpTime) -> ?vdebug("prepare to send v1 trap " "~n '~p'" "~n with" @@ -747,9 +759,10 @@ send_v1_trap(#trap{enterpriseoid = Enter, specificcode = Spec}, lists:foreach(fun({Community, Addrs}) -> ?vtrace("send v1 trap pdu to ~p",[Addrs]), NetIf ! {send_pdu, 'version-1', TrapPdu, - {community, Community}, Addrs} + {community, Community}, Addrs, ExtraInfo} end, AddrCommunities); -send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, NetIf, SysUpTime) -> +send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, ExtraInfo, NetIf, + SysUpTime) -> %% Use alg. in rfc2089 to map a v2 trap to a v1 trap % delete Counter64 objects from vbs ?vdebug("prepare to send v1 trap '~p'",[Oid]), @@ -771,31 +784,31 @@ send_v1_trap(#notification{oid = Oid}, V1Res, Vbs, NetIf, SysUpTime) -> lists:foreach(fun({Community, Addrs}) -> ?vtrace("send v1 trap to ~p",[Addrs]), NetIf ! {send_pdu, 'version-1', TrapPdu, - {community, Community}, Addrs} + {community, Community}, Addrs, ExtraInfo} end, AddrCommunities). -send_v2_trap(_TrapRec, [], _Vbs, _Recv, _NetIf, _SysUpTime) -> +send_v2_trap(_TrapRec, [], _Vbs, _Recv, _ExtraInfo, _NetIf, _SysUpTime) -> ok; -send_v2_trap(TrapRec, V2Res, Vbs, Recv, NetIf, SysUpTime) -> +send_v2_trap(TrapRec, V2Res, Vbs, Recv, ExtraInfo, NetIf, SysUpTime) -> ?vdebug("prepare to send v2 trap",[]), {_Oid, IVbs} = mk_v2_trap(TrapRec, Vbs, SysUpTime), - TrapRecvs = get_trap_recvs(V2Res), - InformRecvs = get_inform_recvs(V2Res), - do_send_v2_trap(TrapRecvs, IVbs, NetIf), - do_send_v2_inform(InformRecvs, IVbs, Recv, NetIf). + TrapRecvs = get_trap_recvs(V2Res), + InformRecvs = get_inform_recvs(V2Res), + do_send_v2_trap(TrapRecvs, IVbs, ExtraInfo, NetIf), + do_send_v2_inform(InformRecvs, IVbs, Recv, ExtraInfo, NetIf). -send_v3_trap(_TrapRec, [], _Vbs, _Recv, _LocalEngineID, +send_v3_trap(_TrapRec, [], _Vbs, _Recv, _LocalEngineID, _ExtraInfo, _NetIf, _SysUpTime, _ContextName) -> ok; -send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, +send_v3_trap(TrapRec, V3Res, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf, SysUpTime, ContextName) -> ?vdebug("prepare to send v3 trap",[]), {_Oid, IVbs} = mk_v2_trap(TrapRec, Vbs, SysUpTime), % v2 refers to SMIv2; TrapRecvs = get_trap_recvs(V3Res), % same SMI for v3 InformRecvs = get_inform_recvs(V3Res), - do_send_v3_trap(TrapRecvs, ContextName, IVbs, NetIf), + do_send_v3_trap(TrapRecvs, ContextName, IVbs, ExtraInfo, NetIf), do_send_v3_inform(InformRecvs, ContextName, IVbs, Recv, - LocalEngineID, NetIf). + LocalEngineID, ExtraInfo, NetIf). mk_v2_trap(#notification{oid = Oid}, Vbs, SysUpTime) -> @@ -833,60 +846,70 @@ get_inform_recvs(InformRecvs) -> [{Addr, MsgData, Timeout, Retry} || {Addr, MsgData, {inform, Timeout, Retry}} <- InformRecvs]. -do_send_v2_trap([], _Vbs, _NetIf) -> +do_send_v2_trap([], _Vbs, _ExtraInfo, _NetIf) -> ok; -do_send_v2_trap(Recvs, Vbs, NetIf) -> +do_send_v2_trap(Recvs, Vbs, ExtraInfo, NetIf) -> TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'), AddrCommunities = mk_addr_communities(Recvs), lists:foreach(fun({Community, Addrs}) -> ?vtrace("~n send v2 trap to ~p",[Addrs]), NetIf ! {send_pdu, 'version-2', TrapPdu, - {community, Community}, Addrs} + {community, Community}, Addrs, ExtraInfo} end, AddrCommunities), ok. -do_send_v2_inform([], _Vbs, _Recv, _NetIf) -> +do_send_v2_inform([], _Vbs, _Recv, _ExtraInfo, _NetIf) -> ok; -do_send_v2_inform(Recvs, Vbs, Recv, NetIf) -> +do_send_v2_inform(Recvs, Vbs, Recv, ExtraInfo, NetIf) -> lists:foreach( fun({Addr, Community, Timeout, Retry}) -> ?vtrace("~n start inform sender to send v2 inform to ~p", [Addr]), proc_lib:spawn_link(?MODULE, init_v2_inform, [Addr, Timeout, Retry, Vbs, - Recv, NetIf, Community, + Recv, ExtraInfo, NetIf, Community, get(verbosity), get(sname)]) end, Recvs). -do_send_v3_trap([], _ContextName, _Vbs, _NetIf) -> +do_send_v3_trap([], _ContextName, _Vbs, _ExtraInfo, _NetIf) -> ok; -do_send_v3_trap(Recvs, ContextName, Vbs, NetIf) -> +do_send_v3_trap(Recvs, ContextName, Vbs, ExtraInfo, NetIf) -> TrapPdu = make_v2_notif_pdu(Vbs, 'snmpv2-trap'), % Yes, v2 ContextEngineId = snmp_framework_mib:get_engine_id(), lists:foreach(fun(Recv) -> ?vtrace("~n send v3 notif to ~p",[Recv]), NetIf ! {send_pdu, 'version-3', TrapPdu, - {v3, ContextEngineId, ContextName}, [Recv]} + {v3, ContextEngineId, ContextName}, + [Recv], ExtraInfo} end, Recvs), ok. -do_send_v3_inform([], _ContextName, _Vbs, _Recv, _LocalEngineID, _NetIf) -> +do_send_v3_inform([], _ContextName, _Vbs, _Recv, + _LocalEngineID, _ExtraInfo, _NetIf) -> ok; -do_send_v3_inform(Recvs, ContextName, Vbs, Recv, LocalEngineID, NetIf) -> +do_send_v3_inform(Recvs, ContextName, Vbs, Recv, + LocalEngineID, ExtraInfo, NetIf) -> lists:foreach( fun({Addr, MsgData, Timeout, Retry}) -> ?vtrace("~n start inform sender to send v3 inform to ~p", [Addr]), proc_lib:spawn_link(?MODULE, init_v3_inform, [{Addr, MsgData}, Timeout, Retry, Vbs, - Recv, LocalEngineID, NetIf, ContextName, + Recv, LocalEngineID, ExtraInfo, + NetIf, ContextName, get(verbosity), get(sname)]) end, Recvs). %% New process -init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community,V,S) -> +init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community, V, S) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, ExtraInfo, NetIf, + Community, V, S). + +init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, ExtraInfo, NetIf, + Community, V, S) -> %% Make a new Inform for each recipient; they need unique %% request-ids! put(verbosity,V), @@ -895,17 +918,26 @@ init_v2_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, Community,V,S) -> [Timeout,Retry]), InformPdu = make_v2_notif_pdu(Vbs, 'inform-request'), Msg = {send_pdu_req, 'version-2', InformPdu, {community, Community}, - [Addr], self()}, + [Addr], self(), ExtraInfo}, ?MODULE:send_inform(Addr, Timeout*10, Retry, Msg, Recv, NetIf). %% New process init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, NetIf, ContextName, V, S) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, LocalEngineID = ?DEFAULT_LOCAL_ENGINE_ID, - init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, + init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, + LocalEngineID, ExtraInfo, + NetIf, ContextName, V, S). + +init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, NetIf, + ContextName, V, S) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, + LocalEngineID, ExtraInfo, NetIf, ContextName, V, S). -init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, +init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf, ContextName, V, S) -> %% Make a new Inform for each recipient; they need unique %% request-ids! @@ -916,7 +948,7 @@ init_v3_inform(Addr, Timeout, Retry, Vbs, Recv, LocalEngineID, InformPdu = make_v2_notif_pdu(Vbs, 'inform-request'), % Yes, v2 ContextEngineId = LocalEngineID, Msg = {send_pdu_req, 'version-3', InformPdu, - {v3, ContextEngineId, ContextName}, [Addr], self()}, + {v3, ContextEngineId, ContextName}, [Addr], self(), ExtraInfo}, ?MODULE:send_inform(Addr, Timeout*10, Retry, Msg, Recv, NetIf). send_inform(Addr, _Timeout, -1, _Msg, Recv, _NetIf) -> -- cgit v1.2.3 From e9a4e34c2903abfd2d41a509b6d9cdea47ec7b89 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 4 Apr 2011 20:48:49 +0200 Subject: Added initial interface change. --- lib/snmp/src/manager/snmpm_server.erl | 61 +++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index d64b5b1d53..a5d4cdee32 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -32,14 +32,14 @@ register_user/4, register_user_monitor/4, unregister_user/1, - sync_get/4, sync_get/5, sync_get/6, - async_get/4, async_get/5, async_get/6, - sync_get_next/4, sync_get_next/5, sync_get_next/6, - async_get_next/4, async_get_next/5, async_get_next/6, - sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, - async_get_bulk/6, async_get_bulk/7, async_get_bulk/8, - sync_set/4, sync_set/5, sync_set/6, - async_set/4, async_set/5, async_set/6, + sync_get2/4, + async_get2/4, + sync_get_next2/4, + async_get_next2/4, + sync_get_bulk2/6, + async_get_bulk2/6, + sync_set2/4, + async_set2/4, cancel_async_request/2, %% discovery/2, discovery/3, discovery/4, discovery/5, discovery/6, @@ -55,6 +55,20 @@ ]). +%% +-export([sync_get/4, sync_get/5, sync_get/6, + async_get/4, async_get/5, async_get/6, + sync_get_next/4, sync_get_next/5, sync_get_next/6, + async_get_next/4, async_get_next/5, async_get_next/6, + sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, + async_get_bulk/6, async_get_bulk/7, async_get_bulk/8, + sync_set/4, sync_set/5, sync_set/6, + async_set/4, async_set/5, async_set/6 + ]). +%% + + + %% Internal exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]). @@ -192,6 +206,13 @@ unregister_user(UserId) -> %% -- [sync] get -- +%% The reason why we have a sync_get2 is to simplify backward +%% compatibillity. + +sync_get2(UserId, TargetName, Oids, Opts) -> + call({sync_get, self(), UserId, TargetName, Oids, Opts}). + +%% sync_get(UserId, TargetName, CtxName, Oids) -> sync_get(UserId, TargetName, CtxName, Oids, ?SYNC_GET_TIMEOUT). @@ -204,10 +225,17 @@ sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - call({sync_get, self(), UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}). + Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get2(UserId, TargetName, Oids, Opts). +%% + %% -- [async] get -- +async_get2(UserId, TargetName, Oids, Opts) -> + call({async_get, self(), UserId, TargetName, Oids, Opts}). + +%% async_get(UserId, TargetName, CtxName, Oids) -> async_get(UserId, TargetName, CtxName, Oids, ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). @@ -220,11 +248,17 @@ async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) andalso (Expire >= 0)) -> - call({async_get, self(), UserId, TargetName, CtxName, Oids, Expire, - ExtraInfo}). + Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_get2(UserId, TargetName, Oids, Opts). +%% + %% -- [sync] get-next -- +sync_get_next2(UserId, TargetName, Oids, Opts) -> + call({sync_get_next, UserId, TargetName, Oids, Opts}). + +%% sync_get_next(UserId, TargetName, CtxName, Oids) -> sync_get_next(UserId, TargetName, CtxName, Oids, ?SYNC_GET_TIMEOUT, ?EXTRA_INFO). @@ -237,8 +271,9 @@ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - call({sync_get_next, self(), UserId, TargetName, CtxName, Oids, Timeout, - ExtraInfo}). + Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}), + sync_get_next2(UserId, TargetName, Oids, Opts). +%% %% -- [async] get-next -- -- cgit v1.2.3 From 011728ea60468be455bb4b2ad7f79220f59fefda Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 5 Apr 2011 00:37:25 +0200 Subject: sync_get handled (also fixed the common agent-data creation function). --- lib/snmp/src/manager/snmpm_server.erl | 87 ++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index a5d4cdee32..55abca4b71 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -606,6 +606,19 @@ handle_call({unregister_user, UserId}, _From, State) -> %% We will reply to this request later, when the reply comes in from the %% agent, or when the timeout hits (unless we get an error now). +handle_call({sync_get, Pid, UserId, TargetName, Oids, SendOpts}, + From, State) -> + ?vlog("received sync_get [~p] request", [TargetName]), + case (catch handle_sync_get(Pid, + UserId, TargetName, Oids, SendOpts, + From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; + +%% handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> ?vlog("received sync_get [~p] request", [CtxName]), @@ -617,6 +630,7 @@ handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInf Error -> {reply, Error, State} end; +%% handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> @@ -936,36 +950,46 @@ terminate(Reason, #state{gct = GCT}) -> handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, Extra} + ], + handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State). + +handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State) -> ?vtrace("handle_sync_get -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]), - case agent_data(TargetName, CtxName) of + [Pid, UserId, TargetName, Oids, SendOpts, From]), + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get -> send a ~p message", [Vsn]), + Extra = get_opt(extra, ?EXTRA_INO, SendOpts), ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo, State), ?vdebug("handle_sync_get -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = get_opt(timeout, ?DEFAULT_SYNC_GET_TIMEOUT, SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_get -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = get, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = get, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; Error -> @@ -987,7 +1011,7 @@ handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, "~n Timeout: ~p" "~n From: ~p", [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]), - case agent_data(TargetName, CtxName) of + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_next -> send a ~p message", [Vsn]), ReqId = send_get_next_request(Oids, Vsn, MsgData, @@ -1034,7 +1058,7 @@ handle_sync_get_bulk(Pid, UserId, TargetName, CtxName, "~n From: ~p", [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Timeout, From]), - case agent_data(TargetName, CtxName) of + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_bulk -> send a ~p message", [Vsn]), ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, @@ -1077,7 +1101,7 @@ handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, "~n Timeout: ~p" "~n From: ~p", [Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, From]), - case agent_data(TargetName, CtxName) of + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_set -> send a ~p message", [Vsn]), ReqId = send_set_request(VarsAndVals, Vsn, MsgData, @@ -1119,7 +1143,7 @@ handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, "~n Oids: ~p" "~n Expire: ~p", [Pid, UserId, TargetName, CtxName, Oids, Expire]), - case agent_data(TargetName, CtxName) of + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get -> send a ~p message", [Vsn]), ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port, @@ -1157,7 +1181,7 @@ handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire, "~n Oids: ~p" "~n Expire: ~p", [Pid, UserId, TargetName, CtxName, Oids, Expire]), - case agent_data(TargetName, CtxName) of + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get_next -> send a ~p message", [Vsn]), ReqId = send_get_next_request(Oids, Vsn, MsgData, @@ -1235,7 +1259,7 @@ handle_async_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Expire, "~n VarsAndVals: ~p" "~n Expire: ~p", [Pid, UserId, TargetName, CtxName, VarsAndVals, Expire]), - case agent_data(TargetName, CtxName) of + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_set -> send a ~p message", [Vsn]), ReqId = send_set_request(VarsAndVals, Vsn, MsgData, @@ -2833,10 +2857,7 @@ request_id() -> %%---------------------------------------------------------------------- -agent_data(TargetName, CtxName) -> - agent_data(TargetName, CtxName, []). - -agent_data(TargetName, CtxName, Config) -> +agent_data(TargetName, SendOpts) -> case snmpm_config:agent_info(TargetName, all) of {ok, Info} -> Version = agent_data_item(version, Info), @@ -2850,13 +2871,13 @@ agent_data(TargetName, CtxName, Config) -> EngineId = agent_data_item(engine_id, Info), SecModel = agent_data_item(sec_model, - Config, + SendOpts, DefSecModel), SecName = agent_data_item(sec_name, - Config, + SendOpts, DefSecName), SecLevel = agent_data_item(sec_level, - Config, + SendOpts, DefSecLevel), {SecModel, SecName, mk_sec_level_flag(SecLevel), @@ -2866,10 +2887,10 @@ agent_data(TargetName, CtxName, Config) -> DefSecModel = agent_data_item(sec_model, Info), Comm = agent_data_item(community, - Config, + SendOPts, DefComm), SecModel = agent_data_item(sec_model, - Config, + SendOPts, DefSecModel), {Comm, SecModel} -- cgit v1.2.3 From dbacaf9db7977c3ad4ff35f7f6fe0085a6ab9956 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 5 Apr 2011 14:37:40 +0200 Subject: Documentation updated. --- lib/snmp/doc/src/notes.xml | 15 ++ lib/snmp/doc/src/snmp_agent_netif.xml | 198 ++++++++++++++++----------- lib/snmp/doc/src/snmpa.xml | 251 +++++++++++++++++++++------------- 3 files changed, 291 insertions(+), 173 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index f5fa7065fb..e9787c17da 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -43,6 +43,21 @@

-

--> + +

[agent] Pass extra info through the agent to the net-if + process when sending notifications.

+

See + + snmpa:send_notification2/3 for more info. + See also the incomming net-if messages when sending a + trap + and + + notification.

+

Own Id: OTP-9183

+

Aux Id: Seq 11817

+
+

[agent] Added support for sending traps to IPv6 targets.

See the diff --git a/lib/snmp/doc/src/snmp_agent_netif.xml b/lib/snmp/doc/src/snmp_agent_netif.xml index 1f2dbe80db..d751740a82 100644 --- a/lib/snmp/doc/src/snmp_agent_netif.xml +++ b/lib/snmp/doc/src/snmp_agent_netif.xml @@ -1,4 +1,4 @@ - + @@ -65,16 +65,19 @@

+ Messages

The section Messages describes mandatory messages, which Net if must send and be able to receive.

+ Outgoing Messages

Net if must send the following message when it receives an - SNMP PDU from the network that is aimed for the MasterAgent: -

+ SNMP PDU from the network that is aimed for the MasterAgent: +

+
 MasterAgent ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, Extra}
       
@@ -106,9 +109,10 @@ MasterAgent ! {snmp_pdu, Vsn, Pdu, PduMS, ACMData, From, Extra} the request.

The following message is used to report that a response to a - request has been received. The only request an agent can send - is an Inform-Request. -

+ request has been received. The only request an agent can send + is an Inform-Request. +

+
 Pid ! {snmp_response_received, Vsn, Pdu, From}
       
@@ -131,119 +135,153 @@ Pid ! {snmp_response_received, Vsn, Pdu, From}
+ Incoming Messages

This section describes the incoming messages which a Net if - process must be able to receive. -

+ process must be able to receive. +

+

{snmp_response, Vsn, Pdu, Type, ACMData, To, Extra}

This message is sent to the Net if process from a master agent as a response to a previously received request.

- Vsn is either 'version-1', - 'version-2', or 'version-3'. + +

Vsn is either 'version-1', + 'version-2', or 'version-3'.

- Pdu is an SNMP PDU record (as defined in - snmp_types.hrl) with the SNMP response. + +

Pdu is an SNMP PDU record (as defined in + snmp_types.hrl) with the SNMP response.

- Type is the #pdu.type of the original - request. + +

Type is the #pdu.type + of the original request.

- ACMData is data used by the Access Control - Module in use. Normally this is just sent to - snmpa_mpd:generate_response_message (see Reference Manual). + +

ACMData is data used by the Access Control + Module in use. Normally this is just sent to + snmpa_mpd:generate_response_message + (see Reference Manual).

- To is the destination address. If UDP over IP - is used, this should be a 2-tuple {IP, UDPport}, - where IP is a 4-tuple with the IP address, and - UDPport is an integer. + +

To is the destination address. If UDP over IP + is used, this should be a 2-tuple {IP, UDPport}, + where IP is a 4-tuple with the IP address, and + UDPport is an integer.

- Extra is the term that the Net if process - sent to the agent when the request was sent to the agent. + +

Extra is the term that the Net if process + sent to the agent when the request was sent to the agent.

+

{discarded_pdu, Vsn, ReqId, ACMData, Variable, Extra}

This message is sent from a master agent if it for some - reason decided to discard the pdu. -

+ reason decided to discard the pdu.

- Vsn is either 'version-1', - 'version-2', or 'version-3'. + +

Vsn is either 'version-1', + 'version-2', or 'version-3'.

- ReqId is the request id of the original - request. + +

ReqId is the request id of the original request.

- ACMData is data used by the Access Control - Module in use. Normally this is just sent to - snmpa_mpd:generate_response_message (see Reference Manual). + +

ACMData is data used by the Access Control + Module in use. Normally this is just sent to + snmpa_mpd:generate_response_message + (see Reference Manual).

- Variable is the name of an snmp counter that - represents the error, e.g. snmpInBadCommunityUses. + +

Variable is the name of an snmp counter that + represents the error, e.g. snmpInBadCommunityUses.

- Extra is the term that the Net if process - sent to the agent when the request was sent to the agent. + +

Extra is the term that the Net if process + sent to the agent when the request was sent to the agent.

-

{send_pdu, Vsn, Pdu, MsgData, To}

+ +

{send_pdu, Vsn, Pdu, MsgData, To, Extra}

This message is sent from a master agent when a trap is - to be sent. -

+ to be sent.

- Vsn is either 'version-1', - 'version-2', or 'version-3'. + +

Vsn is either 'version-1', + 'version-2', or 'version-3'.

- Pdu is an SNMP PDU record (as defined in - snmp_types.hrl) with the SNMP response. + +

Pdu is an SNMP PDU record (as defined in + snmp_types.hrl) with the SNMP response.

+
+ +

MsgData is the message specific data used in + the SNMP message. This value is normally sent to + snmpa_mpd:generate_message/4. In SNMPv1 and + SNMPv2c, this message data is the community string. In + SNMPv3, it is the context information.

- MsgData is the message specific data used in - the SNMP message. This value is normally sent to - snmpa_mpd:generate_message/4. In SNMPv1 and - SNMPv2c, this message data is the community string. In - SNMPv3, it is the context information. + +

To is a list of the destination addresses and + their corresponding security parameters. This value is + normally sent to snmpa_mpd:generate_message/4.

- To is a list of the destination addresses and - their corresponding security parameters. This value is - normally sent to snmpa_mpd:generate_message/4. + +

Extra is any term that the notification sender + wishes to pass to the Net if process when sending a notification + (see + send notification + for more info).

-

{send_pdu_req, Vsn, Pdu, MsgData, To, Pid}

-

This - message is sent from a master - agent when a request is - to be sent. The only request an agent can send is - Inform-Request. The net if process needs to remember the - request id and the Pid, and when a response is received for - the request id, send it to Pid, using a - snmp_response_received message. -

- - Vsn is either 'version-1', - 'version-2', or 'version-3'. - - Pdu is an SNMP PDU record (as defined in - snmp_types.hrl) with the SNMP response. - - MsgData is the message specific data used in - the SNMP message. This value is normally sent to - snmpa_mpd:generate_message/4. In SNMPv1 and - SNMPv2c, this message data is the community string. In - SNMPv3, it is the context information. + +

{send_pdu_req, Vsn, Pdu, MsgData, To, Pid, Extra}

+

This message is sent from a master agent when a request is to + be sent. The only request an agent can send is Inform-Request. + The net if process needs to remember the request id and the Pid, + and when a response is received for the request id, send it to Pid, + using a snmp_response_received message.

+ + +

Vsn is either 'version-1', + 'version-2', or 'version-3'.

+
+ +

Pdu is an SNMP PDU record (as defined in + snmp_types.hrl) with the SNMP response.

+
+ +

MsgData is the message specific data used in + the SNMP message. This value is normally sent to + snmpa_mpd:generate_message/4. In SNMPv1 and + SNMPv2c, this message data is the community string. In + SNMPv3, it is the context information.

+
+ +

To is a list of the destination addresses and + their corresponding security parameters. This value is + normally sent to snmpa_mpd:generate_message/4.

+
+ +

Pid is a process identifier.

+
+ +

Extra is any term that the notification sender + wishes to pass to the Net if process when sending a notification + (see + send notification + for more info).

- To is a list of the destination addresses and - their corresponding security parameters. This value is - normally sent to snmpa_mpd:generate_message/4. - - Pid is a process identifier. - -
+
diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml index 1d680e80f5..1800bbc91a 100644 --- a/lib/snmp/doc/src/snmpa.xml +++ b/lib/snmp/doc/src/snmpa.xml @@ -872,10 +872,139 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). then that sub-agent will be unregistered from all trees in Agent.

- + + + + send_notification2(Agent, Notification, SendOpts) -> void() + Send notification + + Agent = pid() | atom() + Notification = atom() + SendOpts = [send_option()] + send_option() = {receiver, receiver()} | {name, notify_name()} | {context, context_name()} | {varbinds, varbinds()} | {local_engine_id, string()} | {extra, extra_info()} + override_option() = atom() + receiver() = no_receiver | {tag(), tag_receiver()} | notification_delivery_info() + tag() = term(() + tag_receiver() = pid() | registered_name() | {Mod, Func, Args} + registered_name() = atom() + Mod = atom() + Func = atom() + Args = list() + notify_name() = string() + context_name() = string() + varbinds() = [varbind()] + varbind() = {variable(), value()} | {column(), row_index(), value()} | {oid(), value()} + variable() = atom() + value() = term() + column() = atom() + row_index() = [int()] + extra_info() = term() + + +

Send the notification Notification to the management + targets defined for notify-name (name) in the + snmpNotifyTable in SNMP-NOTIFICATION-MIB from the + specified context.

+ +

If no name is specified (or if it is ""), the + notification is sent to all management targets.

+ +

If no context is specified, the default context, "", + is used.

+ +

The send option receiver specifies where information + about delivery of Inform-Requests should be sent. The agent + sends Inform-Requests and waits for acknowledgments from the + management targets. + The receiver can have three values:

+ + + +

no_receiver - No information is delivered.

+
+ + +

notification_delivery_info() - The information is + delivered via a function call according to this data. See the + DATA TYPES section + above for details.

+
+ + +

{tag(), tag_receiver()} - The information is delivered + either via messages or via a function call according to the value + of tag_receiver().

+

Delivery is done differently depending on the value + of tag_receiver():

+ + + +

pid() | registered_name() - The info will be delivered in + the following messages:

+ + +

{snmp_targets, tag(), Addresses}

+

This informs the user which target addresses the + notification was sent to.

+
+ +

{snmp_notification, tag(), {got_response, Address}}

+

This informs the user that this target address + acknowledged the notification.

+
+ +

{snmp_notification, tag(), {no_response, Address}}

+

This informs the user that this target address + did not acknowledge the notification.

+
+
+

The notification is sent as an Inform-Request to each + target address in Addresses and if there are no + targets for which an Inform-Request is sent, Addresses + is the empty list [].

+

The tag_receiver() will first be sent the + snmp_targets message, and then for each address in + Addresses list, one of the two snmp_notification + messages.

+
+ + +

{Mod, Func, Args} - The info will be delivered via + the function call:

+

Mod:Func([Msg | Args])

+

where Msg has the same content and purpose as the + messages descrived above.

+
+ +
+
+
+ + +

The extra info is not normally interpreted by the agent, + instead it is passed through to the + net-if process. It is + up to the implementor of that process to make use of this data.

+

The version of net-if provided by this application makes no use + of this data, with one exception: + Any tuple containing the atom + snmpa_default_notification_extra_info + may be used by the agent and is therefor reserved.

+

See the net-if incomming messages for sending a + + trap and + + notification for more info.

+
+ + +
+
+ + send_notification(Agent, Notification, Receiver) send_notification(Agent, Notification, Receiver, Varbinds) @@ -907,29 +1036,25 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2).

Sends the notification Notification to the - management targets defined for NotifyName in the - snmpNotifyTable in SNMP-NOTIFICATION-MIB from the - specified context. If no NotifyName is specified (or - if it is ""), the notification is sent to all - management targets (Addresses below). If no ContextName - is specified, the default "" context is used. -

+ management targets defined for NotifyName in the + snmpNotifyTable in SNMP-NOTIFICATION-MIB from the + specified context.

+

If no NotifyName is specified (or if it is ""), + the notification is sent to all management targets + (Addresses below).

+

If no ContextName is specified, the default "" + context is used.

The parameter Receiver specifies where information - about delivery of Inform-Requests should be sent. The agent - sends Inform-Requests and waits for acknowledgments from the - managers. Receiver can have three values:

+ about delivery of Inform-Requests should be sent. The agent + sends Inform-Requests and waits for acknowledgments from the + managers. Receiver can have three values:

no_receiver - No information is delivered.

- -

{Tag, Recv} - The information is delivered either via messages - or via a function call according to the value of Recv.

-
-

notification_delivery_info() - The information is delivered via a function call according to this data. See the @@ -937,6 +1062,12 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). above for details.

+ +

{Tag, Recv} - The information is delivered either via + messages or via a function call according to the value of + Recv.

+
+
@@ -1064,86 +1195,20 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). (see SNMP-FRAMEWORK-MIB).

- - +

ExtraInfo is not normally used in any way by the agent. + It is intended to be passed along to the net-if process, which is + a component that a user can implement themself. The users own net-if + may then make use of ExtraInfo. The net-if provided with this + application does not process ExtraInfo.

+

There is one exception. Any tuple containing the atom + snmpa_default_notification_extra_info will, in this context, + be considered belonging to this application, and may be processed + by the agent.

+ +
- discovery(TargetName, Notification) -> {ok, ManagerEngineID} | {error, Reason} -- cgit v1.2.3 From 21259a5cdcf5d7308fedbb85925f83cee7d43ef1 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 5 Apr 2011 14:38:17 +0200 Subject: Chenged default extra-info value (for future use). --- lib/snmp/src/agent/snmpa_internal.hrl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/snmp/src/agent/snmpa_internal.hrl b/lib/snmp/src/agent/snmpa_internal.hrl index fba800175b..a91f30a4a6 100644 --- a/lib/snmp/src/agent/snmpa_internal.hrl +++ b/lib/snmp/src/agent/snmpa_internal.hrl @@ -23,7 +23,7 @@ -include_lib("snmp/src/app/snmp_internal.hrl"). -define(DEFAULT_LOCAL_ENGINE_ID, snmp_framework_mib:get_engine_id()). --define(DEFAULT_NOTIF_EXTRA_INFO, undefined). +-define(DEFAULT_NOTIF_EXTRA_INFO, {snmpa_default_notification_extra_info}). -define(snmpa_info(F, A), ?snmp_info("agent", F, A)). -define(snmpa_warning(F, A), ?snmp_warning("agent", F, A)). -- cgit v1.2.3 From 1b5c5d07d539516aae36d13d119183954087ac79 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 5 Apr 2011 14:39:01 +0200 Subject: Cosmetic... --- lib/snmp/src/agent/snmpa_trap.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 0f6a5f8984..2a233fd38e 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -639,8 +639,8 @@ send_discovery_inform(Parent, Timeout, Retry, Msg, NetIf) -> %% should be used for the target, and determine the message %% specific parameters to be used. %%----------------------------------------------------------------- -send_trap_pdus([{DestAddr, TargetName, {MpModel, SecModel, SecName, SecLevel}, - Type} | T], +send_trap_pdus([{DestAddr, TargetName, + {MpModel, SecModel, SecName, SecLevel}, Type} | T], ContextName, {TrapRec, Vbs}, V1Res, V2Res, V3Res, Recv, LocalEngineID, ExtraInfo, NetIf) -> -- cgit v1.2.3 From 1900a5513cc743f5cba1d8df2bb2052bdc42a9ec Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 6 Apr 2011 12:55:35 +0200 Subject: Updated handle_sync... --- lib/snmp/src/manager/snmpm_server.erl | 202 ++++++++++++++++++++++------------ 1 file changed, 131 insertions(+), 71 deletions(-) diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index 55abca4b71..0e6eb2d7be 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -89,13 +89,41 @@ -define(SERVER, ?MODULE). --define(SYNC_GET_TIMEOUT, 5000). --define(SYNC_SET_TIMEOUT, 5000). --define(DEFAULT_ASYNC_EXPIRE, 5000). --define(EXTRA_INFO, undefined). - --define(SNMP_AGENT_PORT, 161). - +-define(DEFAULT_SYNC_TIMEOUT, 5000). +-define(DEFAULT_SYNC_GET_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). +-define(DEFAULT_SYNC_GET_NEXT_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). +-define(DEFAULT_SYNC_GET_BULK_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). +-define(DEFAULT_SYNC_SET_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). + +-define(DEFAULT_ASYNC_EXPIRE, 5000). +-define(DEFAULT_ASYNC_GET_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). +-define(DEFAULT_ASYNC_GET_NEXT_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). +-define(DEFAULT_ASYNC_GET_BULK_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). +-define(DEFAULT_ASYNC_SET_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). + +-define(EXTRA_INFO, undefined). + +-define(SNMP_AGENT_PORT, 161). + +-define(GET_SYNC_GET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_GET_TIMEOUT, SendOpts)). +-define(GET_SYNC_GET_NEXT_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_GET_NEXT_TIMEOUT, SendOpts)). +-define(GET_SYNC_GET_BULK_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_GET_BULK_TIMEOUT, SendOpts)). +-define(SYNC_SET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_SYNC_SET_TIMEOUT, SendOpts)). + +-define(GET_ASYNC_GET_EXPIRE(SendOpts), + get_opt(expire, ?DEFAULT_ASYNC_GET_EXPIRE, SendOpts)). +-define(GET_ASYNC_GET_NEXT_EXPIRE(SendOpts), + get_opt(expire, ?DEFAULT_ASYNC_GET_NEXT_EXPIRE, SendOpts)). +-define(GET_ASYNC_GET_BULK_EXPIRE(SendOpts), + get_opt(expire, ?DEFAULT_ASYNC_GET_BULK_EXPIRE, SendOpts)). +-define(GET_ASYNC_SET_EXPIRE(SendOpts), + get_opt(expire, ?DEFAULT_ASYNC_SET_EXPIRE, SendOpts)). + +-define(GET_EXTRA(SendOpts), get_opt(extra, ?DEFAULT_EXTRA, SendOpts)). -ifdef(snmp_debug). -define(GS_START_LINK(Args), @@ -954,7 +982,7 @@ handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, [ {context, CtxName}, {timeout, Timeout}, - {extra, Extra} + {extra, ExtraInfo} ], handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State). @@ -970,12 +998,12 @@ handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State) -> case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get -> send a ~p message", [Vsn]), - Extra = get_opt(extra, ?EXTRA_INO, SendOpts), - ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port, - ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_get_request(Oids, Vsn, MsgData, + Addr, Port, Extra, State), ?vdebug("handle_sync_get -> ReqId: ~p", [ReqId]), Msg = {sync_timeout, ReqId, From}, - Timeout = get_opt(timeout, ?DEFAULT_SYNC_GET_TIMEOUT, SendOpts), + Timeout = ?SYNC_GET_TIMEOUT(SendOpts), Ref = erlang:send_after(Timeout, self(), Msg), MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_get -> MonRef: ~p", [MonRef]), @@ -999,39 +1027,49 @@ handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State) -> Error end. - handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, ExtraInfo} + ], + handle_sync_get_next(Pid, UserId, TargetName, Oids, SendOpts, From, State). + +handle_sync_get_next(Pid, UserId, TargetName, Oids, SendOpts, + From, State) -> ?vtrace("handle_sync_get_next -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Timeout, From]), + [Pid, UserId, TargetName, Oids, SendOpts, From]), case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_next -> send a ~p message", [Vsn]), - ReqId = send_get_next_request(Oids, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_get_next_request(Oids, Vsn, MsgData, + Addr, Port, Extra, State), ?vdebug("handle_sync_get_next -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = ?SYNC_GET_NEXT_TIMEOUT(SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_get_next -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = get_next, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = get_next, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; @@ -1046,39 +1084,50 @@ handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, handle_sync_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, ExtraInfo} + ], + handle_sync_get_bulk(Pid, UserId, TargetName, NonRep, MaxRep, Oids, + SendOpts, From, State). + +handle_sync_get_bulk(Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, + From, State) -> ?vtrace("handle_sync_get_bulk -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n NonRep: ~p" "~n MaxRep: ~p" "~n Oids: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, - Timeout, From]), + [Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, From]), case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_bulk -> send a ~p message", [Vsn]), - ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, - NonRep, MaxRep, ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, + NonRep, MaxRep, Extra, State), ?vdebug("handle_sync_get_bulk -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = ?SYNC_GET_BULK_TIMEOUT(SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_get_bulk -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = get_bulk, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = get_bulk, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; @@ -1092,36 +1141,47 @@ handle_sync_get_bulk(Pid, UserId, TargetName, CtxName, handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo, From, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Timeout}, + {extra, ExtraInfo} + ], + handle_sync_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, + From, State). + +handle_sync_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, From, State) -> ?vtrace("handle_sync_set -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n VarsAndVals: ~p" - "~n Timeout: ~p" + "~n SendOpts: ~p" "~n From: ~p", - [Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, From]), + [Pid, UserId, TargetName, VarsAndVals, From]), case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_set -> send a ~p message", [Vsn]), - ReqId = send_set_request(VarsAndVals, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Extra = ?GET_EXTRA(SendOpts), + ReqId = send_set_request(VarsAndVals, Vsn, MsgData, + Addr, Port, Extra, State), ?vdebug("handle_sync_set -> ReqId: ~p", [ReqId]), - Msg = {sync_timeout, ReqId, From}, - Ref = erlang:send_after(Timeout, self(), Msg), - MonRef = erlang:monitor(process, Pid), + Msg = {sync_timeout, ReqId, From}, + Timeout = ?SYNC_SET_TIMEOUT(SendOpts), + Ref = erlang:send_after(Timeout, self(), Msg), + MonRef = erlang:monitor(process, Pid), ?vtrace("handle_sync_set -> MonRef: ~p", [MonRef]), - Req = #request{id = ReqId, - user_id = UserId, - reg_type = RegType, - target = TargetName, - addr = Addr, - port = Port, - type = set, - data = MsgData, - ref = Ref, - mon = MonRef, - from = From}, + Req = #request{id = ReqId, + user_id = UserId, + reg_type = RegType, + target = TargetName, + addr = Addr, + port = Port, + type = set, + data = MsgData, + ref = Ref, + mon = MonRef, + from = From}, ets:insert(snmpm_request_table, Req), ok; -- cgit v1.2.3 From 7861327b7830c64675dd73d424de7d62007fd5a0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 6 Apr 2011 17:40:42 +0200 Subject: Accepting file descriptors (fd) in the config for socket type ip_comm. --- lib/inets/src/http_lib/http_transport.erl | 69 ++++++++++++++++++------------- lib/inets/src/http_server/httpd_sup.erl | 45 ++++++++++++-------- 2 files changed, 67 insertions(+), 47 deletions(-) diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 0024d19fc1..173911b868 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -23,7 +23,7 @@ -export([ start/1, connect/3, connect/4, - listen/2, listen/3, + listen/2, listen/3, listen/4, accept/2, accept/3, close/2, send/3, @@ -136,10 +136,11 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> %%------------------------------------------------------------------------- -%% listen(SocketType, Port) -> {ok, Socket} | {error, Reason} +%% listen(SocketType, Addr, Port, Fd) -> {ok, Socket} | {error, Reason} %% SocketType = ip_comm | {ssl, SSLConfig} %% Port = integer() -%% Socket = socket() +%% Socket = socket() +%% Fd = undefined | fd() %% %% Description: Sets up socket to listen on the port Port on the local %% host using either gen_tcp or ssl. In the gen_tcp case the port @@ -151,13 +152,8 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> listen(SocketType, Port) -> listen(SocketType, undefined, Port). -listen(ip_comm, Addr, Port) -> - case (catch listen_ip_comm(Addr, Port)) of - {'EXIT', Reason} -> - {error, {exit, Reason}}; - Else -> - Else - end; +listen(ip_comm = SocketType, Addr, Port) -> + listen(SocketType, Addr, Port, undefined); %% Wrapper for backaward compatibillity listen({ssl, SSLConfig}, Addr, Port) -> @@ -186,9 +182,17 @@ listen({essl, SSLConfig} = Ssl, Addr, Port) -> Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt], ssl:listen(Port, Opt2). +listen(ip_comm, Addr, Port, Fd) -> + case (catch listen_ip_comm(Addr, Port, Fd)) of + {'EXIT', Reason} -> + {error, {exit, Reason}}; + Else -> + Else + end. + -listen_ip_comm(Addr, Port) -> - {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port), +listen_ip_comm(Addr, Port, Fd) -> + {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd), case IpFamily of inet6fb4 -> Opts2 = [inet6 | Opts], @@ -223,29 +227,36 @@ listen_ip_comm(Addr, Port) -> ipfamily_default(Addr, Port) -> httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4). -get_socket_info(Addr, Port) -> - Key = list_to_atom("httpd_" ++ integer_to_list(Port)), - BaseOpts = [{backlog, 128}, {reuseaddr, true}], +get_socket_info(Addr, Port, Fd0) -> + BaseOpts = [{backlog, 128}, {reuseaddr, true}], IpFamilyDefault = ipfamily_default(Addr, Port), - case init:get_argument(Key) of - {ok, [[Value]]} -> - {Fd, IpFamily} = - case string:tokens(Value, [$|]) of - [FdStr, IpFamilyStr] -> - Fd0 = fd_of(FdStr), - IpFamily0 = ip_family_of(IpFamilyStr), - {Fd0, IpFamily0}; - [FdStr] -> - {fd_of(FdStr), IpFamilyDefault}; - _ -> - throw({error, {bad_descriptor, Value}}) - end, + %% 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}; - error -> + undefined -> {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault} end. +get_fd(Port, undefined = _Fd, IpFamilyDefault) -> + FdKey = list_to_atom("httpd_" ++ integer_to_list(Port)), + case init:get_argument(FdKey) of + {ok, [[Value]]} -> + case string:tokens(Value, [$|]) of + [FdStr, IpFamilyStr] -> + {fd_of(FdStr), ip_family_of(IpFamilyStr)}; + [FdStr] -> + {fd_of(FdStr), IpFamilyDefault}; + _ -> + throw({error, {bad_descriptor, Value}}) + end; + error -> + undefined + end; +get_fd(_Port, Fd, IpFamilyDefault) -> + {Fd, IpFamilyDefault}. + fd_of(FdStr) -> case (catch list_to_integer(FdStr)) of Fd when is_integer(Fd) -> diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index b248c9bcf0..d028a19bf0 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -182,24 +182,32 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> Error end. -httpd_child_spec(Config, AcceptTimeout, Debug, Addr, 0) -> - case start_listen(Addr, 0, Config) of - {Pid, {NewPort, NewConfig, ListenSocket}} -> - Name = {httpd_instance_sup, Addr, NewPort}, - StartFunc = {httpd_instance_sup, start_link, - [NewConfig, AcceptTimeout, - {Pid, ListenSocket}, Debug]}, - Restart = permanent, - Shutdown = infinity, - Modules = [httpd_instance_sup], - Type = supervisor, - {Name, StartFunc, Restart, Shutdown, Type, Modules}; - {Pid, {error, Reason}} -> - exit(Pid, normal), - {error, Reason} - end; - httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> + case Port == 0 orelse proplists:is_defined(fd, Config) of + true -> + httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); + false -> + httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) + end. + +httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port) -> + case start_listen(Addr, Port, Config) of + {Pid, {NewPort, NewConfig, ListenSocket}} -> + Name = {httpd_instance_sup, Addr, NewPort}, + StartFunc = {httpd_instance_sup, start_link, + [NewConfig, AcceptTimeout, + {Pid, ListenSocket}, Debug]}, + Restart = permanent, + Shutdown = infinity, + Modules = [httpd_instance_sup], + Type = supervisor, + {Name, StartFunc, Restart, Shutdown, Type, Modules}; + {Pid, {error, Reason}} -> + exit(Pid, normal), + {error, Reason} + end. + +httpd_child_spec_nolisten(Config, AcceptTimeout, Debug, Addr, Port) -> Name = {httpd_instance_sup, Addr, Port}, StartFunc = {httpd_instance_sup, start_link, [Config, AcceptTimeout, Debug]}, @@ -224,7 +232,8 @@ listen(Address, Port, Config) -> SocketType = proplists:get_value(socket_type, Config, ip_comm), case http_transport:start(SocketType) of ok -> - case http_transport:listen(SocketType, Address, Port) of + Fd = proplists:get_value(fd, Config), + case http_transport:listen(SocketType, Address, Port, Fd) of {ok, ListenSocket} -> NewConfig = proplists:delete(port, Config), {ok, NewPort} = inet:port(ListenSocket), -- cgit v1.2.3 From 332b91c46d67dc31ca6080b2ba5c03636f3d584f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 6 Apr 2011 17:46:04 +0200 Subject: Backup checkin (still working on the interface functions). --- lib/snmp/src/manager/snmpm_server.erl | 86 +++++++++++++++++++++++++++++------ 1 file changed, 73 insertions(+), 13 deletions(-) diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index 0e6eb2d7be..08f9703e9d 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -237,9 +237,12 @@ unregister_user(UserId) -> %% The reason why we have a sync_get2 is to simplify backward %% compatibillity. +sync_get2(UserId, TargetName, Oids) -> + sync_get2(UserId, TargetName, Oids, []). sync_get2(UserId, TargetName, Oids, Opts) -> call({sync_get, self(), UserId, TargetName, Oids, Opts}). + %% sync_get(UserId, TargetName, CtxName, Oids) -> sync_get(UserId, TargetName, CtxName, Oids, @@ -258,10 +261,14 @@ sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) %% + %% -- [async] get -- -async_get2(UserId, TargetName, Oids, Opts) -> - call({async_get, self(), UserId, TargetName, Oids, Opts}). +async_get2(UserId, TargetName, Oids) -> + async_get2(UserId, TargetName, Oids, []) -> +async_get2(UserId, TargetName, Oids, SendOpts) -> + call({async_get, self(), UserId, TargetName, Oids, SendOpts}). + %% async_get(UserId, TargetName, CtxName, Oids) -> @@ -281,10 +288,14 @@ async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) %% + %% -- [sync] get-next -- +sync_get_next2(UserId, TargetName, Oids) -> + sync_get_next2(UserId, TargetName, Oids, []). sync_get_next2(UserId, TargetName, Oids, Opts) -> - call({sync_get_next, UserId, TargetName, Oids, Opts}). + call({sync_get_next, self(), UserId, TargetName, Oids, Opts}). + %% sync_get_next(UserId, TargetName, CtxName, Oids) -> @@ -303,8 +314,16 @@ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) sync_get_next2(UserId, TargetName, Oids, Opts). %% + + %% -- [async] get-next -- +async_get_next2(UserId, TargetName, Oids) -> + async_get_next2(UserId, TargetName, Oids, []). +async_get_next2(UserId, TargetName, Oids, SendOpts) -> + call({async_get_next, self(), UserId, TargetName, Oids, Opts}). + + async_get_next(UserId, TargetName, CtxName, Oids) -> async_get_next(UserId, TargetName, CtxName, Oids, ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). @@ -317,11 +336,19 @@ async_get_next(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) andalso (Expire >= 0)) -> - call({async_get_next, self(), UserId, TargetName, CtxName, Oids, - Expire, ExtraInfo}). + Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}), + async_get_next2(UserId, TargetName, Oids, Opts). + + %% -- [sync] get-bulk -- +sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). +sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + call({sync_get_bulk, self(), UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts}). + sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) -> sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, @@ -340,11 +367,19 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - call({sync_get_bulk, self(), UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo}). + Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}), + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, Opts}). + %% -- [async] get-bulk -- +async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). +async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + call({async_get_bulk, self(), UserId, TargetName, NonRep, MaxRep, + Oids, SendOpts}). + + async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) -> async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, @@ -363,12 +398,20 @@ async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) -> - call({async_get_bulk, self(), UserId, TargetName, - NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo}). + Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}), + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, Opts}). + + %% -- [sync] set -- %% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value} +sync_set2(UserId, TargetName, VarsAndVals) -> + sync_set2(UserId, TargetName, VarsAndVals, []). +sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> + call({sync_set, self(), UserId, TargetName, VarsAndVals, SendOpts}). + + sync_set(UserId, TargetName, CtxName, VarsAndVals) -> sync_set(UserId, TargetName, CtxName, VarsAndVals, ?SYNC_SET_TIMEOUT, ?EXTRA_INFO). @@ -382,11 +425,19 @@ sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo) is_list(CtxName) andalso is_list(VarsAndVals) andalso is_integer(Timeout) -> - call({sync_set, self(), UserId, TargetName, - CtxName, VarsAndVals, Timeout, ExtraInfo}). + Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}), + sync_set2(UserId, TargetName, VarsAndVals, Opts}). + + %% -- [async] set -- +async_set2(UserId, TargetName, VarsAndVals) -> + async_set2(UserId, TargetName, VarsAndVals, []). +async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> + call({async_set, self(), UserId, TargetName, VarsAndVals, SendOpts}). + + async_set(UserId, TargetName, CtxName, VarsAndVals) -> async_set(UserId, TargetName, CtxName, VarsAndVals, ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). @@ -400,8 +451,8 @@ async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo) is_list(CtxName) andalso is_list(VarsAndVals) andalso is_integer(Expire) andalso (Expire >= 0)) -> - call({async_set, self(), UserId, TargetName, - CtxName, VarsAndVals, Expire, ExtraInfo}). + Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}), + async_set2(UserId, TargetName, VarsAndVals, Opts}). cancel_async_request(UserId, ReqId) -> @@ -1195,6 +1246,15 @@ handle_sync_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, From, State) -> handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State). + +handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State) -> ?vtrace("handle_async_get -> entry with" "~n Pid: ~p" "~n UserId: ~p" -- cgit v1.2.3 From 2f045f17afd8a286a06ceddf6fced1d3b81b4c5b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 6 Apr 2011 18:11:58 +0200 Subject: Updated release notes and linked file (that is added markers in the inets file). --- lib/inets/doc/src/inets.xml | 54 +++++++++++++++++++++++++++++++-------------- lib/inets/doc/src/notes.xml | 17 +++++++++++++- 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/lib/inets/doc/src/inets.xml b/lib/inets/doc/src/inets.xml index c367d7fa77..a2bf42320f 100644 --- a/lib/inets/doc/src/inets.xml +++ b/lib/inets/doc/src/inets.xml @@ -1,4 +1,4 @@ - + @@ -32,8 +32,10 @@ The inets services API

This module provides the most basic API to the - clients and servers, that are part of the Inets application, - such as start and stop.

+ clients and servers, that are part of the Inets application, + such as start and stop.

+ +
@@ -42,7 +44,10 @@ this module:

service() = ftpc | tftp | httpc | httpd

property() = atom()

+ +
+ services() -> [{Service, Pid}] @@ -54,11 +59,13 @@

Returns a list of currently running services.

-

Services started as stand_alone will not - be listed.

+

Services started as stand_alone will not be listed.

+ +
+ services_info() -> [{Service, Pid, Info}] Returns a list of currently running services where @@ -73,11 +80,13 @@

Returns a list of currently running services where each - service is described by a [{Option, Value}] list. The - information given in the list is specific for each service - and it is probable that each service will have its own info - function that gives you even more details about the - service.

+ service is described by a [{Option, Value}] list. The + information given in the list is specific for each service + and it is probable that each service will have its own info + function that gives you even more details about the + service.

+ +
@@ -89,6 +98,8 @@

Returns a list of available service names.

+ +
@@ -101,18 +112,24 @@

Starts the Inets application. Default type - is temporary. See also - application(3)

+ is temporary. See also + application(3).

+ +
+ stop() -> ok Stops the inets application.

Stops the inets application. See also - application(3)

+ application(3).

+ +
+ start(Service, ServiceConfig) -> {ok, Pid} | {error, Reason} start(Service, ServiceConfig, How) -> {ok, Pid} | {error, Reason} @@ -144,8 +161,11 @@ some sense the calling process has now become the top supervisor.

+ +
+ stop(Service, Reference) -> ok | {error, Reason} Stops a started service of the inets application or takes @@ -157,9 +177,11 @@

Stops a started service of the inets application or takes - down a "stand_alone-service" gracefully. When the - stand_alone option is used in start, - only the pid is a valid argument to stop.

+ down a "stand_alone-service" gracefully. When the + stand_alone option is used in start, + only the pid is a valid argument to stop.

+ +
diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index f9411fed2a..db1752e6f9 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -77,6 +77,20 @@

Own Id: OTP-9157

+ +

[httpd] Extended support for file descriptors. + In order to be able to bind to a privileged port + without running the erlang VM as root, the support + for using file descriptors has been improved. + It is now possible to add to the config (option fd) when + calling the + inets:start(httpd, ...) + function.

+

Attila Rajmund Nohl

+

Own Id: OTP-9202

+

Aux Id: seq11819

+
+
@@ -162,7 +176,8 @@ are URL-encoded. Added support in http-client to use URL-encoding. Also added the missing include directory for the inets application.

-

Own Id: OTP-8940 Aux Id: seq11735

+

Own Id: OTP-8940

+

Aux Id: seq11735

-- cgit v1.2.3 From 96273e7f909b14163aa117174994362736590140 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 7 Apr 2011 12:20:56 +0200 Subject: Release notes and API cleanup. --- lib/snmp/doc/src/notes.xml | 30 +++++++++++++++--------------- lib/snmp/doc/src/snmpa.xml | 1 - 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index e9787c17da..6853eefad2 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -43,21 +43,6 @@

-

--> - -

[agent] Pass extra info through the agent to the net-if - process when sending notifications.

-

See - - snmpa:send_notification2/3 for more info. - See also the incomming net-if messages when sending a - trap - and - - notification.

-

Own Id: OTP-9183

-

Aux Id: Seq 11817

-
-

[agent] Added support for sending traps to IPv6 targets.

See the @@ -75,6 +60,21 @@

Own Id: OTP-9174

+ +

[agent] Pass extra info through the agent to the net-if + process when sending notifications.

+

See + + snmpa:send_notification2/3 for more info. + See also the incomming net-if messages when sending a + trap + and + + notification.

+

Own Id: OTP-9183

+

Aux Id: Seq 11817

+
+
diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml index 1800bbc91a..27d89ea4e3 100644 --- a/lib/snmp/doc/src/snmpa.xml +++ b/lib/snmp/doc/src/snmpa.xml @@ -885,7 +885,6 @@ snmp_agent:register_subagent(SA1,[1,2,3], SA2). Notification = atom() SendOpts = [send_option()] send_option() = {receiver, receiver()} | {name, notify_name()} | {context, context_name()} | {varbinds, varbinds()} | {local_engine_id, string()} | {extra, extra_info()} - override_option() = atom() receiver() = no_receiver | {tag(), tag_receiver()} | notification_delivery_info() tag() = term(() tag_receiver() = pid() | registered_name() | {Mod, Func, Args} -- cgit v1.2.3 From d620fd8d49314d0f412fbfa83c9f42f7d338558a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 7 Apr 2011 12:22:10 +0200 Subject: Added export of new send notification function.c --- lib/snmp/src/agent/snmpa.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index 4cdc9d0064..630a14907d 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -60,6 +60,7 @@ register_subagent/3, unregister_subagent/2, + send_notification2/3, send_notification/3, send_notification/4, send_notification/5, send_notification/6, send_notification/7, send_trap/3, send_trap/4, -- cgit v1.2.3 From 554991eb76601ad74d5c8c045fcf53049c9fa39f Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 8 Apr 2011 17:52:22 +0200 Subject: Some handle_call-cases handled. --- lib/snmp/src/manager/snmpm_server.erl | 46 ++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index 08f9703e9d..13136ef1c0 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -687,7 +687,8 @@ handle_call({unregister_user, UserId}, _From, State) -> %% agent, or when the timeout hits (unless we get an error now). handle_call({sync_get, Pid, UserId, TargetName, Oids, SendOpts}, From, State) -> - ?vlog("received sync_get [~p] request", [TargetName]), + ?vlog("[~p,~p] received sync_get request for" + "~n ~p", [UserId, TargetName, Opts]), case (catch handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State)) of @@ -698,9 +699,11 @@ handle_call({sync_get, Pid, UserId, TargetName, Oids, SendOpts}, end; %% +%% The only case where this would be called is during code upgrade handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_get [~p] request", [CtxName]), + ?vlog("[~p,~p] received sync_get request for" + "~n ~p", [UserId, TargetName, Opts]), case (catch handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State)) of @@ -712,8 +715,26 @@ handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInf %% -handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_get_next [~p] request", [CtxName]), +handle_call({sync_get_next, Pid, UserId, TargetName, Oids, SendOpts}, + From, State) -> + ?vlog("[~p,~p] received sync_get_next request for: " + "~n ~p", [UserId, TargetName, Opts]), + case (catch handle_sync_get_next(Pid, + UserId, TargetName, Oids, SendOpts, + From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; + + +%% +%% The only case where this would be called is during code upgrade +handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, + Timeout, ExtraInfo}, From, State) -> + ?vlog("[~p,~p] received sync_get_next request for" + "~n ~p", [UserId, TargetName, Opts]), case (catch handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State)) of @@ -722,9 +743,25 @@ handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, Timeout, Ext Error -> {reply, Error, State} end; +%% %% Check agent version? This op not in v1 +handle_call({sync_get_bulk, Pid, UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts}, + From, State) -> + ?vlog("received sync_get_bulk [~p] request", [CtxName]), + case (catch handle_sync_get_bulk(Pid, + UserId, TargetName, NonRep, MaxRep, Oids, + SendOpts, From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; + +%% +%% The only case where this would be called is during code upgrade handle_call({sync_get_bulk, Pid, UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> @@ -738,6 +775,7 @@ handle_call({sync_get_bulk, Pid, UserId, TargetName, Error -> {reply, Error, State} end; +%% handle_call({sync_set, Pid, UserId, TargetName, -- cgit v1.2.3 From 43b0aa5aac83beed77eff40df769c099fefaab43 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Apr 2011 14:40:21 +0200 Subject: "Time to go to the gym"-backup... --- lib/snmp/src/manager/snmpm_server.erl | 295 ++++++++++++++++++++++++---------- 1 file changed, 207 insertions(+), 88 deletions(-) diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index 13136ef1c0..cf29770244 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -89,41 +89,41 @@ -define(SERVER, ?MODULE). --define(DEFAULT_SYNC_TIMEOUT, 5000). +-define(DEFAULT_SYNC_TIMEOUT, timer:seconds(5)). -define(DEFAULT_SYNC_GET_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). -define(DEFAULT_SYNC_GET_NEXT_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). -define(DEFAULT_SYNC_GET_BULK_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). -define(DEFAULT_SYNC_SET_TIMEOUT, ?DEFAULT_SYNC_TIMEOUT). --define(DEFAULT_ASYNC_EXPIRE, 5000). --define(DEFAULT_ASYNC_GET_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). --define(DEFAULT_ASYNC_GET_NEXT_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). --define(DEFAULT_ASYNC_GET_BULK_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). --define(DEFAULT_ASYNC_SET_TIMEOUT, ?DEFAULT_ASYNC_EXPIRE). +-define(DEFAULT_ASYNC_TIMEOUT, timer:seconds(5)). +-define(DEFAULT_ASYNC_GET_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). +-define(DEFAULT_ASYNC_GET_NEXT_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). +-define(DEFAULT_ASYNC_GET_BULK_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). +-define(DEFAULT_ASYNC_SET_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). --define(EXTRA_INFO, undefined). +-define(DEFAULT_EXTRA_INFO, undefined). -define(SNMP_AGENT_PORT, 161). --define(GET_SYNC_GET_TIMEOUT(SendOpts), +-define(SYNC_GET_TIMEOUT(SendOpts), get_opt(timeout, ?DEFAULT_SYNC_GET_TIMEOUT, SendOpts)). --define(GET_SYNC_GET_NEXT_TIMEOUT(SendOpts), +-define(SYNC_GET_NEXT_TIMEOUT(SendOpts), get_opt(timeout, ?DEFAULT_SYNC_GET_NEXT_TIMEOUT, SendOpts)). --define(GET_SYNC_GET_BULK_TIMEOUT(SendOpts), +-define(SYNC_GET_BULK_TIMEOUT(SendOpts), get_opt(timeout, ?DEFAULT_SYNC_GET_BULK_TIMEOUT, SendOpts)). -define(SYNC_SET_TIMEOUT(SendOpts), get_opt(timeout, ?DEFAULT_SYNC_SET_TIMEOUT, SendOpts)). --define(GET_ASYNC_GET_EXPIRE(SendOpts), - get_opt(expire, ?DEFAULT_ASYNC_GET_EXPIRE, SendOpts)). --define(GET_ASYNC_GET_NEXT_EXPIRE(SendOpts), - get_opt(expire, ?DEFAULT_ASYNC_GET_NEXT_EXPIRE, SendOpts)). --define(GET_ASYNC_GET_BULK_EXPIRE(SendOpts), - get_opt(expire, ?DEFAULT_ASYNC_GET_BULK_EXPIRE, SendOpts)). --define(GET_ASYNC_SET_EXPIRE(SendOpts), - get_opt(expire, ?DEFAULT_ASYNC_SET_EXPIRE, SendOpts)). +-define(ASYNC_GET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_GET_TIMEOUT, SendOpts)). +-define(ASYNC_GET_NEXT_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_GET_NEXT_TIMEOUT, SendOpts)). +-define(ASYNC_GET_BULK_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_GET_BULK_TIMEOUT, SendOpts)). +-define(ASYNC_SET_TIMEOUT(SendOpts), + get_opt(timeout, ?DEFAULT_ASYNC_SET_TIMEOUT, SendOpts)). --define(GET_EXTRA(SendOpts), get_opt(extra, ?DEFAULT_EXTRA, SendOpts)). +-define(GET_EXTRA(SendOpts), get_opt(extra, ?DEFAULT_EXTRA_INFO, SendOpts)). -ifdef(snmp_debug). -define(GS_START_LINK(Args), @@ -246,10 +246,10 @@ sync_get2(UserId, TargetName, Oids, Opts) -> %% sync_get(UserId, TargetName, CtxName, Oids) -> sync_get(UserId, TargetName, CtxName, Oids, - ?SYNC_GET_TIMEOUT). + ?DEFAULT_SYNC_GET_TIMEOUT). sync_get(UserId, TargetName, CtxName, Oids, Timeout) -> - sync_get(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO). + sync_get(UserId, TargetName, CtxName, Oids, Timeout, ?DEFAULT_EXTRA_INFO). sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) when is_list(TargetName) andalso @@ -265,7 +265,7 @@ sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) %% -- [async] get -- async_get2(UserId, TargetName, Oids) -> - async_get2(UserId, TargetName, Oids, []) -> + async_get2(UserId, TargetName, Oids, []). async_get2(UserId, TargetName, Oids, SendOpts) -> call({async_get, self(), UserId, TargetName, Oids, SendOpts}). @@ -273,18 +273,18 @@ async_get2(UserId, TargetName, Oids, SendOpts) -> %% async_get(UserId, TargetName, CtxName, Oids) -> async_get(UserId, TargetName, CtxName, Oids, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_GET_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_get(UserId, TargetName, CtxName, Oids, Expire) -> - async_get(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO). + async_get(UserId, TargetName, CtxName, Oids, Expire, ?DEFAULT_EXTRA_INFO). async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) when (is_list(TargetName) andalso is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) andalso (Expire >= 0)) -> - Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], - async_get2(UserId, TargetName, Oids, Opts). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_get2(UserId, TargetName, Oids, SendOpts). %% @@ -293,25 +293,26 @@ async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) sync_get_next2(UserId, TargetName, Oids) -> sync_get_next2(UserId, TargetName, Oids, []). -sync_get_next2(UserId, TargetName, Oids, Opts) -> - call({sync_get_next, self(), UserId, TargetName, Oids, Opts}). +sync_get_next2(UserId, TargetName, Oids, SendOpts) -> + call({sync_get_next, self(), UserId, TargetName, Oids, SendOpts}). %% sync_get_next(UserId, TargetName, CtxName, Oids) -> - sync_get_next(UserId, TargetName, CtxName, Oids, ?SYNC_GET_TIMEOUT, - ?EXTRA_INFO). + sync_get_next(UserId, TargetName, CtxName, Oids, + ?DEFAULT_SYNC_GET_TIMEOUT, ?DEFAULT_EXTRA_INFO). sync_get_next(UserId, TargetName, CtxName, Oids, Timeout) -> - sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ?EXTRA_INFO). + sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, + ?DEFAULT_EXTRA_INFO). sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) when is_list(TargetName) andalso is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}), - sync_get_next2(UserId, TargetName, Oids, Opts). + SendOpts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). %% @@ -321,23 +322,24 @@ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) async_get_next2(UserId, TargetName, Oids) -> async_get_next2(UserId, TargetName, Oids, []). async_get_next2(UserId, TargetName, Oids, SendOpts) -> - call({async_get_next, self(), UserId, TargetName, Oids, Opts}). + call({async_get_next, self(), UserId, TargetName, Oids, SendOpts}). async_get_next(UserId, TargetName, CtxName, Oids) -> async_get_next(UserId, TargetName, CtxName, Oids, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_GET_NEXT_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_get_next(UserId, TargetName, CtxName, Oids, Expire) -> - async_get_next(UserId, TargetName, CtxName, Oids, Expire, ?EXTRA_INFO). + async_get_next(UserId, TargetName, CtxName, Oids, Expire, + ?DEFAULT_EXTRA_INFO). async_get_next(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) when (is_list(TargetName) andalso is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) andalso (Expire >= 0)) -> - Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}), - async_get_next2(UserId, TargetName, Oids, Opts). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_get_next2(UserId, TargetName, Oids, SendOpts). @@ -352,12 +354,12 @@ sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) -> sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - ?SYNC_GET_TIMEOUT, ?EXTRA_INFO). + ?DEFAULT_SYNC_GET_TIMEOUT, ?DEFAULT_EXTRA_INFO). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout) -> sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - Timeout, ?EXTRA_INFO). + Timeout, ?DEFAULT_EXTRA_INFO). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) @@ -367,8 +369,8 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> - Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}), - sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, Opts}). + SendOpts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). %% -- [async] get-bulk -- @@ -383,12 +385,12 @@ async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids) -> async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_GET_BULK_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire) -> async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, - Expire, ?EXTRA_INFO). + Expire, ?DEFAULT_EXTRA_INFO). async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo) @@ -398,8 +400,8 @@ async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, is_list(CtxName) andalso is_list(Oids) andalso is_integer(Expire) -> - Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}), - async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, Opts}). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). @@ -414,19 +416,19 @@ sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> sync_set(UserId, TargetName, CtxName, VarsAndVals) -> sync_set(UserId, TargetName, CtxName, VarsAndVals, - ?SYNC_SET_TIMEOUT, ?EXTRA_INFO). + ?DEFAULT_SYNC_SET_TIMEOUT, ?DEFAULT_EXTRA_INFO). sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout) -> sync_set(UserId, TargetName, CtxName, VarsAndVals, - Timeout, ?EXTRA_INFO). + Timeout, ?DEFAULT_EXTRA_INFO). sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo) when is_list(TargetName) andalso is_list(CtxName) andalso is_list(VarsAndVals) andalso is_integer(Timeout) -> - Opts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}), - sync_set2(UserId, TargetName, VarsAndVals, Opts}). + SendOpts = [{context, CtxName}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). @@ -440,19 +442,19 @@ async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> async_set(UserId, TargetName, CtxName, VarsAndVals) -> async_set(UserId, TargetName, CtxName, VarsAndVals, - ?DEFAULT_ASYNC_EXPIRE, ?EXTRA_INFO). + ?DEFAULT_ASYNC_SET_TIMEOUT, ?DEFAULT_EXTRA_INFO). async_set(UserId, TargetName, CtxName, VarsAndVals, Expire) -> async_set(UserId, TargetName, CtxName, VarsAndVals, - Expire, ?EXTRA_INFO). + Expire, ?DEFAULT_EXTRA_INFO). async_set(UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo) when (is_list(TargetName) andalso is_list(CtxName) andalso is_list(VarsAndVals) andalso is_integer(Expire) andalso (Expire >= 0)) -> - Opts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}), - async_set2(UserId, TargetName, VarsAndVals, Opts}). + SendOpts = [{context, CtxName}, {expire, Expire}, {extra, ExtraInfo}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). cancel_async_request(UserId, ReqId) -> @@ -687,8 +689,8 @@ handle_call({unregister_user, UserId}, _From, State) -> %% agent, or when the timeout hits (unless we get an error now). handle_call({sync_get, Pid, UserId, TargetName, Oids, SendOpts}, From, State) -> - ?vlog("[~p,~p] received sync_get request for" - "~n ~p", [UserId, TargetName, Opts]), + ?vlog("[~p,~p] received sync_get request for: " + "~n ~p", [UserId, TargetName, Oids]), case (catch handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State)) of @@ -700,10 +702,11 @@ handle_call({sync_get, Pid, UserId, TargetName, Oids, SendOpts}, %% %% The only case where this would be called is during code upgrade -handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, +handle_call({sync_get, + Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> - ?vlog("[~p,~p] received sync_get request for" - "~n ~p", [UserId, TargetName, Opts]), + ?vlog("[~p,~p,~p] received sync_get request for: " + "~n ~p", [UserId, TargetName, CtxName, Oids]), case (catch handle_sync_get(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State)) of @@ -718,7 +721,7 @@ handle_call({sync_get, Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInf handle_call({sync_get_next, Pid, UserId, TargetName, Oids, SendOpts}, From, State) -> ?vlog("[~p,~p] received sync_get_next request for: " - "~n ~p", [UserId, TargetName, Opts]), + "~n ~p", [UserId, TargetName, SendOpts]), case (catch handle_sync_get_next(Pid, UserId, TargetName, Oids, SendOpts, From, State)) of @@ -731,10 +734,11 @@ handle_call({sync_get_next, Pid, UserId, TargetName, Oids, SendOpts}, %% %% The only case where this would be called is during code upgrade -handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, - Timeout, ExtraInfo}, From, State) -> - ?vlog("[~p,~p] received sync_get_next request for" - "~n ~p", [UserId, TargetName, Opts]), +handle_call({sync_get_next, + Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo}, + From, State) -> + ?vlog("[~p,~p,~p] received sync_get_next request for" + "~n ~p", [UserId, TargetName, CtxName, Oids]), case (catch handle_sync_get_next(Pid, UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo, From, State)) of @@ -747,10 +751,11 @@ handle_call({sync_get_next, Pid, UserId, TargetName, CtxName, Oids, %% Check agent version? This op not in v1 -handle_call({sync_get_bulk, Pid, UserId, TargetName, - NonRep, MaxRep, Oids, SendOpts}, +handle_call({sync_get_bulk, + Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts}, From, State) -> - ?vlog("received sync_get_bulk [~p] request", [CtxName]), + ?vlog("[~p,~p] received sync_get_bulk request for: " + "~n ~p", [UserId, TargetName, Oids]), case (catch handle_sync_get_bulk(Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, From, State)) of @@ -765,7 +770,8 @@ handle_call({sync_get_bulk, Pid, UserId, TargetName, handle_call({sync_get_bulk, Pid, UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_get_bulk [~p] request", [CtxName]), + ?vlog("[~p,~p] received sync_get_bulk request for: ~p" + "~n ~p", [UserId, TargetName, CtxName, Oids]), case (catch handle_sync_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, @@ -778,10 +784,28 @@ handle_call({sync_get_bulk, Pid, UserId, TargetName, %% +handle_call({sync_set, + Pid, UserId, TargetName, VarsAndVals, SendOpts}, + From, State) -> + ?vlog("[~p,~p] received sync_set request for: " + "~n ~p", [UserId, TargetName, VarsAndVals]), + case (catch handle_sync_set(Pid, + UserId, TargetName, VarsAndVals, SendOpts, + From, State)) of + ok -> + {noreply, State}; + Error -> + {reply, Error, State} + end; + + +%% +%% The only case where this would be called is during code upgrade handle_call({sync_set, Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo}, From, State) -> - ?vlog("received sync_set [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received sync_set request for: " + "~n ~p", [UserId, TargetName, CtxName, VarsAndVals]), case (catch handle_sync_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo, From, State)) of @@ -790,45 +814,105 @@ handle_call({sync_set, Pid, UserId, TargetName, Error -> {reply, Error, State} end; +%% +handle_call({async_get, Pid, UserId, TargetName, Oids, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_get request for: " + "~n ~p", [UserId, TargetName, Oids]), + Reply = (catch handle_async_get(Pid, + UserId, TargetName, Oids, SendOpts, + State)), + {reply, Reply, State}; + + +%% +%% The only case where this would be called is during code upgrade handle_call({async_get, Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_get [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_get request for: " + "~n ~p", [UserId, TargetName, CtxName, Oids]), Reply = (catch handle_async_get(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% + + +handle_call({async_get_next, Pid, UserId, TargetName, Oids, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_get_next request for: " + "~n ~p", [UserId, TargetName, Oids]), + Reply = (catch handle_async_get_next(Pid, + UserId, TargetName, Oids, SendOpts, + State)), + {reply, Reply, State}; +%% +%% The only case where this would be called is during code upgrade handle_call({async_get_next, Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_get_next [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_get_next request for: ", + [UserId, TargetName, CtxName, Oids]), Reply = (catch handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% %% Check agent version? This op not in v1 +handle_call({async_get_bulk, + Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_get_bulk request for: " + "~n ~p", [UserId, TargetName, Oids]), + Reply = (catch handle_async_get_bulk(Pid, + UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts, + State)), + {reply, Reply, State}; + + +%% +%% The only case where this would be called is during code upgrade handle_call({async_get_bulk, Pid, UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_get_bulk [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_get_bulk request for: " + "~n ~p", [UserId, TargetName, CtxName, Oids]), Reply = (catch handle_async_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% +handle_call({async_set, + Pid, UserId, TargetName, VarsAndVals, SendOpts}, + _From, State) -> + ?vlog("[~p,~p] received async_set request for: " + "~n ~p", [UserId, TargetName, VarsAndVals]), + Reply = (catch handle_async_set(Pid, + UserId, TargetName, VarsAndVals, SendOpts, + State)), + {reply, Reply, State}; + + +%% +%% The only case where this would be called is during code upgrade handle_call({async_set, Pid, UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo}, _From, State) -> - ?vlog("received async_set [~p] request", [CtxName]), + ?vlog("[~p,~p,~p] received async_set request for: " + "~n ~p", [UserId, TargetName, CtxName, VarsAndVals]), Reply = (catch handle_async_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo, State)), {reply, Reply, State}; +%% handle_call({cancel_async_request, UserId, ReqId}, _From, State) -> @@ -1297,16 +1381,17 @@ handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State) -> "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Expire]), + "~n SendOpts: ~p", + [Pid, UserId, TargetName, Oids, SendOpts]), case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_request(Oids, Vsn, MsgData, Addr, Port, - ExtraInfo, State), + Extra, State), ?vdebug("handle_async_get -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_GET_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, @@ -1331,20 +1416,30 @@ handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State) -> handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_get_next(Pid, UserId, TargetName, Oids, SendOpts, State). + +handle_async_get_next(Pid, UserId, TargetName, Oids, SendOpts, State) -> ?vtrace("handle_async_get_next -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n Oids: ~p" "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, Oids, Expire]), + [Pid, UserId, TargetName, Oids, Expire]), case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get_next -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_next_request(Oids, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Addr, Port, Extra, State), ?vdebug("handle_async_get_next -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_GET_NEXT_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, @@ -1370,22 +1465,36 @@ handle_async_get_next(Pid, UserId, TargetName, CtxName, Oids, Expire, handle_async_get_bulk(Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_get_bulk(Pid, + UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, + State). + +handle_async_get_bulk(Pid, + UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, + State) -> ?vtrace("handle_async_get_bulk -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n NonRep: ~p" "~n MaxRep: ~p" "~n Oids: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, NonRep, MaxRep, Oids, Expire]), + "~n SendOpts: ~p", + [Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), case agent_data(TargetName, CtxName) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get_bulk -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, - NonRep, MaxRep, ExtraInfo, State), + NonRep, MaxRep, Extra, State), ?vdebug("handle_async_get_bulk -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_GET_BULK_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, @@ -1409,20 +1518,30 @@ handle_async_get_bulk(Pid, UserId, TargetName, CtxName, handle_async_set(Pid, UserId, TargetName, CtxName, VarsAndVals, Expire, ExtraInfo, State) -> + SendOpts = + [ + {context, CtxName}, + {timeout, Expire}, + {extra, ExtraInfo} + ], + handle_async_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, State). + +handle_async_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, State) -> ?vtrace("handle_async_set -> entry with" "~n Pid: ~p" "~n UserId: ~p" "~n TargetName: ~p" - "~n CtxName: ~p" "~n VarsAndVals: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, CtxName, VarsAndVals, Expire]), + "~n SendOpts: ~p", + [Pid, UserId, TargetName, VarsAndVals, SendOpts]), case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_set -> send a ~p message", [Vsn]), + Extra = ?GET_EXTRA(SendOpts), ReqId = send_set_request(VarsAndVals, Vsn, MsgData, - Addr, Port, ExtraInfo, State), + Addr, Port, Extra, State), ?vdebug("handle_async_set -> ReqId: ~p", [ReqId]), + Expire = ?ASYNC_SET_TIMEOUT(SendOpts), Req = #request{id = ReqId, user_id = UserId, reg_type = RegType, -- cgit v1.2.3 From 21244028b693c6a1d4726b6a7d10bc947d9fb0f4 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Apr 2011 15:29:54 +0200 Subject: First time through the compiler. --- lib/snmp/src/manager/snmpm.erl | 7 +++-- lib/snmp/src/manager/snmpm_internal.hrl | 2 ++ lib/snmp/src/manager/snmpm_server.erl | 51 +++++++++++++++++++-------------- 3 files changed, 36 insertions(+), 24 deletions(-) diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 36b4901e9a..7657f6df6e 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -145,12 +145,13 @@ -export([start_link/3, snmpm_start_verify/2, snmpm_start_verify/3]). --include("snmp_debug.hrl"). +-include_lib("snmp/src/misc/snmp_debug.hrl"). +-include_lib("snmp/include/snmp_types.hrl"). -include("snmpm_atl.hrl"). --include("snmp_types.hrl"). +-include("snmpm_internal.hrl"). -define(DEFAULT_AGENT_PORT, 161). --define(DEFAULT_CONTEXT, ""). +%% -define(DEFAULT_CONTEXT, ""). %% This function is called when the snmp application diff --git a/lib/snmp/src/manager/snmpm_internal.hrl b/lib/snmp/src/manager/snmpm_internal.hrl index 5d9b32e3f6..389aeaf197 100644 --- a/lib/snmp/src/manager/snmpm_internal.hrl +++ b/lib/snmp/src/manager/snmpm_internal.hrl @@ -20,6 +20,8 @@ -ifndef(snmpm_internal). -define(snmpm_internal, true). +-define(DEFAULT_CONTEXT, ""). + -include_lib("snmp/src/app/snmp_internal.hrl"). -define(snmpm_info(F, A), ?snmp_info("manager", F, A)). diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index cf29770244..3b49267994 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -237,8 +237,8 @@ unregister_user(UserId) -> %% The reason why we have a sync_get2 is to simplify backward %% compatibillity. -sync_get2(UserId, TargetName, Oids) -> - sync_get2(UserId, TargetName, Oids, []). +%% sync_get2(UserId, TargetName, Oids) -> +%% sync_get2(UserId, TargetName, Oids, []). sync_get2(UserId, TargetName, Oids, Opts) -> call({sync_get, self(), UserId, TargetName, Oids, Opts}). @@ -264,8 +264,8 @@ sync_get(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) %% -- [async] get -- -async_get2(UserId, TargetName, Oids) -> - async_get2(UserId, TargetName, Oids, []). +%% async_get2(UserId, TargetName, Oids) -> +%% async_get2(UserId, TargetName, Oids, []). async_get2(UserId, TargetName, Oids, SendOpts) -> call({async_get, self(), UserId, TargetName, Oids, SendOpts}). @@ -291,8 +291,8 @@ async_get(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) %% -- [sync] get-next -- -sync_get_next2(UserId, TargetName, Oids) -> - sync_get_next2(UserId, TargetName, Oids, []). +%% sync_get_next2(UserId, TargetName, Oids) -> +%% sync_get_next2(UserId, TargetName, Oids, []). sync_get_next2(UserId, TargetName, Oids, SendOpts) -> call({sync_get_next, self(), UserId, TargetName, Oids, SendOpts}). @@ -319,8 +319,8 @@ sync_get_next(UserId, TargetName, CtxName, Oids, Timeout, ExtraInfo) %% -- [async] get-next -- -async_get_next2(UserId, TargetName, Oids) -> - async_get_next2(UserId, TargetName, Oids, []). +%% async_get_next2(UserId, TargetName, Oids) -> +%% async_get_next2(UserId, TargetName, Oids, []). async_get_next2(UserId, TargetName, Oids, SendOpts) -> call({async_get_next, self(), UserId, TargetName, Oids, SendOpts}). @@ -345,8 +345,8 @@ async_get_next(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) %% -- [sync] get-bulk -- -sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> - sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). +%% sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> +%% sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> call({sync_get_bulk, self(), UserId, TargetName, NonRep, MaxRep, Oids, SendOpts}). @@ -375,8 +375,8 @@ sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Timeout, %% -- [async] get-bulk -- -async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> - async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). +%% async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> +%% async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> call({async_get_bulk, self(), UserId, TargetName, NonRep, MaxRep, Oids, SendOpts}). @@ -408,8 +408,8 @@ async_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids, Expire, %% -- [sync] set -- %% VarsAndValues is: {PlainOid, o|s|i, Value} (unknown mibs) | {Oid, Value} -sync_set2(UserId, TargetName, VarsAndVals) -> - sync_set2(UserId, TargetName, VarsAndVals, []). +%% sync_set2(UserId, TargetName, VarsAndVals) -> +%% sync_set2(UserId, TargetName, VarsAndVals, []). sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> call({sync_set, self(), UserId, TargetName, VarsAndVals, SendOpts}). @@ -434,8 +434,8 @@ sync_set(UserId, TargetName, CtxName, VarsAndVals, Timeout, ExtraInfo) %% -- [async] set -- -async_set2(UserId, TargetName, VarsAndVals) -> - async_set2(UserId, TargetName, VarsAndVals, []). +%% async_set2(UserId, TargetName, VarsAndVals) -> +%% async_set2(UserId, TargetName, VarsAndVals, []). async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> call({async_set, self(), UserId, TargetName, VarsAndVals, SendOpts}). @@ -1430,8 +1430,8 @@ handle_async_get_next(Pid, UserId, TargetName, Oids, SendOpts, State) -> "~n UserId: ~p" "~n TargetName: ~p" "~n Oids: ~p" - "~n Expire: ~p", - [Pid, UserId, TargetName, Oids, Expire]), + "~n SendOpts: ~p", + [Pid, UserId, TargetName, Oids, SendOpts]), case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get_next -> send a ~p message", [Vsn]), @@ -1487,7 +1487,7 @@ handle_async_get_bulk(Pid, "~n Oids: ~p" "~n SendOpts: ~p", [Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), - case agent_data(TargetName, CtxName) of + case agent_data(TargetName, SendOpts) of {ok, RegType, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_async_get_bulk -> send a ~p message", [Vsn]), Extra = ?GET_EXTRA(SendOpts), @@ -3146,6 +3146,9 @@ agent_data(TargetName, SendOpts) -> DefSecLevel = agent_data_item(sec_level, Info), EngineId = agent_data_item(engine_id, Info), + CtxName = agent_data_item(context, + SendOpts, + ?DEFAULT_CONTEXT), SecModel = agent_data_item(sec_model, SendOpts, @@ -3164,10 +3167,10 @@ agent_data(TargetName, SendOpts) -> DefSecModel = agent_data_item(sec_model, Info), Comm = agent_data_item(community, - SendOPts, + SendOpts, DefComm), SecModel = agent_data_item(sec_model, - SendOPts, + SendOpts, DefSecModel), {Comm, SecModel} @@ -3334,6 +3337,12 @@ default_agent_config() -> end. +%%---------------------------------------------------------------------- + +get_opt(Key, Default, Opts) -> + proplists:get_value(Key, Opts, Default). + + %%---------------------------------------------------------------------- is_started(#state{net_if = _Pid, net_if_mod = _Mod}) -> -- cgit v1.2.3 From d0f399b4b08b5cd9fb54bec5d71c82b560199c86 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 12 Apr 2011 18:06:45 +0200 Subject: Proper release notes. --- lib/inets/src/inets_app/inets.appup.src | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index df18ffa9e8..c14f8d51f6 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -22,12 +22,14 @@ [ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} ] @@ -37,12 +39,14 @@ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request, http_chunk]} @@ -64,12 +68,14 @@ [ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} ] @@ -79,12 +85,14 @@ {load_module, ftp, soft_purge, soft_purge, []}, {load_module, http_chunk, soft_purge, soft_purge, []}, {load_module, http_util, soft_purge, soft_purge, []}, + {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, {load_module, httpc_request, soft_purge, soft_purge, []}, + {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request, http_chunk]} -- cgit v1.2.3 From 6f7913a20db1f59a67cc22ae3b6ce6d4d013deee Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 13 Apr 2011 12:05:20 +0200 Subject: Proper interface documentation for new API. Also release notes with links into API. --- lib/snmp/doc/src/notes.xml | 23 ++- lib/snmp/doc/src/snmpm.xml | 354 ++++++++++++++++++++++++++++----------------- 2 files changed, 247 insertions(+), 130 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index f5fa7065fb..11d2a513df 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -53,9 +53,30 @@

Aux Id: Seq 11790

+ +

[manager] The API for snmp requests has been changed to + allow the caller to override some configuration.

+

See + sync_get/3,4, + async_get/3,4, + sync_get_next/3,4, + async_get_next/3,4, + sync_get_bulk/5,6, + async_get_bulk/5,6, + sync_set/3,4 and + async_set/3,4 + for more info.

+

Own Id: OTP-9162

+
+

[manager] The old API functions (for get and set - requests) are now officially deprecated. + requests: + snmpm:g/3,4,5,6,7, snmpm:ag/3,4,5,6,7, + snmpm:gn/3,4,5,6,7, snmpm:agn/3,4,5,6,7, + snmpm:s/3,4,5,6,7, snmpm:s/3,4,5,6,7, + snmpm:gb/5,6,7,8,9 and snmpm:agb/5,6,7,8,9) + are now officially deprecated. They will be removed as of R16B.

Own Id: OTP-9174

diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index 1ee391d9ba..db7abd6867 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -1,4 +1,4 @@ - + @@ -63,6 +63,10 @@ value_type() = o ('OBJECT IDENTIFIER') | c64 ('Counter64') | tt ('TimeTicks') value() = term() +community() = string() +sec_model() = any | v1 | v2c | usm +sec_name() = string() +sec_level() = noAuthNoPriv | authNoPriv | authPriv ]]> @@ -488,22 +492,20 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 sync_get(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous get-request UserId = term() TargetName = target_name() - ContextName = string() Oids = [oid()] - Timeout = integer() - ExtraInfo = term() + SendOpts = send_opts() + send_opts() = [send_opt()] + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} SnmpReply = snmp_reply() Remaining = integer() - Reason = {send_failed, ReqId, R} | {invalid_sec_info, SecInfo, SnmpInfo} | term() - R = term() + Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + ReqId = term() + ActualReason = term() SecInfo = [sec_info()] sec_info() = {sec_tag(), ExpectedValue, ReceivedValue} sec_tag() = atom() @@ -512,19 +514,29 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Synchronous get-request.

-

Remaining is the remaining time of the given or - default timeout time.

+ +

Remaining is the remaining time of the given (or default) + timeout time.

+

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

-

For SnmpInfo, see the user callback function - handle_report.

+ the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + ActualReason is the actual reason in this case.

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a option (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

+ +

For SnmpInfo, see the user callback function + handle_report.

@@ -532,33 +544,39 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 async_get(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} - async_get(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason} - async_get(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason} - async_get(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason} - async_get(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + async_get(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous get-request UserId = term() TargetName = target_name() - ContextName = string() Oids = [oid()] - Expire = integer() - ExtraInfo = term() + SendOpts = send_opts() + send_opts() = [send_opt()] + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} ReqId = term() Reason = term()

Asynchronous get-request.

+

The reply, if it arrives, will be delivered to the user - through a call to the snmpm_user callback function - handle_pdu.

-

The Expire time indicates for how long the request is - valid (after which the manager is free to delete it).

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ through a call to the snmpm_user callback function + handle_pdu.

+ +

The send option timeout specifies for how long the request is + valid (after which the manager is free to delete it).

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

@@ -566,35 +584,51 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 sync_get_next(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_next(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_next(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_next(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_next(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_next(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous get-next-request UserId = term() TargetName = target_name() - ContextName = string() Oids = [oid()] - Timeout = integer() - ExtraInfo = term() + SendOpts = send_opts() + send_opts() = [send_opt()] + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} SnmpReply = snmp_reply() Remaining = integer() - Reason = {send_failed, ReqId, R} | {invalid_sec_info, SecInfo, SnmpInfo} | term() - R = term() + Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + ReqId = term() + ActualReason = term() + SecInfo = [sec_info()] + sec_info() = {sec_tag(), ExpectedValue, ReceivedValue} + sec_tag() = atom() + ExpectedValue = ReceivedValue = term() + SnmpInfo = term()

Synchronous get-next-request.

-

Remaining time of the given or default timeout time.

+ +

Remaining is the remaining time of the given (or default) + timeout time.

+

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + ActualReason is the actual reason in this case.

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

+ +

For SnmpInfo, see the user callback function + handle_report.

@@ -602,27 +636,36 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 async_get_next(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} - async_get_next(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason} - async_get_next(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason} - async_get_next(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason} - async_get_next(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + async_get_next(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous get-next-request UserId = term() TargetName = target_name() - ContextName = string() Oids = [oid()] - Expire = integer() - ExtraInfo = term() + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} ReqId = integer() Reason = term()

Asynchronous get-next-request.

+

The reply will be delivered to the user through a call - to the snmpm_user callback function handle_pdu.

-

The Expire time indicates for how long the request is - valid (after which the manager is free to delete it).

+ to the snmpm_user callback function handle_pdu.

+ +

The send option timeout specifies for how long the request is + valid (after which the manager is free to delete it).

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

@@ -630,37 +673,54 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 sync_set(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_set(UserId, TargetName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_set(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous set-request UserId = term() TargetName = target_name() - ContextName = string() VarsAndVals = vars_and_vals() - Timeout = integer() - ExtraInfo = term() + SendOpts = send_opts() + send_opts() = [send_opt()] + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} SnmpReply = snmp_reply() Remaining = integer() Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + ReqId = term() ActualReason = term() + SecInfo = [sec_info()] + sec_info() = {sec_tag(), ExpectedValue, ReceivedValue} + sec_tag() = atom() + ExpectedValue = ReceivedValue = term() + SnmpInfo = term()

Synchronous set-request.

-

Remaining time of the given or default timeout time.

-

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

When var_and_val() is {oid(), value()}, the - manager makes an educated guess based on the loaded mibs.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ +

Remaining is the remaining time of the given (or default) + timeout time.

+ +

When Reason is {send_failed, ...} it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + ActualReason is the actual reason in this case.

+ +

When var_and_val() is {oid(), value()}, the + manager makes an educated guess based on the loaded mibs.

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

+ +

For SnmpInfo, see the user callback function + handle_report.

@@ -668,33 +728,41 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 async_set(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason} - async_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, ReqId} | {error, Reason} - async_set(UserId, TargetName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason} - async_set(UserId, TargetName, ContextName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason} - async_set(UserId, TargetName, ContextName, VarsAndVals, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + async_set(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous set-request UserId = term() TargetName = target_name() VarsAndVals = vars_and_vals() - Expire = integer() - ExtraInfo = term() + SendOpts = send_opts() + send_opts() = [send_opt()] + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} ReqId = term() Reason = term()

Asynchronous set-request.

-

The reply will be delivered to the user through a call - to the snmpm_user callback function handle_pdu.

-

The Expire time indicates for how long the request is - valid (after which the manager is free to delete it).

-

When var_and_val() is {oid(), value()}, the - manager makes an educated guess based on the loaded mibs.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ +

The reply will be delivered to the user through a call + to the snmpm_user callback function handle_pdu.

+ +

The send option timeout specifies for how long the request is + valid (after which the manager is free to delete it).

+ +

When var_and_val() is {oid(), value()}, the + manager makes an educated guess based on the loaded mibs.

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

@@ -702,36 +770,53 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous get-bulk-request UserId = term() TargetName = target_name() NonRep = integer() MaxRep = integer() - ContextName = string() Oids = [oid()] - Timeout = integer() - ExtraInfo = term() + SendOpts = send_opts() + send_opts() = [send_opt()] + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} SnmpReply = snmp_reply() Remaining = integer() - Reason = {send_failed, ReqId, R} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + ReqId = term() + ActualReason = term() + SecInfo = [sec_info()] + sec_info() = {sec_tag(), ExpectedValue, ReceivedValue} + sec_tag() = atom() + ExpectedValue = ReceivedValue = term() + SnmpInfo = term()

Synchronous get-bulk-request (See RFC1905).

-

Remaining time of the given or default timeout time.

+ +

Remaining is the remaining time of the given (or default) + timeout time.

+

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. + ActualReason is the actual reason in this case.

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

+ +

For SnmpInfo, see the user callback function + handle_report.

@@ -739,29 +824,40 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason} - async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids) -> {ok, ReqId} | {error, Reason} - async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Expire) -> {ok, ReqId} | {error, Reason} - async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason} - async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous get-bulk-request UserId = term() TargetName = target_name() NonRep = integer() MaxRep = integer() - ContextName = string() Oids = [oid()] - Expire = integer() - ExtraInfo = term() + SendOpts = send_opts() + send_opts() = [send_opt()] + send_opt() = {context, string()} | {timeout, pos_integer()} | {extra, term()} | {community, community()} | {sec_model, sec_model()} | {sec_name, string()} | {sec_level, sec_level()} | {max_message_size, pos_integer()} ReqId = integer() Reason = term()

Asynchronous get-bulk-request (See RFC1905).

+

The reply will be delivered to the user through a call - to the snmpm_user callback function handle_pdu.

-

The Expire time indicates for how long the request is - valid (after which the manager is free to delete it).

+ to the snmpm_user callback function handle_pdu.

+ +

The send option timeout specifies for how long the request is + valid (after which the manager is free to delete it).

+ +

The send option extra specifies an opaque data structure + passed on to the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+ +

Some of the send options (community, sec_model, + sec_name, sec_level and max_message_size) + are override options. That is, + for this request, they override any configuration done + when the agent was registered.

-- cgit v1.2.3 From 32aa680d7b2a06299f2cf37d54496060fff1b44a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 09:07:04 +0200 Subject: Final version of the (new) API. --- lib/snmp/doc/src/notes.xml | 24 +- lib/snmp/doc/src/snmpm.xml | 48 ++-- lib/snmp/src/manager/snmpm.erl | 402 ++++++++++++++++++++++++++-------- lib/snmp/src/manager/snmpm_server.erl | 8 + lib/snmp/test/snmp_manager_test.erl | 208 +++++++++++------- lib/snmp/test/snmp_manager_user.erl | 6 +- 6 files changed, 483 insertions(+), 213 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 11d2a513df..ae00c42a7c 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -54,17 +54,17 @@ -

[manager] The API for snmp requests has been changed to +

[manager] The API for snmp requests has been augmented to allow the caller to override some configuration.

-

See - sync_get/3,4, - async_get/3,4, - sync_get_next/3,4, - async_get_next/3,4, - sync_get_bulk/5,6, - async_get_bulk/5,6, - sync_set/3,4 and - async_set/3,4 +

This has been done by introducing a new set of API functions, see + sync_get2/3,4, + async_get2/3,4, + sync_get_next2/3,4, + async_get_next2/3,4, + sync_get_bulk2/5,6, + async_get_bulk2/5,6, + sync_set2/3,4 and + async_set2/3,4 for more info.

Own Id: OTP-9162

@@ -304,7 +304,7 @@ snmp_view_basec_acm_mib:vacmAccessTable(set, RowIndex, Cols).

The config utility (snmp:config/0) generated a default notify.conf - with a bad name for the starndard trap entry (was "stadard trap", + with a bad name for the standard trap entry (was "stadard trap", but should have been "standard trap"). This has been corrected.

Kenji Rikitake

Own Id: OTP-8433

@@ -508,7 +508,7 @@ snmp_view_basec_acm_mib:vacmAccessTable(set, RowIndex, Cols).

The config utility (snmp:config/0) generated a default notify.conf - with a bad name for the starndard trap entry (was "stadard trap", + with a bad name for the standard trap entry (was "stadard trap", but should have been "standard trap"). This has been corrected.

Kenji Rikitake

Own Id: OTP-8433

diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index db7abd6867..ff072205bc 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -486,13 +486,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Get a list of all registered usm users with engine-id EngineID.

- +
- sync_get(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous get-request UserId = term() @@ -538,13 +538,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

- +
- async_get(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} - async_get(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} + async_get2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} + async_get2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous get-request UserId = term() @@ -578,13 +578,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 for this request, they override any configuration done when the agent was registered.

- +
- sync_get_next(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_next(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_next2(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous get-next-request UserId = term() @@ -630,13 +630,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

- +
- async_get_next(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} - async_get_next(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} + async_get_next2(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} + async_get_next2(UserId, TargetName, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous get-next-request UserId = term() @@ -667,13 +667,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 for this request, they override any configuration done when the agent was registered.

- +
- sync_set(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_set(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_set2(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous set-request UserId = term() @@ -722,13 +722,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

- +
- async_set(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason} - async_set(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, ReqId} | {error, Reason} + async_set2(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason} + async_set2(UserId, TargetName, VarsAndVals, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous set-request UserId = term() @@ -764,13 +764,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 for this request, they override any configuration done when the agent was registered.

- +
- sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} - sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_bulk2(UserId, TragetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, SnmpReply, Remaining} | {error, Reason} Synchronous get-bulk-request UserId = term() @@ -818,13 +818,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

- +
- async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason} - async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason} + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> {ok, ReqId} | {error, Reason} Asynchronous get-bulk-request UserId = term() diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 7657f6df6e..dd85ec9f41 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -57,15 +57,16 @@ usm_user_info/3, update_usm_user_info/4, %% - %% Basic SNMP API - sync_get/3, sync_get/4, sync_get/5, sync_get/6, - async_get/3, async_get/4, async_get/5, async_get/6, - sync_get_next/3, sync_get_next/4, sync_get_next/5, sync_get_next/6, - async_get_next/3, async_get_next/4, async_get_next/5, async_get_next/6, - sync_set/3, sync_set/4, sync_set/5, sync_set/6, - async_set/3, async_set/4, async_set/5, async_set/6, - sync_get_bulk/5, sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, - async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8, + %% Basic SNMP API (version "3"). + sync_get2/3, sync_get2/4, + async_get2/3, async_get2/4, + sync_get_next2/3, sync_get_next2/4, + async_get_next2/3, async_get_next2/4, + sync_set2/3, sync_set2/4, + async_set2/3, async_set2/4, + sync_get_bulk2/5, sync_get_bulk2/6, + async_get_bulk2/5, async_get_bulk2/6, + cancel_async_request/2, %% @@ -91,7 +92,19 @@ -export([format_reason/1, format_reason/2]). -%% Backward compatibillity exports +%% Backward compatibillity exports (API version "2") +-export([ + sync_get/3, sync_get/4, sync_get/5, sync_get/6, + async_get/3, async_get/4, async_get/5, async_get/6, + sync_get_next/3, sync_get_next/4, sync_get_next/5, sync_get_next/6, + async_get_next/3, async_get_next/4, async_get_next/5, async_get_next/6, + sync_set/3, sync_set/4, sync_set/5, sync_set/6, + async_set/3, async_set/4, async_set/5, async_set/6, + sync_get_bulk/5, sync_get_bulk/6, sync_get_bulk/7, sync_get_bulk/8, + async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8 + ]). + +%% Backward compatibillity exports (API version "1") -deprecated({agent_info, 3}). -deprecated({update_agent_info, 5}). -deprecated({g, 3}). @@ -498,23 +511,37 @@ which_usm_users(EngineID) when is_list(EngineID) -> %% --- synchroneous get-request --- %% -sync_get(UserId, TargetName, Oids) -> - sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). +sync_get2(UserId, TargetName, Oids) -> + sync_get2(UserId, TargetName, Oids, []). -sync_get(UserId, TargetName, Context, Oids) when is_list(Oids) -> - snmpm_server:sync_get(UserId, TargetName, Context, Oids); +sync_get2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:sync_get2(UserId, TargetName, Oids, SendOpts). -sync_get(UserId, TargetName, Oids, Timeout) when is_integer(Timeout) -> - sync_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout). +%% +sync_get(UserId, TargetName, Oids) -> + sync_get2(UserId, TargetName, Oids). + +sync_get(UserId, TargetName, Oids, Timeout) + when is_list(Oids) andalso is_integer(Timeout) -> + SendOpts = [{timeout, Timeout}], + sync_get2(UserId, TargetName, Oids, SendOpts); +sync_get(UserId, TargetName, Context, [OH|_] = Oids) + when is_list(Context) andalso is_list(OH) -> + SendOpts = [{context, Context}], + sync_get2(UserId, TargetName, Oids, SendOpts). sync_get(UserId, TargetName, Context, Oids, Timeout) -> - snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout). + SendOpts = [{context, Context}, {timeout, Timeout}], + sync_get2(UserId, TargetName, Oids, SendOpts). sync_get(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> - snmpm_server:sync_get(UserId, TargetName, Context, Oids, Timeout, - ExtraInfo). + SendOpts = [{context, Context}, {timeout, Timeout}, {extra, ExtraInfo}], + sync_get2(UserId, TargetName, Oids, SendOpts). +%% +%% g(UserId, Addr, Oids) -> g(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). @@ -560,6 +587,7 @@ g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> Error -> Error end. +%% @@ -569,23 +597,36 @@ g(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> %% through a call to handle_pdu/5 %% -async_get(UserId, TargetName, Oids) -> - async_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). +async_get2(UserId, TargetName, Oids) -> + async_get2(UserId, TargetName, Oids, []). + +async_get2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:async_get2(UserId, TargetName, Oids, SendOpts). -async_get(UserId, TargetName, Context, Oids) when is_list(Oids) -> - snmpm_server:async_get(UserId, TargetName, Context, Oids); +%% +async_get(UserId, TargetName, Oids) -> + async_get2(UserId, TargetName, Oids). async_get(UserId, TargetName, Oids, Expire) when is_integer(Expire) -> - async_get(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Expire). + SendOpts = [{timeout, Expire}], + async_get2(UserId, TargetName, Oids, SendOpts); +async_get(UserId, TargetName, Context, Oids) + when is_list(Context) andalso is_list(Oids) -> + SendOpts = [{context, Context}], + async_get2(UserId, TargetName, Oids, SendOpts). async_get(UserId, TargetName, Context, Oids, Expire) -> - snmpm_server:async_get(UserId, TargetName, Context, Oids, Expire). + SendOpts = [{timeout, Expire}, {context, Context}], + async_get2(UserId, TargetName, Oids, SendOpts). async_get(UserId, TargetName, Context, Oids, Expire, ExtraInfo) -> - snmpm_server:async_get(UserId, TargetName, Context, Oids, Expire, - ExtraInfo). + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_get2(UserId, TargetName, Oids, SendOpts). +%% +%% ag(UserId, Addr, Oids) -> ag(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). @@ -630,31 +671,44 @@ ag(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) -> Error -> Error end. +%% %% --- synchroneous get_next-request --- %% -sync_get_next(UserId, TargetName, Oids) -> - sync_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). +sync_get_next2(UserId, TargetName, Oids) -> + sync_get_next2(UserId, TargetName, Oids, []). -sync_get_next(UserId, TargetName, Context, Oids) - when is_list(Context) andalso is_list(Oids) -> - snmpm_server:sync_get_next(UserId, TargetName, Context, Oids); +sync_get_next2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:sync_get_next2(UserId, TargetName, Oids, SendOpts). + +%% +sync_get_next(UserId, TargetName, Oids) -> + sync_get_next2(UserId, TargetName, Oids). sync_get_next(UserId, TargetName, Oids, Timeout) when is_list(Oids) andalso is_integer(Timeout) -> - sync_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout). + SendOpts = [{timeout, Timeout}], + sync_get_next2(UserId, TargetName, Oids, SendOpts); +sync_get_next(UserId, TargetName, Context, Oids) + when is_list(Context) andalso is_list(Oids) -> + SendOpts = [{context, Context}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). sync_get_next(UserId, TargetName, Context, Oids, Timeout) -> - snmpm_server:sync_get_next(UserId, TargetName, Context, Oids, Timeout). + SendOpts = [{timeout, Timeout}, {context, Context}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). sync_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> - snmpm_server:sync_get_next(UserId, TargetName, Context, Oids, Timeout, - ExtraInfo). + SendOpts = [{timeout, Timeout}, {context, Context}, {extra, ExtraInfo}], + sync_get_next2(UserId, TargetName, Oids, SendOpts). +%% +%% gn(UserId, Addr, Oids) -> gn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). @@ -699,30 +753,44 @@ gn(UserId, Addr, Port, CtxName, Oids, Timeout, ExtraInfo) -> Error -> Error end. +%% %% --- asynchroneous get_next-request --- %% +async_get_next2(UserId, TargetName, Oids) -> + async_get_next2(UserId, TargetName, Oids, []). + +async_get_next2(UserId, TargetName, Oids, SendOpts) + when is_list(Oids) andalso is_list(SendOpts) -> + snmpm_server:async_get_next2(UserId, TargetName, Oids, SendOpts). + +%% async_get_next(UserId, TargetName, Oids) -> - async_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids). + async_get_next2(UserId, TargetName, Oids). +async_get_next(UserId, TargetName, Oids, Expire) + when is_list(Oids) andalso is_integer(Expire) -> + SendOpts = [{timeout, Expire}], + async_get_next2(UserId, TargetName, Oids, SendOpts); async_get_next(UserId, TargetName, Context, Oids) when is_list(Context) andalso is_list(Oids) -> - snmpm_server:async_get_next(UserId, TargetName, Context, Oids); + SendOpts = [{context, Context}], + async_get_next2(UserId, TargetName, Oids, SendOpts). -async_get_next(UserId, TargetName, Oids, Timeout) - when is_list(Oids) andalso is_integer(Timeout) -> - async_get_next(UserId, TargetName, ?DEFAULT_CONTEXT, Oids, Timeout). +async_get_next(UserId, TargetName, Context, Oids, Expire) -> + SendOpts = [{timeout, Expire}, {context, Context}], + async_get_next2(UserId, TargetName, Oids, SendOpts). -async_get_next(UserId, TargetName, Context, Oids, Timeout) -> - snmpm_server:async_get_next(UserId, TargetName, Context, Oids, Timeout). +async_get_next(UserId, TargetName, Context, Oids, Expire, ExtraInfo) -> + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_get_next2(UserId, TargetName, Oids, SendOpts). +%% -async_get_next(UserId, TargetName, Context, Oids, Timeout, ExtraInfo) -> - snmpm_server:async_get_next(UserId, TargetName, Context, Oids, Timeout, - ExtraInfo). +%% agn(UserId, Addr, Oids) -> agn(UserId, Addr, ?DEFAULT_AGENT_PORT, Oids). @@ -768,31 +836,44 @@ agn(UserId, Addr, Port, CtxName, Oids, Expire, ExtraInfo) -> Error -> Error end. +%% %% --- synchroneous set-request --- %% -sync_set(UserId, TargetName, VarsAndVals) -> - sync_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals). +sync_set2(UserId, TargetName, VarsAndVals) -> + sync_set2(UserId, TargetName, VarsAndVals, []). -sync_set(UserId, TargetName, Context, VarsAndVals) - when is_list(Context) andalso is_list(VarsAndVals) -> - snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals); +sync_set2(UserId, TargetName, VarsAndVals, SendOpts) + when is_list(VarsAndVals) andalso is_list(SendOpts) -> + snmpm_server:sync_set2(UserId, TargetName, VarsAndVals, SendOpts). + +%% +sync_set(UserId, TargetName, VarsAndVals) -> + sync_set2(UserId, TargetName, VarsAndVals). sync_set(UserId, TargetName, VarsAndVals, Timeout) when is_list(VarsAndVals) andalso is_integer(Timeout) -> - sync_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals, Timeout). + SendOpts = [{timeout, Timeout}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts); +sync_set(UserId, TargetName, Context, VarsAndVals) + when is_list(Context) andalso is_list(VarsAndVals) -> + SendOpts = [{context, Context}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). sync_set(UserId, TargetName, Context, VarsAndVals, Timeout) -> - snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals, Timeout). + SendOpts = [{timeout, Timeout}, {context, Context}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, ExtraInfo) -> - snmpm_server:sync_set(UserId, TargetName, Context, VarsAndVals, Timeout, - ExtraInfo). + SendOpts = [{timeout, Timeout}, {context, Context}, {extra, ExtraInfo}], + sync_set2(UserId, TargetName, VarsAndVals, SendOpts). +%% +%% s(UserId, Addr, VarsAndVals) -> s(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals). @@ -846,31 +927,44 @@ s(UserId, Addr, Port, CtxName, VarsAndVals, Timeout, ExtraInfo) -> Error -> Error end. +%% %% --- asynchroneous set-request --- %% -async_set(UserId, TargetName, VarsAndVals) -> - async_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals). +async_set2(UserId, TargetName, VarsAndVals) -> + async_set2(UserId, TargetName, VarsAndVals, []). -async_set(UserId, TargetName, Context, VarsAndVals) - when is_list(Context) andalso is_list(VarsAndVals) -> - snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals); +async_set2(UserId, TargetName, VarsAndVals, SendOpts) + when is_list(VarsAndVals) andalso is_list(SendOpts) -> + snmpm_server:async_set2(UserId, TargetName, VarsAndVals, SendOpts). + +%% +async_set(UserId, TargetName, VarsAndVals) -> + async_set2(UserId, TargetName, VarsAndVals). async_set(UserId, TargetName, VarsAndVals, Expire) when is_list(VarsAndVals) andalso is_integer(Expire) -> - async_set(UserId, TargetName, ?DEFAULT_CONTEXT, VarsAndVals, Expire). + SendOpts = [{timeout, Expire}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts); +async_set(UserId, TargetName, Context, VarsAndVals) + when is_list(Context) andalso is_list(VarsAndVals) -> + SendOpts = [{context, Context}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). async_set(UserId, TargetName, Context, VarsAndVals, Expire) -> - snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals, Expire). + SendOpts = [{timeout, Expire}, {context, Context}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). async_set(UserId, TargetName, Context, VarsAndVals, Expire, ExtraInfo) -> - snmpm_server:async_set(UserId, TargetName, Context, VarsAndVals, Expire, - ExtraInfo). + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_set2(UserId, TargetName, VarsAndVals, SendOpts). +%% +%% as(UserId, Addr, VarsAndVals) -> as(UserId, Addr, ?DEFAULT_AGENT_PORT, VarsAndVals). @@ -924,44 +1018,77 @@ as(UserId, Addr, Port, CtxName, VarsAndVals, Expire, ExtraInfo) -> Error -> Error end. - - +%% %% --- synchroneous get-bulk --- %% -sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> - sync_get_bulk(UserId, TargetName, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids). +sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). -sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) +sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) when is_integer(NonRep) andalso is_integer(MaxRep) andalso - is_list(Context) andalso - is_list(Oids) -> - snmpm_server:sync_get_bulk(UserId, TargetName, - NonRep, MaxRep, - Context, Oids); + is_list(Oids) andalso + is_list(SendOpts) -> + %% p("sync_get_bulk -> entry with" + %% "~n UserId: ~p" + %% "~n TargetName: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n SendOpts: ~p", + %% [UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), + snmpm_server:sync_get_bulk2(UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts). + +%% +sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Timeout) when is_integer(NonRep) andalso is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Timeout) -> - sync_get_bulk(UserId, TargetName, NonRep, MaxRep, - ?DEFAULT_CONTEXT, Oids, Timeout). + SendOpts = [{timeout, Timeout}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts); +sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) + when is_integer(NonRep) andalso + is_integer(MaxRep) andalso + is_list(Context) andalso + is_list(Oids) -> + %% p("sync_get_bulk -> entry with" + %% "~n UserId: ~p" + %% "~n TargetName: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Context: ~p" + %% "~n Oids: ~p", [UserId, TargetName, NonRep, MaxRep, Context, Oids]), + SendOpts = [{context, Context}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout) -> - snmpm_server:sync_get_bulk(UserId, TargetName, NonRep, MaxRep, - Context, Oids, Timeout). + SendOpts = [{timeout, Timeout}, {context, Context}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). sync_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Timeout, ExtraInfo) -> - snmpm_server:sync_get_bulk(UserId, TargetName, NonRep, MaxRep, - Context, Oids, Timeout, ExtraInfo). + SendOpts = [{timeout, Timeout}, {context, Context}, {extra, ExtraInfo}], + sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). +%% +%% gb(UserId, Addr, NonRep, MaxRep, Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, NonRep, MaxRep, Oids]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids). gb(UserId, Addr, Port, NonRep, MaxRep, Oids) @@ -969,6 +1096,14 @@ gb(UserId, Addr, Port, NonRep, MaxRep, Oids) is_integer(NonRep) andalso is_integer(MaxRep) andalso is_list(Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, Oids]), gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids); gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids) @@ -976,6 +1111,14 @@ gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids) is_integer(MaxRep) andalso is_list(CtxName) andalso is_list(Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids); gb(UserId, Addr, NonRep, MaxRep, Oids, Timeout) @@ -983,6 +1126,14 @@ gb(UserId, Addr, NonRep, MaxRep, Oids, Timeout) is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, NonRep, MaxRep, Oids, Timeout]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids, Timeout). gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids) @@ -991,8 +1142,18 @@ gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids) is_integer(MaxRep) andalso is_list(CtxName) andalso is_list(Oids) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids]), case target_name(Addr, Port) of {ok, TargetName} -> + %% p("gb -> TargetName: ~p", [TargetName]), sync_get_bulk(UserId, TargetName, NonRep, MaxRep, CtxName, Oids); Error -> Error @@ -1004,6 +1165,15 @@ gb(UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout) is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, Oids, Timeout]), gb(UserId, Addr, Port, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Timeout); gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout) @@ -1012,10 +1182,29 @@ gb(UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout) is_list(CtxName) andalso is_list(Oids) andalso is_integer(Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, NonRep, MaxRep, CtxName, Oids, Timeout]), gb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, CtxName, Oids, Timeout). gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout]), case target_name(Addr, Port) of {ok, TargetName} -> sync_get_bulk(UserId, TargetName, @@ -1025,6 +1214,17 @@ gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout) -> end. gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) -> + %% p("gb -> entry with" + %% "~n UserId: ~p" + %% "~n Addr: ~p" + %% "~n Port: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n CtxName: ~p" + %% "~n Oids: ~p" + %% "~n Timeout: ~p" + %% "~n ExtraInfo: ~p", + %% [UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo]), case target_name(Addr, Port) of {ok, TargetName} -> sync_get_bulk(UserId, TargetName, @@ -1032,42 +1232,55 @@ gb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Timeout, ExtraInfo) -> Error -> Error end. +%% %% --- asynchroneous get-bulk --- %% -async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> - async_get_bulk(UserId, TargetName, NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids). +async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). -async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) +async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) when is_integer(NonRep) andalso is_integer(MaxRep) andalso - is_list(Context) andalso - is_list(Oids) -> - snmpm_server:async_get_bulk(UserId, TargetName, - NonRep, MaxRep, Context, Oids); + is_list(Oids) andalso + is_list(SendOpts) -> + snmpm_server:async_get_bulk2(UserId, TargetName, + NonRep, MaxRep, Oids, SendOpts). + +%% +async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids). async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Expire) when is_integer(NonRep) andalso is_integer(MaxRep) andalso is_list(Oids) andalso is_integer(Expire) -> - async_get_bulk(UserId, TargetName, - NonRep, MaxRep, ?DEFAULT_CONTEXT, Oids, Expire). + SendOpts = [{timeout, Expire}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts); +async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids) + when is_integer(NonRep) andalso + is_integer(MaxRep) andalso + is_list(Context) andalso + is_list(Oids) -> + SendOpts = [{context, Context}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire) -> - snmpm_server:async_get_bulk(UserId, TargetName, - NonRep, MaxRep, Context, Oids, Expire). + SendOpts = [{timeout, Expire}, {context, Context}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). async_get_bulk(UserId, TargetName, NonRep, MaxRep, Context, Oids, Expire, ExtraInfo) -> - snmpm_server:async_get_bulk(UserId, TargetName, - NonRep, MaxRep, - Context, Oids, Expire, ExtraInfo). + SendOpts = [{timeout, Expire}, {context, Context}, {extra, ExtraInfo}], + async_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts). +%% +%% agb(UserId, Addr, NonRep, MaxRep, Oids) -> agb(UserId, Addr, ?DEFAULT_AGENT_PORT, NonRep, MaxRep, Oids). @@ -1140,6 +1353,7 @@ agb(UserId, Addr, Port, NonRep, MaxRep, CtxName, Oids, Expire, ExtraInfo) -> Error -> Error end. +%% cancel_async_request(UserId, ReqId) -> diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index 3b49267994..29f0c61b0e 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -348,6 +348,14 @@ async_get_next(UserId, TargetName, CtxName, Oids, Expire, ExtraInfo) %% sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids) -> %% sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, []). sync_get_bulk2(UserId, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + %% p("sync_get_bulk2 -> entry with" + %% "~n UserId: ~p" + %% "~n TargetName: ~p" + %% "~n NonRep: ~p" + %% "~n MaxRep: ~p" + %% "~n Oids: ~p" + %% "~n SendOpts: ~p", + %% [UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), call({sync_get_bulk, self(), UserId, TargetName, NonRep, MaxRep, Oids, SendOpts}). diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 50836db731..a98ab889d0 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -43,7 +43,7 @@ %% External exports %%---------------------------------------------------------------------- -export([ - all/0,groups/0,init_per_group/2,end_per_group/2, + all/0, groups/0, init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, @@ -360,42 +360,100 @@ end_per_testcase2(Case, Config) -> %%====================================================================== all() -> -[{group, start_and_stop_tests}, {group, misc_tests}, - {group, user_tests}, {group, agent_tests}, - {group, request_tests}, {group, event_tests}, discovery, - {group, tickets}]. + [ + {group, start_and_stop_tests}, + {group, misc_tests}, + {group, user_tests}, + {group, agent_tests}, + {group, request_tests}, + {group, event_tests}, + discovery, + {group, tickets} + ]. groups() -> - [{start_and_stop_tests, [], - [simple_start_and_stop1, simple_start_and_stop2, - simple_start_and_monitor_crash1, - simple_start_and_monitor_crash2, notify_started01, - notify_started02]}, - {misc_tests, [], [info]}, - {user_tests, [], [register_user1]}, - {agent_tests, [], [register_agent1, register_agent2]}, - {request_tests, [], - [{group, get_tests}, {group, get_next_tests}, - {group, set_tests}, {group, bulk_tests}, - {group, misc_request_tests}]}, - {get_tests, [], - [simple_sync_get1, simple_sync_get2, simple_async_get1, - simple_async_get2]}, - {get_next_tests, [], - [simple_sync_get_next1, simple_sync_get_next2, - simple_async_get_next1, simple_async_get_next2]}, - {set_tests, [], - [simple_sync_set1, simple_sync_set2, simple_async_set1, - simple_async_set2]}, - {bulk_tests, [], - [simple_sync_get_bulk1, simple_sync_get_bulk2, - simple_async_get_bulk1, simple_async_get_bulk2]}, - {misc_request_tests, [], [misc_async1, misc_async2]}, - {event_tests, [], - [trap1, trap2, inform1, inform2, inform3, inform4, - inform_swarm, report]}, - {tickets, [], [{group, otp8015}, {group, otp8395}]}, - {otp8015, [], [otp8015_1]}, {otp8395, [], [otp8395_1]}]. + [ + {start_and_stop_tests, [], + [ + simple_start_and_stop1, + simple_start_and_stop2, + simple_start_and_monitor_crash1, + simple_start_and_monitor_crash2, + notify_started01, + notify_started02 + ] + }, + {misc_tests, [], [info]}, + {user_tests, [], [register_user1]}, + {agent_tests, [], [register_agent1, register_agent2]}, + {request_tests, [], + [ + {group, get_tests}, + {group, get_next_tests}, + {group, set_tests}, + {group, bulk_tests}, + {group, misc_request_tests} + ] + }, + {get_tests, [], + [ + simple_sync_get1, + simple_sync_get2, + simple_async_get1, + simple_async_get2 + ] + }, + {get_next_tests, [], + [ + simple_sync_get_next1, + simple_sync_get_next2, + simple_async_get_next1, + simple_async_get_next2 + ] + }, + {set_tests, [], + [ + simple_sync_set1, + simple_sync_set2, + simple_async_set1, + simple_async_set2 + ] + }, + {bulk_tests, [], + [ + simple_sync_get_bulk1, + simple_sync_get_bulk2, + simple_async_get_bulk1, + simple_async_get_bulk2 + ] + }, + {misc_request_tests, [], + [ + misc_async1, + misc_async2 + ] + }, + {event_tests, [], + [ + trap1, + trap2, + inform1, + inform2, + inform3, + inform4, + inform_swarm, + report + ] + }, + {tickets, [], + [ + {group, otp8015}, + {group, otp8395} + ] + }, + {otp8015, [], [otp8015_1]}, + {otp8395, [], [otp8395_1]} + ]. init_per_group(_GroupName, Config) -> Config. @@ -404,21 +462,6 @@ end_per_group(_GroupName, Config) -> Config. - - - - - - - - - - - - - - - %%====================================================================== %% Test functions %%====================================================================== @@ -1435,40 +1478,41 @@ simple_async_get1(Config) when is_list(Config) -> ?line ok = agent_load_mib(AgentNode, Test2Mib), Exec = fun(Data) -> - async_g_exec1(MgrNode, Addr, Port, Data) + async_g_exec1(MgrNode, Addr, Port, Data) end, - Requests = [ - { 1, - [?sysObjectID_instance], - Exec, - fun(X) -> sag_verify(X, [?sysObjectID_instance]) end}, - { 2, - [?sysDescr_instance, ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysUpTime_instance]) - end}, - { 3, - [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end}, - { 4, - [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end} - ], + Requests = + [ + { 1, + [?sysObjectID_instance], + Exec, + fun(X) -> sag_verify(X, [?sysObjectID_instance]) end }, + { 2, + [?sysDescr_instance, ?sysUpTime_instance], + Exec, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysUpTime_instance]) + end }, + { 3, + [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], + Exec, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance]) + end }, + { 4, + [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance], + Exec, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance]) + end } + ], p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index b0e192344d..71fdebb67b 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -847,7 +847,11 @@ call(Req, To) when is_integer(To) -> {error, timeout} end. -reply(Pid, Reply, Ref) -> +reply(Pid, Reply, Ref) -> + d("reply -> entry with" + "~n Pid: ~p" + "~n Reply: ~p" + "~n Ref: ~p", [Pid, Reply, Ref]), Pid ! {Reply, Ref}. cast(Msg) -> -- cgit v1.2.3 From e6a14f6efc61aec9eef4197c08fd8fbe9116281b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 09:31:32 +0200 Subject: Added the documentation for the previous request API (removed by misstake). --- lib/snmp/doc/src/snmpm.xml | 281 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index ff072205bc..b1c8e56721 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -538,6 +538,50 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

+ + +
+ + + sync_get(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + Synchronous get-request + + UserId = term() + TargetName = target_name() + ContextName = string() + Oids = [oid()] + Timeout = integer() + ExtraInfo = term() + SnmpReply = snmp_reply() + Remaining = integer() + Reason = {send_failed, ReqId, R} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + R = term() + SecInfo = [sec_info()] + sec_info() = {sec_tag(), ExpectedValue, ReceivedValue} + sec_tag() = atom() + ExpectedValue = ReceivedValue = term() + SnmpInfo = term() + + +

Synchronous get-request.

+

Remaining is the remaining time of the given or + default timeout time.

+

When Reason is {send_failed, ...} it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+

For SnmpInfo, see the user callback function + handle_report.

+
@@ -578,6 +622,40 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 for this request, they override any configuration done when the agent was registered.

+ + +
+ + + async_get(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} + async_get(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason} + async_get(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason} + async_get(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason} + async_get(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + Asynchronous get-request + + UserId = term() + TargetName = target_name() + ContextName = string() + Oids = [oid()] + Expire = integer() + ExtraInfo = term() + ReqId = term() + Reason = term() + + +

Asynchronous get-request.

+

The reply, if it arrives, will be delivered to the user + through a call to the snmpm_user callback function + handle_pdu.

+

The Expire time indicates for how long the request is + valid (after which the manager is free to delete it).

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+
@@ -630,6 +708,42 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

+ + +
+ + + sync_get_next(UserId, TargetName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_next(UserId, TargetName, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_next(UserId, TargetName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_next(UserId, TargetName, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_next(UserId, TargetName, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + Synchronous get-next-request + + UserId = term() + TargetName = target_name() + ContextName = string() + Oids = [oid()] + Timeout = integer() + ExtraInfo = term() + SnmpReply = snmp_reply() + Remaining = integer() + Reason = {send_failed, ReqId, R} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + R = term() + + +

Synchronous get-next-request.

+

Remaining time of the given or default timeout time.

+

When Reason is {send_failed, ...} it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+
@@ -667,6 +781,34 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 for this request, they override any configuration done when the agent was registered.

+ + +
+ + + async_get_next(UserId, TargetName, Oids) -> {ok, ReqId} | {error, Reason} + async_get_next(UserId, TargetName, ContextName, Oids) -> {ok, ReqId} | {error, Reason} + async_get_next(UserId, TargetName, Oids, Expire) -> {ok, ReqId} | {error, Reason} + async_get_next(UserId, TargetName, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason} + async_get_next(UserId, TargetName, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + Asynchronous get-next-request + + UserId = term() + TargetName = target_name() + ContextName = string() + Oids = [oid()] + Expire = integer() + ExtraInfo = term() + ReqId = integer() + Reason = term() + + +

Asynchronous get-next-request.

+

The reply will be delivered to the user through a call + to the snmpm_user callback function handle_pdu.

+

The Expire time indicates for how long the request is + valid (after which the manager is free to delete it).

+
@@ -722,6 +864,44 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

+ + +
+ + + sync_set(UserId, TargetName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_set(UserId, TargetName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_set(UserId, TargetName, ContextName, VarsAndVals, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + Synchronous set-request + + UserId = term() + TargetName = target_name() + ContextName = string() + VarsAndVals = vars_and_vals() + Timeout = integer() + ExtraInfo = term() + SnmpReply = snmp_reply() + Remaining = integer() + Reason = {send_failed, ReqId, ActualReason} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + ActualReason = term() + + +

Synchronous set-request.

+

Remaining time of the given or default timeout time.

+

When Reason is {send_failed, ...} it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

When var_and_val() is {oid(), value()}, the + manager makes an educated guess based on the loaded mibs.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+
@@ -764,6 +944,40 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 for this request, they override any configuration done when the agent was registered.

+ + +
+ + + async_set(UserId, TargetName, VarsAndVals) -> {ok, ReqId} | {error, Reason} + async_set(UserId, TargetName, ContextName, VarsAndVals) -> {ok, ReqId} | {error, Reason} + async_set(UserId, TargetName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason} + async_set(UserId, TargetName, ContextName, VarsAndVals, Expire) -> {ok, ReqId} | {error, Reason} + async_set(UserId, TargetName, ContextName, VarsAndVals, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + Asynchronous set-request + + UserId = term() + TargetName = target_name() + VarsAndVals = vars_and_vals() + Expire = integer() + ExtraInfo = term() + ReqId = term() + Reason = term() + + +

Asynchronous set-request.

+

The reply will be delivered to the user through a call + to the snmpm_user callback function handle_pdu.

+

The Expire time indicates for how long the request is + valid (after which the manager is free to delete it).

+

When var_and_val() is {oid(), value()}, the + manager makes an educated guess based on the loaded mibs.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+
@@ -818,6 +1032,43 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

For SnmpInfo, see the user callback function handle_report.

+ + +
+ + + sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_bulk(UserId, TragetName, NonRep, MaxRep, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout) -> {ok, SnmpReply, Remaining} | {error, Reason} + sync_get_bulk(UserId, TragetName, NonRep, MaxRep, ContextName, Oids, Timeout, ExtraInfo) -> {ok, SnmpReply, Remaining} | {error, Reason} + Synchronous get-bulk-request + + UserId = term() + TargetName = target_name() + NonRep = integer() + MaxRep = integer() + ContextName = string() + Oids = [oid()] + Timeout = integer() + ExtraInfo = term() + SnmpReply = snmp_reply() + Remaining = integer() + Reason = {send_failed, ReqId, R} | {invalid_sec_info, SecInfo, SnmpInfo} | term() + + +

Synchronous get-bulk-request (See RFC1905).

+

Remaining time of the given or default timeout time.

+

When Reason is {send_failed, ...} it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes no use of this info, so the only use for it + in such a configuration (when using the built in net-if) would + be tracing.

+
@@ -859,6 +1110,36 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 for this request, they override any configuration done when the agent was registered.

+ + +
+ + + async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids) -> {ok, ReqId} | {error, Reason} + async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids) -> {ok, ReqId} | {error, Reason} + async_get_bulk(UserId, TargetName, NonRep, MaxRep, Oids, Expire) -> {ok, ReqId} | {error, Reason} + async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire) -> {ok, ReqId} | {error, Reason} + async_get_bulk(UserId, TargetName, NonRep, MaxRep, ContextName, Oids, Expire, ExtraInfo) -> {ok, ReqId} | {error, Reason} + Asynchronous get-bulk-request + + UserId = term() + TargetName = target_name() + NonRep = integer() + MaxRep = integer() + ContextName = string() + Oids = [oid()] + Expire = integer() + ExtraInfo = term() + ReqId = integer() + Reason = term() + + +

Asynchronous get-bulk-request (See RFC1905).

+

The reply will be delivered to the user through a call + to the snmpm_user callback function handle_pdu.

+

The Expire time indicates for how long the request is + valid (after which the manager is free to delete it).

+
-- cgit v1.2.3 From 29ac3cf1c1e6620344192944ebeae7ee8f220bb7 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 12:24:51 +0200 Subject: Detailed usage of "Extra" usage in the request API functions, including the reserved value. --- lib/snmp/doc/src/snmpm.xml | 210 +++++++++++++++++++------------- lib/snmp/src/manager/snmpm.erl | 1 - lib/snmp/src/manager/snmpm_internal.hrl | 5 +- lib/snmp/src/manager/snmpm_net_if.erl | 18 ++- lib/snmp/src/manager/snmpm_server.erl | 2 - 5 files changed, 145 insertions(+), 91 deletions(-) diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index b1c8e56721..72849b9c9e 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -525,9 +525,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

The send option extra specifies an opaque data structure passed on to the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a option (when using the built in net-if) would - be tracing.

+ application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

Some of the send options (community, sec_model, sec_name, sec_level and max_message_size) @@ -569,16 +571,18 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Synchronous get-request.

Remaining is the remaining time of the given or - default timeout time.

+ default timeout time.

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

For SnmpInfo, see the user callback function handle_report.

@@ -610,12 +614,14 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

The send option timeout specifies for how long the request is valid (after which the manager is free to delete it).

-

The send option extra specifies an opaque data structure +

The send option extra specifies an opaque data structure passed on to the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

- + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

+

Some of the send options (community, sec_model, sec_name, sec_level and max_message_size) are override options. That is, @@ -646,15 +652,17 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Asynchronous get-request.

The reply, if it arrives, will be delivered to the user - through a call to the snmpm_user callback function - handle_pdu.

-

The Expire time indicates for how long the request is - valid (after which the manager is free to delete it).

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ through a call to the snmpm_user callback function + handle_pdu.

+

The Expire time indicates for how long the request is + valid (after which the manager is free to delete it).

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

@@ -695,9 +703,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

The send option extra specifies an opaque data structure passed on to the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

Some of the send options (community, sec_model, sec_name, sec_level and max_message_size) @@ -735,15 +745,17 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Synchronous get-next-request.

Remaining time of the given or default timeout time.

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

- + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

+ @@ -769,12 +781,14 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

The send option timeout specifies for how long the request is valid (after which the manager is free to delete it).

-

The send option extra specifies an opaque data structure +

The send option extra specifies an opaque data structure passed on to the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

- + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

+

Some of the send options (community, sec_model, sec_name, sec_level and max_message_size) are override options. That is, @@ -808,6 +822,13 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 to the snmpm_user callback function handle_pdu.

The Expire time indicates for how long the request is valid (after which the manager is free to delete it).

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

@@ -851,9 +872,11 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

The send option extra specifies an opaque data structure passed on to the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

Some of the send options (community, sec_model, sec_name, sec_level and max_message_size) @@ -891,16 +914,18 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Synchronous set-request.

Remaining time of the given or default timeout time.

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

When var_and_val() is {oid(), value()}, the - manager makes an educated guess based on the loaded mibs.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+ the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

When var_and_val() is {oid(), value()}, the + manager makes an educated guess based on the loaded mibs.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

@@ -934,10 +959,12 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

The send option extra specifies an opaque data structure passed on to the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

- + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

+

Some of the send options (community, sec_model, sec_name, sec_level and max_message_size) are override options. That is, @@ -967,17 +994,19 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Asynchronous set-request.

The reply will be delivered to the user through a call - to the snmpm_user callback function handle_pdu.

+ to the snmpm_user callback function handle_pdu.

The Expire time indicates for how long the request is - valid (after which the manager is free to delete it).

-

When var_and_val() is {oid(), value()}, the - manager makes an educated guess based on the loaded mibs.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

- + valid (after which the manager is free to delete it).

+

When var_and_val() is {oid(), value()}, the + manager makes an educated guess based on the loaded mibs.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

+ @@ -1019,10 +1048,12 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

The send option extra specifies an opaque data structure passed on to the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

- + application makes, with one exception, no use of this info, + so the only use for it in such a option (when using the built in + net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

+

Some of the send options (community, sec_model, sec_name, sec_level and max_message_size) are override options. That is, @@ -1059,15 +1090,17 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Synchronous get-bulk-request (See RFC1905).

Remaining time of the given or default timeout time.

-

When Reason is {send_failed, ...} it means that - the net_if process failed to send the message. This could happen - because of any number of reasons, i.e. encoding error. R - is the actual reason in this case.

-

ExtraInfo is an opaque data structure passed on to - the net-if process. The net-if process included in this - application makes no use of this info, so the only use for it - in such a configuration (when using the built in net-if) would - be tracing.

+

When Reason is {send_failed, ...} it means that + the net_if process failed to send the message. This could happen + because of any number of reasons, i.e. encoding error. R + is the actual reason in this case.

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

@@ -1136,9 +1169,16 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1

Asynchronous get-bulk-request (See RFC1905).

The reply will be delivered to the user through a call - to the snmpm_user callback function handle_pdu.

+ to the snmpm_user callback function handle_pdu.

The Expire time indicates for how long the request is - valid (after which the manager is free to delete it).

+ valid (after which the manager is free to delete it).

+

ExtraInfo is an opaque data structure passed on to + the net-if process. The net-if process included in this + application makes, with one exception, no use of this info, + so the only use for it in such a configuration (when using the + built in net-if) would be tracing. The one usage exception is: + Any tuple with snmpm_extra_info_tag as its first + element is reserved for internal use.

diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index dd85ec9f41..8a629eaf3b 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -164,7 +164,6 @@ -include("snmpm_internal.hrl"). -define(DEFAULT_AGENT_PORT, 161). -%% -define(DEFAULT_CONTEXT, ""). %% This function is called when the snmp application diff --git a/lib/snmp/src/manager/snmpm_internal.hrl b/lib/snmp/src/manager/snmpm_internal.hrl index 389aeaf197..53ad41c6b0 100644 --- a/lib/snmp/src/manager/snmpm_internal.hrl +++ b/lib/snmp/src/manager/snmpm_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. 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 @@ -21,6 +21,9 @@ -define(snmpm_internal, true). -define(DEFAULT_CONTEXT, ""). +-define(SNMPM_EXTRA_INFO_TAG, snmpm_extra_info_tag). +-define(DEFAULT_EXTRA_INFO, {?SNMPM_EXTRA_INFO_TAG}). + -include_lib("snmp/src/app/snmp_internal.hrl"). diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl index 07156dacd9..3d248fff57 100644 --- a/lib/snmp/src/manager/snmpm_net_if.erl +++ b/lib/snmp/src/manager/snmpm_net_if.erl @@ -99,7 +99,7 @@ stop(Pid) -> call(Pid, stop). send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port) -> - send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, undefined). + send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ?DEFAULT_EXTRA_INFO). send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo) when is_record(Pdu, pdu) -> @@ -380,13 +380,14 @@ handle_call(Req, From, State) -> %% {noreply, State, Timeout} | %% {stop, Reason, State} (terminate/2 is called) %%-------------------------------------------------------------------- -handle_cast({send_pdu, Pdu, Vsn, MsgData, Addr, Port, _ExtraInfo}, State) -> +handle_cast({send_pdu, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo}, State) -> ?vlog("received send_pdu message with" "~n Pdu: ~p" "~n Vsn: ~p" "~n MsgData: ~p" "~n Addr: ~p" "~n Port: ~p", [Pdu, Vsn, MsgData, Addr, Port]), + maybe_process_extra_info(ExtraInfo), maybe_handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, State), {noreply, State}; @@ -997,6 +998,19 @@ pdu_type_of(TrapPdu) when is_record(TrapPdu, trappdu) -> trap. +%% ------------------------------------------------------------------- + +%% At this point this function is used during testing +maybe_process_extra_info(?DEFAULT_EXTRA_INFO) -> + ok; +maybe_process_extra_info({?SNMPM_EXTRA_INFO_TAG, Fun}) + when is_function(Fun, 0) -> + (catch Fun()), + ok; +maybe_process_extra_info(_ExtraInfo) -> + ok. + + %% ------------------------------------------------------------------- t() -> diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index 29f0c61b0e..b8d7cf6375 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -101,8 +101,6 @@ -define(DEFAULT_ASYNC_GET_BULK_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). -define(DEFAULT_ASYNC_SET_TIMEOUT, ?DEFAULT_ASYNC_TIMEOUT). --define(DEFAULT_EXTRA_INFO, undefined). - -define(SNMP_AGENT_PORT, 161). -define(SYNC_GET_TIMEOUT(SendOpts), -- cgit v1.2.3 From c733034aa59ee536b166e2c676bb0239ee62b1a6 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 12:27:04 +0200 Subject: First test case for request API version 3. --- lib/snmp/test/Makefile | 7 +- lib/snmp/test/snmp_manager_test.erl | 179 +++++++++++++++++++++++++----------- lib/snmp/test/snmp_manager_user.erl | 16 +++- 3 files changed, 143 insertions(+), 59 deletions(-) diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile index b7975024b4..0e9c73081d 100644 --- a/lib/snmp/test/Makefile +++ b/lib/snmp/test/Makefile @@ -145,9 +145,12 @@ endif # ---------------------------------------------------- EBIN = . -ERL_COMPILE_FLAGS += -I../src \ +ERL_COMPILE_FLAGS += -I../../snmp/src/app \ + -I../../snmp/src/misc \ + -I../../snmp/src/agent \ + -I../../snmp/src/manager \ -I$(ERL_TOP)/lib/test_server/include \ - -I../include \ + -I../../snmp/include \ -Dsnmp_test_data=snmp_test_data \ -Dversion=\"$(VSN)$(PRE_VSN)\" \ +'{parse_transform,sys_pre_attributes}' \ diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index a98ab889d0..962c449617 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -37,13 +37,16 @@ -include_lib("snmp/include/snmp_types.hrl"). -include_lib("snmp/include/STANDARD-MIB.hrl"). +-include_lib("snmp/src/manager/snmpm_internal.hrl"). %%---------------------------------------------------------------------- %% External exports %%---------------------------------------------------------------------- -export([ - all/0, groups/0, init_per_group/2, end_per_group/2, + all/0, + groups/0, + init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, @@ -54,49 +57,38 @@ notify_started01/1, notify_started02/1, - register_user1/1, - register_agent1/1, register_agent2/1, - info/1, - - - simple_sync_get1/1, simple_sync_get2/1, + simple_sync_get3/1, simple_async_get1/1, simple_async_get2/1, - simple_sync_get_next1/1, simple_sync_get_next2/1, simple_async_get_next1/1, simple_async_get_next2/1, - simple_sync_set1/1, simple_sync_set2/1, simple_async_set1/1, simple_async_set2/1, - simple_sync_get_bulk1/1, simple_sync_get_bulk2/1, simple_async_get_bulk1/1, simple_async_get_bulk2/1, - misc_async1/1, misc_async2/1, discovery/1, - - trap1/1, trap2/1, @@ -109,8 +101,6 @@ report/1, - - otp8015_1/1, otp8395_1/1 @@ -220,7 +210,7 @@ init_per_testcase2(Case, Config) -> Conf2. init_per_testcase3(Case, Config) -> - OldApiCases = + ApiCases01 = [ simple_sync_get1, simple_async_get1, @@ -232,7 +222,7 @@ init_per_testcase3(Case, Config) -> simple_async_get_bulk1, misc_async1 ], - NewApiCases = + ApiCases02 = [ simple_sync_get2, simple_async_get2, @@ -245,6 +235,10 @@ init_per_testcase3(Case, Config) -> misc_async2, otp8395_1 ], + ApiCases03 = + [ + simple_sync_get3 + ], Cases = [ trap1, @@ -256,8 +250,9 @@ init_per_testcase3(Case, Config) -> inform_swarm, report ] ++ - OldApiCases ++ - NewApiCases, + ApiCases01 ++ + ApiCases02 ++ + ApiCases03, case lists:member(Case, Cases) of true -> NoAutoInformCases = [inform1, inform2, inform3, inform_swarm], @@ -279,7 +274,7 @@ init_per_testcase3(Case, Config) -> Conf2 = init_agent(Conf1), Conf3 = init_manager(AutoInform, Conf2), Conf4 = init_mgr_user(Conf3), - case lists:member(Case, NewApiCases) of + case lists:member(Case, ApiCases02 ++ ApiCases03) of true -> init_mgr_user_data2(Conf4); false -> @@ -301,7 +296,7 @@ end_per_testcase(Case, Config) when is_list(Config) -> Conf2. end_per_testcase2(Case, Config) -> - OldApiCases = + ApiCases01 = [ simple_sync_get1, simple_async_get1, @@ -313,7 +308,7 @@ end_per_testcase2(Case, Config) -> simple_async_get_bulk1, misc_async1 ], - NewApiCases = + ApiCases02 = [ simple_sync_get2, simple_async_get2, @@ -326,6 +321,10 @@ end_per_testcase2(Case, Config) -> misc_async2, otp8395_1 ], + ApiCases03 = + [ + simple_sync_get3 + ], Cases = [ trap1, @@ -337,11 +336,12 @@ end_per_testcase2(Case, Config) -> inform_swarm, report ] ++ - OldApiCases ++ - NewApiCases, + ApiCases01 ++ + ApiCases02 ++ + ApiCases03, case lists:member(Case, Cases) of true -> - Conf1 = case lists:member(Case, NewApiCases) of + Conf1 = case lists:member(Case, ApiCases02 ++ ApiCases03) of true -> fin_mgr_user_data2(Config); false -> @@ -383,9 +383,22 @@ groups() -> notify_started02 ] }, - {misc_tests, [], [info]}, - {user_tests, [], [register_user1]}, - {agent_tests, [], [register_agent1, register_agent2]}, + {misc_tests, [], + [ + info + ] + }, + {user_tests, [], + [ + register_user1 + ] + }, + {agent_tests, [], + [ + register_agent1, + register_agent2 + ] + }, {request_tests, [], [ {group, get_tests}, @@ -399,6 +412,7 @@ groups() -> [ simple_sync_get1, simple_sync_get2, + simple_sync_get3, simple_async_get1, simple_async_get2 ] @@ -451,8 +465,16 @@ groups() -> {group, otp8395} ] }, - {otp8015, [], [otp8015_1]}, - {otp8395, [], [otp8395_1]} + {otp8015, [], + [ + otp8015_1 + ] + }, + {otp8395, [], + [ + otp8395_1 + ] + } ]. init_per_group(_GroupName, Config) -> @@ -1403,14 +1425,22 @@ do_simple_get(Node, Addr, Port, Oids) -> %%====================================================================== -simple_sync_get2(doc) -> ["Simple sync get-request - New style (TargetName)"]; +simple_sync_get2(doc) -> + ["Simple sync get-request - Version 2 API (TargetName)"]; simple_sync_get2(suite) -> []; simple_sync_get2(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, ssg2), - do_simple_get(Config). + do_simple_get2(Config). + +do_simple_get2(Config) -> + Get = fun(Node, TargetName, Oids) -> + mgr_user_sync_get(Node, TargetName, Oids) + end, + PostVerify = fun() -> ok end, + do_simple_get2(Config, Get, PostVerify). -do_simple_get(Config) -> +do_simple_get2(Config, Get, PostVerify) -> p("starting with Config: ~p~n", [Config]), Node = ?config(manager_node, Config), @@ -1418,20 +1448,21 @@ do_simple_get(Config) -> p("issue get-request without loading the mib"), Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance], - ?line ok = do_simple_get(Node, TargetName, Oids1), + ?line ok = do_simple_get2(Node, TargetName, Oids1, Get, PostVerify), p("issue get-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - ?line ok = do_simple_get(Node, TargetName, Oids2), + ?line ok = do_simple_get2(Node, TargetName, Oids2, Get, PostVerify), ok. - -do_simple_get(Node, TargetName, Oids) -> - ?line {ok, Reply, Rem} = mgr_user_sync_get(Node, TargetName, Oids), + +do_simple_get2(Node, TargetName, Oids, Get, PostVerify) + when is_function(Get, 3) andalso is_function(PostVerify, 0) -> + ?line {ok, Reply, Rem} = Get(Node, TargetName, Oids), ?DBG("~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), - + %% verify that the operation actually worked: %% The order should be the same, so no need to seach ?line ok = case Reply of @@ -1446,7 +1477,7 @@ do_simple_get(Node, TargetName, Oids) -> "~n SysDescr: ~s" "~n SysUpTime: ~w", [SysObjectID, SysDescr, SysUpTime]), - ok; + PostVerify(); {noError, 0, Vbs} -> p("unexpected varbinds: ~n~p", [Vbs]), {error, {unexpected_vbs, Vbs}}; @@ -1457,6 +1488,38 @@ do_simple_get(Node, TargetName, Oids) -> ok. +%%====================================================================== + +simple_sync_get3(doc) -> + ["Simple sync get-request - Version 3 API (TargetName and send-opts)"]; +simple_sync_get3(suite) -> []; +simple_sync_get3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssg3), + do_simple_get3(Config). + +do_simple_get3(Config) -> + Self = self(), + Msg = simple_sync_get3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + Get = fun(Node, TargetName, Oids) -> + mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) + end, + PostVerify = + fun() -> + receive + Msg -> + ok + end + end, + do_simple_get2(Config, Get, PostVerify). + + %%====================================================================== simple_async_get1(doc) -> ["Simple (async) get-request - " @@ -1560,8 +1623,8 @@ sag_verify_vbs([Vb|_], [E|_]) -> %%====================================================================== -simple_async_get2(doc) -> ["Simple (async) get-request - " - "New style (TargetName)"]; +simple_async_get2(doc) -> + ["Simple (async) get-request - Version 2 API (TargetName)"]; simple_async_get2(suite) -> []; simple_async_get2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -1766,8 +1829,8 @@ check_ssgn_vbs([Vb|_], [E|_]) -> %%====================================================================== -simple_sync_get_next2(doc) -> ["Simple (sync) get_next-request - " - "New style (TargetName)"]; +simple_sync_get_next2(doc) -> + ["Simple (sync) get_next-request - Version 2 API (TargetName)"]; simple_sync_get_next2(suite) -> []; simple_sync_get_next2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -1967,8 +2030,8 @@ async_gn_exec1(Node, Addr, Port, Oids) -> %%====================================================================== -simple_async_get_next2(doc) -> ["Simple (async) get_next-request - " - "New style (TargetName)"]; +simple_async_get_next2(doc) -> + ["Simple (async) get_next-request - Version 2 API (TargetName)"]; simple_async_get_next2(suite) -> []; simple_async_get_next2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -2132,7 +2195,8 @@ value_of_vavs([{_Oid, Val}|VAVs], Acc) -> %%====================================================================== -simple_sync_set2(doc) -> ["Simple (sync) set-request - New style (TargetName)"]; +simple_sync_set2(doc) -> + ["Simple (sync) set-request - Version 2 API (TargetName)"]; simple_sync_set2(suite) -> []; simple_sync_set2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -2281,8 +2345,8 @@ sas_verify_vbs([Vb|_], [E|_]) -> %%====================================================================== -simple_async_set2(doc) -> ["Simple (async) set-request - " - "New style (TargetName)"]; +simple_async_set2(doc) -> + ["Simple (async) set-request - Version 2 API (TargetName)"]; simple_async_set2(suite) -> []; simple_async_set2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -2514,8 +2578,8 @@ check_ssgb_vbs([R|_], [E|_]) -> %%====================================================================== -simple_sync_get_bulk2(doc) -> ["Simple (sync) get_bulk-request - " - "New style (TargetName)"]; +simple_sync_get_bulk2(doc) -> + ["Simple (sync) get_bulk-request - Version 2 API (TargetName)"]; simple_sync_get_bulk2(suite) -> []; simple_sync_get_bulk2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -2792,8 +2856,8 @@ async_gb_exec1(Node, Addr, Port, {NR, MR, Oids}) -> %%====================================================================== -simple_async_get_bulk2(doc) -> ["Simple (async) get_bulk-request - " - "New style (TargetName)"]; +simple_async_get_bulk2(doc) -> + ["Simple (async) get_bulk-request - Version 2 API (TargetName)"]; simple_async_get_bulk2(suite) -> []; simple_async_get_bulk2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -3123,8 +3187,8 @@ misc_async1(Config) when is_list(Config) -> %%====================================================================== -misc_async2(doc) -> ["Misc (async) request(s) - " - "New style (TargetName)"]; +misc_async2(doc) -> + ["Misc (async) request(s) - Version 2 API (TargetName)"]; misc_async2(suite) -> []; misc_async2(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -4484,7 +4548,7 @@ otp8395_1(suite) -> []; otp8395_1(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, otp8395_1), - do_simple_get(Config). + do_simple_get2(Config). %%====================================================================== @@ -5030,6 +5094,9 @@ mgr_user_sync_get(Node, Addr_or_TargetName, Oids) -> mgr_user_sync_get(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, sync_get, [Addr, Port, Oids]). +mgr_user_sync_get2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, sync_get2, [TargetName, Oids, SendOpts]). + %% mgr_user_async_get(Node, Oids) -> %% mgr_user_async_get(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). mgr_user_async_get(Node, Addr_or_TargetName, Oids) -> diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index 71fdebb67b..af0d7839a8 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -50,7 +50,7 @@ update_agent_info/3, update_agent_info/4, which_all_agents/0, which_own_agents/0, load_mib/1, unload_mib/1, - sync_get/1, sync_get/2, sync_get/3, + sync_get/1, sync_get/2, sync_get/3, sync_get2/3, async_get/1, async_get/2, async_get/3, sync_get_next/1, sync_get_next/2, sync_get_next/3, async_get_next/1, async_get_next/2, async_get_next/3, @@ -171,6 +171,10 @@ sync_get(Addr_or_TargetName, Oids) -> sync_get(Addr, Port, Oids) -> call({sync_get, Addr, Port, Oids}). +sync_get2(TargetName, Oids, SendOpts) -> + call({sync_get2, TargetName, Oids, SendOpts}). + + %% -- async_get(Oids) -> @@ -400,6 +404,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) get-request -- %% + {{sync_get2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_get2 request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:sync_get2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{sync_get, Oids}, From, Ref} -> d("loop -> received sync_get request " -- cgit v1.2.3 From efacc58e49d5b25dc4b1c74e5f8330c9f26a4511 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 15:07:59 +0200 Subject: Async get test case for version 3 request API. --- lib/snmp/test/snmp_manager_test.erl | 161 +++++++++++++++++++++++------------- lib/snmp/test/snmp_manager_user.erl | 15 +++- 2 files changed, 117 insertions(+), 59 deletions(-) diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 962c449617..e34e08906d 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -69,6 +69,7 @@ simple_sync_get3/1, simple_async_get1/1, simple_async_get2/1, + simple_async_get3/1, simple_sync_get_next1/1, simple_sync_get_next2/1, @@ -237,7 +238,8 @@ init_per_testcase3(Case, Config) -> ], ApiCases03 = [ - simple_sync_get3 + simple_sync_get3, + simple_async_get3 ], Cases = [ @@ -323,7 +325,8 @@ end_per_testcase2(Case, Config) -> ], ApiCases03 = [ - simple_sync_get3 + simple_sync_get3, + simple_async_get3 ], Cases = [ @@ -414,7 +417,8 @@ groups() -> simple_sync_get2, simple_sync_get3, simple_async_get1, - simple_async_get2 + simple_async_get2, + simple_async_get3 ] }, {get_next_tests, [], @@ -1384,15 +1388,15 @@ simple_sync_get1(Config) when is_list(Config) -> p("issue get-request without loading the mib"), Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance], - ?line ok = do_simple_get(Node, Addr, Port, Oids1), + ?line ok = do_simple_sync_get(Node, Addr, Port, Oids1), p("issue get-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - ?line ok = do_simple_get(Node, Addr, Port, Oids2), + ?line ok = do_simple_sync_get(Node, Addr, Port, Oids2), ok. -do_simple_get(Node, Addr, Port, Oids) -> +do_simple_sync_get(Node, Addr, Port, Oids) -> ?line {ok, Reply, Rem} = mgr_user_sync_get(Node, Addr, Port, Oids), ?DBG("~n Reply: ~p" @@ -1431,16 +1435,16 @@ simple_sync_get2(suite) -> []; simple_sync_get2(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, ssg2), - do_simple_get2(Config). + do_simple_sync_get2(Config). -do_simple_get2(Config) -> +do_simple_sync_get2(Config) -> Get = fun(Node, TargetName, Oids) -> mgr_user_sync_get(Node, TargetName, Oids) end, PostVerify = fun() -> ok end, - do_simple_get2(Config, Get, PostVerify). + do_simple_sync_get2(Config, Get, PostVerify). -do_simple_get2(Config, Get, PostVerify) -> +do_simple_sync_get2(Config, Get, PostVerify) -> p("starting with Config: ~p~n", [Config]), Node = ?config(manager_node, Config), @@ -1448,15 +1452,15 @@ do_simple_get2(Config, Get, PostVerify) -> p("issue get-request without loading the mib"), Oids1 = [?sysObjectID_instance, ?sysDescr_instance, ?sysUpTime_instance], - ?line ok = do_simple_get2(Node, TargetName, Oids1, Get, PostVerify), + ?line ok = do_simple_sync_get2(Node, TargetName, Oids1, Get, PostVerify), p("issue get-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - ?line ok = do_simple_get2(Node, TargetName, Oids2, Get, PostVerify), + ?line ok = do_simple_sync_get2(Node, TargetName, Oids2, Get, PostVerify), ok. -do_simple_get2(Node, TargetName, Oids, Get, PostVerify) +do_simple_sync_get2(Node, TargetName, Oids, Get, PostVerify) when is_function(Get, 3) andalso is_function(PostVerify, 0) -> ?line {ok, Reply, Rem} = Get(Node, TargetName, Oids), @@ -1496,9 +1500,9 @@ simple_sync_get3(suite) -> []; simple_sync_get3(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, ssg3), - do_simple_get3(Config). + do_simple_sync_get3(Config). -do_simple_get3(Config) -> +do_simple_sync_get3(Config) -> Self = self(), Msg = simple_sync_get3, Fun = fun() -> Self ! Msg end, @@ -1517,7 +1521,7 @@ do_simple_get3(Config) -> ok end end, - do_simple_get2(Config, Get, PostVerify). + do_simple_sync_get2(Config, Get, PostVerify). %%====================================================================== @@ -1630,59 +1634,69 @@ simple_async_get2(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, sag2), p("starting with Config: ~p~n", [Config]), - MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), + Get = fun(Oids) -> async_g_exec2(MgrNode, TargetName, Oids) end, + PostVerify = fun() -> ok end, + do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). +do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify) -> ?line ok = mgr_user_load_mib(MgrNode, std_mib()), Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), - - Exec = fun(Data) -> - async_g_exec2(MgrNode, TargetName, Data) - end, - - Requests = [ - { 1, - [?sysObjectID_instance], - Exec, - fun(X) -> sag_verify(X, [?sysObjectID_instance]) end}, - { 2, - [?sysDescr_instance, ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysUpTime_instance]) - end}, - { 3, - [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end}, - { 4, - [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance], - Exec, - fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]) - end} - ], + do_simple_async_sync_get2(fun() -> mgr_info(MgrNode) end, + fun() -> agent_info(AgentNode) end, + Get, PostVerify). + +do_simple_async_sync_get2(MgrInfo, AgentInfo, Get, PostVerify) + when is_function(MgrInfo, 0) andalso + is_function(AgentInfo, 0) andalso + is_function(Get, 1) andalso + is_function(PostVerify, 0) -> + Requests = + [ + { 1, + [?sysObjectID_instance], + Get, + fun(X) -> sag_verify(X, [?sysObjectID_instance]), PostVerify() end}, + { 2, + [?sysDescr_instance, ?sysUpTime_instance], + Get, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysUpTime_instance]) + end}, + { 3, + [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], + Get, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance]), + PostVerify() + end}, + { 4, + [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance], + Get, + fun(X) -> + sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance]), + PostVerify() + end} + ], - p("manager info when starting test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when starting test: ~n~p", [agent_info(AgentNode)]), + p("manager info when starting test: ~n~p", [MgrInfo()]), + p("agent info when starting test: ~n~p", [AgentInfo()]), ?line ok = async_exec(Requests, []), - p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), - p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + p("manager info when ending test: ~n~p", [MgrInfo()]), + p("agent info when ending test: ~n~p", [AgentInfo()]), ok. @@ -1690,6 +1704,34 @@ async_g_exec2(Node, TargetName, Oids) -> mgr_user_async_get(Node, TargetName, Oids). +%%====================================================================== + +simple_async_get3(doc) -> + ["Simple (async) get-request - Version 3 API (TargetName and send-opts)"]; +simple_async_get3(suite) -> []; +simple_async_get3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sag3), + p("starting with Config: ~p~n", [Config]), + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + Self = self(), + Msg = simple_async_get3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + Get = fun(Oids) -> async_g_exec3(MgrNode, TargetName, Oids, SendOpts) end, + PostVerify = fun() -> receive Msg -> ok end end, + do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). + +async_g_exec3(Node, TargetName, Oids, SendOpts) -> + mgr_user_async_get2(Node, TargetName, Oids, SendOpts). + + %%====================================================================== simple_sync_get_next1(doc) -> ["Simple (sync) get_next-request - " @@ -4548,7 +4590,7 @@ otp8395_1(suite) -> []; otp8395_1(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, otp8395_1), - do_simple_get2(Config). + do_simple_sync_get2(Config). %%====================================================================== @@ -5104,6 +5146,9 @@ mgr_user_async_get(Node, Addr_or_TargetName, Oids) -> mgr_user_async_get(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, async_get, [Addr, Port, Oids]). +mgr_user_async_get2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, async_get2, [TargetName, Oids, SendOpts]). + %% mgr_user_sync_get_next(Node, Oids) -> %% mgr_user_sync_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). mgr_user_sync_get_next(Node, Addr_or_TargetName, Oids) -> diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index af0d7839a8..d4c2574b17 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -51,7 +51,7 @@ which_all_agents/0, which_own_agents/0, load_mib/1, unload_mib/1, sync_get/1, sync_get/2, sync_get/3, sync_get2/3, - async_get/1, async_get/2, async_get/3, + async_get/1, async_get/2, async_get/3, async_get2/3, sync_get_next/1, sync_get_next/2, sync_get_next/3, async_get_next/1, async_get_next/2, async_get_next/3, sync_set/1, sync_set/2, sync_set/3, @@ -186,6 +186,9 @@ async_get(Addr_or_TargetName, Oids) -> async_get(Addr, Port, Oids) -> call({async_get, Addr, Port, Oids}). +async_get2(TargetName, Oids, SendOpts) -> + call({async_get2, TargetName, Oids, SendOpts}). + %% -- sync_get_next(Oids) -> @@ -453,6 +456,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) get-request -- %% + {{async_get2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received async_get request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:async_get2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{async_get, Oids}, From, Ref} -> d("loop -> received async_get request"), -- cgit v1.2.3 From be8ad07f8247d3b223846059eca6ab7192f6402b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 15:47:45 +0200 Subject: Sync get-next test case for version 3 request API. --- lib/snmp/test/snmp_manager_test.erl | 83 ++++++++++++++++++++++++++++++------- lib/snmp/test/snmp_manager_user.erl | 15 ++++++- 2 files changed, 81 insertions(+), 17 deletions(-) diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index e34e08906d..1ffcc651d7 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -73,6 +73,7 @@ simple_sync_get_next1/1, simple_sync_get_next2/1, + simple_sync_get_next3/1, simple_async_get_next1/1, simple_async_get_next2/1, @@ -239,7 +240,8 @@ init_per_testcase3(Case, Config) -> ApiCases03 = [ simple_sync_get3, - simple_async_get3 + simple_async_get3, + simple_sync_get_next3 ], Cases = [ @@ -326,7 +328,8 @@ end_per_testcase2(Case, Config) -> ApiCases03 = [ simple_sync_get3, - simple_async_get3 + simple_async_get3, + simple_sync_get_next3 ], Cases = [ @@ -425,6 +428,7 @@ groups() -> [ simple_sync_get_next1, simple_sync_get_next2, + simple_sync_get_next3, simple_async_get_next1, simple_async_get_next2 ] @@ -1876,18 +1880,28 @@ simple_sync_get_next2(doc) -> simple_sync_get_next2(suite) -> []; simple_sync_get_next2(Config) when is_list(Config) -> process_flag(trap_exit, true), - put(tname, ssgn), + put(tname, ssgn2), p("starting with Config: ~p~n", [Config]), - MgrNode = ?config(manager_node, Config), - AgentNode = ?config(agent_node, Config), + GetNext = fun(Node, TargetName, Oids) -> + mgr_user_sync_get_next(Node, TargetName, Oids) + end, + PostVerify = fun(Res) -> Res end, + do_simple_sync_get_next2(Config, GetNext, PostVerify). + +do_simple_sync_get_next2(Config, GetNext, PostVerify) + when is_function(GetNext, 3) andalso is_function(PostVerify, 1) -> + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), %% -- 1 -- Oids01 = [[1,3,7,1]], VF01 = fun(X) -> verify_ssgn_reply1(X, [{[1,3,7,1],endOfMibView}]) end, ?line ok = do_simple_get_next(1, - MgrNode, TargetName, Oids01, VF01), + MgrNode, TargetName, Oids01, VF01, + GetNext, PostVerify), ?line ok = mgr_user_load_mib(MgrNode, std_mib()), @@ -1897,7 +1911,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView]) end, ?line ok = do_simple_get_next(2, - MgrNode, TargetName, Oids02, VF02), + MgrNode, TargetName, Oids02, VF02, + GetNext, PostVerify), Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), @@ -1910,7 +1925,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}]) end, ?line ok = do_simple_get_next(3, - MgrNode, TargetName, Oids03, VF03), + MgrNode, TargetName, Oids03, VF03, + GetNext, PostVerify), %% -- 4 -- Oids04 = [[TCnt2, 2]], @@ -1918,7 +1934,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}]) end, ?line ok = do_simple_get_next(4, - MgrNode, TargetName, Oids04, VF04), + MgrNode, TargetName, Oids04, VF04, + GetNext, PostVerify), %% -- 5 -- ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1), @@ -1927,7 +1944,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]}) end, ?line ok = do_simple_get_next(5, - MgrNode, TargetName, Oids05, VF05), + MgrNode, TargetName, Oids05, VF05, + GetNext, PostVerify), %% -- 6 -- ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2), @@ -1936,7 +1954,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]}) end, ?line ok = do_simple_get_next(6, - MgrNode, TargetName, Oids06, VF06), + MgrNode, TargetName, Oids06, VF06, + GetNext, PostVerify), %% -- 7 -- ?line {ok, [TGenErr3|_]} = mgr_user_name_to_oid(MgrNode, tGenErr3), @@ -1946,7 +1965,8 @@ simple_sync_get_next2(Config) when is_list(Config) -> [?sysDescr, TGenErr3]}) end, ?line ok = do_simple_get_next(7, - MgrNode, TargetName, Oids07, VF07), + MgrNode, TargetName, Oids07, VF07, + GetNext, PostVerify), %% -- 8 -- ?line {ok, [TTooBig|_]} = mgr_user_name_to_oid(MgrNode, tTooBig), @@ -1955,24 +1975,52 @@ simple_sync_get_next2(Config) when is_list(Config) -> verify_ssgn_reply2(X, {tooBig, 0, []}) end, ?line ok = do_simple_get_next(8, - MgrNode, TargetName, Oids08, VF08), + MgrNode, TargetName, Oids08, VF08, + GetNext, PostVerify), ok. -do_simple_get_next(N, Node, TargetName, Oids, Verify) -> +do_simple_get_next(N, Node, TargetName, Oids, Verify, GetNext, PostVerify) -> p("issue get-next command ~w", [N]), - case mgr_user_sync_get_next(Node, TargetName, Oids) of + case GetNext(Node, TargetName, Oids) of {ok, Reply, Rem} -> ?DBG("get-next ok:" "~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), - Verify(Reply); + PostVerify(Verify(Reply)); Error -> {error, {unexpected_reply, Error}} end. +%%====================================================================== + +simple_sync_get_next3(doc) -> + ["Simple (sync) get_next-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_sync_get_next3(suite) -> []; +simple_sync_get_next3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssgn3), + p("starting with Config: ~p~n", [Config]), + Self = self(), + Msg = simple_sync_get_next3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + GetNext = fun(Node, TargetName, Oids) -> + mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Error) -> Error + end, + do_simple_sync_get_next2(Config, GetNext, PostVerify). + + %%====================================================================== simple_async_get_next1(doc) -> ["Simple (async) get_next-request - " @@ -5156,6 +5204,9 @@ mgr_user_sync_get_next(Node, Addr_or_TargetName, Oids) -> mgr_user_sync_get_next(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, sync_get_next, [Addr, Port, Oids]). +mgr_user_sync_get_next2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, sync_get_next2, [TargetName, Oids, SendOpts]). + %% mgr_user_async_get_next(Node, Oids) -> %% mgr_user_async_get_next(Node, ?LOCALHOST(), ?AGENT_PORT, Oids). mgr_user_async_get_next(Node, Addr_or_TargetName, Oids) -> diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index d4c2574b17..21543e4194 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -52,7 +52,7 @@ load_mib/1, unload_mib/1, sync_get/1, sync_get/2, sync_get/3, sync_get2/3, async_get/1, async_get/2, async_get/3, async_get2/3, - sync_get_next/1, sync_get_next/2, sync_get_next/3, + sync_get_next/1, sync_get_next/2, sync_get_next/3, sync_get_next2/3, async_get_next/1, async_get_next/2, async_get_next/3, sync_set/1, sync_set/2, sync_set/3, async_set/1, async_set/2, async_set/3, @@ -200,6 +200,9 @@ sync_get_next(Addr_or_TargetName, Oids) -> sync_get_next(Addr, Port, Oids) -> call({sync_get_next, Addr, Port, Oids}). +sync_get_next2(TargetName, Oids, SendOpts) -> + call({sync_get_next2, TargetName, Oids, SendOpts}). + %% -- async_get_next(Oids) -> @@ -499,6 +502,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) get_next-request -- %% + {{sync_get_next2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_get_next request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:sync_get_next2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{sync_get_next, Oids}, From, Ref} -> d("loop -> received sync_get_next request"), -- cgit v1.2.3 From ceb31ba2c09943bf2580f1cc9403905422a7dd44 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 16:35:14 +0200 Subject: Async get-next test cases for version 3 request API. --- lib/snmp/test/snmp_manager_test.erl | 137 ++++++++++++++++++++++++++---------- lib/snmp/test/snmp_manager_user.erl | 15 +++- 2 files changed, 114 insertions(+), 38 deletions(-) diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 1ffcc651d7..986c324179 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -76,6 +76,7 @@ simple_sync_get_next3/1, simple_async_get_next1/1, simple_async_get_next2/1, + simple_async_get_next3/1, simple_sync_set1/1, simple_sync_set2/1, @@ -241,7 +242,8 @@ init_per_testcase3(Case, Config) -> [ simple_sync_get3, simple_async_get3, - simple_sync_get_next3 + simple_sync_get_next3, + simple_async_get_next3 ], Cases = [ @@ -329,7 +331,8 @@ end_per_testcase2(Case, Config) -> [ simple_sync_get3, simple_async_get3, - simple_sync_get_next3 + simple_sync_get_next3, + simple_async_get_next3 ], Cases = [ @@ -430,7 +433,8 @@ groups() -> simple_sync_get_next2, simple_sync_get_next3, simple_async_get_next1, - simple_async_get_next2 + simple_async_get_next2, + simple_async_get_next3 ] }, {set_tests, [], @@ -1642,7 +1646,7 @@ simple_async_get2(Config) when is_list(Config) -> AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), Get = fun(Oids) -> async_g_exec2(MgrNode, TargetName, Oids) end, - PostVerify = fun() -> ok end, + PostVerify = fun(Res) -> Res end, do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify) -> @@ -1658,28 +1662,29 @@ do_simple_async_sync_get2(MgrInfo, AgentInfo, Get, PostVerify) when is_function(MgrInfo, 0) andalso is_function(AgentInfo, 0) andalso is_function(Get, 1) andalso - is_function(PostVerify, 0) -> + is_function(PostVerify, 1) -> Requests = [ { 1, [?sysObjectID_instance], Get, - fun(X) -> sag_verify(X, [?sysObjectID_instance]), PostVerify() end}, + fun(X) -> + PostVerify(sag_verify(X, [?sysObjectID_instance])) end}, { 2, [?sysDescr_instance, ?sysUpTime_instance], Get, fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysUpTime_instance]) + PostVerify(sag_verify(X, [?sysObjectID_instance, + ?sysUpTime_instance])) end}, { 3, [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], Get, fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]), - PostVerify() + PostVerify(sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance])) + end}, { 4, [?sysObjectID_instance, @@ -1687,10 +1692,9 @@ do_simple_async_sync_get2(MgrInfo, AgentInfo, Get, PostVerify) ?sysUpTime_instance], Get, fun(X) -> - sag_verify(X, [?sysObjectID_instance, - ?sysDescr_instance, - ?sysUpTime_instance]), - PostVerify() + PostVerify(sag_verify(X, [?sysObjectID_instance, + ?sysDescr_instance, + ?sysUpTime_instance])) end} ], @@ -1729,7 +1733,9 @@ simple_async_get3(Config) when is_list(Config) -> {extra, Extra} ], Get = fun(Oids) -> async_g_exec3(MgrNode, TargetName, Oids, SendOpts) end, - PostVerify = fun() -> receive Msg -> ok end end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Error) -> Error + end, do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). async_g_exec3(Node, TargetName, Oids, SendOpts) -> @@ -2136,11 +2142,14 @@ simple_async_get_next2(Config) when is_list(Config) -> Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), + GetNext = fun(Oids) -> + async_gn_exec2(MgrNode, TargetName, Oids) + end, + PostVerify = fun(Res) -> Res end, + do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify). - Exec = fun(X) -> - async_gn_exec2(MgrNode, TargetName, X) - end, - +do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify) + when is_function(GetNext, 1) andalso is_function(PostVerify, 1) -> ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2), ?line {ok, [TGenErr1|_]} = mgr_user_name_to_oid(MgrNode, tGenErr1), ?line {ok, [TGenErr2|_]} = mgr_user_name_to_oid(MgrNode, tGenErr2), @@ -2151,51 +2160,60 @@ simple_async_get_next2(Config) when is_list(Config) -> [ {1, [[1,3,7,1]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [{[1,3,7,1], endOfMibView}]) + PostVerify( + verify_ssgn_reply1(X, [{[1,3,7,1], endOfMibView}])) + end}, {2, [[sysDescr], [1,3,7,1]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView]) + PostVerify( + verify_ssgn_reply1(X, [?sysDescr_instance, endOfMibView])) end}, {3, [[TCnt2, 1]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}]) + PostVerify( + verify_ssgn_reply1(X, [{fl([TCnt2,2]), 100}])) end}, {4, [[TCnt2, 2]], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}]) + PostVerify( + verify_ssgn_reply1(X, [{fl([TCnt2,2]), endOfMibView}])) end}, {5, [TGenErr1], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]}) + PostVerify( + verify_ssgn_reply2(X, {genErr, 1, [TGenErr1]})) end}, {6, [TGenErr2], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]}) + PostVerify( + verify_ssgn_reply2(X, {genErr, 1, [TGenErr2]})) end}, {7, [[sysDescr], TGenErr3], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]}) + PostVerify( + verify_ssgn_reply2(X, {genErr, 2, [TGenErr3]})) end}, {8, [TTooBig], - Exec, + GetNext, fun(X) -> - verify_ssgn_reply2(X, {tooBig, 0, []}) + PostVerify( + verify_ssgn_reply2(X, {tooBig, 0, []})) end} ], @@ -2214,6 +2232,48 @@ async_gn_exec2(Node, TargetName, Oids) -> mgr_user_async_get_next(Node, TargetName, Oids). +%%====================================================================== + +simple_async_get_next3(doc) -> + ["Simple (async) get_next-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_async_get_next3(suite) -> []; +simple_async_get_next3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssgn2), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + ?line ok = mgr_user_load_mib(MgrNode, std_mib()), + Test2Mib = test2_mib(Config), + ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), + ?line ok = agent_load_mib(AgentNode, Test2Mib), + + Self = self(), + Msg = simple_async_get_next3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + GetNext = fun(Oids) -> + async_gn_exec3(MgrNode, TargetName, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Error) -> Error + end, + + do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify). + +async_gn_exec3(Node, TargetName, Oids, SendOpts) -> + mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts). + + %%====================================================================== simple_sync_set1(doc) -> ["Simple (sync) set-request - " @@ -5214,6 +5274,9 @@ mgr_user_async_get_next(Node, Addr_or_TargetName, Oids) -> mgr_user_async_get_next(Node, Addr, Port, Oids) -> rcall(Node, snmp_manager_user, async_get_next, [Addr, Port, Oids]). +mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, async_get_next2, [TargetName, Oids, SendOpts]). + %% mgr_user_sync_set(Node, VAV) -> %% mgr_user_sync_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV). mgr_user_sync_set(Node, Addr_or_TargetName, VAV) -> diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index 21543e4194..cb3f57ad1c 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -53,7 +53,7 @@ sync_get/1, sync_get/2, sync_get/3, sync_get2/3, async_get/1, async_get/2, async_get/3, async_get2/3, sync_get_next/1, sync_get_next/2, sync_get_next/3, sync_get_next2/3, - async_get_next/1, async_get_next/2, async_get_next/3, + async_get_next/1, async_get_next/2, async_get_next/3, async_get_next2/3, sync_set/1, sync_set/2, sync_set/3, async_set/1, async_set/2, async_set/3, sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, @@ -214,6 +214,9 @@ async_get_next(Addr_or_TargetName, Oids) -> async_get_next(Addr, Port, Oids) -> call({async_get_next, Addr, Port, Oids}). +async_get_next2(TargetName, Oids, SendOpts) -> + call({async_get_next2, TargetName, Oids, SendOpts}). + %% -- sync_set(VAV) -> @@ -545,6 +548,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) get_next-request -- %% + {{async_get_next2, TargetName, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received async_get_next request with" + "~n TargetName: ~p" + "~n Oids: ~p" + "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), + Res = snmpm:async_get_next2(Id, TargetName, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{async_get_next, Oids}, From, Ref} -> d("loop -> received async_get_next request"), -- cgit v1.2.3 From fe2f0b21b8da7e7a83c74d5bb174d52bfcd380bd Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 14 Apr 2011 17:11:03 +0200 Subject: Sync set test case for version 2 request API. --- lib/snmp/test/snmp_manager_test.erl | 60 ++++++++++++++++++++++++++++++++----- lib/snmp/test/snmp_manager_user.erl | 21 ++++++++++--- 2 files changed, 69 insertions(+), 12 deletions(-) diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 986c324179..d638047701 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -80,6 +80,7 @@ simple_sync_set1/1, simple_sync_set2/1, + simple_sync_set3/1, simple_async_set1/1, simple_async_set2/1, @@ -243,7 +244,8 @@ init_per_testcase3(Case, Config) -> simple_sync_get3, simple_async_get3, simple_sync_get_next3, - simple_async_get_next3 + simple_async_get_next3, + simple_sync_set3 ], Cases = [ @@ -332,7 +334,8 @@ end_per_testcase2(Case, Config) -> simple_sync_get3, simple_async_get3, simple_sync_get_next3, - simple_async_get_next3 + simple_async_get_next3, + simple_sync_set3 ], Cases = [ @@ -441,6 +444,7 @@ groups() -> [ simple_sync_set1, simple_sync_set2, + simple_sync_set3, simple_async_set1, simple_async_set2 ] @@ -2353,7 +2357,17 @@ simple_sync_set2(Config) when is_list(Config) -> put(tname, sss2), p("starting with Config: ~p~n", [Config]), - Node = ?config(manager_node, Config), + Set = fun(Node, TargetName, VAVs) -> + mgr_user_sync_set(Node, TargetName, VAVs) + end, + PostVerify = fun() -> ok end, + + do_simple_sync_set2(Config, Set, PostVerify). + +do_simple_sync_set2(Config, Set, PostVerify) + when is_function(Set, 3) andalso is_function(PostVerify, 0) -> + + Node = ?config(manager_node, Config), TargetName = ?config(manager_agent_target_name, Config), p("issue set-request without loading the mib"), @@ -2363,7 +2377,7 @@ simple_sync_set2(Config) when is_list(Config) -> {?sysName_instance, s, Val11}, {?sysLocation_instance, s, Val12} ], - ?line ok = do_simple_set2(Node, TargetName, VAVs1), + ?line ok = do_simple_set2(Node, TargetName, VAVs1, Set, PostVerify), p("issue set-request after first loading the mibs"), ?line ok = mgr_user_load_mib(Node, std_mib()), @@ -2373,12 +2387,12 @@ simple_sync_set2(Config) when is_list(Config) -> {[sysName, 0], Val21}, {[sysLocation, 0], Val22} ], - ?line ok = do_simple_set2(Node, TargetName, VAVs2), + ?line ok = do_simple_set2(Node, TargetName, VAVs2, Set, PostVerify), ok. -do_simple_set2(Node, TargetName, VAVs) -> +do_simple_set2(Node, TargetName, VAVs, Set, PostVerify) -> [SysName, SysLoc] = value_of_vavs(VAVs), - ?line {ok, Reply, Rem} = mgr_user_sync_set(Node, TargetName, VAVs), + ?line {ok, Reply, Rem} = Set(Node, TargetName, VAVs), ?DBG("~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), @@ -2391,7 +2405,7 @@ do_simple_set2(Node, TargetName, VAVs) -> value = SysName}, #varbind{oid = ?sysLocation_instance, value = SysLoc}]} -> - ok; + PostVerify(); {noError, 0, Vbs} -> {error, {unexpected_vbs, Vbs}}; Else -> @@ -2401,6 +2415,33 @@ do_simple_set2(Node, TargetName, VAVs) -> ok. +%%====================================================================== + +simple_sync_set3(doc) -> + ["Simple (sync) set-request - Version 3 API (TargetName with send-opts)"]; +simple_sync_set3(suite) -> []; +simple_sync_set3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sss3), + p("starting with Config: ~p~n", [Config]), + + Self = self(), + Msg = simple_sync_set3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + Set = fun(Node, TargetName, VAVs) -> + mgr_user_sync_set2(Node, TargetName, VAVs, SendOpts) + end, + PostVerify = fun() -> receive Msg -> ok end end, + + do_simple_sync_set2(Config, Set, PostVerify). + + %%====================================================================== simple_async_set1(doc) -> ["Simple (async) set-request - " @@ -5284,6 +5325,9 @@ mgr_user_sync_set(Node, Addr_or_TargetName, VAV) -> mgr_user_sync_set(Node, Addr, Port, VAV) -> rcall(Node, snmp_manager_user, sync_set, [Addr, Port, VAV]). +mgr_user_sync_set2(Node, TargetName, VAV, SendOpts) -> + rcall(Node, snmp_manager_user, sync_set2, [TargetName, VAV, SendOpts]). + %% mgr_user_async_set(Node, VAV) -> %% mgr_user_async_set(Node, ?LOCALHOST(), ?AGENT_PORT, VAV). mgr_user_async_set(Node, Addr_or_TargetName, VAV) -> diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index cb3f57ad1c..7c4bdbfb0a 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -54,7 +54,7 @@ async_get/1, async_get/2, async_get/3, async_get2/3, sync_get_next/1, sync_get_next/2, sync_get_next/3, sync_get_next2/3, async_get_next/1, async_get_next/2, async_get_next/3, async_get_next2/3, - sync_set/1, sync_set/2, sync_set/3, + sync_set/1, sync_set/2, sync_set/3, sync_set2/3, async_set/1, async_set/2, async_set/3, sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, @@ -228,6 +228,9 @@ sync_set(Addr_or_TargetName, VAV) -> sync_set(Addr, Port, VAV) -> call({sync_set, Addr, Port, VAV}). +sync_set2(TargetName, VAV, SendOpts) -> + call({sync_set2, TargetName, VAV, SendOpts}). + %% -- async_set(VAV) -> @@ -464,7 +467,7 @@ loop(#state{parent = Parent, id = Id} = S) -> {{async_get2, TargetName, Oids, SendOpts}, From, Ref} when is_list(TargetName) -> - d("loop -> received async_get request with" + d("loop -> received async_get2 request with" "~n TargetName: ~p" "~n Oids: ~p" "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), @@ -507,7 +510,7 @@ loop(#state{parent = Parent, id = Id} = S) -> {{sync_get_next2, TargetName, Oids, SendOpts}, From, Ref} when is_list(TargetName) -> - d("loop -> received sync_get_next request with" + d("loop -> received sync_get_next2 request with" "~n TargetName: ~p" "~n Oids: ~p" "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), @@ -550,7 +553,7 @@ loop(#state{parent = Parent, id = Id} = S) -> {{async_get_next2, TargetName, Oids, SendOpts}, From, Ref} when is_list(TargetName) -> - d("loop -> received async_get_next request with" + d("loop -> received async_get_next2 request with" "~n TargetName: ~p" "~n Oids: ~p" "~n SendOpts: ~p", [TargetName, Oids, SendOpts]), @@ -591,6 +594,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) set-request -- %% + {{sync_set2, TargetName, VAV, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_set2 request with" + "~n TargetName: ~p" + "~n VAV: ~p" + "~n SendOpts: ~p", [TargetName, VAV, SendOpts]), + Res = snmpm:sync_set2(Id, TargetName, VAV, SendOpts), + reply(From, Res, Ref), + loop(S); + {{sync_set, VAV}, From, Ref} -> d("loop -> received sync_set request"), Res = [snmpm:sync_set(Id, TargetName, VAV) || -- cgit v1.2.3 From 625b265bfe3ba77f7554f8e7a7be29abff9eaaba Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 15 Apr 2011 10:37:44 +0200 Subject: Async set test case for version 3 request API. --- lib/snmp/test/snmp_manager_test.erl | 82 +++++++++++++++++++++++++++++++------ lib/snmp/test/snmp_manager_user.erl | 15 ++++++- 2 files changed, 84 insertions(+), 13 deletions(-) diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index d638047701..808d10f116 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -83,6 +83,7 @@ simple_sync_set3/1, simple_async_set1/1, simple_async_set2/1, + simple_async_set3/1, simple_sync_get_bulk1/1, simple_sync_get_bulk2/1, @@ -245,7 +246,8 @@ init_per_testcase3(Case, Config) -> simple_async_get3, simple_sync_get_next3, simple_async_get_next3, - simple_sync_set3 + simple_sync_set3, + simple_async_set3 ], Cases = [ @@ -335,7 +337,8 @@ end_per_testcase2(Case, Config) -> simple_async_get3, simple_sync_get_next3, simple_async_get_next3, - simple_sync_set3 + simple_sync_set3, + simple_async_set3 ], Cases = [ @@ -446,7 +449,8 @@ groups() -> simple_sync_set2, simple_sync_set3, simple_async_set1, - simple_async_set2 + simple_async_set2, + simple_async_set3 ] }, {bulk_tests, [], @@ -2553,31 +2557,40 @@ simple_async_set2(Config) when is_list(Config) -> ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), - Exec = fun(X) -> - async_s_exec2(MgrNode, TargetName, X) - end, + Set = + fun(Oids) -> + async_s_exec2(MgrNode, TargetName, Oids) + end, + PostVerify = fun(Res) -> Res end, + + do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify). +do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify) -> Requests = [ {1, [{?sysName_instance, s, "Arne Anka"}], - Exec, + Set, fun(X) -> - sas_verify(X, [?sysName_instance]) + PostVerify(sas_verify(X, [?sysName_instance])) end}, {2, [{?sysLocation_instance, s, "Stockholm"}, {?sysName_instance, s, "Arne Anka"}], - Exec, + Set, fun(X) -> - sas_verify(X, [?sysLocation_instance, ?sysName_instance]) + PostVerify(sas_verify(X, + [?sysLocation_instance, + ?sysName_instance])) end}, {3, [{[sysName, 0], "Gothenburg"}, {[sysLocation, 0], "Sune Anka"}], - Exec, + Set, fun(X) -> - sas_verify(X, [?sysName_instance, ?sysLocation_instance]) + PostVerify(sas_verify(X, + [?sysName_instance, + ?sysLocation_instance])) end} ], @@ -2596,6 +2609,48 @@ async_s_exec2(Node, TargetName, VAVs) -> mgr_user_async_set(Node, TargetName, VAVs). +%%====================================================================== + +simple_async_set3(doc) -> + ["Simple (async) set-request - Version 3 API (TargetName with send-opts)"]; +simple_async_set3(suite) -> []; +simple_async_set3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sas3), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + ?line ok = mgr_user_load_mib(MgrNode, std_mib()), + Test2Mib = test2_mib(Config), + ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), + ?line ok = agent_load_mib(AgentNode, Test2Mib), + + Self = self(), + Msg = simple_async_set3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + Set = + fun(Oids) -> + async_s_exec3(MgrNode, TargetName, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Res) -> Res + end, + + do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify). + +async_s_exec3(Node, TargetName, VAVs, SendOpts) -> + mgr_user_async_set2(Node, TargetName, VAVs, SendOpts). + + %%====================================================================== simple_sync_get_bulk1(doc) -> ["Simple (sync) get_bulk-request - " @@ -5335,6 +5390,9 @@ mgr_user_async_set(Node, Addr_or_TargetName, VAV) -> mgr_user_async_set(Node, Addr, Port, VAV) -> rcall(Node, snmp_manager_user, async_set, [Addr, Port, VAV]). +mgr_user_async_set2(Node, TargetName, VAV, SendOpts) -> + rcall(Node, snmp_manager_user, async_set2, [TargetName, VAV, SendOpts]). + %% mgr_user_sync_get_bulk(Node, NonRep, MaxRep, Oids) -> %% mgr_user_sync_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT, %% NonRep, MaxRep, Oids). diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index 7c4bdbfb0a..34f0779e9c 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -55,7 +55,7 @@ sync_get_next/1, sync_get_next/2, sync_get_next/3, sync_get_next2/3, async_get_next/1, async_get_next/2, async_get_next/3, async_get_next2/3, sync_set/1, sync_set/2, sync_set/3, sync_set2/3, - async_set/1, async_set/2, async_set/3, + async_set/1, async_set/2, async_set/3, async_set2/3, sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, name_to_oid/1, oid_to_name/1, @@ -242,6 +242,9 @@ async_set(Addr_or_TargetName, VAV) -> async_set(Addr, Port, VAV) -> call({async_set, Addr, Port, VAV}). +async_set2(TargetName, VAV, SendOpts) -> + call({async_set2, TargetName, VAV, SendOpts}). + %% -- sync_get_bulk(NonRep, MaxRep, Oids) -> @@ -634,6 +637,16 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) set-request -- %% + {{async_set2, TargetName, VAV, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received async_set2 request with" + "~n TargetName: ~p" + "~n VAV: ~p" + "~n SendOpts: ~p", [TargetName, VAV, SendOpts]), + Res = snmpm:async_set2(Id, TargetName, VAV, SendOpts), + reply(From, Res, Ref), + loop(S); + {{async_set, VAV}, From, Ref} -> d("loop -> received async_set request"), Res = [snmpm:async_set(Id, TargetName, VAV) || -- cgit v1.2.3 From 4bdcd8b56cc6446012542db1df5fba893f0f38f0 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 15 Apr 2011 11:06:59 +0200 Subject: Sync get-bulk test case for version 3 of the request API. --- lib/snmp/test/snmp_manager_test.erl | 118 +++++++++++++++++++++++++++--------- lib/snmp/test/snmp_manager_user.erl | 19 +++++- 2 files changed, 108 insertions(+), 29 deletions(-) diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 808d10f116..3efb648c0b 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -87,6 +87,7 @@ simple_sync_get_bulk1/1, simple_sync_get_bulk2/1, + simple_sync_get_bulk3/1, simple_async_get_bulk1/1, simple_async_get_bulk2/1, @@ -247,7 +248,8 @@ init_per_testcase3(Case, Config) -> simple_sync_get_next3, simple_async_get_next3, simple_sync_set3, - simple_async_set3 + simple_async_set3, + simple_sync_get_bulk3 ], Cases = [ @@ -338,7 +340,8 @@ end_per_testcase2(Case, Config) -> simple_sync_get_next3, simple_async_get_next3, simple_sync_set3, - simple_async_set3 + simple_async_set3, + simple_sync_get_bulk3 ], Cases = [ @@ -457,6 +460,7 @@ groups() -> [ simple_sync_get_bulk1, simple_sync_get_bulk2, + simple_sync_get_bulk3, simple_async_get_bulk1, simple_async_get_bulk2 ] @@ -2836,20 +2840,33 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> AgentNode = ?config(agent_node, Config), TargetName = ?config(manager_agent_target_name, Config), + GetBulk = + fun(NonRep, MaxRep, Oids) -> + mgr_user_sync_get_bulk(MgrNode, TargetName, + NonRep, MaxRep, Oids) + end, + PostVerify = fun(Res) -> Res end, + + do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify). + +do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify) -> %% -- 1 -- ?line ok = do_simple_get_bulk2(1, - MgrNode, TargetName, 1, 1, [], - fun verify_ssgb_reply1/1), + 1, 1, [], + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), %% -- 2 -- ?line ok = do_simple_get_bulk2(2, - MgrNode, TargetName, -1, 1, [], - fun verify_ssgb_reply1/1), + -1, 1, [], + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), %% -- 3 -- ?line ok = do_simple_get_bulk2(3, - MgrNode, TargetName, -1, -1, [], - fun verify_ssgb_reply1/1), + -1, -1, [], + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), ?line ok = mgr_user_load_mib(MgrNode, std_mib()), %% -- 4 -- @@ -2857,13 +2874,13 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView]) end, ?line ok = do_simple_get_bulk2(4, - MgrNode, TargetName, - 2, 0, [[sysDescr],[1,3,7,1]], VF04), + 2, 0, [[sysDescr],[1,3,7,1]], VF04, + GetBulk, PostVerify), %% -- 5 -- ?line ok = do_simple_get_bulk2(5, - MgrNode, TargetName, - 1, 2, [[sysDescr],[1,3,7,1]], VF04), + 1, 2, [[sysDescr],[1,3,7,1]], VF04, + GetBulk, PostVerify), %% -- 6 -- VF06 = fun(X) -> @@ -2872,8 +2889,8 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> ?sysObjectID_instance, endOfMibView]) end, ?line ok = do_simple_get_bulk2(6, - MgrNode, TargetName, - 0, 2, [[sysDescr],[1,3,7,1]], VF06), + 0, 2, [[sysDescr],[1,3,7,1]], VF06, + GetBulk, PostVerify), %% -- 7 -- VF07 = fun(X) -> @@ -2883,10 +2900,10 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> ?sysObjectID_instance, endOfMibView]) end, ?line ok = do_simple_get_bulk2(7, - MgrNode, TargetName, 2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]], - VF07), + VF07, + GetBulk, PostVerify), Test2Mib = test2_mib(Config), ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), @@ -2899,17 +2916,17 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> ?sysDescr_instance]) end, ?line ok = do_simple_get_bulk2(8, - MgrNode, TargetName, 1, 2, [[sysDescr],[sysDescr],[tTooBig]], - VF08), + VF08, + GetBulk, PostVerify), %% -- 9 -- ?line ok = do_simple_get_bulk2(9, - MgrNode, TargetName, 1, 12, [[tDescr2], [sysDescr]], - fun verify_ssgb_reply1/1), + fun verify_ssgb_reply1/1, + GetBulk, PostVerify), %% -- 10 -- VF10 = fun(X) -> @@ -2920,13 +2937,13 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> {?sysDescr, 'NULL'}]) end, ?line ok = do_simple_get_bulk2(10, - MgrNode, TargetName, 2, 2, [[sysDescr], [sysObjectID], [tGenErr1], [sysDescr]], - VF10), + VF10, + GetBulk, PostVerify), %% -- 11 -- ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2), @@ -2937,26 +2954,67 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> {fl([TCnt2,2]), endOfMibView}]) end, ?line ok = do_simple_get_bulk2(11, - MgrNode, TargetName, 0, 2, - [[TCnt2, 1]], VF11), + [[TCnt2, 1]], VF11, + GetBulk, PostVerify), ok. -do_simple_get_bulk2(N, Node, TargetName, NonRep, MaxRep, Oids, Verify) -> +do_simple_get_bulk2(N, + NonRep, MaxRep, Oids, + Verify, GetBulk, PostVerify) + when is_function(Verify, 1) andalso + is_function(GetBulk, 3) andalso + is_function(PostVerify) -> p("issue get-bulk command ~w", [N]), - case mgr_user_sync_get_bulk(Node, TargetName, NonRep, MaxRep, Oids) of + case GetBulk(NonRep, MaxRep, Oids) of {ok, Reply, Rem} -> ?DBG("get-bulk ok:" "~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), - Verify(Reply); + PostVerify(Verify(Reply)); Error -> {error, {unexpected_reply, Error}} end. +%%====================================================================== + +simple_sync_get_bulk3(doc) -> + ["Simple (sync) get_bulk-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_sync_get_bulk3(suite) -> []; +simple_sync_get_bulk3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ssgb3), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + Self = self(), + Msg = simple_async_set3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + GetBulk = + fun(NonRep, MaxRep, Oids) -> + mgr_user_sync_get_bulk2(MgrNode, TargetName, + NonRep, MaxRep, Oids, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Res) -> Res + end, + + do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify). + + %%====================================================================== simple_async_get_bulk1(doc) -> ["Simple (async) get_bulk-request - " @@ -5398,11 +5456,15 @@ mgr_user_async_set2(Node, TargetName, VAV, SendOpts) -> %% NonRep, MaxRep, Oids). mgr_user_sync_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, sync_get_bulk, - [Addr_or_TargetName, NonRep, MaxRep, Oids]). + [Addr_or_TargetName, NonRep, MaxRep, Oids]). mgr_user_sync_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, sync_get_bulk, [Addr, Port, NonRep, MaxRep, Oids]). +mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, sync_get_bulk2, + [TargetName, NonRep, MaxRep, Oids, SendOpts]). + %% mgr_user_async_get_bulk(Node, NonRep, MaxRep, Oids) -> %% mgr_user_async_get_bulk(Node, ?LOCALHOST(), ?AGENT_PORT, %% NonRep, MaxRep, Oids). diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index 34f0779e9c..c2b5886c4a 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -56,7 +56,7 @@ async_get_next/1, async_get_next/2, async_get_next/3, async_get_next2/3, sync_set/1, sync_set/2, sync_set/3, sync_set2/3, async_set/1, async_set/2, async_set/3, async_set2/3, - sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, + sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, sync_get_bulk2/5, async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, name_to_oid/1, oid_to_name/1, purify_oid/1 @@ -256,6 +256,9 @@ sync_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) -> sync_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> call({sync_get_bulk, Addr, Port, NonRep, MaxRep, Oids}). +sync_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) -> + call({sync_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}). + %% -- async_get_bulk(NonRep, MaxRep, Oids) -> @@ -677,6 +680,20 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (sync) get-bulk-request -- %% + {{sync_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}, From, Ref} + when is_list(TargetName) -> + d("loop -> received sync_get_bulk request with" + "~n TargetName: ~p" + "~n NonRep: ~w" + "~n MaxRep: ~w" + "~n Oids: ~p" + "~n SendOpts: ~p", + [TargetName, NonRep, MaxRep, Oids, SendOpts]), + Res = snmpm:sync_get_bulk2(Id, TargetName, + NonRep, MaxRep, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{sync_get_bulk, NonRep, MaxRep, Oids}, From, Ref} -> d("loop -> received sync_get_bulk request with" -- cgit v1.2.3 From f794bf0043b8955f74061b004dcb056ab0952815 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 15 Apr 2011 12:01:11 +0200 Subject: Async get-bulk test case for version 3 request API. --- lib/snmp/test/snmp_manager_test.erl | 158 +++++++++++++++++++++++++----------- lib/snmp/test/snmp_manager_user.erl | 19 ++++- 2 files changed, 128 insertions(+), 49 deletions(-) diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 3efb648c0b..6bd62df655 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -90,6 +90,7 @@ simple_sync_get_bulk3/1, simple_async_get_bulk1/1, simple_async_get_bulk2/1, + simple_async_get_bulk3/1, misc_async1/1, misc_async2/1, @@ -249,7 +250,8 @@ init_per_testcase3(Case, Config) -> simple_async_get_next3, simple_sync_set3, simple_async_set3, - simple_sync_get_bulk3 + simple_sync_get_bulk3, + simple_async_get_bulk3 ], Cases = [ @@ -341,7 +343,8 @@ end_per_testcase2(Case, Config) -> simple_async_get_next3, simple_sync_set3, simple_async_set3, - simple_sync_get_bulk3 + simple_sync_get_bulk3, + simple_async_get_bulk3 ], Cases = [ @@ -462,7 +465,8 @@ groups() -> simple_sync_get_bulk2, simple_sync_get_bulk3, simple_async_get_bulk1, - simple_async_get_bulk2 + simple_async_get_bulk2, + simple_async_get_bulk3 ] }, {misc_request_tests, [], @@ -3177,111 +3181,122 @@ simple_async_get_bulk2(Config) when is_list(Config) -> ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), ?line ok = agent_load_mib(AgentNode, Test2Mib), - Exec = fun(Data) -> - async_gb_exec2(MgrNode, TargetName, Data) - end, + GetBulk = + fun(Data) -> + async_gb_exec2(MgrNode, TargetName, Data) + end, + PostVerify = fun(Res) -> Res end, + + do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify). +do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify) -> %% We re-use the verification functions from the ssgb test-case VF04 = fun(X) -> - verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView]) + PostVerify( + verify_ssgb_reply2(X, [?sysDescr_instance, endOfMibView])) end, VF06 = fun(X) -> - verify_ssgb_reply2(X, - [?sysDescr_instance, endOfMibView, - ?sysObjectID_instance, endOfMibView]) + PostVerify( + verify_ssgb_reply2(X, + [?sysDescr_instance, endOfMibView, + ?sysObjectID_instance, endOfMibView])) end, VF07 = fun(X) -> - verify_ssgb_reply2(X, - [?sysDescr_instance, endOfMibView, - ?sysDescr_instance, endOfMibView, - ?sysObjectID_instance, endOfMibView]) + PostVerify( + verify_ssgb_reply2(X, + [?sysDescr_instance, endOfMibView, + ?sysDescr_instance, endOfMibView, + ?sysObjectID_instance, endOfMibView])) end, VF08 = fun(X) -> - verify_ssgb_reply2(X, - [?sysDescr_instance, - ?sysDescr_instance]) + PostVerify( + verify_ssgb_reply2(X, + [?sysDescr_instance, + ?sysDescr_instance])) end, VF10 = fun(X) -> - verify_ssgb_reply3(X, - [{?sysDescr, 'NULL'}, - {?sysObjectID, 'NULL'}, - {?tGenErr1, 'NULL'}, - {?sysDescr, 'NULL'}]) + PostVerify( + verify_ssgb_reply3(X, + [{?sysDescr, 'NULL'}, + {?sysObjectID, 'NULL'}, + {?tGenErr1, 'NULL'}, + {?sysDescr, 'NULL'}])) end, ?line {ok, [TCnt2|_]} = mgr_user_name_to_oid(MgrNode, tCnt2), VF11 = fun(X) -> - verify_ssgb_reply2(X, - [{fl([TCnt2,2]), 100}, - {fl([TCnt2,2]), endOfMibView}]) + PostVerify( + verify_ssgb_reply2(X, + [{fl([TCnt2,2]), 100}, + {fl([TCnt2,2]), endOfMibView}])) end, Requests = [ { 1, {1, 1, []}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, { 2, {-1, 1, []}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, { 3, {-1, -1, []}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, { 4, {2, 0, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF04}, { 5, {1, 2, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF04}, { 6, {0, 2, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF06}, { 7, {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF07}, { 8, {1, 2, [[sysDescr],[sysDescr],[tTooBig]]}, - Exec, + GetBulk, VF08}, { 9, {1, 12, [[tDescr2], [sysDescr]]}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, {10, {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]}, - Exec, + GetBulk, VF10}, {11, {0, 2, [[TCnt2, 1]]}, - Exec, + GetBulk, VF11}, {12, {2, 0, [[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF04}, {13, {1, 12, [[tDescr2], [sysDescr]]}, - Exec, - fun verify_ssgb_reply1/1}, + GetBulk, + fun(X) -> PostVerify(verify_ssgb_reply1(X)) end}, {14, {2, 2, [[sysDescr],[sysObjectID],[tGenErr1],[sysDescr]]}, - Exec, + GetBulk, VF10}, {15, {0, 2, [[TCnt2, 1]]}, - Exec, + GetBulk, VF11}, {16, {2, 2, [[sysDescr],[1,3,7,1],[sysDescr],[1,3,7,1]]}, - Exec, + GetBulk, VF07}, {17, {2, 2, [[sysDescr],[sysObjectID], [tGenErr1],[sysDescr]]}, - Exec, + GetBulk, VF10} ], @@ -3300,6 +3315,49 @@ async_gb_exec2(Node, TargetName, {NR, MR, Oids}) -> mgr_user_async_get_bulk(Node, TargetName, NR, MR, Oids). +%%====================================================================== + +simple_async_get_bulk3(doc) -> + ["Simple (async) get_bulk-request - " + "Version 3 API (TargetName with send-opts)"]; +simple_async_get_bulk3(suite) -> []; +simple_async_get_bulk3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, sagb3), + p("starting with Config: ~p~n", [Config]), + + MgrNode = ?config(manager_node, Config), + AgentNode = ?config(agent_node, Config), + TargetName = ?config(manager_agent_target_name, Config), + + ?line ok = mgr_user_load_mib(MgrNode, std_mib()), + Test2Mib = test2_mib(Config), + ?line ok = mgr_user_load_mib(MgrNode, Test2Mib), + ?line ok = agent_load_mib(AgentNode, Test2Mib), + + Self = self(), + Msg = simple_async_get_bulk3, + Fun = fun() -> Self ! Msg end, + Extra = {?SNMPM_EXTRA_INFO_TAG, Fun}, + SendOpts = + [ + {extra, Extra} + ], + + GetBulk = + fun(Data) -> + async_gb_exec3(MgrNode, TargetName, Data, SendOpts) + end, + PostVerify = fun(ok) -> receive Msg -> ok end; + (Res) -> Res + end, + + do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify). + +async_gb_exec3(Node, TargetName, {NR, MR, Oids}, SendOpts) -> + mgr_user_async_get_bulk2(Node, TargetName, NR, MR, Oids, SendOpts). + + %%====================================================================== misc_async1(doc) -> ["Misc (async) request(s) - " @@ -5470,11 +5528,15 @@ mgr_user_sync_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) -> %% NonRep, MaxRep, Oids). mgr_user_async_get_bulk(Node, Addr_or_TargetName, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, async_get_bulk, - [Addr_or_TargetName, NonRep, MaxRep, Oids]). + [Addr_or_TargetName, NonRep, MaxRep, Oids]). mgr_user_async_get_bulk(Node, Addr, Port, NonRep, MaxRep, Oids) -> rcall(Node, snmp_manager_user, async_get_bulk, [Addr, Port, NonRep, MaxRep, Oids]). +mgr_user_async_get_bulk2(Node, TargetName, NonRep, MaxRep, Oids, SendOpts) -> + rcall(Node, snmp_manager_user, async_get_bulk2, + [TargetName, NonRep, MaxRep, Oids, SendOpts]). + mgr_user_purify_oid(Node, Oid) -> rcall(Node, snmp_manager_user, purify_oid, [Oid]). diff --git a/lib/snmp/test/snmp_manager_user.erl b/lib/snmp/test/snmp_manager_user.erl index c2b5886c4a..30b5dd1fc7 100644 --- a/lib/snmp/test/snmp_manager_user.erl +++ b/lib/snmp/test/snmp_manager_user.erl @@ -57,7 +57,7 @@ sync_set/1, sync_set/2, sync_set/3, sync_set2/3, async_set/1, async_set/2, async_set/3, async_set2/3, sync_get_bulk/3, sync_get_bulk/4, sync_get_bulk/5, sync_get_bulk2/5, - async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, + async_get_bulk/3, async_get_bulk/4, async_get_bulk/5, async_get_bulk2/5, name_to_oid/1, oid_to_name/1, purify_oid/1 ]). @@ -270,6 +270,9 @@ async_get_bulk(Addr_or_TargetName, NonRep, MaxRep, Oids) -> async_get_bulk(Addr, Port, NonRep, MaxRep, Oids) -> call({async_get_bulk, Addr, Port, NonRep, MaxRep, Oids}). +async_get_bulk2(TargetName, NonRep, MaxRep, Oids, SendOpts) -> + call({async_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}). + %% -- name_to_oid(Name) -> @@ -741,6 +744,20 @@ loop(#state{parent = Parent, id = Id} = S) -> %% -- (async) get-bulk-request -- %% + {{async_get_bulk2, TargetName, NonRep, MaxRep, Oids, SendOpts}, + From, Ref} when is_list(TargetName) -> + d("loop -> received async_get_bulk2 request with" + "~n TargetName: ~p" + "~n NonRep: ~w" + "~n MaxRep: ~w" + "~n Oids: ~p" + "~n SendOpts: ~p", + [TargetName, NonRep, MaxRep, Oids, SendOpts]), + Res = snmpm:async_get_bulk2(Id, TargetName, + NonRep, MaxRep, Oids, SendOpts), + reply(From, Res, Ref), + loop(S); + %% No agent specified, so send it to all of them {{async_get_bulk, NonRep, MaxRep, Oids}, From, Ref} -> d("loop -> received async_get_bulk request with" -- cgit v1.2.3 From 8bf62843795deb64ccc0abcb11c35adc32b4a6a6 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 15 Apr 2011 13:35:57 +0200 Subject: Fixed appup. --- lib/snmp/src/app/snmp.appup.src | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index fd9f40caa0..1efb888730 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -24,7 +24,7 @@ [ {"4.19", [ - {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -40,6 +40,8 @@ [snmp_conf, snmp_target_mib]}, {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, {update, snmpa_net_if, soft, soft_purge, soft_purge, [snmp_conf, snmpa_mpd]}, {update, snmpa_agent, soft, soft_purge, soft_purge, @@ -48,7 +50,7 @@ }, {"4.18", [ - {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, [snmp_conf]}, @@ -82,6 +84,9 @@ [snmpa_mib_lib, snmpa_vacm]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, + {update, snmpa_net_if, soft, soft_purge, soft_purge, [snmp_conf, snmpa_mpd]}, {update, snmpa_agent, soft, soft_purge, soft_purge, @@ -95,7 +100,7 @@ [ {"4.19", [ - {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -111,6 +116,10 @@ [snmp_conf, snmp_target_mib]}, {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, + {update, snmpa_net_if, soft, soft_purge, soft_purge, [snmp_conf, snmpa_mpd]}, {update, snmpa_agent, soft, soft_purge, soft_purge, @@ -119,7 +128,7 @@ }, {"4.18", [ - {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, @@ -153,6 +162,9 @@ [snmpa_mib_lib, snmpa_vacm]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, []}, + {update, snmpm_server, soft, soft_purge, soft_purge, [snmpm_net_if]}, + {update, snmpa_net_if, soft, soft_purge, soft_purge, [snmp_conf, snmpa_mpd]}, {update, snmpa_agent, soft, soft_purge, soft_purge, -- cgit v1.2.3 From 30a9f962d349f322f389ebc056e972f871e689a8 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Apr 2011 15:09:39 +0200 Subject: Patch received from Attila Rajmund Nohl. --- lib/snmp/src/agent/snmpa.erl | 2 +- lib/snmp/src/agent/snmpa_agent.erl | 9 +++++--- lib/snmp/src/agent/snmpa_net_if.erl | 13 ++++++++++++ lib/snmp/src/agent/snmpa_trap.erl | 41 +++++++++++++++++++++++++++++-------- lib/snmp/src/app/snmp.appup.src | 12 +++++++---- 5 files changed, 60 insertions(+), 17 deletions(-) diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index 630a14907d..e9023693d3 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -673,7 +673,7 @@ send_notification(Agent, Notification, Recv, {name, NotifyName}, {context, ContextName}, {extra, ?DEFAULT_NOTIF_EXTRA_INFO}, - {local_enging_id, LocalEngineID} + {local_engine_id, LocalEngineID} ], send_notification2(Agent, Notification, SendOpts). diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index 0a28e753ce..e4cfeddb6a 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -2126,7 +2126,7 @@ send_discovery(S, From, TargetName, Record, ContextName, InitVars, DiscoHandler, ExtraInfo) -> case snmpa_trap:send_discovery(TargetName, Record, ContextName, - InitVars, get(net_if)) of + InitVars, get(net_if), ExtraInfo) of {ok, Sender, SecLevel} -> Disco = #disco{from = From, rec = Record, @@ -2203,9 +2203,12 @@ handle_discovery_response(#state{disco = #disco{target = TargetName, #disco{rec = Record, ctx = ContextName, ivbs = InitVars} = Disco, - case snmpa_trap:send_discovery(TargetName, Record, + case snmpa_trap:send_discovery(TargetName, + Record, ContextName, - InitVars, get(net_if)) of + InitVars, + get(net_if), + ExtraInfo) of {ok, Sender, _SecLevel} -> ?vdebug("handle_discovery_response(1) -> " "stage 2 trap sent", []), diff --git a/lib/snmp/src/agent/snmpa_net_if.erl b/lib/snmp/src/agent/snmpa_net_if.erl index bbc43c3da7..bbc5568cde 100644 --- a/lib/snmp/src/agent/snmpa_net_if.erl +++ b/lib/snmp/src/agent/snmpa_net_if.erl @@ -343,6 +343,7 @@ loop(S) -> loop(NewS); %% Discovery Inform + %% {send_discovery, Pdu, MsgData, To, From} -> ?vdebug("received send discovery request: " "~n Pdu: ~p" @@ -351,6 +352,18 @@ loop(S) -> [Pdu, To, toname(From)]), NewS = handle_send_discovery(S, Pdu, MsgData, To, From), loop(NewS); + %% + + %% Discovery Inform + {send_discovery, Pdu, MsgData, To, From, ExtraInfo} -> + ?vdebug("received send discovery request: " + "~n Pdu: ~p" + "~n To: ~p" + "~n From: ~p" + "~n ExtraInfo: ~p", + [Pdu, To, toname(From), ExtraInfo]), + NewS = handle_send_discovery(S, Pdu, MsgData, To, From), + loop(NewS); {discarded_pdu, _Vsn, ReqId, _ACMData, Variable, _Extra} -> ?vdebug("discard PDU: ~p", [Variable]), diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 2a233fd38e..04b78a3847 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -25,13 +25,18 @@ -export([construct_trap/2, try_initialise_vars/2, send_trap/6, send_trap/7, send_trap/8]). --export([send_discovery/5]). +-export([send_discovery/6]). %% Internal exports -export([init_v2_inform/9, init_v2_inform/10, init_v3_inform/9, init_v3_inform/10, init_v3_inform/11, send_inform/6]). --export([init_discovery_inform/12, send_discovery_inform/5]). +-export([init_discovery_inform/13, send_discovery_inform/5]). + +%% +-export([send_discovery/5, + init_discovery_inform/12]). +%% -include_lib("snmp/include/snmp_types.hrl"). -include_lib("snmp/src/agent/snmpa_internal.hrl"). @@ -360,9 +365,13 @@ do_send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, Recv, LocalEngineID, ExtraInfo, NetIf). send_discovery(TargetName, Record, ContextName, Vbs, NetIf) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + send_discovery(TargetName, Record, ContextName, Vbs, NetIf, ExtraInfo). +send_discovery(TargetName, Record, ContextName, Vbs, NetIf, ExtraInfo) -> case find_dest(TargetName) of {ok, Dest} -> - send_discovery_pdu(Dest, Record, ContextName, Vbs, NetIf); + send_discovery_pdu(Dest, Record, ContextName, Vbs, NetIf, + ExtraInfo); Error -> Error end. @@ -540,7 +549,8 @@ find_dest(TargetName) -> send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, Timeout, Retry}, - Record, ContextName, Vbs, NetIf) -> + Record, ContextName, Vbs, NetIf, + ExtraInfo) -> ?vdebug("send_discovery_pdu -> entry with " "~n Destination address: ~p" "~n Target name: ~p" @@ -550,9 +560,10 @@ send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, "~n Timeout: ~p" "~n Retry: ~p" "~n Record: ~p" - "~n ContextName: ~p", + "~n ContextName: ~p" + "~n ExtraInfo: ~p", [Dest, TargetName, SecModel, SecName, SecLevel, - Timeout, Retry, Record, ContextName]), + Timeout, Retry, Record, ContextName, ExtraInfo]), case get_mib_view(SecModel, SecName, SecLevel, ContextName) of {ok, MibView} -> case check_all_varbinds(Record, Vbs, MibView) of @@ -562,7 +573,7 @@ send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, SecModel, SecName, SecLevel, TargetName, ContextName, Timeout, Retry, - SysUpTime, NetIf); + SysUpTime, NetIf, ExtraInfo); false -> {error, {mibview_validation_failed, Vbs, MibView}} end; @@ -572,7 +583,7 @@ send_discovery_pdu({Dest, TargetName, {SecModel, SecName, SecLevel}, send_discovery_pdu(Record, Dest, Vbs, SecModel, SecName, SecLevel, TargetName, - ContextName, Timeout, Retry, SysUpTime, NetIf) -> + ContextName, Timeout, Retry, SysUpTime, NetIf, ExtraInfo) -> {_Oid, IVbs} = mk_v2_trap(Record, Vbs, SysUpTime), % v2 refers to SMIv2; Sender = proc_lib:spawn_link(?MODULE, init_discovery_inform, [self(), @@ -581,6 +592,7 @@ send_discovery_pdu(Record, Dest, Vbs, ContextName, Timeout, Retry, IVbs, NetIf, + ExtraInfo, get(verbosity)]), {ok, Sender, SecLevel}. @@ -588,6 +600,17 @@ init_discovery_inform(Parent, Dest, SecModel, SecName, SecLevel, TargetName, ContextName, Timeout, Retry, Vbs, NetIf, Verbosity) -> + ExtraInfo = ?DEFAULT_NOTIF_EXTRA_INFO, + init_discovery_inform(Parent, + Dest, + SecModel, SecName, SecLevel, TargetName, + ContextName, Timeout, Retry, Vbs, NetIf, + Verbosity, ExtraInfo). +init_discovery_inform(Parent, + Dest, + SecModel, SecName, SecLevel, TargetName, + ContextName, Timeout, Retry, Vbs, NetIf, + Verbosity, ExtraInfo) -> put(verbosity, Verbosity), put(sname, madis), Pdu = make_discovery_pdu(Vbs), @@ -595,7 +618,7 @@ init_discovery_inform(Parent, SecLevelFlag = mk_flag(SecLevel), SecData = {SecModel, SecName, SecLevelFlag, TargetName}, MsgData = {SecData, ContextEngineId, ContextName}, - Msg = {send_discovery, Pdu, MsgData, Dest, self()}, + Msg = {send_discovery, Pdu, MsgData, Dest, self(), ExtraInfo}, ?MODULE:send_discovery_inform(Parent, Timeout*10, Retry, Msg, NetIf). %% note_timeout(Timeout, Retry) diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index fd9f40caa0..46b2efffca 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -25,13 +25,14 @@ {"4.19", [ {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpa, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_trap, soft_purge, soft_purge, - [snmpa_mpd, snmp_notification_mib, snmp_target_mib]}, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, {load_module, snmpa_acm, soft_purge, soft_purge, [snmp_conf, snmpa_mpd, snmp_target_mib]}, {load_module, snmpa_conf, soft_purge, soft_purge, @@ -49,6 +50,7 @@ {"4.18", [ {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpa, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, [snmp_conf]}, @@ -56,7 +58,7 @@ {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, - [snmpa_mpd, snmp_notification_mib, snmp_target_mib]}, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, {load_module, snmpa_acm, soft_purge, soft_purge, [snmp_conf, snmpa_mpd, snmp_target_mib]}, {load_module, snmpa, soft_purge, soft_purge, @@ -96,13 +98,14 @@ {"4.19", [ {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpa, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_trap, soft_purge, soft_purge, - [snmpa_mpd, snmp_notification_mib, snmp_target_mib]}, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, {load_module, snmpa_acm, soft_purge, soft_purge, [snmp_conf, snmpa_mpd, snmp_target_mib]}, {load_module, snmpa_conf, soft_purge, soft_purge, @@ -120,6 +123,7 @@ {"4.18", [ {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmpa, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, @@ -127,7 +131,7 @@ {load_module, snmpa_mpd, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, - [snmpa_mpd, snmp_notification_mib, snmp_target_mib]}, + [snmpa_mpd, snmp_notification_mib, snmp_target_mib, snmpa_net_if]}, {load_module, snmpa_acm, soft_purge, soft_purge, [snmp_conf, snmpa_mpd, snmp_target_mib]}, {load_module, snmpa, soft_purge, soft_purge, -- cgit v1.2.3 From 9022ef27b8628ca680654d542b4d520426480c2d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Apr 2011 15:57:01 +0200 Subject: Fixed appup. --- lib/snmp/src/app/snmp.appup.src | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 07e696ce88..998b52f3df 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -24,6 +24,7 @@ [ {"4.19", [ + {load_module, snmp_community_mib, soft_purge, soft_purge, []} ] }, {"4.18", @@ -59,6 +60,7 @@ [ {"4.19", [ + {load_module, snmp_community_mib, soft_purge, soft_purge, []} ] }, {"4.18", -- cgit v1.2.3 From 3e372b28827c3b5bc683ac08002af9cb8da5c692 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Apr 2011 16:48:34 +0200 Subject: Also updated the dokumentation for the added function. --- lib/snmp/doc/src/notes.xml | 4 ++-- lib/snmp/doc/src/snmp_community_mib.xml | 26 +++++++++++++++++++------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index b7e50c47e4..a098c909d4 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -46,8 +46,8 @@

[agent] To be able to handle multiple engine-id(s) when sending trap(s), the function - add_community/6 - has been added.

+ + add_community/6 has been added.

Own Id: OTP-9119 Aux Id: Seq 11792

diff --git a/lib/snmp/doc/src/snmp_community_mib.xml b/lib/snmp/doc/src/snmp_community_mib.xml index 7c7386af19..5e7bca3e27 100644 --- a/lib/snmp/doc/src/snmp_community_mib.xml +++ b/lib/snmp/doc/src/snmp_community_mib.xml @@ -1,10 +1,10 @@ - +
- 19992009 + 19992011 Ericsson AB. All Rights Reserved. @@ -35,11 +35,13 @@ Instrumentation Functions for SNMP-COMMUNITY-MIB

The module snmp_community_mib implements the instrumentation - functions for the - SNMP-COMMUNITY-MIB, and functions for configuring the database. -

+ functions for the SNMP-COMMUNITY-MIB, and functions for configuring the + database.

The configuration files are described in the SNMP User's Manual.

+ +
+ configure(ConfDir) -> void() @@ -68,8 +70,11 @@

The configuration file read is: community.conf.

+ +
+ reconfigure(ConfDir) -> void() Configure the SNMP-COMMUNITY-MIB @@ -96,28 +101,35 @@ where the configuration files are found.

The configuration file read is: community.conf.

+
+ add_community(Idx, CommName, SecName, CtxName, TransportTag) -> Ret + add_community(Idx, CommName, SecName, EngineId, CtxName, TransportTag) -> Ret Added one community Idx = string() CommName = string() SecName = string() + EngineId = string() CtxName = string() TransportTag = string() Ret = {ok, Key} | {error, Reason} Key = term() - Reason = term() + Reason = term()

Adds a community to the agent config. - Equivalent to one line in the community.conf file.

+ Equivalent to one line in the community.conf file.

+

With the EngineId argument it is possible to + override the configured engine-id (SNMP-FRAMEWORK-MIB).

+ delete_community(Key) -> Ret Delete one community -- cgit v1.2.3 From 178cec8c7290e82db5d7d80a9d74ab0cb3f85bed Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 18 Apr 2011 17:00:05 +0200 Subject: Removed unused function. --- lib/snmp/src/agent/snmpa.erl | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index e9023693d3..b2e4f253ab 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -601,17 +601,6 @@ set_request_limit(Agent, NewLimit) -> send_notification2(Agent, Notification, SendOpts) -> snmpa_agent:send_notification(Agent, Notification, SendOpts). -send_notification(Agent, Notification) -> - SendOpts = - [ - {receiver, no_receiver}, - {varbinds, []}, - {name, ""}, - {context, ""}, - {extra, ?DEFAULT_NOTIF_EXTRA_INFO} - ], - send_notification2(Agent, Notification, SendOpts). - send_notification(Agent, Notification, Recv) -> SendOpts = [ -- cgit v1.2.3 From 5488c7843c8930d14d1d0d2ec0aa23d0d491afbc Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 19 Apr 2011 13:37:06 +0200 Subject: Default ssl kind changed to essl (from ossl). --- lib/inets/doc/src/httpc.xml | 21 ++++++--------------- lib/inets/doc/src/httpd.xml | 2 +- lib/inets/doc/src/notes.xml | 13 +++++++++++++ lib/inets/src/http_lib/http_internal.hrl | 6 +++--- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 532f5eefde..f6b6827e93 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -105,17 +105,8 @@ filename() = string()
SSL DATA TYPES -

Some type definitions relevant when using https, - for details ssl(3):

- +

See ssl(3) for information + about ssl options (ssloptions()).

@@ -175,9 +166,9 @@ ssl_options() = {verify, code()} | http_options() = [http_option()] http_option() = {timeout, timeout()} | {connect_timeout, timeout()} | - {ssl, ssl_options()} | - {ossl, ssl_options()} | - {essl, ssl_options()} | + {ssl, ssloptions()} | + {ossl, ssloptions()} | + {essl, ssloptions()} | {autoredirect, boolean()} | {proxy_auth, {userstring(), passwordstring()}} | {version, http_version()} | @@ -236,7 +227,7 @@ ssl_options() = {verify, code()} |

This is the default ssl config option, currently defaults to - ossl, see below.

+ essl, see below.

Defaults to [].

diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index ac49a37296..edacb73b65 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -154,7 +154,7 @@ ossl specifically uses the OpenSSL based (old) SSL. essl specifically uses the Erlang based (new) SSL. When using ssl it currently defaults to - ossl.

+ essl.

Defaults to ip_comm.

diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index df3bf19066..b885bcbcdb 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -91,6 +91,19 @@

Aux Id: seq11819

+ +

The default ssl kind has now been changed to essl.

+

ossl will work for as long as the ssl application + supports it.

+

See the httpd + socket_type + communication property or the httpc + request/4,5 function + for more info.

+

Own Id: OTP-9230

+

*** POTENTIAL INCOMPATIBILITY ***

+
+
diff --git a/lib/inets/src/http_lib/http_internal.hrl b/lib/inets/src/http_lib/http_internal.hrl index 5440f214b5..2e924667c6 100644 --- a/lib/inets/src/http_lib/http_internal.hrl +++ b/lib/inets/src/http_lib/http_internal.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 @@ -28,8 +28,8 @@ -define(HTTP_MAX_URI_SIZE, nolimit). -ifndef(HTTP_DEFAULT_SSL_KIND). --define(HTTP_DEFAULT_SSL_KIND, ossl). -%% -define(HTTP_DEFAULT_SSL_KIND, essl). +%% -define(HTTP_DEFAULT_SSL_KIND, ossl). +-define(HTTP_DEFAULT_SSL_KIND, essl). -endif. % -ifdef(HTTP_DEFAULT_SSL_KIND). -- cgit v1.2.3 From 29d4a0f745b69f7d2a75664b857b6ad65fca920d Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Tue, 19 Apr 2011 13:43:27 +0200 Subject: Updated appup file. --- lib/inets/src/inets_app/inets.appup.src | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index c14f8d51f6..0a590c9c36 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -24,6 +24,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, @@ -41,6 +42,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, @@ -70,6 +72,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, @@ -87,6 +90,7 @@ {load_module, http_util, soft_purge, soft_purge, []}, {load_module, http_transport, soft_purge, soft_purge, []}, {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, + {load_module, httpd_conf, soft_purge, soft_purge, []}, {load_module, httpd_file, soft_purge, soft_purge, []}, {load_module, httpd_log, soft_purge, soft_purge, []}, {load_module, mod_esi, soft_purge, soft_purge, []}, -- cgit v1.2.3 From 5b15b32e2d1aec2716c58879f0ada02557c757f5 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 20 Apr 2011 10:44:46 +0200 Subject: Just a save commit (I need to work on another branch...) --- lib/snmp/src/misc/snmp_pdus.erl | 50 +++++++++++++++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl index dc8900c8cd..9c273bc0e0 100644 --- a/lib/snmp/src/misc/snmp_pdus.erl +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -268,25 +268,42 @@ dec_value([4 | Bytes]) -> dec_value([64 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'IpAddress', Value}, Rest}; +%% dec_value([65 | Bytes]) -> +%% {Value, Rest} = dec_integer_notag(Bytes), +%% if +%% (Value >= 0) andalso (Value =< 4294967295) -> +%% {{'Counter32', Value}, Rest}; +%% true -> +%% exit({error, {bad_counter32, Value}}) +%% end; dec_value([65 | Bytes]) -> + %% Counter32 is an unsigned 32 but is actually encoded as + %% a signed integer 32 (INTEGER). {Value, Rest} = dec_integer_notag(Bytes), - if Value >= 0, Value =< 4294967295 -> - {{'Counter32', Value}, Rest}; - true -> - exit({error, {bad_counter32, Value}}) - end; + Value2 = + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + %% We accept value above 16#7fffffff + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; + true -> + exit({error, {bad_counter32, Value}}) end, + {{'Counter64', Value2}, Rest}; dec_value([66 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), - if Value >= 0, Value =< 4294967295 -> + if + (Value >= 0) andalso (Value =< 4294967295) -> {{'Unsigned32', Value}, Rest}; - true -> + true -> exit({error, {bad_unsigned32, Value}}) end; dec_value([67 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), - if Value >= 0, Value =< 4294967295 -> + if + (Value >= 0) andalso (Value =< 4294967295) -> {{'TimeTicks', Value}, Rest}; - true -> + true -> exit({error, {bad_timeticks, Value}}) end; dec_value([68 | Bytes]) -> @@ -642,6 +659,21 @@ enc_value(_Type, endOfMibView) -> [130,0]; enc_value('NULL', _Val) -> [5,0]; +enc_value('Counter32', Val) -> + Val2 = + if + Val > 16#ffffffff -> + exit({error, {bad_counter32, Val}}); + Val >= 16#80000000 -> + (Val band 16#7fffffff) - 16#80000000; + Val >= 0 -> + Val; + true -> + exit({error, {bad_counter32, Val}}) + end, + Bytes2 = enc_integer_notag(Val2), + Len2 = elength(length(Bytes2)), + lists:append([65 | Len2],Bytes2); enc_value('Counter64', Val) -> Val2 = if -- cgit v1.2.3 From b880d9d8f983b08d5d9e14226b8dbfc1643dbf58 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 20 Apr 2011 10:48:29 +0200 Subject: Patch from Attila Rajmund Nohl: Arguments switched. --- lib/snmp/src/agent/snmpa_trap.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 04b78a3847..3c7ae804a5 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -592,8 +592,8 @@ send_discovery_pdu(Record, Dest, Vbs, ContextName, Timeout, Retry, IVbs, NetIf, - ExtraInfo, - get(verbosity)]), + get(verbosity), + ExtraInfo]), {ok, Sender, SecLevel}. init_discovery_inform(Parent, -- cgit v1.2.3 From 0568a0ea0f8bf115b639589f060f62d0435a956a Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Fri, 29 Apr 2011 02:55:39 +0900 Subject: Fix file descriptor leak File descriptors to import cover data are left opened. When we export and import cover data many times, leaked descriptors cause an error. --- lib/tools/src/cover.erl | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 73a736f0e8..905ad895c9 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -662,6 +662,7 @@ main_process_loop(State) -> Imported = do_import_to_table(Fd,File, State#main_state.imported), reply(From, ok), + file:close(Fd), main_process_loop(State#main_state{imported=Imported}); {error,Reason} -> reply(From, {error, {cant_open_file,File,Reason}}), -- cgit v1.2.3 From fbcefc345a5fc677cce94e5b2ae6a52919d5ee2c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 2 May 2011 16:15:58 +0200 Subject: Forgot to deprecate snmpm:agb/5,6,7,8,9 --- lib/snmp/src/manager/snmpm.erl | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 36b4901e9a..9cab8f29c2 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -119,6 +119,11 @@ -deprecated({gb, 7}). -deprecated({gb, 8}). -deprecated({gb, 9}). +-deprecated({agb, 5}). +-deprecated({agb, 6}). +-deprecated({agb, 7}). +-deprecated({agb, 8}). +-deprecated({agb, 9}). -deprecated({s, 3}). -deprecated({s, 4}). -deprecated({s, 5}). -- cgit v1.2.3 From 881d45107dfff7e0a640114ce808e284fa7a83f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 27 Apr 2011 05:57:07 +0200 Subject: binary_SUITE: Remove workaround for avoiding stack overflow In older releases of Erlang/OTP, the '=:=' operator could run out of stack if used to compare very deep lists. Since that problem has been fixed, we can remove the workaround used in the deep/1 test case. --- erts/emulator/test/binary_SUITE.erl | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 7e409f053e..96a5a501b4 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -1183,34 +1183,7 @@ deep(Config) when is_list(Config) -> deep_roundtrip(T) -> B = term_to_binary(T), - true = deep_eq(T, binary_to_term(B)). - -%% -%% FIXME: =:= runs out of stack. -%% -deep_eq([H1|T1], [H2|T2]) -> - deep_eq(H1, H2) andalso deep_eq(T1, T2); -deep_eq(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> - deep_eq_tup(T1, T2, tuple_size(T1)); -deep_eq(T1, T2) when is_function(T1), is_function(T2) -> - {uniq,U1} = erlang:fun_info(T1, uniq), - {index,I1} = erlang:fun_info(T1, index), - {arity,A1} = erlang:fun_info(T1, arity), - {env,E1} = erlang:fun_info(T1, env), - {uniq,U2} = erlang:fun_info(T2, uniq), - {index,I2} = erlang:fun_info(T2, index), - {arity,A2} = erlang:fun_info(T2, arity), - {env,E2} = erlang:fun_info(T2, env), - U1 =:= U2 andalso I1 =:= I2 andalso A1 =:= A2 andalso - deep_eq(E1, E2); -deep_eq(T1, T2) -> - T1 =:= T2. - -deep_eq_tup(_T1, _T2, 0) -> - true; -deep_eq_tup(T1, T2, N) -> - deep_eq(element(N, T1), element(N, T2)) andalso - deep_eq_tup(T1, T2, N-1). + T = binary_to_term(B). obsolete_funs(Config) when is_list(Config) -> erts_debug:set_internal_state(available_internal_state, true), -- cgit v1.2.3 From 73830b087d3a69611c97751156e9f1c61e1ce26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 27 Apr 2011 05:43:00 +0200 Subject: Test iolist_size/1 with bad arguments --- erts/emulator/test/binary_SUITE.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 96a5a501b4..b7b2aaf89e 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -280,7 +280,8 @@ bad_list_to_binary(Config) when is_list(Config) -> test_bad_bin(List) -> {'EXIT',{badarg,_}} = (catch list_to_binary(List)), {'EXIT',{badarg,_}} = (catch iolist_to_binary(List)), - {'EXIT',{badarg,_}} = (catch list_to_bitstring(List)). + {'EXIT',{badarg,_}} = (catch list_to_bitstring(List)), + {'EXIT',{badarg,_}} = (catch iolist_size(List)). bad_binary_to_list(doc) -> "Tries binary_to_list/1,3 with bad arguments."; bad_binary_to_list(Config) when is_list(Config) -> -- cgit v1.2.3 From 963843aad736997a66db8f8fdec6b3099373b742 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Wed, 27 Apr 2011 05:24:38 +0200 Subject: iolist_size/1: Fix truncation of result iolist_size/1 would silently return a truncated result for iolists whose size exceeded the word size. For example: iolist(lists:duplicate(256, <<0:(1 bsl 24)/unit:8>>)). would return 0 (instead of 4294967296 or 1 bsl 32). Rewrite iolist_size/1 to accumulate the size in a bignum if necessary and result the correct result. --- erts/emulator/beam/bif.c | 142 ++++++++++++++++++++++++++++++++++-- erts/emulator/test/binary_SUITE.erl | 67 ++++++++++++++--- 2 files changed, 191 insertions(+), 18 deletions(-) diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 6b1ce823cb..e886792ba7 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2168,20 +2168,146 @@ BIF_RETTYPE tl_1(BIF_ALIST_1) /**********************************************************************/ /* return the size of an I/O list */ -BIF_RETTYPE iolist_size_1(BIF_ALIST_1) +static Eterm +accumulate(Eterm acc, Uint size) { - Sint size = io_list_len(BIF_ARG_1); + if (is_non_value(acc)) { + /* + * There is no pre-existing accumulator. Allocate a + * bignum buffer with one extra word to be used if + * the bignum grows in the future. + */ + Eterm* hp = (Eterm *) erts_alloc(ERTS_ALC_T_TEMP_TERM, + (BIG_UINT_HEAP_SIZE+1) * + sizeof(Eterm)); + return uint_to_big(size, hp); + } else { + Eterm* big; + int need_heap; - if (size == -1) { - BIF_ERROR(BIF_P, BADARG); - } else if (IS_USMALL(0, (Uint) size)) { - BIF_RET(make_small(size)); + /* + * Add 'size' to 'acc' in place. There is always one + * extra word allocated in case the bignum grows by one word. + */ + big = big_val(acc); + need_heap = BIG_NEED_SIZE(BIG_SIZE(big)); + acc = big_plus_small(acc, size, big); + if (BIG_NEED_SIZE(big_size(acc)) > need_heap) { + /* + * The extra word has been consumed. Grow the + * allocation by one word. + */ + big = (Eterm *) erts_realloc(ERTS_ALC_T_TEMP_TERM, + big_val(acc), + (need_heap+1) * sizeof(Eterm)); + acc = make_big(big); + } + return acc; + } +} + +static Eterm +consolidate(Process* p, Eterm acc, Uint size) +{ + Eterm* hp; + + if (is_non_value(acc)) { + return erts_make_integer(size, p); } else { - Eterm* hp = HAlloc(BIF_P, BIG_UINT_HEAP_SIZE); - BIF_RET(uint_to_big(size, hp)); + Eterm* big; + Uint sz; + Eterm res; + + acc = accumulate(acc, size); + big = big_val(acc); + sz = BIG_NEED_SIZE(BIG_SIZE(big)); + hp = HAlloc(p, sz); + res = make_big(hp); + while (sz--) { + *hp++ = *big++; + } + erts_free(ERTS_ALC_T_TEMP_TERM, (void *) big_val(acc)); + return res; } } +BIF_RETTYPE iolist_size_1(BIF_ALIST_1) +{ + Eterm obj, hd; + Eterm* objp; + Uint size = 0; + Uint cur_size; + Uint new_size; + Eterm acc = THE_NON_VALUE; + DECLARE_ESTACK(s); + + obj = BIF_ARG_1; + goto L_again; + + while (!ESTACK_ISEMPTY(s)) { + obj = ESTACK_POP(s); + L_again: + if (is_list(obj)) { + L_iter_list: + objp = list_val(obj); + hd = CAR(objp); + obj = CDR(objp); + /* Head */ + if (is_byte(hd)) { + size++; + if (size == 0) { + acc = accumulate(acc, (Uint) -1); + size = 1; + } + } else if (is_binary(hd) && binary_bitsize(hd) == 0) { + cur_size = binary_size(hd); + if ((new_size = size + cur_size) >= size) { + size = new_size; + } else { + acc = accumulate(acc, size); + size = cur_size; + } + } else if (is_list(hd)) { + ESTACK_PUSH(s, obj); + obj = hd; + goto L_iter_list; + } else if (is_not_nil(hd)) { + goto L_type_error; + } + /* Tail */ + if (is_list(obj)) { + goto L_iter_list; + } else if (is_binary(obj) && binary_bitsize(obj) == 0) { + cur_size = binary_size(obj); + if ((new_size = size + cur_size) >= size) { + size = new_size; + } else { + acc = accumulate(acc, size); + size = cur_size; + } + } else if (is_not_nil(obj)) { + goto L_type_error; + } + } else if (is_binary(obj) && binary_bitsize(obj) == 0) { + cur_size = binary_size(obj); + if ((new_size = size + cur_size) >= size) { + size = new_size; + } else { + acc = accumulate(acc, size); + size = cur_size; + } + } else if (is_not_nil(obj)) { + goto L_type_error; + } + } + + DESTROY_ESTACK(s); + BIF_RET(consolidate(BIF_P, acc, size)); + + L_type_error: + DESTROY_ESTACK(s); + BIF_ERROR(BIF_P, BADARG); +} /**********************************************************************/ diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index b7b2aaf89e..401b3c23b6 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -517,18 +517,65 @@ external_size_1(Term, Size0, Limit) when Size0 < Limit -> external_size_1(_, _, _) -> ok. t_iolist_size(Config) when is_list(Config) -> - %% Build a term whose external size only fits in a big num (on 32-bit CPU). - Bin = iolist_to_binary(lists:seq(0, 254)), - ?line ok = t_iolist_size_1(Bin, 0, 16#7FFFFFFF), - ?line ok = t_iolist_size_1(make_unaligned_sub_binary(Bin), 0, 16#7FFFFFFF). + ?line Seed = now(), + ?line io:format("Seed: ~p", [Seed]), + ?line random:seed(Seed), + ?line Base = <<0:(1 bsl 20)/unit:8>>, + ?line Powers = [1 bsl N || N <- lists:seq(2, 37)], + ?line Sizes0 = [[N - random:uniform(N div 2), + lists:seq(N-2, N+2), + N+N div 2, + N + random:uniform(N div 2)] || + N <- Powers], + %% Test sizes around 1^32 more thoroughly. + FourGigs = 1 bsl 32, + ?line Sizes1 = [FourGigs+N || N <- lists:seq(-8, 40)] ++ Sizes0, + ?line Sizes2 = lists:flatten(Sizes1), + ?line Sizes = lists:usort(Sizes2), + io:format("~p sizes:", [length(Sizes)]), + io:format("~p\n", [Sizes]), + ?line [Sz = iolist_size(build_iolist(Sz, Base)) || Sz <- Sizes], + ok. -t_iolist_size_1(IOList, Size0, Limit) when Size0 < Limit -> - case iolist_size(IOList) of - Size when is_integer(Size), Size0 < Size -> - io:format("~p", [Size]), - t_iolist_size_1([IOList|IOList], Size, Limit) +build_iolist(N, Base) when N < 16 -> + case random:uniform(3) of + 1 -> + <> = Base, + Bin; + _ -> + lists:seq(1, N) end; -t_iolist_size_1(_, _, _) -> ok. +build_iolist(N, Base) when N =< byte_size(Base) -> + case random:uniform(3) of + 1 -> + <> = Base, + Bin; + 2 -> + <> = Base, + [Bin]; + 3 -> + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L]; + 1 -> + L = build_iolist(N div 2, Base), + [L,L,45] + end + end; +build_iolist(N0, Base) -> + Small = random:uniform(15), + Seq = lists:seq(1, Small), + N = N0 - Small, + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L|Seq]; + 1 -> + L = build_iolist(N div 2, Base), + [47,L,L|Seq] + end. + bad_binary_to_term_2(doc) -> "OTP-4053."; bad_binary_to_term_2(suite) -> []; -- cgit v1.2.3 From 7c1b371290b3e06834fcc4de7e5d0c89f43e818c Mon Sep 17 00:00:00 2001 From: Shunichi Shinohara Date: Fri, 6 May 2011 11:51:12 +0900 Subject: Add a check logic to prevent file descriptor leak cover module handle files as raw in export and import. Assert counts of ports are the same at the beginning and at the end of the test case. --- lib/tools/test/cover_SUITE.erl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index b5c8e8a1b7..fe7f92de78 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -395,6 +395,7 @@ export_import(suite) -> []; export_import(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), ?line ok = file:set_cwd(DataDir), + ?line PortCount = length(erlang:ports()), %% Export one module ?line {ok,f} = cover:compile(f), @@ -484,6 +485,9 @@ export_import(Config) when is_list(Config) -> ?line ?t:capture_stop(), ?line check_f_calls(1,0), + %% Check no raw files are left open + ?line PortCount = length(erlang:ports()), + %% Cleanup ?line ok = cover:stop(), ?line Files = lsfiles(), -- cgit v1.2.3 From 4aeb38abc1d9162b98a3ba4346ebdabad8532da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 6 May 2011 11:58:03 +0200 Subject: io.c: Make io_list_vec_len() less general io_list_vec_len() is slightly more general than it needs to be. Specifically: * The only caller always passes ERL_SMALL_IO_BIN_LIMIT as the value for the bin_limit parameter, so the bin_limit parameter can be eliminated. * The only caller never passes any NULL pointers, so there is no need test the pointers before writing through them. --- erts/emulator/beam/io.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index f619c6f88b..3610a06c0c 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -82,6 +82,9 @@ static void driver_monitor_unlock_pdl(Port *p); #define DRV_MONITOR_UNLOCK_PDL(Port) /* nothing */ #endif +#define ERL_SMALL_IO_BIN_LIMIT (4*ERL_ONHEAP_BIN_LIMIT) +#define SMALL_WRITE_VEC 16 + static ERTS_INLINE ErlIOQueue* drvport2ioq(ErlDrvPort drvport) { @@ -960,7 +963,7 @@ do { \ b_size += _size; \ in_clist = 0; \ v_size++; \ - if (_size >= bin_limit) { \ + if (_size >= ERL_SMALL_IO_BIN_LIMIT) { \ p_in_clist = 0; \ p_v_size++; \ } else { \ @@ -997,7 +1000,6 @@ do { \ static int io_list_vec_len(Eterm obj, int* vsize, int* csize, - int bin_limit, /* small binaries limit */ int * pvsize, int * pcsize) { DECLARE_ESTACK(s); @@ -1062,14 +1064,10 @@ io_list_vec_len(Eterm obj, int* vsize, int* csize, } DESTROY_ESTACK(s); - if (vsize != NULL) - *vsize = v_size; - if (csize != NULL) - *csize = c_size; - if (pvsize != NULL) - *pvsize = p_v_size; - if (pcsize != NULL) - *pcsize = p_c_size; + *vsize = v_size; + *csize = c_size; + *pvsize = p_v_size; + *pcsize = p_c_size; return c_size + b_size; L_type_error: @@ -1077,10 +1075,6 @@ io_list_vec_len(Eterm obj, int* vsize, int* csize, return -1; } -#define ERL_SMALL_IO_BIN_LIMIT (4*ERL_ONHEAP_BIN_LIMIT) -#define SMALL_WRITE_VEC 16 - - /* write data to a port */ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) { @@ -1107,7 +1101,6 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) ErlIOVec ev; if ((size = io_list_vec_len(list, &vsize, &csize, - ERL_SMALL_IO_BIN_LIMIT, &pvsize, &pcsize)) < 0) { goto bad_value; } -- cgit v1.2.3 From 61c3d766889c79e3d3b95e8eb3da8638a2eccbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 6 May 2011 14:55:13 +0200 Subject: Make port_command/2 reject non-byte sized bitstrings Also rename the fun_to_port/1 test case to outputv_errors/1 and test sending more kinds of bad data to an outputv-enabled driver. --- erts/emulator/beam/io.c | 2 +- erts/emulator/test/driver_SUITE.erl | 44 +++++++++++++++++-------------------- 2 files changed, 21 insertions(+), 25 deletions(-) diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 3610a06c0c..bf49417c3f 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -957,7 +957,7 @@ do { \ int _bitoffs; \ int _bitsize; \ ERTS_GET_REAL_BIN(obj, _real, _offset, _bitoffs, _bitsize); \ - ASSERT(_bitsize == 0); \ + if (_bitsize != 0) goto L_type_error; \ if (thing_subtag(*binary_val(_real)) == REFC_BINARY_SUBTAG && \ _bitoffs == 0) { \ b_size += _size; \ diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 7600a44988..ffd08f2214 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -38,7 +38,7 @@ timer_change/1, timer_delay/1, queue_echo/1, - fun_to_port/1, + outputv_errors/1, driver_unloaded/1, io_ready_exit/1, use_fallback_pollset/1, @@ -129,7 +129,7 @@ end_per_testcase(Case, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [fun_to_port, outputv_echo, queue_echo, {group, timer}, + [outputv_errors, outputv_echo, queue_echo, {group, timer}, driver_unloaded, io_ready_exit, use_fallback_pollset, bad_fd_in_pollset, driver_event, fd_change, steal_control, otp_6602, 'driver_system_info_ver1.0', @@ -165,37 +165,33 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. - -fun_to_port(doc) -> "Test sending a fun to port with an outputv-capable driver."; -fun_to_port(Config) when is_list(Config) -> +outputv_errors(doc) -> "Test sending bad types to port with an outputv-capable driver."; +outputv_errors(Config) when is_list(Config) -> ?line Path = ?config(data_dir, Config), ?line erl_ddll:start(), ?line ok = load_driver(Path, outputv_drv), - ?line fun_to_port_1(fun() -> 33 end), - ?line fun_to_port_1([fun() -> 42 end]), - ?line fun_to_port_1([1|fun() -> 42 end]), - L = build_io_list(65536), - ?line fun_to_port_1([L,fun() -> 42 end]), - ?line fun_to_port_1([L|fun() -> 42 end]), + outputv_bad_types(fun(T) -> + ?line outputv_errors_1(T), + ?line outputv_errors_1([1|T]), + ?line L = [1,2,3], + ?line outputv_errors_1([L,T]), + ?line outputv_errors_1([L|T]) + end), + outputv_errors_1(42), + ok. + +outputv_bad_types(Test) -> + Types = [-1,256,atom,42.0,{a,b,c},make_ref(),fun() -> 42 end, + [1|2],<<1:1>>,<<1:9>>,<<1:15>>], + _ = [Test(Type) || Type <- Types], ok. -fun_to_port_1(Term) -> - Port = open_port({spawn,outputv_drv}, []), +outputv_errors_1(Term) -> + Port = open_port({spawn_driver,outputv_drv}, []), {'EXIT',{badarg,_}} = (catch port_command(Port, Term)), port_close(Port). -build_io_list(0) -> []; -build_io_list(1) -> [7]; -build_io_list(N) -> - L = build_io_list(N div 2), - case N rem 2 of - 0 -> [L|L]; - 1 -> [7,L|L] - end. - - - outputv_echo(doc) -> ["Test echoing data with a driver that supports outputv."]; outputv_echo(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:minutes(10)), -- cgit v1.2.3 From 7f8b2825eed8e45b1cabc766bc7f6fce78700073 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 5 May 2011 16:02:28 +0200 Subject: Another halfword is_same-bug for ETS ordered_set This one is less likely to provoke error as one of the terms is always NULL-based. --- erts/emulator/beam/erl_db_tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index a59c0c258d..63cc311181 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -2694,7 +2694,7 @@ static Sint do_cmp_partly_bound(Eterm a, Eterm b, Eterm* b_base, int *done) while (1) { if ((j = do_cmp_partly_bound(*aa++, *bb++, b_base, done)) != 0 || *done) return j; - if (*aa==*bb) + if (is_same(*aa, NULL, *bb, b_base)) return 0; if (is_not_list(*aa) || is_not_list(*bb)) return do_cmp_partly_bound(*aa, *bb, b_base, done); -- cgit v1.2.3 From 913f72a8c9e1827c812c4795c76a0e57b5ac0eeb Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Wed, 4 May 2011 18:13:49 +0200 Subject: erts_printf %R for relative ets-terms in halfword-vm Conflicts: erts/emulator/beam/erl_printf_term.c --- erts/emulator/beam/big.c | 6 +- erts/emulator/beam/big.h | 4 +- erts/emulator/beam/erl_printf_term.c | 101 ++++++++++++++---------------- erts/emulator/beam/erl_printf_term.h | 4 +- erts/include/internal/erl_printf_format.h | 2 +- erts/lib_src/common/erl_printf_format.c | 29 +++++---- 6 files changed, 71 insertions(+), 75 deletions(-) diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index f47f5a9c0c..d18de9ae5d 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -1588,7 +1588,7 @@ big_to_double(Wterm x, double* resp) /* ** Estimate the number of decimal digits (include sign) */ -int big_decimal_estimate(Eterm x) +int big_decimal_estimate(Wterm x) { Eterm* xp = big_val(x); int lg = I_lg(BIG_V(xp), BIG_SIZE(xp)); @@ -1602,7 +1602,7 @@ int big_decimal_estimate(Eterm x) ** Convert a bignum into a string of decimal numbers */ -static void write_big(Eterm x, void (*write_func)(void *, char), void *arg) +static void write_big(Wterm x, void (*write_func)(void *, char), void *arg) { Eterm* xp = big_val(x); ErtsDigit* dx = BIG_V(xp); @@ -1681,7 +1681,7 @@ write_string(void *arg, char c) *(--(*((char **) arg))) = c; } -char *erts_big_to_string(Eterm x, char *buf, Uint buf_sz) +char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz) { char *big_str = buf + buf_sz - 1; *big_str = '\0'; diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index f28a390aea..2afc37004f 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -114,9 +114,9 @@ typedef Uint dsize_t; /* Vector size type */ #endif -int big_decimal_estimate(Eterm); +int big_decimal_estimate(Wterm); Eterm erts_big_to_list(Eterm, Eterm**); -char *erts_big_to_string(Eterm x, char *buf, Uint buf_sz); +char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz); Eterm small_times(Sint, Sint, Eterm*); diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index b71404fd27..34da9cab84 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -114,13 +114,13 @@ do { \ /* return 0 if list is not a non-empty flat list of printable characters */ static int -is_printable_string(Eterm list) +is_printable_string(Eterm list, Eterm* base) { int len = 0; int c; while(is_list(list)) { - Eterm* consp = list_val(list); + Eterm* consp = list_val_rel(list, base); Eterm hd = CAR(consp); if (!is_byte(hd)) @@ -226,17 +226,20 @@ static int print_atom_name(fmtfn_t fn, void* arg, Eterm atom, long *dcount) #define PRT_LAST_ARRAY_ELEMENT ((Eterm) 7) /* Note! Must be last... */ static int -print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) +print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount, + Eterm* obj_base) /* ignored if !HALFWORD_HEAP */ { DECLARE_WSTACK(s); int res; int i; Eterm val; Uint32 *ref_num; + union { + UWord word; + Eterm* ptr; + }popped; Eterm* nobj; -#if HALFWORD_HEAP - UWord wobj; -#endif + Wterm wobj; res = 0; @@ -258,18 +261,17 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) PRINT_CHAR(res, fn, arg, '}'); goto L_outer_loop; default: -#if HALFWORD_HEAP - obj = (Eterm) (wobj = WSTACK_POP(s)); -#else - obj = WSTACK_POP(s); -#endif + popped.word = WSTACK_POP(s); + switch (val) { case PRT_TERM: + obj = (Eterm) popped.word; break; case PRT_ONE_CONS: + obj = (Eterm) popped.word; L_print_one_cons: { - Eterm* cons = list_val(obj); + Eterm* cons = list_val_rel(obj, obj_base); Eterm tl; obj = CAR(cons); @@ -288,27 +290,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } break; case PRT_LAST_ARRAY_ELEMENT: - { -#if HALFWORD_HEAP - Eterm* ptr = (Eterm *) wobj; -#else - Eterm* ptr = (Eterm *) obj; -#endif - obj = *ptr; - } + obj = *popped.ptr; break; default: /* PRT_LAST_ARRAY_ELEMENT+1 and upwards */ - { -#if HALFWORD_HEAP - Eterm* ptr = (Eterm *) wobj; -#else - Eterm* ptr = (Eterm *) obj; -#endif - obj = *ptr++; - WSTACK_PUSH(s, (UWord) ptr); - WSTACK_PUSH(s, val-1); - WSTACK_PUSH(s, PRT_COMMA); - } + obj = *popped.ptr; + WSTACK_PUSH(s, (UWord) (popped.ptr + 1)); + WSTACK_PUSH(s, val-1); + WSTACK_PUSH(s, PRT_COMMA); break; } break; @@ -325,8 +313,12 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) PRINT_CHAR(res, fn, arg, '>'); goto L_done; } - - switch (tag_val_def(obj)) { +#if HALFWORD_HEAP + wobj = is_immed(obj) ? (Wterm)obj : rterm2wterm(obj, obj_base); +#else + wobj = (Wterm)obj; +#endif + switch (tag_val_def(wobj)) { case NIL_DEF: PRINT_STRING(res, fn, arg, "[]"); break; @@ -348,13 +340,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) int print_res; char def_buf[64]; char *buf, *big_str; - Uint sz = (Uint) big_decimal_estimate(obj); + Uint sz = (Uint) big_decimal_estimate(wobj); sz++; if (sz <= 64) buf = &def_buf[0]; else buf = erts_alloc(ERTS_ALC_T_TMP, sz); - big_str = erts_big_to_string(obj, buf, sz); + big_str = erts_big_to_string(wobj, buf, sz); print_res = erts_printf_string(fn, arg, big_str); if (buf != &def_buf[0]) erts_free(ERTS_ALC_T_TMP, (void *) buf); @@ -369,9 +361,9 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) case EXTERNAL_REF_DEF: PRINT_STRING(res, fn, arg, "#Ref<"); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) ref_channel_no(obj)); - ref_num = ref_numbers(obj); - for (i = ref_no_of_numbers(obj)-1; i >= 0; i--) { + (unsigned long) ref_channel_no(wobj)); + ref_num = ref_numbers(wobj); + for (i = ref_no_of_numbers(wobj)-1; i >= 0; i--) { PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, (unsigned long) ref_num[i]); } @@ -381,30 +373,30 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) case EXTERNAL_PID_DEF: PRINT_CHAR(res, fn, arg, '<'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) pid_channel_no(obj)); + (unsigned long) pid_channel_no(wobj)); PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) pid_number(obj)); + (unsigned long) pid_number(wobj)); PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) pid_serial(obj)); + (unsigned long) pid_serial(wobj)); PRINT_CHAR(res, fn, arg, '>'); break; case PORT_DEF: case EXTERNAL_PORT_DEF: PRINT_STRING(res, fn, arg, "#Port<"); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) port_channel_no(obj)); + (unsigned long) port_channel_no(wobj)); PRINT_CHAR(res, fn, arg, '.'); PRINT_ULONG(res, fn, arg, 'u', 0, 1, - (unsigned long) port_number(obj)); + (unsigned long) port_number(wobj)); PRINT_CHAR(res, fn, arg, '>'); break; case LIST_DEF: - if (is_printable_string(obj)) { + if (is_printable_string(obj, obj_base)) { int c; PRINT_CHAR(res, fn, arg, '"'); - nobj = list_val(obj); + nobj = list_val_rel(obj, obj_base); while (1) { if ((*dcount)-- <= 0) goto L_done; @@ -418,7 +410,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } if (is_not_list(*nobj)) break; - nobj = list_val(*nobj); + nobj = list_val_rel(*nobj, obj_base); } PRINT_CHAR(res, fn, arg, '"'); } else { @@ -428,7 +420,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } break; case TUPLE_DEF: - nobj = tuple_val(obj); /* pointer to arity */ + nobj = tuple_val(wobj); /* pointer to arity */ i = arityval(*nobj); /* arity */ PRINT_CHAR(res, fn, arg, '{'); WSTACK_PUSH(s,PRT_CLOSE_TUPLE); @@ -440,13 +432,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; case FLOAT_DEF: { FloatDef ff; - GET_DOUBLE(obj, ff); + GET_DOUBLE(wobj, ff); PRINT_DOUBLE(res, fn, arg, 'e', 6, 0, ff.fd); } break; case BINARY_DEF: { - ProcBin* pb = (ProcBin *) binary_val(obj); + ProcBin* pb = (ProcBin *) binary_val(wobj); if (pb->size == 1) PRINT_STRING(res, fn, arg, "<<1 byte>>"); else { @@ -458,7 +450,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; case EXPORT_DEF: { - Export* ep = *((Export **) (export_val(obj) + 1)); + Export* ep = *((Export **) (export_val(wobj) + 1)); Atom* module = atom_tab(atom_val(ep->code[0])); Atom* name = atom_tab(atom_val(ep->code[1])); @@ -474,7 +466,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; case FUN_DEF: { - ErlFunThing *funp = (ErlFunThing *) fun_val(obj); + ErlFunThing *funp = (ErlFunThing *) fun_val(wobj); Atom *ap = atom_tab(atom_val(funp->fe->module)); PRINT_STRING(res, fn, arg, "#Fun<"); @@ -490,7 +482,7 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) break; default: PRINT_STRING(res, fn, arg, "'); break; } @@ -503,9 +495,10 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) } int -erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision) +erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision, + unsigned long* term_base) { - int res = print_term(fn, arg, (Uint) term, &precision); + int res = print_term(fn, arg, (Eterm)term, &precision, (Eterm*)term_base); if (res < 0) return res; if (precision <= 0) diff --git a/erts/emulator/beam/erl_printf_term.h b/erts/emulator/beam/erl_printf_term.h index 4f76028396..4ba22f12de 100644 --- a/erts/emulator/beam/erl_printf_term.h +++ b/erts/emulator/beam/erl_printf_term.h @@ -21,6 +21,6 @@ #define ERL_PRINTF_TERM_H__ #include "erl_printf_format.h" -int erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision); - +int erts_printf_term(fmtfn_t fn, void* arg, unsigned long term, long precision, + unsigned long* term_base); #endif diff --git a/erts/include/internal/erl_printf_format.h b/erts/include/internal/erl_printf_format.h index 45818079ea..400cc7dafd 100644 --- a/erts/include/internal/erl_printf_format.h +++ b/erts/include/internal/erl_printf_format.h @@ -40,7 +40,7 @@ extern int erts_printf_ulong(fmtfn_t, void*, char, int, int, unsigned long); extern int erts_printf_slong(fmtfn_t, void*, char, int, int, signed long); extern int erts_printf_double(fmtfn_t, void *, char, int, int, double); -extern int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long); +extern int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long, unsigned long*); #endif diff --git a/erts/lib_src/common/erl_printf_format.c b/erts/lib_src/common/erl_printf_format.c index 968d563325..fba3fd723c 100644 --- a/erts/lib_src/common/erl_printf_format.c +++ b/erts/lib_src/common/erl_printf_format.c @@ -25,7 +25,7 @@ * width: [0-9]+ | '*' * precision: [0-9]+ | '*' * length: hh | h | l | ll | L | j | t | b - * conversion: d,i | o,u,x,X | e,E | f,F | g,G | a,A | c | s | T | + * conversion: d,i | o,u,x,X | e,E | f,F | g,G | a,A | c | s | T | R | * p | n | % * sz: 8 | 16 | 32 | 64 | p | e */ @@ -101,7 +101,7 @@ #endif #define FMTC_d 0x0000 -#define FMTC_i 0x0001 +#define FMTC_R 0x0001 #define FMTC_o 0x0002 #define FMTC_u 0x0003 #define FMTC_x 0x0004 @@ -165,7 +165,7 @@ static char heX[] = "0123456789ABCDEF"; #define SIGN(X) ((X) > 0 ? 1 : ((X) < 0 ? -1 : 0)) #define USIGN(X) ((X) == 0 ? 0 : 1) -int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long) = NULL; +int (*erts_printf_eterm_func)(fmtfn_t, void*, unsigned long, long, unsigned long*) = NULL; static int noop_fn(void *vfp, char* buf, size_t len) @@ -183,8 +183,8 @@ static int fmt_fld(fmtfn_t fn,void* arg, int len; /* format the prefix */ - if ((sign || (fmt & (FMTF_sgn|FMTF_blk))) && - (((fmt & FMTC_MASK) == FMTC_d) || ((fmt & FMTC_MASK) == FMTC_i))) { + if ((sign || (fmt & (FMTF_sgn|FMTF_blk))) + && (fmt & FMTC_MASK) == FMTC_d) { if (sign < 0) *pp++ = '-'; else if ((fmt & FMTF_sgn)) @@ -245,7 +245,6 @@ static int fmt_long(fmtfn_t fn,void* arg,int sign,unsigned long uval, switch(fmt & FMTC_MASK) { case FMTC_d: - case FMTC_i: case FMTC_u: break; case FMTC_o: @@ -298,7 +297,6 @@ static int fmt_long_long(fmtfn_t fn,void* arg,int sign, switch(fmt & FMTC_MASK) { case FMTC_d: - case FMTC_i: case FMTC_u: break; case FMTC_o: @@ -622,7 +620,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) /* specifier */ switch(*ptr) { case 'd': ptr++; fmt |= FMTC_d; break; - case 'i': ptr++; fmt |= FMTC_i; break; + case 'i': ptr++; fmt |= FMTC_d; break; case 'o': ptr++; fmt |= FMTC_o; break; case 'u': ptr++; fmt |= FMTC_u; break; case 'x': ptr++; fmt |= FMTC_x; break; @@ -637,6 +635,7 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) case 'p': ptr++; fmt |= FMTC_p; break; case 'n': ptr++; fmt |= FMTC_n; break; case 'T': ptr++; fmt |= FMTC_T; break; + case 'R': ptr++; fmt |= FMTC_R; break; case '%': FMT(fn,arg,ptr,1,count); ptr++; @@ -650,7 +649,6 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) switch(fmt & FMTC_MASK) { case FMTC_d: - case FMTC_i: switch(fmt & FMTL_MASK) { case FMTL_hh: { signed char tval = (signed char) va_arg(ap,int); @@ -814,9 +812,12 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) default: *va_arg(ap,int*) = count; break; } break; - case FMTC_T: { + case FMTC_T: /* Eterm */ + case FMTC_R: { /* Eterm, Eterm* base (base ignored if !HALFWORD_HEAP) */ long prec; unsigned long eterm; + unsigned long* eterm_base; + if (!erts_printf_eterm_func) return -EINVAL; if (precision < 0) @@ -826,14 +827,16 @@ int erts_printf_format(fmtfn_t fn, void* arg, char* fmt, va_list ap) else prec = (long) precision; eterm = va_arg(ap, unsigned long); + eterm_base = ((fmt & FMTC_MASK) == FMTC_R) ? + va_arg(ap, unsigned long*) : NULL; if (width > 0 && !(fmt & FMTF_adj)) { - res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec); + res = (*erts_printf_eterm_func)(noop_fn, NULL, eterm, prec, eterm_base); if (res < 0) return res; if (width > res) BLANKS(fn, arg, width - res, count); } - res = (*erts_printf_eterm_func)(fn, arg, eterm, prec); + res = (*erts_printf_eterm_func)(fn, arg, eterm, prec, eterm_base); if (res < 0) return res; count += res; @@ -924,7 +927,7 @@ erts_printf_slong(fmtfn_t fn, void *arg, char conv, int pad, int width, unsigned long ul_val; switch (conv) { case 'd': fmt |= FMTC_d; break; - case 'i': fmt |= FMTC_i; break; + case 'i': fmt |= FMTC_d; break; case 'o': fmt |= FMTC_o; break; case 'x': fmt |= FMTC_x; break; case 'X': fmt |= FMTC_X; break; -- cgit v1.2.3 From 9b1910d81ae53a4141ab84be6e1b4dd92bf3dde0 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 6 May 2011 15:50:32 +0200 Subject: ETS usage of erts_printf %R --- erts/emulator/beam/erl_db_hash.c | 9 +++++- erts/emulator/beam/erl_db_tree.c | 60 +++++++++++++++++++++++----------------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 9ef990cc4f..694348e31d 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -2085,7 +2085,14 @@ static void db_print_hash(int to, void *to_arg, int show, DbTable *tbl) while(list != 0) { if (list->hvalue == INVALID_HASH) erts_print(to, to_arg, "*"); - erts_print(to, to_arg, "%T", make_tuple(list->dbterm.tpl)); + if (tb->common.compress) { + Eterm key = GETKEY(tb, list->dbterm.tpl); + erts_print(to, to_arg, "key=%R", key, list->dbterm.tpl); + } + else { + Eterm obj = make_tuple_rel(list->dbterm.tpl,list->dbterm.tpl); + erts_print(to, to_arg, "%R", obj, list->dbterm.tpl); + } if (list->next != 0) erts_print(to, to_arg, ","); list = list->next; diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index 63cc311181..eb77d1281a 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -179,7 +179,7 @@ static ERTS_INLINE TreeDbTerm* replace_dbterm(DbTableTree *tb, TreeDbTerm* old, static TreeDbTerm *traverse_until(TreeDbTerm *t, int *current, int to); static void check_slot_pos(DbTableTree *tb); static void check_saved_stack(DbTableTree *tb); -static int check_table_tree(TreeDbTerm *t); +static int check_table_tree(DbTableTree* tb, TreeDbTerm *t); #define TREE_DEBUG #endif @@ -194,8 +194,8 @@ static int check_table_tree(TreeDbTerm *t); ** Debugging dump */ -static void do_dump_tree2(int to, void *to_arg, int show, TreeDbTerm *t, - int offset); +static void do_dump_tree2(DbTableTree*, int to, void *to_arg, int show, + TreeDbTerm *t, int offset); #else @@ -1730,6 +1730,7 @@ static int db_select_delete_tree(Process *p, DbTable *tbl, ** Other interface routines (not directly coupled to one bif) */ + /* Display tree contents (for dump) */ static void db_print_tree(int to, void *to_arg, int show, @@ -1740,7 +1741,7 @@ static void db_print_tree(int to, void *to_arg, if (show) erts_print(to, to_arg, "\nTree data dump:\n" "------------------------------------------------\n"); - do_dump_tree2(to, to_arg, show, tb->root, 0); + do_dump_tree2(&tbl->tree, to, to_arg, show, tb->root, 0); if (show) erts_print(to, to_arg, "\n" "------------------------------------------------\n"); @@ -2742,7 +2743,7 @@ static Sint cmp_partly_bound(Eterm partly_bound_key, Eterm bound_key, Eterm* bk_ erts_fprintf(stderr," > "); else erts_fprintf(stderr," == "); - erts_fprintf(stderr,"%T\n",bound_key); // HALFWORD BUG: printing rterm + erts_fprintf(stderr,"%R\n", bound_key, bk_base); #endif return ret; } @@ -3084,19 +3085,28 @@ static int doit_select_delete(DbTableTree *tb, TreeDbTerm *this, void *ptr, } #ifdef TREE_DEBUG -static void do_dump_tree2(int to, void *to_arg, int show, TreeDbTerm *t, - int offset) +static void do_dump_tree2(DbTableTree* tb, int to, void *to_arg, int show, + TreeDbTerm *t, int offset) { if (t == NULL) - return 0; - do_dump_tree2(to, to_arg, show, t->right, offset + 4); + return; + do_dump_tree2(tb, to, to_arg, show, t->right, offset + 4); if (show) { - erts_print(to, to_arg, "%*s%T (addr = %p, bal = %d)\n" - offset, "", make_tuple(t->dbterm.tpl), + const char* prefix; + Eterm term; + if (tb->common.compress) { + prefix = "key="; + term = GETKEY(tb, t->dbterm.tpl); + } + else { + prefix = ""; + term = make_tuple_rel(t->dbterm.tpl,t->dbterm.tpl); + } + erts_print(to, to_arg, "%*s%s%R (addr = %p, bal = %d)\n", + offset, "", prefix, term, t->dbterm.tpl, t, t->balance); } - do_dump_tree2(to, to_arg, show, t->left, offset + 4); - return sum; + do_dump_tree2(tb, to, to_arg, show, t->left, offset + 4); } #endif @@ -3106,7 +3116,7 @@ static void do_dump_tree2(int to, void *to_arg, int show, TreeDbTerm *t, void db_check_table_tree(DbTable *tbl) { DbTableTree *tb = &tbl->tree; - check_table_tree(tb->root); + check_table_tree(tb, tb->root); check_saved_stack(tb); check_slot_pos(tb); } @@ -3137,7 +3147,7 @@ static void check_slot_pos(DbTableTree *tb) "element position %d is really 0x%08X, when stack says " "it's 0x%08X\n", tb->stack.slot, t, tb->stack.array[tb->stack.pos - 1]); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); } } @@ -3152,14 +3162,14 @@ static void check_saved_stack(DbTableTree *tb) if (t != stack->array[0]) { erts_fprintf(stderr,"tb->stack[0] is 0x%08X, should be 0x%08X\n", stack->array[0], t); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); return; } while (n < stack->pos) { if (t == NULL) { erts_fprintf(stderr, "NULL pointer in tree when stack not empty," " stack depth is %d\n", n); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); return; } n++; @@ -3173,28 +3183,26 @@ static void check_saved_stack(DbTableTree *tb) "represent child pointer in tree!" "(left == 0x%08X, right == 0x%08X\n", n, tb->stack[n], t->left, t->right); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, tb->root, 0); return; } } } } -static int check_table_tree(TreeDbTerm *t) +static int check_table_tree(DbTableTree* tb, TreeDbTerm *t) { int lh, rh; if (t == NULL) return 0; - lh = check_table_tree(t->left); - rh = check_table_tree(t->right); + lh = check_table_tree(tb, t->left); + rh = check_table_tree(tb, t->right); if ((rh - lh) != t->balance) { erts_fprintf(stderr, "Invalid tree balance for this node:\n"); - erts_fprintf(stderr,"balance = %d, left = 0x%08X, right = 0x%08X\n" - "data = %T", - t->balance, t->left, t->right, - make_tuple(t->dbterm.tpl)); + erts_fprintf(stderr,"balance = %d, left = 0x%08X, right = 0x%08X\n", + t->balance, t->left, t->right); erts_fprintf(stderr,"\nDump:\n---------------------------------\n"); - do_dump_tree2(ERTS_PRINT_STDERR, NULL, 1, t, 0); + do_dump_tree2(tb, ERTS_PRINT_STDERR, NULL, 1, t, 0); erts_fprintf(stderr,"\n---------------------------------\n"); } return ((rh > lh) ? rh : lh) + 1; -- cgit v1.2.3 From bd5e9c5746eb02666e8c6ae7af3f4dff514e3677 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 6 May 2011 15:15:43 +0200 Subject: Types and specifications have been modified and added --- lib/kernel/include/inet.hrl | 15 +- lib/kernel/src/application.erl | 172 ++++++++++++----- lib/kernel/src/auth.erl | 16 +- lib/kernel/src/disk_log.erl | 252 +++++++++++++++++++------ lib/kernel/src/disk_log.hrl | 24 ++- lib/kernel/src/erl_boot_server.erl | 28 ++- lib/kernel/src/erl_ddll.erl | 49 +++-- lib/kernel/src/error_handler.erl | 13 +- lib/kernel/src/error_logger.erl | 79 +++++--- lib/kernel/src/file.erl | 375 ++++++++++++++++++++++++++----------- lib/kernel/src/gen_sctp.erl | 148 ++++++++++++++- lib/kernel/src/gen_tcp.erl | 82 +++++++- lib/kernel/src/gen_udp.erl | 52 ++++- lib/kernel/src/global.erl | 92 ++++++--- lib/kernel/src/global_group.erl | 58 ++++-- lib/kernel/src/global_search.erl | 5 +- lib/kernel/src/heart.erl | 10 +- lib/kernel/src/inet.erl | 96 +++++++--- lib/kernel/src/inet_res.erl | 176 ++++++++++++++++- lib/kernel/src/inet_udp.erl | 6 +- lib/kernel/src/net_adm.erl | 41 ++-- lib/kernel/src/net_kernel.erl | 37 ++++ lib/kernel/src/os.erl | 23 ++- lib/kernel/src/pg2.erl | 68 +++---- lib/kernel/src/rpc.erl | 192 ++++++++++++++++--- lib/kernel/src/seq_trace.erl | 44 +++-- lib/kernel/src/wrap_log_reader.erl | 35 ++-- 27 files changed, 1721 insertions(+), 467 deletions(-) diff --git a/lib/kernel/include/inet.hrl b/lib/kernel/include/inet.hrl index 929b2ee294..3e64d4bb79 100644 --- a/lib/kernel/include/inet.hrl +++ b/lib/kernel/include/inet.hrl @@ -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 @@ -19,18 +19,11 @@ %% This record is returned by inet:gethostbyaddr/2 and inet:gethostbyname/2. --type hostname() :: atom() | string(). --type ip4_address() :: {0..255,0..255,0..255,0..255}. --type ip6_address() :: {0..65535,0..65535,0..65535,0..65535, - 0..65535,0..65535,0..65535,0..65535}. --type ip_address() :: ip4_address() | ip6_address(). --type ip_port() :: 0..65535. - -record(hostent, { - h_name :: hostname(), %% offical name of host - h_aliases = [] :: [hostname()], %% alias list + h_name :: inet:hostname(), %% offical name of host + h_aliases = [] :: [inet:hostname()], %% alias list h_addrtype :: 'inet' | 'inet6', %% host address type h_length :: non_neg_integer(), %% length of address - h_addr_list = [] :: [ip_address()] %% list of addresses from name server + h_addr_list = [] :: [inet:ip_address()]%% list of addresses from name server }). diff --git a/lib/kernel/src/application.erl b/lib/kernel/src/application.erl index 2a193affd4..fa3a4c3d36 100644 --- a/lib/kernel/src/application.erl +++ b/lib/kernel/src/application.erl @@ -32,20 +32,30 @@ %%%----------------------------------------------------------------- +-type start_type() :: 'normal' + | {'takeover', Node :: node()} + | {'failover', Node :: node()}. -type restart_type() :: 'permanent' | 'transient' | 'temporary'. --type application_opt() :: {'description', string()} - | {'vsn', string()} - | {'id', string()} - | {'modules', [atom() | {atom(), any()}]} - | {'registered', [atom()]} - | {'applications', [atom()]} - | {'included_applications', [atom()]} - | {'env', [{atom(), any()}]} - | {'start_phases', [{atom(), any()}] | 'undefined'} - | {'maxT', timeout()} % max timeout - | {'maxP', integer() | 'infinity'} % max processes - | {'mod', {atom(), any()}}. --type application_spec() :: {'application', atom(), [application_opt()]}. +-type application_opt() :: {'description', Description :: string()} + | {'vsn', Vsn :: string()} + | {'id', Id :: string()} + | {'modules', [(Module :: module()) | + {Module :: module(), Version :: term()}]} + | {'registered', Names :: [Name :: atom()]} + | {'applications', [Application :: atom()]} + | {'included_applications', [Application :: atom()]} + | {'env', [{Par :: atom(), Val :: term()}]} + | {'start_phases', + [{Phase :: atom(), PhaseArgs :: term()}] | 'undefined'} + | {'maxT', MaxT :: timeout()} % max timeout + | {'maxP', + MaxP :: pos_integer() | 'infinity'} % max processes + | {'mod', Start :: {Module :: module(), StartArgs :: term()}}. +-type application_spec() :: {'application', + Application :: atom(), + AppSpecKeys :: [application_opt()]}. + +-type(tuple_of(_T) :: tuple()). %%------------------------------------------------------------------ @@ -61,16 +71,29 @@ behaviour_info(_Other) -> %%% application_master. %%%----------------------------------------------------------------- --spec load(Application :: atom() | application_spec()) -> - 'ok' | {'error', term()}. +-spec load(AppDescr) -> 'ok' | {'error', Reason} when + AppDescr :: Application | (AppSpec :: application_spec()), + Application :: atom(), + Reason :: term(). load(Application) -> - load(Application, []). - --spec load(Application :: atom() | application_spec(), - Distributed :: any()) -> 'ok' | {'error', term()}. + load1(Application, []). + +-spec load(AppDescr, Distributed) -> 'ok' | {'error', Reason} when + AppDescr :: Application | (AppSpec :: application_spec()), + Application :: atom(), + Distributed :: {Application,Nodes} + | {Application,Time,Nodes} + | 'default', + Nodes :: [node() | tuple_of(node())], + Time :: pos_integer(), + Reason :: term(). load(Application, DistNodes) -> + load1(Application, DistNodes). + +%% Workaround due to specs. +load1(Application, DistNodes) -> case application_controller:load_application(Application) of ok when DistNodes =/= [] -> AppName = get_appl_name(Application), @@ -85,18 +108,24 @@ load(Application, DistNodes) -> Else end. --spec unload(Application :: atom()) -> 'ok' | {'error', term()}. +-spec unload(Application) -> 'ok' | {'error', Reason} when + Application :: atom(), + Reason :: term(). unload(Application) -> application_controller:unload_application(Application). --spec start(Application :: atom()) -> 'ok' | {'error', term()}. +-spec start(Application) -> 'ok' | {'error', Reason} when + Application :: atom(), + Reason :: term(). start(Application) -> start(Application, temporary). --spec start(Application :: atom() | application_spec(), - RestartType :: restart_type()) -> any(). +-spec start(Application, Type) -> 'ok' | {'error', Reason} when + Application :: atom(), + Type :: restart_type(), + Reason :: term(). start(Application, RestartType) -> case load(Application) of @@ -120,12 +149,18 @@ start_boot(Application) -> start_boot(Application, RestartType) -> application_controller:start_boot_application(Application, RestartType). --spec takeover(Application :: atom(), RestartType :: restart_type()) -> any(). +-spec takeover(Application, Type) -> 'ok' | {'error', Reason} when + Application :: atom(), + Type :: restart_type(), + Reason :: term(). takeover(Application, RestartType) -> dist_ac:takeover_application(Application, RestartType). --spec permit(Application :: atom(), Bool :: boolean()) -> 'ok' | {'error', term()}. +-spec permit(Application, Permission) -> 'ok' | {'error', Reason} when + Application :: atom(), + Permission :: boolean(), + Reason :: term(). permit(Application, Bool) -> case Bool of @@ -142,105 +177,146 @@ permit(Application, Bool) -> LocalResult end. --spec stop(Application :: atom()) -> 'ok' | {'error', term()}. +-spec stop(Application) -> 'ok' | {'error', Reason} when + Application :: atom(), + Reason :: term(). stop(Application) -> application_controller:stop_application(Application). --spec which_applications() -> [{atom(), string(), string()}]. +-spec which_applications() -> [{Application, Description, Vsn}] when + Application :: atom(), + Description :: string(), + Vsn :: string(). which_applications() -> application_controller:which_applications(). --spec which_applications(timeout()) -> [{atom(), string(), string()}]. +-spec which_applications(Timeout) -> [{Application, Description, Vsn}] when + Timeout :: timeout(), + Application :: atom(), + Description :: string(), + Vsn :: string(). which_applications(infinity) -> application_controller:which_applications(infinity); which_applications(Timeout) when is_integer(Timeout), Timeout>=0 -> application_controller:which_applications(Timeout). --spec loaded_applications() -> [{atom(), string(), string()}]. +-spec loaded_applications() -> [{Application, Description, Vsn}] when + Application :: atom(), + Description :: string(), + Vsn :: string(). loaded_applications() -> application_controller:loaded_applications(). --spec info() -> any(). +-spec info() -> term(). info() -> application_controller:info(). --spec set_env(Application :: atom(), Key :: atom(), Value :: any()) -> 'ok'. +-spec set_env(Application, Par, Val) -> 'ok' when + Application :: atom(), + Par :: atom(), + Val :: term(). set_env(Application, Key, Val) -> application_controller:set_env(Application, Key, Val). --spec set_env(Application :: atom(), Key :: atom(), - Value :: any(), Timeout :: timeout()) -> 'ok'. +-spec set_env(Application, Par, Val, Timeout) -> 'ok' when + Application :: atom(), + Par :: atom(), + Val :: term(), + Timeout :: timeout(). set_env(Application, Key, Val, infinity) -> application_controller:set_env(Application, Key, Val, infinity); set_env(Application, Key, Val, Timeout) when is_integer(Timeout), Timeout>=0 -> application_controller:set_env(Application, Key, Val, Timeout). --spec unset_env(atom(), atom()) -> 'ok'. +-spec unset_env(Application, Par) -> 'ok' when + Application :: atom(), + Par :: atom(). unset_env(Application, Key) -> application_controller:unset_env(Application, Key). --spec unset_env(atom(), atom(), timeout()) -> 'ok'. +-spec unset_env(Application, Par, Timeout) -> 'ok' when + Application :: atom(), + Par :: atom(), + Timeout :: timeout(). unset_env(Application, Key, infinity) -> application_controller:unset_env(Application, Key, infinity); unset_env(Application, Key, Timeout) when is_integer(Timeout), Timeout>=0 -> application_controller:unset_env(Application, Key, Timeout). --spec get_env(atom()) -> 'undefined' | {'ok', term()}. +-spec get_env(Par) -> 'undefined' | {'ok', Val} when + Par :: atom(), + Val :: term(). get_env(Key) -> application_controller:get_pid_env(group_leader(), Key). --spec get_env(atom(), atom()) -> 'undefined' | {'ok', term()}. +-spec get_env(Application, Par) -> 'undefined' | {'ok', Val} when + Application :: atom(), + Par :: atom(), + Val :: term(). get_env(Application, Key) -> application_controller:get_env(Application, Key). --spec get_all_env() -> [{atom(), any()}]. +-spec get_all_env() -> Env when + Env :: [{Par :: atom(), Val :: term()}]. get_all_env() -> application_controller:get_pid_all_env(group_leader()). --spec get_all_env(atom()) -> [{atom(), any()}]. +-spec get_all_env(Application) -> Env when + Application :: atom(), + Env :: [{Par :: atom(), Val :: term()}]. get_all_env(Application) -> application_controller:get_all_env(Application). --spec get_key(atom()) -> 'undefined' | {'ok', term()}. +-spec get_key(Key) -> 'undefined' | {'ok', Val} when + Key :: atom(), + Val :: term(). get_key(Key) -> application_controller:get_pid_key(group_leader(), Key). --spec get_key(atom(), atom()) -> 'undefined' | {'ok', term()}. +-spec get_key(Application, Key) -> 'undefined' | {'ok', Val} when + Application :: atom(), + Key :: atom(), + Val :: term(). get_key(Application, Key) -> application_controller:get_key(Application, Key). --spec get_all_key() -> 'undefined' | [] | {'ok', [{atom(),any()},...]}. +-spec get_all_key() -> [] | {'ok', Keys} when + Keys :: [{Key :: atom(),Val :: term()},...]. get_all_key() -> application_controller:get_pid_all_key(group_leader()). --spec get_all_key(atom()) -> 'undefined' | {'ok', [{atom(),any()},...]}. +-spec get_all_key(Application) -> 'undefined' | Keys when + Application :: atom(), + Keys :: {'ok', [{Key :: atom(),Val :: term()},...]}. get_all_key(Application) -> application_controller:get_all_key(Application). --spec get_application() -> 'undefined' | {'ok', atom()}. +-spec get_application() -> 'undefined' | {'ok', Application} when + Application :: atom(). get_application() -> application_controller:get_application(group_leader()). --spec get_application(Pid :: pid()) -> 'undefined' | {'ok', atom()} - ; (Module :: atom()) -> 'undefined' | {'ok', atom()}. +-spec get_application(PidOrModule) -> 'undefined' | {'ok', Application} when + PidOrModule :: (Pid :: pid()) | (Module :: module()), + Application :: atom(). get_application(Pid) when is_pid(Pid) -> case process_info(Pid, group_leader) of @@ -252,8 +328,8 @@ get_application(Pid) when is_pid(Pid) -> get_application(Module) when is_atom(Module) -> application_controller:get_application_module(Module). --spec start_type() -> 'undefined' | 'local' | 'normal' - | {'takeover', node()} | {'failover', node()}. +-spec start_type() -> StartType | 'undefined' | 'local' when + StartType :: start_type(). start_type() -> application_controller:start_type(group_leader()). diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index 5c7fe2421d..25c88a4e1d 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.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 @@ -57,7 +57,8 @@ start_link() -> %%--Deprecated interface------------------------------------------------ --spec is_auth(Node :: node()) -> 'yes' | 'no'. +-spec is_auth(Node) -> 'yes' | 'no' when + Node :: Node :: node(). is_auth(Node) -> case net_adm:ping(Node) of @@ -65,12 +66,15 @@ is_auth(Node) -> pang -> no end. --spec cookie() -> cookie(). +-spec cookie() -> Cookie when + Cookie :: cookie(). cookie() -> get_cookie(). --spec cookie(Cookies :: [cookie(),...] | cookie()) -> 'true'. +-spec cookie(TheCookie) -> 'true' when + TheCookie :: Cookie | [Cookie], + Cookie :: cookie(). cookie([Cookie]) -> set_cookie(Cookie); @@ -82,7 +86,9 @@ cookie(Cookie) -> node_cookie([Node, Cookie]) -> node_cookie(Node, Cookie). --spec node_cookie(Node :: node(), Cookie :: cookie()) -> 'yes' | 'no'. +-spec node_cookie(Node, Cookie) -> 'yes' | 'no' when + Node :: node(), + Cookie :: cookie(). node_cookie(Node, Cookie) -> set_cookie(Node, Cookie), diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 7f1b5f9ec6..9b8d2db437 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.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 @@ -70,9 +70,10 @@ %%% Contract type specifications %%%---------------------------------------------------------------------- +-opaque continuation() :: #continuation{}. + -type bytes() :: binary() | [byte()]. --type log() :: term(). % XXX: refine -type file_error() :: term(). % XXX: refine -type invalid_header() :: term(). % XXX: refine @@ -87,27 +88,30 @@ -type open_error_rsn() :: 'no_such_log' | {'badarg', term()} - | {'size_mismatch', dlog_size(), dlog_size()} - | {'arg_mismatch', dlog_optattr(), term(), term()} - | {'name_already_open', log()} - | {'open_read_write', log()} - | {'open_read_only', log()} - | {'need_repair', log()} - | {'not_a_log_file', string()} - | {'invalid_index_file', string()} + | {'size_mismatch', CurrentSize :: dlog_size(), + NewSize :: dlog_size()} + | {'arg_mismatch', OptionName :: dlog_optattr(), + CurrentValue :: term(), Value :: term()} + | {'name_already_open', Log :: log()} + | {'open_read_write', Log :: log()} + | {'open_read_only', Log :: log()} + | {'need_repair', Log :: log()} + | {'not_a_log_file', FileName :: file:filename()} + | {'invalid_index_file', FileName :: file:filename()} | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()} - | {'node_already_open', log()}. + | {'node_already_open', Log :: log()}. -type dist_error_rsn() :: 'nodedown' | open_error_rsn(). --type ret() :: {'ok', log()} - | {'repaired', log(), {'recovered', non_neg_integer()}, - {'badbytes', non_neg_integer()}}. +-type ret() :: {'ok', Log :: log()} + | {'repaired', Log :: log(), + {'recovered', Rec :: non_neg_integer()}, + {'badbytes', Bad :: non_neg_integer()}}. -type open_ret() :: ret() | {'error', open_error_rsn()}. -type dist_open_ret() :: {[{node(), ret()}], [{node(), {'error', dist_error_rsn()}}]}. --type all_open_ret() :: open_ret() | dist_open_ret(). --spec open(Args :: dlog_options()) -> all_open_ret(). +-spec open(ArgL) -> open_ret() | dist_open_ret() when + ArgL :: dlog_options(). open(A) -> disk_log_server:open(check_arg(A, #arg{options = A})). @@ -116,40 +120,57 @@ open(A) -> | {'full', log()} | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()}. --spec log(Log :: log(), Term :: term()) -> 'ok' | {'error', log_error_rsn()}. +-spec log(Log, Term) -> ok | {error, Reason :: log_error_rsn()} when + Log :: log(), + Term :: term(). log(Log, Term) -> req(Log, {log, term_to_binary(Term)}). --spec blog(Log :: log(), Bytes :: bytes()) -> 'ok' | {'error', log_error_rsn()}. +-spec blog(Log, Bytes) -> ok | {error, Reason :: log_error_rsn()} when + Log :: log(), + Bytes :: bytes(). blog(Log, Bytes) -> req(Log, {blog, check_bytes(Bytes)}). --spec log_terms(Log :: log(), Terms :: [term()]) -> 'ok' | {'error', term()}. +-spec log_terms(Log, TermList) -> ok | {error, Resaon :: log_error_rsn()} when + Log :: log(), + TermList :: [term()]. log_terms(Log, Terms) -> Bs = terms2bins(Terms), req(Log, {log, Bs}). --spec blog_terms(Log :: log(), Bytes :: [bytes()]) -> 'ok' | {'error', term()}. +-spec blog_terms(Log, BytesList) -> + ok | {error, Reason :: log_error_rsn()} when + Log :: log(), + BytesList :: [bytes()]. blog_terms(Log, Bytess) -> Bs = check_bytes_list(Bytess, Bytess), req(Log, {blog, Bs}). -type notify_ret() :: 'ok' | {'error', 'no_such_log'}. --spec alog(Log :: log(), Term :: term()) -> notify_ret(). +-spec alog(Log, Term) -> notify_ret() when + Log :: log(), + Term :: term(). alog(Log, Term) -> notify(Log, {alog, term_to_binary(Term)}). --spec alog_terms(Log :: log(), Terms :: [term()]) -> notify_ret(). +-spec alog_terms(Log, TermList) -> notify_ret() when + Log :: log(), + TermList :: [term()]. alog_terms(Log, Terms) -> Bs = terms2bins(Terms), notify(Log, {alog, Bs}). --spec balog(Log :: log(), Bytes :: bytes()) -> notify_ret(). +-spec balog(Log, Bytes) -> notify_ret() when + Log :: log(), + Bytes :: bytes(). balog(Log, Bytes) -> notify(Log, {balog, check_bytes(Bytes)}). --spec balog_terms(Log :: log(), Bytes :: [bytes()]) -> notify_ret(). +-spec balog_terms(Log, ByteList) -> notify_ret() when + Log :: log(), + ByteList :: [bytes()]. balog_terms(Log, Bytess) -> Bs = check_bytes_list(Bytess, Bytess), notify(Log, {balog, Bs}). @@ -157,18 +178,22 @@ balog_terms(Log, Bytess) -> -type close_error_rsn() ::'no_such_log' | 'nonode' | {'file_error', file:filename(), file_error()}. --spec close(Log :: log()) -> 'ok' | {'error', close_error_rsn()}. +-spec close(Log) -> 'ok' | {'error', close_error_rsn()} when + Log :: log(). close(Log) -> req(Log, close). -type lclose_error_rsn() :: 'no_such_log' | {'file_error', file:filename(), file_error()}. --spec lclose(Log :: log()) -> 'ok' | {'error', lclose_error_rsn()}. +-spec lclose(Log) -> 'ok' | {'error', lclose_error_rsn()} when + Log :: log(). lclose(Log) -> lclose(Log, node()). --spec lclose(Log :: log(), Node :: node()) -> 'ok' | {'error', lclose_error_rsn()}. +-spec lclose(Log, Node) -> 'ok' | {'error', lclose_error_rsn()} when + Log :: log(), + Node :: node(). lclose(Log, Node) -> lreq(Log, close, Node). @@ -178,29 +203,49 @@ lclose(Log, Node) -> | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()}. --spec truncate(Log :: log()) -> 'ok' | {'error', trunc_error_rsn()}. +-spec truncate(Log) -> 'ok' | {'error', trunc_error_rsn()} when + Log :: log(). truncate(Log) -> req(Log, {truncate, none, truncate, 1}). --spec truncate(Log :: log(), Head :: term()) -> 'ok' | {'error', trunc_error_rsn()}. +-spec truncate(Log, Head) -> 'ok' | {'error', trunc_error_rsn()} when + Log :: log(), + Head :: term(). truncate(Log, Head) -> req(Log, {truncate, {ok, term_to_binary(Head)}, truncate, 2}). --spec btruncate(Log :: log(), Head :: bytes()) -> 'ok' | {'error', trunc_error_rsn()}. +-spec btruncate(Log, BHead) -> 'ok' | {'error', trunc_error_rsn()} when + Log :: log(), + BHead :: bytes(). btruncate(Log, Head) -> req(Log, {truncate, {ok, check_bytes(Head)}, btruncate, 2}). --spec reopen(Log :: log(), Filename :: file:filename()) -> 'ok' | {'error', term()}. +-type reopen_error_rsn() :: no_such_log + | nonode + | {read_only_mode, log()} + | {blocked_log, log()} + | {same_file_name, log()} | + {invalid_index_file, file:filename()} + | {invalid_header, invalid_header()} + | {'file_error', file:filename(), file_error()}. + +-spec reopen(Log, File) -> 'ok' | {'error', reopen_error_rsn()} when + Log :: log(), + File :: file:filename(). reopen(Log, NewFile) -> req(Log, {reopen, NewFile, none, reopen, 2}). --spec reopen(Log :: log(), Filename :: file:filename(), Head :: term()) -> - 'ok' | {'error', term()}. +-spec reopen(Log, File, Head) -> 'ok' | {'error', reopen_error_rsn()} when + Log :: log(), + File :: file:filename(), + Head :: term(). reopen(Log, NewFile, NewHead) -> req(Log, {reopen, NewFile, {ok, term_to_binary(NewHead)}, reopen, 3}). --spec breopen(Log :: log(), Filename :: file:filename(), Head :: bytes()) -> - 'ok' | {'error', term()}. +-spec breopen(Log, File, BHead) -> 'ok' | {'error', reopen_error_rsn()} when + Log :: log(), + File :: file:filename(), + BHead :: bytes(). breopen(Log, NewFile, NewHead) -> req(Log, {reopen, NewFile, {ok, check_bytes(NewHead)}, breopen, 3}). @@ -210,21 +255,36 @@ breopen(Log, NewFile, NewHead) -> | {'invalid_header', invalid_header()} | {'file_error', file:filename(), file_error()}. --spec inc_wrap_file(Log :: log()) -> 'ok' | {'error', inc_wrap_error_rsn()}. +-spec inc_wrap_file(Log) -> 'ok' | {'error', inc_wrap_error_rsn()} when + Log :: log(). inc_wrap_file(Log) -> req(Log, inc_wrap_file). --spec change_size(Log :: log(), Size :: dlog_size()) -> 'ok' | {'error', term()}. +-spec change_size(Log, Size) -> 'ok' | {'error', Reason} when + Log :: log(), + Size :: dlog_size(), + Reason :: no_such_log | nonode | {read_only_mode, Log} + | {blocked_log, Log} + | {new_size_too_small, CurrentSize :: pos_integer()} + | {badarg, size} + | {file_error, file:filename(), file_error()}. change_size(Log, NewSize) -> req(Log, {change_size, NewSize}). --spec change_notify(Log :: log(), Pid :: pid(), Notify :: boolean()) -> - 'ok' | {'error', term()}. +-spec change_notify(Log, Owner, Notify) -> 'ok' | {'error', Reason} when + Log :: log(), + Owner :: pid(), + Notify :: boolean(), + Reason :: no_such_log | nonode | {blocked_log, Log} + | {badarg, notify} | {not_owner, Owner}. change_notify(Log, Pid, NewNotify) -> req(Log, {change_notify, Pid, NewNotify}). --spec change_header(Log :: log(), Head :: {atom(), term()}) -> - 'ok' | {'error', term()}. +-spec change_header(Log, Header) -> 'ok' | {'error', Reason} when + Log :: log(), + Header :: {head, dlog_head_opt()} | {head_func, mfa()}, + Reason :: no_such_log | nonode | {read_only_mode, Log} + | {blocked_log, Log} | {badarg, head}. change_header(Log, NewHead) -> req(Log, {change_header, NewHead}). @@ -232,17 +292,21 @@ change_header(Log, NewHead) -> | {'blocked_log', log()} | {'file_error', file:filename(), file_error()}. --spec sync(Log :: log()) -> 'ok' | {'error', sync_error_rsn()}. +-spec sync(Log) -> 'ok' | {'error', sync_error_rsn()} when + Log :: log(). sync(Log) -> req(Log, sync). -type block_error_rsn() :: 'no_such_log' | 'nonode' | {'blocked_log', log()}. --spec block(Log :: log()) -> 'ok' | {'error', block_error_rsn()}. +-spec block(Log) -> 'ok' | {'error', block_error_rsn()} when + Log :: log(). block(Log) -> block(Log, true). --spec block(Log :: log(), QueueLogRecords :: boolean()) -> 'ok' | {'error', term()}. +-spec block(Log, QueueLogRecords) -> 'ok' | {'error', block_error_rsn()} when + Log :: log(), + QueueLogRecords :: boolean(). block(Log, QueueLogRecords) -> req(Log, {block, QueueLogRecords}). @@ -250,19 +314,46 @@ block(Log, QueueLogRecords) -> | {'not_blocked', log()} | {'not_blocked_by_pid', log()}. --spec unblock(Log :: log()) -> 'ok' | {'error', unblock_error_rsn()}. +-spec unblock(Log) -> 'ok' | {'error', unblock_error_rsn()} when + Log :: log(). unblock(Log) -> req(Log, unblock). --spec format_error(Error :: term()) -> string(). +-spec format_error(Error) -> io_lib:chars() when + Error :: term(). format_error(Error) -> do_format_error(Error). --spec info(Log :: log()) -> [{atom(), any()}] | {'error', term()}. +-type dlog_info() :: {name, Log :: log()} + | {file, File :: file:filename()} + | {type, Type :: dlog_type()} + | {format, Format :: dlog_format()} + | {size, Size :: dlog_size()} + | {mode, Mode :: dlog_mode()} + | {owners, [{pid(), Notify :: boolean()}]} + | {users, Users :: non_neg_integer()} + | {status, Status :: + ok | {blocked, QueueLogRecords :: boolean()}} + | {node, Node :: node()} + | {distributed, Dist :: local | [node()]} + | {head, Head :: none | {head, term()} | mfa()} + | {no_written_items, NoWrittenItems ::non_neg_integer()} + | {full, Full :: boolean} + | {no_current_bytes, non_neg_integer()} + | {no_current_items, non_neg_integer()} + | {no_items, non_neg_integer()} + | {current_file, pos_integer()} + | {no_overflows, {SinceLogWasOpened :: non_neg_integer(), + SinceLastInfo :: non_neg_integer()}}. +-spec info(Log) -> InfoList | {'error', no_such_log} when + Log :: log(), + InfoList :: [dlog_info()]. info(Log) -> sreq(Log, info). --spec pid2name(Pid :: pid()) -> {'ok', log()} | 'undefined'. +-spec pid2name(Pid) -> {'ok', Log} | 'undefined' when + Pid :: pid(), + Log :: log(). pid2name(Pid) -> disk_log_server:start(), case ets:lookup(?DISK_LOG_PID_TABLE, Pid) of @@ -274,13 +365,31 @@ pid2name(Pid) -> %% It retuns a {Cont2, ObjList} | eof | {error, Reason} %% The initial continuation is the atom 'start' --spec chunk(Log :: log(), Cont :: any()) -> - {'error', term()} | 'eof' | {any(), [any()]} | {any(), [any()], integer()}. +-type chunk_error_rsn() :: no_such_log + | {format_external, log()} + | {blocked_log, log()} + | {badarg, continuation} + | {not_internal_wrap, log()} + | {corrupt_log_file, FileName :: file:filename()} + | {file_error, file:filename(), file_error()}. + +-type chunk_ret() :: {Continuation2 :: continuation(), Terms :: [term()]} + | {Continuation2 :: continuation(), + Terms :: [term()], + Badbytes :: non_neg_integer()} + | eof + | {error, Reason :: chunk_error_rsn()}. + +-spec chunk(Log, Continuation) -> chunk_ret() when + Log :: log(), + Continuation :: start | continuation(). chunk(Log, Cont) -> chunk(Log, Cont, infinity). --spec chunk(Log :: log(), Cont :: any(), N :: pos_integer() | 'infinity') -> - {'error', term()} | 'eof' | {any(), [any()]} | {any(), [any()], integer()}. +-spec chunk(Log, Continuation, N) -> chunk_ret() when + Log :: log(), + Continuation :: start | continuation(), + N :: pos_integer() | infinity. chunk(Log, Cont, infinity) -> %% There cannot be more than ?MAX_CHUNK_SIZE terms in a chunk. ichunk(Log, Cont, ?MAX_CHUNK_SIZE); @@ -346,13 +455,24 @@ ichunk_bad_end([B | Bs], Mode, Log, C, Bad, A) -> ichunk_bad_end(Bs, Mode, Log, C, Bad, [T | A]) end. --spec bchunk(Log :: log(), Cont :: any()) -> - {'error', any()} | 'eof' | {any(), [binary()]} | {any(), [binary()], integer()}. +-type bchunk_ret() :: {Continuation2 :: continuation(), + Binaries :: [binary()]} + | {Continuation2 :: continuation(), + Binaries :: [binary()], + Badbytes :: non_neg_integer()} + | eof + | {error, Reason :: chunk_error_rsn()}. + +-spec bchunk(Log, Continuation) -> bchunk_ret() when + Log :: log(), + Continuation :: start | continuation(). bchunk(Log, Cont) -> bchunk(Log, Cont, infinity). --spec bchunk(Log :: log(), Cont :: any(), N :: 'infinity' | pos_integer()) -> - {'error', any()} | 'eof' | {any(), [binary()]} | {any(), [binary()], integer()}. +-spec bchunk(Log, Continuation, N) -> bchunk_ret() when + Log :: log(), + Continuation :: start | continuation(), + N :: pos_integer() | infinity. bchunk(Log, Cont, infinity) -> %% There cannot be more than ?MAX_CHUNK_SIZE terms in a chunk. bichunk(Log, Cont, ?MAX_CHUNK_SIZE); @@ -375,8 +495,14 @@ bichunk_end({C = #continuation{}, R, Bad}) -> bichunk_end(R) -> R. --spec chunk_step(Log :: log(), Cont :: any(), N :: integer()) -> - {'ok', any()} | {'error', term()}. +-spec chunk_step(Log, Continuation, Step) -> + {'ok', any()} | {'error', Reason} when + Log :: log(), + Continuation :: start | continuation(), + Step :: integer(), + Reason :: no_such_log | end_of_log | {format_external, Log} + | {blocked_log, Log} | {badarg, continuation} + | {file_error, file:filename(), file_error()}. chunk_step(Log, Cont, N) when is_integer(N) -> ichunk_step(Log, Cont, N). @@ -387,14 +513,18 @@ ichunk_step(_Log, More, N) when is_record(More, continuation) -> ichunk_step(_Log, _, _) -> {error, {badarg, continuation}}. --spec chunk_info(More :: any()) -> - [{'node', node()},...] | {'error', {'no_continuation', any()}}. +-spec chunk_info(Continuation) -> InfoList | {error, Reason} when + Continuation :: continuation(), + InfoList :: [{node, Node :: node()}, ...], + Reason :: {no_continuation, Continuation}. chunk_info(More = #continuation{}) -> [{node, node(More#continuation.pid)}]; chunk_info(BadCont) -> {error, {no_continuation, BadCont}}. --spec accessible_logs() -> {[_], [_]}. +-spec accessible_logs() -> {[LocalLog], [DistributedLog]} when + LocalLog :: log(), + DistributedLog :: log(). accessible_logs() -> disk_log_server:accessible_logs(). diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 9a94d4d3b9..259967650f 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -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 @@ -53,18 +53,34 @@ %% Types -- alphabetically %%------------------------------------------------------------------------ +-type dlog_byte() :: [dlog_byte()] | byte(). -type dlog_format() :: 'external' | 'internal'. -type dlog_format_type() :: 'halt_ext' | 'halt_int' | 'wrap_ext' | 'wrap_int'. -type dlog_head() :: 'none' | {'ok', binary()} | mfa(). +-type dlog_head_opt() :: none | term() | binary() | [dlog_byte()]. +-type log() :: term(). % XXX: refine -type dlog_mode() :: 'read_only' | 'read_write'. -type dlog_name() :: atom() | string(). -type dlog_optattr() :: 'name' | 'file' | 'linkto' | 'repair' | 'type' | 'format' | 'size' | 'distributed' | 'notify' | 'head' | 'head_func' | 'mode'. --type dlog_options() :: [{dlog_optattr(), any()}]. +-type dlog_option() :: {name, Log :: log()} + | {file, FileName :: file:filename()} + | {linkto, LinkTo :: none | pid()} + | {repair, Repair :: true | false | truncate} + | {type, Type :: dlog_type} + | {format, Format :: dlog_format()} + | {size, Size :: dlog_size()} + | {distributed, Nodes :: [node()]} + | {notify, boolean()} + | {head, Head :: dlog_head_opt()} + | {head_func, mfa()} + | {mode, Mode :: dlog_mode()}. +-type dlog_options() :: [dlog_option()]. -type dlog_repair() :: 'truncate' | boolean(). -type dlog_size() :: 'infinity' | pos_integer() - | {pos_integer(), pos_integer()}. + | {MaxNoBytes :: pos_integer(), + MaxNoFiles :: pos_integer()}. -type dlog_status() :: 'ok' | {'blocked', 'false' | [_]}. %QueueLogRecords -type dlog_type() :: 'halt' | 'wrap'. @@ -75,7 +91,7 @@ %% record of args for open -record(arg, {name = 0, version = undefined, - file = none :: 'none' | string(), + file = none :: 'none' | file:filename(), repair = true :: dlog_repair(), size = infinity :: dlog_size(), type = halt :: dlog_type(), diff --git a/lib/kernel/src/erl_boot_server.erl b/lib/kernel/src/erl_boot_server.erl index b4c5f5e27c..0d68d3e198 100644 --- a/lib/kernel/src/erl_boot_server.erl +++ b/lib/kernel/src/erl_boot_server.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 @@ -59,7 +59,11 @@ -type ip4_address() :: {0..255,0..255,0..255,0..255}. --spec start(Slaves :: [atom()]) -> {'ok', pid()} | {'error', any()}. +-spec start(Slaves) -> {'ok', Pid} | {'error', What} when + Slaves :: [Host], + Host :: atom(), + Pid :: pid(), + What :: any(). start(Slaves) -> case check_arg(Slaves) of @@ -69,7 +73,11 @@ start(Slaves) -> {error, {badarg, Slaves}} end. --spec start_link(Slaves :: [atom()]) -> {'ok', pid()} | {'error', any()}. +-spec start_link(Slaves) -> {'ok', Pid} | {'error', What} when + Slaves :: [Host], + Host :: atom(), + Pid :: pid(), + What :: any(). start_link(Slaves) -> case check_arg(Slaves) of @@ -95,7 +103,10 @@ check_arg([], Result) -> check_arg(_, _Result) -> error. --spec add_slave(Slave :: atom()) -> 'ok' | {'error', any()}. +-spec add_slave(Slave) -> 'ok' | {'error', What} when + Slave :: Host, + Host :: atom(), + What :: any(). add_slave(Slave) -> case inet:getaddr(Slave, inet) of @@ -105,7 +116,10 @@ add_slave(Slave) -> {error, {badarg, Slave}} end. --spec delete_slave(Slave :: atom()) -> 'ok' | {'error', any()}. +-spec delete_slave(Slave) -> 'ok' | {'error', What} when + Slave :: Host, + Host :: atom(), + What :: any(). delete_slave(Slave) -> case inet:getaddr(Slave, inet) of @@ -131,7 +145,9 @@ add_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) -> delete_subnet(Mask, Addr) when is_tuple(Mask), is_tuple(Addr) -> gen_server:call(boot_server, {delete, {Mask, Addr}}). --spec which_slaves() -> [atom()]. +-spec which_slaves() -> Slaves when + Slaves :: [Host], + Host :: atom(). which_slaves() -> gen_server:call(boot_server, which). diff --git a/lib/kernel/src/erl_ddll.erl b/lib/kernel/src/erl_ddll.erl index ce64589a29..646cac99c5 100644 --- a/lib/kernel/src/erl_ddll.erl +++ b/lib/kernel/src/erl_ddll.erl @@ -44,14 +44,18 @@ start() -> stop() -> ok. --spec load_driver(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec load_driver(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc :: term(). load_driver(Path, Driver) -> do_load_driver(Path, Driver, [{driver_options,[kill_ports]}]). --spec load(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec load(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc ::term(). load(Path, Driver) -> do_load_driver(Path, Driver, []). @@ -100,30 +104,41 @@ do_unload_driver(Driver,Flags) -> end end. --spec unload_driver(Driver :: driver()) -> 'ok' | {'error', any()}. +-spec unload_driver(Name) -> 'ok' | {'error', ErrorDesc} when + Name :: driver(), + ErrorDesc :: term(). unload_driver(Driver) -> do_unload_driver(Driver,[{monitor,pending_driver},kill_ports]). --spec unload(Driver :: driver()) -> 'ok' | {'error', any()}. +-spec unload(Name) -> 'ok' | {'error', ErrorDesc} when + Name :: driver(), + ErrorDesc :: term(). unload(Driver) -> do_unload_driver(Driver,[]). --spec reload(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec reload(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc :: pending_process | OpaqueError, + OpaqueError :: term(). reload(Path,Driver) -> do_load_driver(Path, Driver, [{reload,pending_driver}]). --spec reload_driver(Path :: path(), Driver :: driver()) -> - 'ok' | {'error', any()}. +-spec reload_driver(Path, Name) -> 'ok' | {'error', ErrorDesc} when + Path :: path(), + Name :: driver(), + ErrorDesc :: pending_process | OpaqueError, + OpaqueError :: term(). reload_driver(Path,Driver) -> do_load_driver(Path, Driver, [{reload,pending_driver}, {driver_options,[kill_ports]}]). --spec format_error(Code :: atom()) -> string(). +-spec format_error(ErrorDesc) -> string() when + ErrorDesc :: term(). format_error(Code) -> case Code of @@ -135,7 +150,10 @@ format_error(Code) -> erl_ddll:format_error_int(Code) end. --spec info(Driver :: driver()) -> [{atom(), any()}, ...]. +-spec info(Name) -> InfoList when + Name :: driver(), + InfoList :: [InfoItem, ...], + InfoItem :: {Tag :: atom(), Value :: term()}. info(Driver) -> [{processes, erl_ddll:info(Driver,processes)}, @@ -146,7 +164,12 @@ info(Driver) -> {awaiting_load, erl_ddll:info(Driver,awaiting_load)}, {awaiting_unload, erl_ddll:info(Driver,awaiting_unload)}]. --spec info() -> [{string(), [{atom(), any()}]}]. +-spec info() -> AllInfoList when + AllInfoList :: [DriverInfo], + DriverInfo :: {DriverName, InfoList}, + DriverName :: string(), + InfoList :: [InfoItem], + InfoItem :: {Tag :: atom(), Value :: term()}. info() -> {ok,DriverList} = erl_ddll:loaded_drivers(), diff --git a/lib/kernel/src/error_handler.erl b/lib/kernel/src/error_handler.erl index 6f69f4ccb9..e1f99bf417 100644 --- a/lib/kernel/src/error_handler.erl +++ b/lib/kernel/src/error_handler.erl @@ -28,8 +28,11 @@ -export([undefined_function/3, undefined_lambda/3, stub_function/3, breakpoint/3]). --spec undefined_function(Module :: atom(), Function :: atom(), Args :: [_]) -> - any(). +-spec undefined_function(Module, Function, Args) -> + any() when + Module :: atom(), + Function :: atom(), + Args :: list(). undefined_function(Module, Func, Args) -> case ensure_loaded(Module) of @@ -51,8 +54,10 @@ undefined_function(Module, Func, Args) -> crash(Module, Func, Args) end. --spec undefined_lambda(Module :: atom(), Function :: fun(), Args :: [_]) -> - any(). +-spec undefined_lambda(Module, Fun, Args) -> term() when + Module :: atom(), + Fun :: fun(), + Args :: list(). undefined_lambda(Module, Fun, Args) -> case ensure_loaded(Module) of diff --git a/lib/kernel/src/error_logger.erl b/lib/kernel/src/error_logger.erl index cafdc52e84..f94cca000f 100644 --- a/lib/kernel/src/error_logger.erl +++ b/lib/kernel/src/error_logger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -69,17 +69,22 @@ start_link() -> %% Used for simple messages; error or information. %%----------------------------------------------------------------- --spec error_msg(Format :: string()) -> 'ok'. +-spec error_msg(Format) -> 'ok' when + Format :: string(). error_msg(Format) -> error_msg(Format,[]). --spec error_msg(Format :: string(), Args :: list()) -> 'ok'. +-spec error_msg(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). error_msg(Format, Args) -> notify({error, group_leader(), {self(), Format, Args}}). --spec format(Format :: string(), Args :: list()) -> 'ok'. +-spec format(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). format(Format, Args) -> notify({error, group_leader(), {self(), Format, Args}}). @@ -90,12 +95,18 @@ format(Format, Args) -> %% The 'std_error' error_report type can always be used. %%----------------------------------------------------------------- --spec error_report(Report :: any()) -> 'ok'. +-type report() :: + [{Tag :: term(), Data :: term()} | term()] | string() | term(). + +-spec error_report(Report) -> 'ok' when + Report :: report(). error_report(Report) -> error_report(std_error, Report). --spec error_report(Type :: any(), Report :: any()) -> 'ok'. +-spec error_report(Type, Report) -> 'ok' when + Type :: term(), + Report :: report(). error_report(Type, Report) -> notify({error_report, group_leader(), {self(), Type, Report}}). @@ -109,12 +120,15 @@ error_report(Type, Report) -> %% mapped to std_info or std_error accordingly. %%----------------------------------------------------------------- --spec warning_report(Report :: any()) -> 'ok'. +-spec warning_report(Report) -> 'ok' when + Report :: report(). warning_report(Report) -> warning_report(std_warning, Report). --spec warning_report(Type :: any(), Report :: any()) -> 'ok'. +-spec warning_report(Type, Report) -> 'ok' when + Type :: any(), + Report :: report(). warning_report(Type, Report) -> {Tag, NType} = case error_logger:warning_map() of @@ -143,12 +157,15 @@ warning_report(Type, Report) -> %% other types of reports. %%----------------------------------------------------------------- --spec warning_msg(Format :: string()) -> 'ok'. +-spec warning_msg(Format) -> 'ok' when + Format :: string(). warning_msg(Format) -> warning_msg(Format,[]). --spec warning_msg(Format :: string(), Args :: list()) -> 'ok'. +-spec warning_msg(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). warning_msg(Format, Args) -> Tag = case error_logger:warning_map() of @@ -167,12 +184,15 @@ warning_msg(Format, Args) -> %% The 'std_info' info_report type can always be used. %%----------------------------------------------------------------- --spec info_report(Report :: any()) -> 'ok'. +-spec info_report(Report) -> 'ok' when + Report :: report(). info_report(Report) -> info_report(std_info, Report). --spec info_report(Type :: any(), Report :: any()) -> 'ok'. +-spec info_report(Type, Report) -> 'ok' when + Type :: any(), + Report :: report(). info_report(Type, Report) -> notify({info_report, group_leader(), {self(), Type, Report}}). @@ -182,12 +202,15 @@ info_report(Type, Report) -> %% information messages. %%----------------------------------------------------------------- --spec info_msg(Format :: string()) -> 'ok'. +-spec info_msg(Format) -> 'ok' when + Format :: string(). info_msg(Format) -> info_msg(Format,[]). --spec info_msg(Format :: string(), Args :: list()) -> 'ok'. +-spec info_msg(Format, Data) -> 'ok' when + Format :: string(), + Data :: list(). info_msg(Format, Args) -> notify({info_msg, group_leader(), {self(), Format, Args}}). @@ -223,17 +246,23 @@ swap_handler(silent) -> swap_handler(false) -> ok. % keep primitive event handler as-is --spec add_report_handler(Module :: atom()) -> any(). +-spec add_report_handler(Handler) -> any() when + Handler :: module(). add_report_handler(Module) when is_atom(Module) -> gen_event:add_handler(error_logger, Module, []). --spec add_report_handler(atom(), any()) -> any(). +-spec add_report_handler(Handler, Args) -> Result when + Handler :: module(), + Args :: gen_event:handler_args(), + Result :: gen_event:add_handler_ret(). add_report_handler(Module, Args) when is_atom(Module) -> gen_event:add_handler(error_logger, Module, Args). --spec delete_report_handler(Module :: atom()) -> any(). +-spec delete_report_handler(Handler) -> Result when + Handler :: module(), + Result :: gen_event:del_handler_ret(). delete_report_handler(Module) when is_atom(Module) -> gen_event:delete_handler(error_logger, Module, []). @@ -250,9 +279,16 @@ simple_logger() -> %% Log all errors to File for all eternity --spec logfile(Request :: {'open', string()}) -> 'ok' | {'error',any()} - ; (Request :: 'close') -> 'ok' | {'error', any()} - ; (Request :: 'filename') -> atom() | string() | {'error', any()}. +-type open_error() :: file:posix() | badarg | system_limit. + +-spec logfile(Request :: {open, Filename}) -> ok | {error, OpenReason} when + Filename ::file:name(), + OpenReason :: allready_have_logfile | open_error() + ; (Request :: close) -> ok | {error, CloseReason} when + CloseReason :: module_not_found + ; (Request :: filename) -> Filename | {error, FilenameReason} when + Filename :: file:name(), + FilenameReason :: no_log_file. logfile({open, File}) -> case lists:member(error_logger_file_h, @@ -280,7 +316,8 @@ logfile(filename) -> %% Possibly turn off all tty printouts, maybe we only want the errors %% to go to a file --spec tty(Flag :: boolean()) -> 'ok'. +-spec tty(Flag) -> 'ok' when + Flag :: boolean(). tty(true) -> Hs = gen_event:which_handlers(error_logger), diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 88bcf9a9cc..f1a8aa9f77 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -79,15 +79,19 @@ -type file_info() :: #file_info{}. -type fd() :: #file_descriptor{}. -type io_device() :: pid() | fd(). --type location() :: integer() | {'bof', integer()} | {'cur', integer()} - | {'eof', integer()} | 'bof' | 'cur' | 'eof'. +-type location() :: integer() | {'bof', Offset :: integer()} + | {'cur', Offset :: integer()} + | {'eof', Offset :: integer()} | 'bof' | 'cur' | 'eof'. -type mode() :: 'read' | 'write' | 'append' | 'exclusive' | 'raw' | 'binary' - | {'delayed_write', non_neg_integer(), non_neg_integer()} - | 'delayed_write' | {'read_ahead', pos_integer()} + | {'delayed_write', + Size :: non_neg_integer(), + Delay :: non_neg_integer()} + | 'delayed_write' | {'read_ahead', Size :: pos_integer()} | 'read_ahead' | 'compressed' | {'encoding', unicode:encoding()}. --type name() :: string() | atom() | [name()] | binary(). +-type deep_list() :: [char() | atom() | deep_list()]. +-type name() :: string() | atom() | deep_list() | (RawFilename :: binary()). -type posix() :: 'eacces' | 'eagain' | 'ebadf' | 'ebusy' | 'edquot' | 'eexist' | 'efault' | 'efbig' | 'eintr' | 'einval' | 'eio' | 'eisdir' | 'eloop' | 'emfile' | 'emlink' @@ -96,10 +100,14 @@ | 'enotblk' | 'enotdir' | 'enotsup' | 'enxio' | 'eperm' | 'epipe' | 'erofs' | 'espipe' | 'esrch' | 'estale' | 'exdev'. --type bindings() :: any(). - --type date() :: {pos_integer(), pos_integer(), pos_integer()}. --type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. +-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 posix_file_advise() :: 'normal' | 'sequential' | 'random' | 'no_reuse' | 'will_need' | 'dont_need'. @@ -107,8 +115,10 @@ %%%----------------------------------------------------------------- %%% General functions --spec format_error(Reason :: posix() | {integer(), atom(), any()}) -> - string(). +-spec format_error(Reason) -> Chars when + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}, + Chars :: string(). format_error({_Line, ?MODULE, undefined_script}) -> "no value returned from script"; @@ -129,7 +139,9 @@ format_error(terminated) -> format_error(ErrorId) -> erl_posix_msg:message(ErrorId). --spec pid2name(Pid :: pid()) -> {'ok', filename()} | 'undefined'. +-spec pid2name(Pid) -> {ok, Filename} | undefined when + Filename :: filename(), + Pid :: pid(). pid2name(Pid) when is_pid(Pid) -> case whereis(?FILE_SERVER) of @@ -148,42 +160,61 @@ pid2name(Pid) when is_pid(Pid) -> %%% File server functions. %%% Functions that do not operate on a single open file. %%% Stateless. --spec get_cwd() -> {'ok', filename()} | {'error', posix()}. +-spec get_cwd() -> {ok, Dir} | {error, Reason} when + Dir :: filename(), + Reason :: posix(). get_cwd() -> call(get_cwd, []). --spec get_cwd(Drive :: string()) -> {'ok', filename()} | {'error', posix()}. +-spec get_cwd(Drive) -> {ok, Dir} | {error, Reason} when + Drive :: string(), + Dir :: filename(), + Reason :: posix() | badarg. get_cwd(Drive) -> check_and_call(get_cwd, [file_name(Drive)]). --spec set_cwd(Dirname :: name()) -> 'ok' | {'error', posix()}. +-spec set_cwd(Dir) -> ok | {error, Reason} when + Dir :: name(), + Reason :: posix() | badarg. set_cwd(Dirname) -> check_and_call(set_cwd, [file_name(Dirname)]). --spec delete(Name :: name()) -> 'ok' | {'error', posix()}. +-spec delete(Filename) -> ok | {error, Reason} when + Filename :: name(), + Reason :: posix() | badarg. delete(Name) -> check_and_call(delete, [file_name(Name)]). --spec rename(From :: name(), To :: name()) -> 'ok' | {'error', posix()}. +-spec rename(Source, Destination) -> ok | {error, Reason} when + Source :: name(), + Destination :: name(), + Reason :: posix() | badarg. rename(From, To) -> check_and_call(rename, [file_name(From), file_name(To)]). --spec make_dir(Name :: name()) -> 'ok' | {'error', posix()}. +-spec make_dir(Dir) -> ok | {error, Reason} when + Dir :: name(), + Reason :: posix() | badarg. make_dir(Name) -> check_and_call(make_dir, [file_name(Name)]). --spec del_dir(Name :: name()) -> 'ok' | {'error', posix()}. +-spec del_dir(Dir) -> ok | {error, Reason} when + Dir :: name(), + Reason :: posix() | badarg. del_dir(Name) -> check_and_call(del_dir, [file_name(Name)]). --spec read_file_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}. +-spec read_file_info(Filename) -> {ok, FileInfo} | {error, Reason} when + Filename :: name(), + FileInfo :: file_info(), + Reason :: posix() | badarg. read_file_info(Name) -> check_and_call(read_file_info, [file_name(Name)]). @@ -193,45 +224,66 @@ read_file_info(Name) -> altname(Name) -> check_and_call(altname, [file_name(Name)]). --spec read_link_info(Name :: name()) -> {'ok', file_info()} | {'error', posix()}. +-spec read_link_info(Name) -> {ok, FileInfo} | {error, Reason} when + Name :: name(), + FileInfo :: file_info(), + Reason :: posix() | badarg. read_link_info(Name) -> check_and_call(read_link_info, [file_name(Name)]). --spec read_link(Name :: name()) -> {'ok', filename()} | {'error', posix()}. +-spec read_link(Name) -> {ok, Filename} | {error, Reason} when + Name :: name(), + Filename :: filename(), + Reason :: posix() | badarg. read_link(Name) -> check_and_call(read_link, [file_name(Name)]). --spec write_file_info(Name :: name(), Info :: file_info()) -> - 'ok' | {'error', posix()}. +-spec write_file_info(Filename, FileInfo) -> ok | {error, Reason} when + Filename :: name(), + FileInfo :: file_info(), + Reason :: posix() | badarg. write_file_info(Name, Info = #file_info{}) -> check_and_call(write_file_info, [file_name(Name), Info]). --spec list_dir(Name :: name()) -> {'ok', [filename()]} | {'error', posix()}. +-spec list_dir(Dir) -> {ok, Filenames} | {error, Reason} when + Dir :: name(), + Filenames :: [filename()], + Reason :: posix() | badarg. list_dir(Name) -> check_and_call(list_dir, [file_name(Name)]). --spec read_file(Name :: name()) -> - {'ok', binary()} | {'error', posix() | 'terminated' | 'system_limit'}. +-spec read_file(Filename) -> {ok, Binary} | {error, Reason} when + Filename :: name(), + Binary :: binary(), + Reason :: posix() | badarg | terminated | system_limit. read_file(Name) -> check_and_call(read_file, [file_name(Name)]). --spec make_link(Old :: name(), New :: name()) -> 'ok' | {'error', posix()}. +-spec make_link(Existing, New) -> ok | {error, Reason} when + Existing :: name(), + New :: name(), + Reason :: posix() | badarg. make_link(Old, New) -> check_and_call(make_link, [file_name(Old), file_name(New)]). --spec make_symlink(Old :: name(), New :: name()) -> 'ok' | {'error', posix()}. +-spec make_symlink(Name1, Name2) -> ok | {error, Reason} when + Name1 :: name(), + Name2 :: name(), + Reason :: posix() | badarg. make_symlink(Old, New) -> check_and_call(make_symlink, [file_name(Old), file_name(New)]). --spec write_file(Name :: name(), Bin :: iodata()) -> - 'ok' | {'error', posix() | 'terminated' | 'system_limit'}. +-spec write_file(Filename, Bytes) -> ok | {error, Reason} when + Filename :: name(), + Bytes :: iodata(), + Reason :: posix() | badarg | terminated | system_limit. write_file(Name, Bin) -> check_and_call(write_file, [file_name(Name), make_binary(Bin)]). @@ -240,8 +292,11 @@ write_file(Name, Bin) -> %% when it is time to change file server protocol again. %% Meanwhile, it is implemented here, slightly less efficient. --spec write_file(Name :: name(), Bin :: iodata(), Modes :: [mode()]) -> - 'ok' | {'error', posix()}. +-spec write_file(Filename, Bytes, Modes) -> ok | {error, Reason} when + Filename :: name(), + Bytes :: iodata(), + Modes :: [mode()], + Reason :: posix() | badarg | terminated | system_limit. write_file(Name, Bin, ModeList) when is_list(ModeList) -> case make_binary(Bin) of @@ -295,8 +350,11 @@ raw_write_file_info(Name, #file_info{} = Info) -> %% Contemporary mode specification - list of options --spec open(Name :: name(), Modes :: [mode()]) -> - {'ok', io_device()} | {'error', posix() | 'system_limit'}. +-spec open(Filename, Modes) -> {ok, IoDevice} | {error, Reason} when + Filename :: name(), + Modes :: [mode()], + IoDevice :: io_device(), + Reason :: posix() | badarg | system_limit. open(Item, ModeList) when is_list(ModeList) -> case lists:member(raw, ModeList) of @@ -349,7 +407,9 @@ open(Item, Mode) -> %%% The File argument must be either a Pid or a handle %%% returned from ?PRIM_FILE:open. --spec close(File :: io_device()) -> 'ok' | {'error', posix() | 'terminated'}. +-spec close(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. close(File) when is_pid(File) -> R = file_request(File, close), @@ -367,9 +427,12 @@ close(#file_descriptor{module = Module} = Handle) -> close(_) -> {error, badarg}. --spec advise(File :: io_device(), Offset :: integer(), - Length :: integer(), Advise :: posix_file_advise()) -> - 'ok' | {'error', posix()}. +-spec advise(IoDevice, Offset, Length, Advise) -> ok | {error, Reason} when + IoDevice :: io_device(), + Offset :: integer(), + Length :: integer(), + Advise :: posix_file_advise(), + Reason :: posix() | badarg. advise(File, Offset, Length, Advise) when is_pid(File) -> R = file_request(File, {advise, Offset, Length, Advise}), @@ -379,8 +442,11 @@ advise(#file_descriptor{module = Module} = Handle, Offset, Length, Advise) -> advise(_, _, _, _) -> {error, badarg}. --spec read(File :: io_device() | atom(), Size :: non_neg_integer()) -> - 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. +-spec read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason} when + IoDevice :: io_device() | atom(), + Number :: non_neg_integer(), + Data :: string() | binary(), + Reason :: posix() | badarg | terminated. read(File, Sz) when (is_pid(File) orelse is_atom(File)), is_integer(Sz), Sz >= 0 -> case io:request(File, {get_chars, '', Sz}) of @@ -395,8 +461,10 @@ read(#file_descriptor{module = Module} = Handle, Sz) read(_, _) -> {error, badarg}. --spec read_line(File :: io_device() | atom()) -> - 'eof' | {'ok', [char()] | binary()} | {'error', posix()}. +-spec read_line(IoDevice) -> {ok, Data} | eof | {error, Reason} when + IoDevice :: io_device() | atom(), + Data :: string() | binary(), + Reason :: posix() | badarg | terminated. read_line(File) when (is_pid(File) orelse is_atom(File)) -> case io:request(File, {get_line, ''}) of @@ -410,9 +478,12 @@ read_line(#file_descriptor{module = Module} = Handle) -> read_line(_) -> {error, badarg}. --spec pread(File :: io_device(), - LocationNumbers :: [{location(), non_neg_integer()}]) -> - {'ok', [string() | binary() | 'eof']} | {'error', posix()}. +-spec pread(IoDevice, LocNums) -> {ok, DataL} | eof | {error, Reason} when + IoDevice :: io_device(), + LocNums :: [{Location :: location(), Number :: non_neg_integer()}], + DataL :: [Data], + Data :: string() | binary() | eof, + Reason :: posix() | badarg | terminated. pread(File, L) when is_pid(File), is_list(L) -> pread_int(File, L, []); @@ -435,10 +506,13 @@ pread_int(File, [{At, Sz} | T], R) when is_integer(Sz), Sz >= 0 -> pread_int(_, _, _) -> {error, badarg}. --spec pread(File :: io_device(), - Location :: location(), - Size :: non_neg_integer()) -> - 'eof' | {'ok', string() | binary()} | {'error', posix()}. +-spec pread(IoDevice, Location, Number) -> + {ok, Data} | eof | {error, Reason} when + IoDevice :: io_device(), + Location :: location(), + Number :: non_neg_integer(), + Data :: string() | binary(), + Reason :: posix() | badarg | terminated. pread(File, At, Sz) when is_pid(File), is_integer(Sz), Sz >= 0 -> R = file_request(File, {pread, At, Sz}), @@ -449,8 +523,10 @@ pread(#file_descriptor{module = Module} = Handle, Offs, Sz) pread(_, _, _) -> {error, badarg}. --spec write(File :: io_device() | atom(), Byte :: iodata()) -> - 'ok' | {'error', posix() | 'terminated'}. +-spec write(IoDevice, Bytes) -> ok | {error, Reason} when + IoDevice :: io_device() | atom(), + Bytes :: iodata(), + Reason :: posix() | badarg | terminated. write(File, Bytes) when (is_pid(File) orelse is_atom(File)) -> case make_binary(Bytes) of @@ -464,8 +540,11 @@ write(#file_descriptor{module = Module} = Handle, Bytes) -> write(_, _) -> {error, badarg}. --spec pwrite(File :: io_device(), L :: [{location(), iodata()}]) -> - 'ok' | {'error', {non_neg_integer(), posix()}}. +-spec pwrite(IoDevice, LocBytes) -> ok | {error, {N, Reason}} when + IoDevice :: io_device(), + LocBytes :: [{Location :: location(), Bytes :: iodata()}], + N :: non_neg_integer(), + Reason :: posix() | badarg | terminated. pwrite(File, L) when is_pid(File), is_list(L) -> pwrite_int(File, L, 0); @@ -486,10 +565,11 @@ pwrite_int(File, [{At, Bytes} | T], R) -> pwrite_int(_, _, _) -> {error, badarg}. --spec pwrite(File :: io_device(), - Location :: location(), - Bytes :: iodata()) -> - 'ok' | {'error', posix()}. +-spec pwrite(IoDevice, Location, Bytes) -> ok | {error, Reason} when + IoDevice :: io_device(), + Location :: location(), + Bytes :: iodata(), + Reason :: posix() | badarg | terminated. pwrite(File, At, Bytes) when is_pid(File) -> R = file_request(File, {pwrite, At, Bytes}), @@ -499,7 +579,9 @@ pwrite(#file_descriptor{module = Module} = Handle, Offs, Bytes) -> pwrite(_, _, _) -> {error, badarg}. --spec datasync(File :: io_device()) -> 'ok' | {'error', posix()}. +-spec datasync(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. datasync(File) when is_pid(File) -> R = file_request(File, datasync), @@ -509,7 +591,9 @@ datasync(#file_descriptor{module = Module} = Handle) -> datasync(_) -> {error, badarg}. --spec sync(File :: io_device()) -> 'ok' | {'error', posix()}. +-spec sync(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. sync(File) when is_pid(File) -> R = file_request(File, sync), @@ -519,8 +603,11 @@ sync(#file_descriptor{module = Module} = Handle) -> sync(_) -> {error, badarg}. --spec position(File :: io_device(), Location :: location()) -> - {'ok',integer()} | {'error', posix()}. +-spec position(IoDevice, Location) -> {ok, NewPosition} | {error, Reason} when + IoDevice :: io_device(), + Location :: location(), + NewPosition :: integer(), + Reason :: posix() | badarg | terminated. position(File, At) when is_pid(File) -> R = file_request(File, {position,At}), @@ -530,7 +617,9 @@ position(#file_descriptor{module = Module} = Handle, At) -> position(_, _) -> {error, badarg}. --spec truncate(File :: io_device()) -> 'ok' | {'error', posix()}. +-spec truncate(IoDevice) -> ok | {error, Reason} when + IoDevice :: io_device(), + Reason :: posix() | badarg | terminated. truncate(File) when is_pid(File) -> R = file_request(File, truncate), @@ -540,17 +629,26 @@ truncate(#file_descriptor{module = Module} = Handle) -> truncate(_) -> {error, badarg}. --spec copy(Source :: io_device() | name() | {name(), [mode()]}, - Destination :: io_device() | name() | {name(), [mode()]}) -> - {'ok', non_neg_integer()} | {'error', posix()}. +-spec copy(Source, Destination) -> {ok, BytesCopied} | {error, Reason} when + Source :: io_device() | Filename | {Filename, Modes}, + Destination :: io_device() | Filename | {Filename, Modes}, + Filename :: name(), + Modes :: [mode()], + BytesCopied :: non_neg_integer(), + Reason :: posix() | badarg | terminated. copy(Source, Dest) -> copy_int(Source, Dest, infinity). --spec copy(Source :: io_device() | name() | {name(), [mode()]}, - Destination :: io_device() | name() | {name(), [mode()]}, - Length :: non_neg_integer() | 'infinity') -> - {'ok', non_neg_integer()} | {'error', posix()}. +-spec copy(Source, Destination, ByteCount) -> + {ok, BytesCopied} | {error, Reason} when + Source :: io_device() | Filename | {Filename, Modes}, + Destination :: io_device() | Filename | {Filename, Modes}, + Filename :: name(), + Modes :: [mode()], + ByteCount :: non_neg_integer() | infinity, + BytesCopied :: non_neg_integer(), + Reason :: posix() | badarg | terminated. copy(Source, Dest, Length) when is_integer(Length), Length >= 0; @@ -772,8 +870,11 @@ ipread_s32bu_p32bu_2(File, %%% The following functions, built upon the other interface functions, %%% provide a higher-lever interface to files. --spec consult(File :: name()) -> - {'ok', list()} | {'error', posix() | {integer(), atom(), any()}}. +-spec consult(Filename) -> {ok, Terms} | {error, Reason} when + Filename :: name(), + Terms :: [term()], + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. consult(File) -> case open(File, [read]) of @@ -785,8 +886,14 @@ consult(File) -> Error end. --spec path_consult(Paths :: [name()], File :: name()) -> - {'ok', list(), filename()} | {'error', posix() | {integer(), atom(), any()}}. +-spec path_consult(Path, Filename) -> {ok, Terms, FullName} | {error, Reason} when + Path :: [Dir], + Dir :: name(), + Filename :: name(), + Terms :: [term()], + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_consult(Path, File) -> case path_open(Path, File, [read]) of @@ -803,13 +910,19 @@ path_consult(Path, File) -> E2 end. --spec eval(File :: name()) -> 'ok' | {'error', posix()}. +-spec eval(Filename) -> ok | {error, Reason} when + Filename :: name(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. eval(File) -> eval(File, erl_eval:new_bindings()). --spec eval(File :: name(), Bindings :: bindings()) -> - 'ok' | {'error', posix()}. +-spec eval(Filename, Bindings) -> ok | {error, Reason} when + Filename :: name(), + Bindings :: bindings(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. eval(File, Bs) -> case open(File, [read]) of @@ -821,14 +934,24 @@ eval(File, Bs) -> Error end. --spec path_eval(Paths :: [name()], File :: name()) -> - {'ok', filename()} | {'error', posix() | {integer(), atom(), any()}}. +-spec path_eval(Path, Filename) -> {ok, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_eval(Path, File) -> path_eval(Path, File, erl_eval:new_bindings()). --spec path_eval(Paths :: [name()], File :: name(), Bindings :: bindings()) -> - {'ok', filename()} | {'error', posix() | {integer(), atom(), any()}}. +-spec path_eval(Path, Filename, Bindings) -> + {ok, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Bindings :: bindings(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_eval(Path, File, Bs) -> case path_open(Path, File, [read]) of @@ -845,14 +968,21 @@ path_eval(Path, File, Bs) -> E2 end. --spec script(File :: name()) -> - {'ok', any()} | {'error', posix() | {integer(), atom(), any()}}. +-spec script(Filename) -> {ok, Value} | {error, Reason} when + Filename :: name(), + Value :: term(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. script(File) -> script(File, erl_eval:new_bindings()). --spec script(File :: name(), Bindings :: bindings()) -> - {'ok', any()} | {'error', posix() | {integer(), atom(), any()}}. +-spec script(Filename, Bindings) -> {ok, Value} | {error, Reason} when + Filename :: name(), + Bindings :: bindings(), + Value :: term(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. script(File, Bs) -> case open(File, [read]) of @@ -864,16 +994,27 @@ script(File, Bs) -> Error end. --spec path_script/2 :: (Paths :: [name()], File :: name()) -> - {'ok', term(), filename()} | {'error', posix() | {integer(), atom(), _}}. +-spec path_script(Path, Filename) -> + {ok, Value, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Value :: term(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_script(Path, File) -> path_script(Path, File, erl_eval:new_bindings()). --spec path_script(Paths :: [name()], - File :: name(), - Bindings :: bindings()) -> - {'ok', term(), filename()} | {'error', posix() | {integer(), atom(), _}}. +-spec path_script(Path, Filename, Bindings) -> + {ok, Value, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Bindings :: bindings(), + Value :: term(), + FullName :: filename(), + Reason :: posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. path_script(Path, File, Bs) -> case path_open(Path, File, [read]) of @@ -898,8 +1039,14 @@ path_script(Path, File, Bs) -> %% Searches the Paths for file Filename which can be opened with Mode. %% The path list is ignored if Filename contains an absolute path. --spec path_open(Paths :: [name()], Name :: name(), Modes :: [mode()]) -> - {'ok', io_device(), filename()} | {'error', posix()}. +-spec path_open(Path, Filename, Modes) -> + {ok, IoDevice, FullName} | {error, Reason} when + Path :: [Dir :: name()], + Filename :: name(), + Modes :: [mode()], + IoDevice :: io_device(), + FullName :: filename(), + Reason :: posix() | badarg | system_limit. path_open(PathList, Name, Mode) -> case file_name(Name) of @@ -919,47 +1066,57 @@ path_open(PathList, Name, Mode) -> end end. --spec change_mode(Name :: name(), Mode :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_mode(Filename, Mode) -> ok | {error, Reason} when + Filename :: name(), + Mode :: integer(), + Reason :: posix() | badarg. change_mode(Name, Mode) when is_integer(Mode) -> write_file_info(Name, #file_info{mode=Mode}). --spec change_owner(Name :: name(), OwnerId :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_owner(Filename, Uid) -> ok | {error, Reason} when + Filename :: name(), + Uid :: integer(), + Reason :: posix() | badarg. change_owner(Name, OwnerId) when is_integer(OwnerId) -> write_file_info(Name, #file_info{uid=OwnerId}). --spec change_owner(Name :: name(), - OwnerId :: integer(), - GroupId :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_owner(Filename, Uid, Gid) -> ok | {error, Reason} when + Filename :: name(), + Uid :: integer(), + Gid :: integer(), + Reason :: posix() | badarg. change_owner(Name, OwnerId, GroupId) when is_integer(OwnerId), is_integer(GroupId) -> write_file_info(Name, #file_info{uid=OwnerId, gid=GroupId}). --spec change_group(Name :: name(), GroupId :: integer()) -> - 'ok' | {'error', posix()}. +-spec change_group(Filename, Gid) -> ok | {error, Reason} when + Filename :: name(), + Gid :: integer(), + Reason :: posix() | badarg. change_group(Name, GroupId) when is_integer(GroupId) -> write_file_info(Name, #file_info{gid=GroupId}). --spec change_time(Name :: name(), Time :: date_time()) -> - 'ok' | {'error', posix()}. +-spec change_time(Filename, Mtime) -> ok | {error, Reason} when + Filename :: name(), + Mtime :: date_time(), + Reason :: posix() | badarg. change_time(Name, Time) when is_tuple(Time) -> write_file_info(Name, #file_info{mtime=Time}). --spec change_time(Name :: name(), - ATime :: date_time(), - MTime :: date_time()) -> - 'ok' | {'error', posix()}. +-spec change_time(Filename, Atime, Mtime) -> ok | {error, Reason} when + Filename :: name(), + Atime :: date_time(), + Mtime :: date_time(), + Reason :: posix() | badarg. change_time(Name, Atime, Mtime) when is_tuple(Atime), is_tuple(Mtime) -> diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index cccfa75005..004f03f231 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.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 @@ -33,11 +33,57 @@ -export([error_string/1]). -export([controlling_process/2]). - +-opaque assoc_id() :: term(). +-type hostname() :: inet:hostname(). +-type ip_address() :: inet:ip_address(). +-type port_number() :: 0..65535. +-type posix() :: inet:posix(). +-type sctp_option() :: + {mode, list | binary} | list | binary + | {active, true | false | once} + | {buffer, non_neg_integer()} + | {tos, integer()} + | {priority, integer()} + | {dontroute, boolean()} + | {reuseaddr, boolean()} + | {linger, {boolean(), non_neg_integer()}} + | {sndbuf, non_neg_integer()} + | {recbuf, non_neg_integer()} + | {sctp_rtoinfo, #sctp_rtoinfo{}} + | {sctp_associnfo, #sctp_assocparams{}} + | {sctp_initmsg, #sctp_initmsg{}} + | {sctp_autoclose, timeout()} + | {sctp_nodelay, boolean()} + | {sctp_disable_fragments, boolean()} + | {sctp_i_want_mapped_v4_addr, boolean()} + | {sctp_maxseg, non_neg_integer()} + | {sctp_primary_addr, #sctp_prim{}} + | {sctp_set_peer_primary_addr, #sctp_setpeerprim{}} + | {sctp_adaptation_layer, #sctp_setadaptation{}} + | {sctp_peer_addr_params, #sctp_paddrparams{}} + | {sctp_default_send_param, #sctp_sndrcvinfo{}} + | {sctp_events, #sctp_event_subscribe{}} + | {sctp_delayed_ack_time, #sctp_assoc_value{}} + | {sctp_status, #sctp_status{}} + | {sctp_get_peer_addr_info, #sctp_paddrinfo{}}. +-opaque sctp_socket() :: port(). + +-spec open() -> {ok, Socket} | {error, posix()} when + Socket :: sctp_socket(). open() -> open([]). +-spec open(Port) -> {ok, Socket} | {error, posix()} when + Port :: port_number(), + Socket :: sctp_socket(); + (Opts) -> {ok, Socket} | {error, posix()} when + Opts :: [Opt], + Opt :: {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option(), + IP :: ip_address() | any | loopback, + Port :: port_number(), + Socket :: sctp_socket(). + open(Opts) when is_list(Opts) -> Mod = mod(Opts, undefined), case Mod:open(Opts) of @@ -52,11 +98,21 @@ open(Port) when is_integer(Port) -> open(X) -> erlang:error(badarg, [X]). +-spec open(Port, Opts) -> {ok, Socket} | {error, posix()} when + Opts :: [Opt], + Opt :: {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option(), + IP :: ip_address() | any | loopback, + Port :: port_number(), + Socket :: sctp_socket(). + open(Port, Opts) when is_integer(Port), is_list(Opts) -> open([{port,Port}|Opts]); open(Port, Opts) -> erlang:error(badarg, [Port,Opts]). +-spec close(Socket) -> ok | {error, posix()} when + Socket :: sctp_socket(). + close(S) when is_port(S) -> case inet_db:lookup_socket(S) of {ok,Mod} -> @@ -68,6 +124,11 @@ close(S) -> +-spec listen(Socket, IsServer) -> ok | {error, Reason} when + Socket :: sctp_socket(), + IsServer :: boolean(), + Reason :: term(). + listen(S, Flag) when is_port(S), is_boolean(Flag) -> case inet_db:lookup_socket(S) of {ok,Mod} -> @@ -77,9 +138,25 @@ listen(S, Flag) when is_port(S), is_boolean(Flag) -> listen(S, Flag) -> erlang:error(badarg, [S,Flag]). +-spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [Opt :: sctp_option()], + Assoc :: #sctp_assoc_change{}. + connect(S, Addr, Port, Opts) -> connect(S, Addr, Port, Opts, infinity). +-spec connect(Socket, Addr, Port, Opts, Timeout) -> + {ok, Assoc} | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [Opt :: sctp_option()], + Timeout :: timeout(), + Assoc :: #sctp_assoc_change{}. + connect(S, Addr, Port, Opts, Timeout) -> case do_connect(S, Addr, Port, Opts, Timeout, true) of badarg -> @@ -88,9 +165,24 @@ connect(S, Addr, Port, Opts, Timeout) -> Result end. +-spec connect_init(Socket, Addr, Port, Opts) -> + ok | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [sctp_option()]. + connect_init(S, Addr, Port, Opts) -> connect_init(S, Addr, Port, Opts, infinity). +-spec connect_init(Socket, Addr, Port, Opts, Timeout) -> + ok | {error, posix()} when + Socket :: sctp_socket(), + Addr :: ip_address() | hostname(), + Port :: port_number(), + Opts :: [sctp_option()], + Timeout :: timeout(). + connect_init(S, Addr, Port, Opts, Timeout) -> case do_connect(S, Addr, Port, Opts, Timeout, false) of badarg -> @@ -130,12 +222,20 @@ do_connect(_S, _Addr, _Port, _Opts, _Timeout, _ConnWait) -> badarg. +-spec eof(Socket, Assoc) -> ok | {error, Reason} when + Socket :: sctp_socket(), + Assoc :: #sctp_assoc_change{}, + Reason :: term(). eof(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> eof_or_abort(S, AssocId, eof); eof(S, Assoc) -> erlang:error(badarg, [S,Assoc]). +-spec abort(Socket, Assoc) -> ok | {error, posix()} when + Socket :: sctp_socket(), + Assoc :: #sctp_assoc_change{}. + abort(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> eof_or_abort(S, AssocId, abort); abort(S, Assoc) -> @@ -151,6 +251,11 @@ eof_or_abort(S, AssocId, Action) -> end. +-spec send(Socket, SndRcvInfo, Data) -> ok | {error, Reason} when + Socket :: sctp_socket(), + SndRcvInfo :: #sctp_sndrcvinfo{}, + Data :: binary | iolist(), + Reason :: term(). %% Full-featured send. Rarely needed. send(S, #sctp_sndrcvinfo{}=SRI, Data) when is_port(S) -> @@ -162,6 +267,13 @@ send(S, #sctp_sndrcvinfo{}=SRI, Data) when is_port(S) -> send(S, SRI, Data) -> erlang:error(badarg, [S,SRI,Data]). +-spec send(Socket, Assoc, Stream, Data) -> ok | {error, Reason} when + Socket :: sctp_socket(), + Assoc :: #sctp_assoc_change{} | assoc_id(), + Stream :: integer(), + Data :: binary | iolist(), + Reason :: term(). + send(S, #sctp_assoc_change{assoc_id=AssocId}, Stream, Data) when is_port(S), is_integer(Stream) -> case inet_db:lookup_socket(S) of @@ -179,9 +291,36 @@ send(S, AssocId, Stream, Data) send(S, AssocChange, Stream, Data) -> erlang:error(badarg, [S,AssocChange,Stream,Data]). +-spec recv(Socket) -> {ok, {FromIP, FromPort, AncData, Data}} + | {error, Reason} when + Socket :: sctp_socket(), + FromIP :: ip_address(), + FromPort :: port_number(), + AncData :: [#sctp_sndrcvinfo{}], + Data :: binary() | string() | #sctp_sndrcvinfo{} + | #sctp_assoc_change{} | #sctp_paddr_change{} + | #sctp_adaptation_event{}, + Reason :: posix() | #sctp_send_failed{} | #sctp_paddr_change{} + | #sctp_pdapi_event{} | #sctp_remote_error{} + | #sctp_shutdown_event{}. + recv(S) -> recv(S, infinity). +-spec recv(Socket, Timeout) -> {ok, {FromIP, FromPort, AncData, Data}} + | {error, Reason} when + Socket :: sctp_socket(), + Timeout :: timeout(), + FromIP :: ip_address(), + FromPort :: port_number(), + AncData :: [#sctp_sndrcvinfo{}], + Data :: binary() | string() | #sctp_sndrcvinfo{} + | #sctp_assoc_change{} | #sctp_paddr_change{} + | #sctp_adaptation_event{}, + Reason :: posix() | #sctp_send_failed{} | #sctp_paddr_change{} + | #sctp_pdapi_event{} | #sctp_remote_error{} + | #sctp_shutdown_event{}. + recv(S, Timeout) when is_port(S) -> case inet_db:lookup_socket(S) of {ok,Mod} -> @@ -192,6 +331,8 @@ recv(S, Timeout) -> erlang:error(badarg, [S,Timeout]). +-spec error_string(ErrorNumber) -> ok | string() | unknown_error when + ErrorNumber :: integer(). error_string(0) -> ok; @@ -224,6 +365,9 @@ error_string(X) -> erlang:error(badarg, [X]). +-spec controlling_process(Socket, Pid) -> ok when + Socket :: sctp_socket(), + Pid :: pid(). controlling_process(S, Pid) when is_port(S), is_pid(Pid) -> inet:udp_controlling_process(S, Pid); diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 16a87d71b6..bee61ca84a 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.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 @@ -28,12 +28,35 @@ -include("inet_int.hrl"). +-type hostname() :: inet:hostname(). +-type ip_address() :: inet:ip_address(). +-type port_number() :: 0..65535. +-type posix() :: inet:posix(). +-type socket() :: port(). + %% %% Connect a socket %% + +-spec connect(Address, Port, Options) -> {ok, Socket} | {error, Reason} when + Address :: ip_address() | hostname(), + Port :: port_number(), + Options :: [Opt :: term()], + Socket :: socket(), + Reason :: posix(). + connect(Address, Port, Opts) -> connect(Address,Port,Opts,infinity). +-spec connect(Address, Port, Options, Timeout) -> + {ok, Socket} | {error, Reason} when + Address :: ip_address() | hostname(), + Port :: port_number(), + Options :: [Opt :: term()], + Timeout :: timeout(), + Socket :: socket(), + Reason :: posix(). + connect(Address, Port, Opts, Time) -> Timer = inet:start_timer(Time), Res = (catch connect1(Address,Port,Opts,Timer)), @@ -72,6 +95,13 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) -> %% %% Listen on a tcp port %% + +-spec listen(Port, Options) -> {ok, ListenSocket} | {error, Reason} when + Port :: port_number(), + Options :: [Opt :: term()], + ListenSocket :: socket(), + Reason :: posix(). + listen(Port, Opts) -> Mod = mod(Opts, undefined), case Mod:getserv(Port) of @@ -85,6 +115,12 @@ listen(Port, Opts) -> %% %% Generic tcp accept %% + +-spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when + ListenSocket :: socket(), + Socket :: socket(), + Reason :: closed | timeout | posix(). + accept(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -93,6 +129,12 @@ accept(S) -> Error end. +-spec accept(ListenSocket, Timeout) -> {ok, Socket} | {error, Reason} when + ListenSocket :: socket(), + Timeout :: timeout(), + Socket :: socket(), + Reason :: closed | timeout | posix(). + accept(S, Time) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -104,6 +146,12 @@ accept(S, Time) when is_port(S) -> %% %% Generic tcp shutdown %% + +-spec shutdown(Socket, How) -> ok | {error, Reason} when + Socket :: socket(), + How :: read | write | read_write, + Reason :: posix(). + shutdown(S, How) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -115,12 +163,22 @@ shutdown(S, How) when is_port(S) -> %% %% Close %% + +-spec close(Socket) -> ok when + Socket :: socket(). + close(S) -> inet:tcp_close(S). %% %% Send %% + +-spec send(Socket, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Packet :: string() | binary(), + Reason :: posix(). + send(S, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -132,6 +190,14 @@ send(S, Packet) when is_port(S) -> %% %% Receive data from a socket (passive mode) %% + +-spec recv(Socket, Length) -> {ok, Packet} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Packet :: string() | binary() | HttpPacket, + Reason :: closed | posix(), + HttpPacket :: term(). + recv(S, Length) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -140,6 +206,14 @@ recv(S, Length) when is_port(S) -> Error end. +-spec recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Timeout :: timeout(), + Packet :: string() | binary() | HttpPacket, + Reason :: closed | posix(), + HttpPacket :: term(). + recv(S, Length, Time) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -159,6 +233,12 @@ unrecv(S, Data) when is_port(S) -> %% %% Set controlling process %% + +-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when + Socket :: socket(), + Pid :: pid(), + Reason :: closed | not_owner | posix(). + controlling_process(S, NewOwner) -> case inet_db:lookup_socket(S) of {ok, _Mod} -> % Just check that this is an open socket diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 99020c7b6c..7d14615c04 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.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 @@ -25,17 +25,44 @@ -include("inet_int.hrl"). +-type hostname() :: inet:hostname(). +-type ip_address() :: inet:ip_address(). +-type port_number() :: 0..65535. +-type posix() :: inet:posix(). +-type socket() :: port(). + +-spec open(Port) -> {ok, Socket} | {error, Reason} when + Port :: port_number(), + Socket :: socket(), + Reason :: posix(). + open(Port) -> open(Port, []). +-spec open(Port, Opts) -> {ok, Socket} | {error, Reason} when + Port :: port_number(), + Opts :: [Opt :: term()], + Socket :: socket(), + Reason :: posix(). + open(Port, Opts) -> Mod = mod(Opts, undefined), {ok,UP} = Mod:getserv(Port), Mod:open(UP, Opts). +-spec close(Socket) -> ok when + Socket :: socket(). + close(S) -> inet:udp_close(S). +-spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Address :: ip_address() | hostname(), + Port :: port_number(), + Packet :: string() | binary(), + Reason :: not_owner | posix(). + send(S, Address, Port, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -61,6 +88,15 @@ send(S, Packet) when is_port(S) -> Error end. +-spec recv(Socket, Length) -> + {ok, {Address, Port, Packet}} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Address :: ip_address(), + Port :: port_number(), + Packet :: string() | binary(), + Reason :: not_owner | posix(). + recv(S,Len) when is_port(S), is_integer(Len) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -69,6 +105,16 @@ recv(S,Len) when is_port(S), is_integer(Len) -> Error end. +-spec recv(Socket, Length, Timeout) -> + {ok, {Address, Port, Packet}} | {error, Reason} when + Socket :: socket(), + Length :: non_neg_integer(), + Timeout :: timeout(), + Address :: ip_address(), + Port :: port_number(), + Packet :: string() | binary(), + Reason :: not_owner | posix(). + recv(S,Len,Time) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> @@ -90,6 +136,10 @@ connect(S, Address, Port) when is_port(S) -> Error end. +-spec controlling_process(Socket, Pid) -> ok when + Socket :: socket(), + Pid :: pid(). + controlling_process(S, NewOwner) -> inet:udp_controlling_process(S, NewOwner). diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index 6343acd000..7d15f8bf83 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -166,7 +166,7 @@ start_link() -> stop() -> gen_server:call(global_name_server, stop, infinity). --spec sync() -> 'ok' | {'error', term()}. +-spec sync() -> 'ok' | {'error', Reason :: term()}. sync() -> case check_sync_nodes() of {error, _} = Error -> @@ -175,7 +175,7 @@ sync() -> gen_server:call(global_name_server, {sync, SyncNodes}, infinity) end. --spec sync([node()]) -> 'ok' | {'error', term()}. +-spec sync([node()]) -> 'ok' | {'error', Reason :: term()}. sync(Nodes) -> case check_sync_nodes(Nodes) of {error, _} = Error -> @@ -184,7 +184,10 @@ sync(Nodes) -> gen_server:call(global_name_server, {sync, SyncNodes}, infinity) end. --spec send(term(), term()) -> pid(). +-spec send(Name, Msg) -> Pid when + Name :: term(), + Msg :: term(), + Pid :: pid(). send(Name, Msg) -> case whereis_name(Name) of Pid when is_pid(Pid) -> @@ -195,7 +198,8 @@ send(Name, Msg) -> end. %% See OTP-3737. --spec whereis_name(term()) -> pid() | 'undefined'. +-spec whereis_name(Name) -> pid() | 'undefined' when + Name :: term(). whereis_name(Name) -> where(Name). @@ -219,13 +223,19 @@ node_disconnected(Node) -> %% undefined which one of them is used. %% Method blocks the name registration, but does not affect global locking. %%----------------------------------------------------------------- --spec register_name(term(), pid()) -> 'yes' | 'no'. +-spec register_name(Name, Pid) -> 'yes' | 'no' when + Name :: term(), + Pid :: pid(). register_name(Name, Pid) when is_pid(Pid) -> register_name(Name, Pid, fun random_exit_name/3). --type method() :: fun((term(), pid(), pid()) -> pid() | 'none'). +-type method() :: fun((Name :: term(), Pid :: pid(), Pid2 :: pid()) -> + pid() | 'none'). --spec register_name(term(), pid(), method()) -> 'yes' | 'no'. +-spec register_name(Name, Pid, Resolve) -> 'yes' | 'no' when + Name :: term(), + Pid :: pid(), + Resolve :: method(). register_name(Name, Pid, Method) when is_pid(Pid) -> Fun = fun(Nodes) -> case (where(Name) =:= undefined) andalso check_dupname(Name, Pid) of @@ -257,7 +267,8 @@ check_dupname(Name, Pid) -> end end. --spec unregister_name(term()) -> _. +-spec unregister_name(Name) -> _ when + Name :: term(). unregister_name(Name) -> case where(Name) of undefined -> @@ -273,11 +284,16 @@ unregister_name(Name) -> gen_server:call(global_name_server, {registrar, Fun}, infinity) end. --spec re_register_name(term(), pid()) -> _. +-spec re_register_name(Name, Pid) -> _ when + Name :: term(), + Pid :: pid(). re_register_name(Name, Pid) when is_pid(Pid) -> re_register_name(Name, Pid, fun random_exit_name/3). --spec re_register_name(term(), pid(), method()) -> _. +-spec re_register_name(Name, Pid, Resolve) -> _ when + Name :: term(), + Pid :: pid(), + Resolve :: method(). re_register_name(Name, Pid, Method) when is_pid(Pid) -> Fun = fun(Nodes) -> gen_server:multi_call(Nodes, @@ -288,7 +304,8 @@ re_register_name(Name, Pid, Method) when is_pid(Pid) -> ?trace({re_register_name, self(), Name, Pid, Method}), gen_server:call(global_name_server, {registrar, Fun}, infinity). --spec registered_names() -> [term()]. +-spec registered_names() -> [Name] when + Name :: term(). registered_names() -> MS = ets:fun2ms(fun({Name,_Pid,_M,_RP,_R}) -> Name end), ets:select(global_names, MS). @@ -329,19 +346,25 @@ register_name_external(Name, Pid, Method) when is_pid(Pid) -> unregister_name_external(Name) -> unregister_name(Name). --type id() :: {term(), term()}. +-type id() :: {ResourceId :: term(), LockRequesterId :: term()}. --spec set_lock(id()) -> boolean(). +-spec set_lock(Id) -> boolean() when + Id :: id(). set_lock(Id) -> set_lock(Id, [node() | nodes()], infinity, 1). -type retries() :: non_neg_integer() | 'infinity'. --spec set_lock(id(), [node()]) -> boolean(). +-spec set_lock(Id, Nodes) -> boolean() when + Id :: id(), + Nodes :: [node()]. set_lock(Id, Nodes) -> set_lock(Id, Nodes, infinity, 1). --spec set_lock(id(), [node()], retries()) -> boolean(). +-spec set_lock(Id, Nodes, Retries) -> boolean() when + Id :: id(), + Nodes :: [node()], + Retries :: retries(). set_lock(Id, Nodes, Retries) when is_integer(Retries), Retries >= 0 -> set_lock(Id, Nodes, Retries, 1); set_lock(Id, Nodes, infinity) -> @@ -363,11 +386,14 @@ set_lock({_ResourceId, _LockRequesterId} = Id, Nodes, Retries, Times) -> set_lock(Id, Nodes, dec(Retries), Times+1) end. --spec del_lock(id()) -> 'true'. +-spec del_lock(Id) -> 'true' when + Id :: id(). del_lock(Id) -> del_lock(Id, [node() | nodes()]). --spec del_lock(id(), [node()]) -> 'true'. +-spec del_lock(Id, Nodes) -> 'true' when + Id :: id(), + Nodes :: [node()]. del_lock({_ResourceId, _LockRequesterId} = Id, Nodes) -> ?trace({del_lock, {me,self()}, Id, {nodes,Nodes}}), gen_server:multi_call(Nodes, global_name_server, {del_lock, Id}), @@ -375,13 +401,25 @@ del_lock({_ResourceId, _LockRequesterId} = Id, Nodes) -> -type trans_fun() :: function() | {module(), atom()}. --spec trans(id(), trans_fun()) -> term(). +-spec trans(Id, Fun) -> Res | aborted when + Id :: id(), + Fun :: trans_fun(), + Res :: term(). trans(Id, Fun) -> trans(Id, Fun, [node() | nodes()], infinity). --spec trans(id(), trans_fun(), [node()]) -> term(). +-spec trans(Id, Fun, Nodes) -> Res | aborted when + Id :: id(), + Fun :: trans_fun(), + Nodes :: [node()], + Res :: term(). trans(Id, Fun, Nodes) -> trans(Id, Fun, Nodes, infinity). --spec trans(id(), trans_fun(), [node()], retries()) -> term(). +-spec trans(Id, Fun, Nodes, Retries) -> Res | aborted when + Id :: id(), + Fun :: trans_fun(), + Nodes :: [node()], + Retries :: retries(), + Res :: term(). trans(Id, Fun, Nodes, Retries) -> case set_lock(Id, Nodes, Retries) of true -> @@ -1928,7 +1966,10 @@ resolve_it(Method, Name, Pid1, Pid2) -> minmax(P1,P2) -> if node(P1) < node(P2) -> {P1, P2}; true -> {P2, P1} end. --spec random_exit_name(term(), pid(), pid()) -> pid(). +-spec random_exit_name(Name, Pid1, Pid2) -> 'none' when + Name :: term(), + Pid1 :: pid(), + Pid2 :: pid(). random_exit_name(Name, Pid, Pid2) -> {Min, Max} = minmax(Pid, Pid2), error_logger:info_msg("global: Name conflict terminating ~w\n", @@ -1936,12 +1977,19 @@ random_exit_name(Name, Pid, Pid2) -> exit(Max, kill), Min. +-spec random_notify_name(Name, Pid1, Pid2) -> 'none' when + Name :: term(), + Pid1 :: pid(), + Pid2 :: pid(). random_notify_name(Name, Pid, Pid2) -> {Min, Max} = minmax(Pid, Pid2), Max ! {global_name_conflict, Name}, Min. --spec notify_all_name(term(), pid(), pid()) -> 'none'. +-spec notify_all_name(Name, Pid1, Pid2) -> 'none' when + Name :: term(), + Pid1 :: pid(), + Pid2 :: pid(). notify_all_name(Name, Pid, Pid2) -> Pid ! {global_name_conflict, Name, Pid2}, Pid2 ! {global_name_conflict, Name, Pid}, diff --git a/lib/kernel/src/global_group.erl b/lib/kernel/src/global_group.erl index 7e141ac5c7..025a9b8a5b 100644 --- a/lib/kernel/src/global_group.erl +++ b/lib/kernel/src/global_group.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 @@ -62,9 +62,10 @@ -type sync_state() :: 'no_conf' | 'synced'. -type group_name() :: atom(). --type group_tuple() :: {group_name(), [node()]} - | {group_name(), publish_type(), [node()]}. - +-type group_tuple() :: {GroupName :: group_name(), [node()]} + | {GroupName :: group_name(), + PublishType :: publish_type(), + [node()]}. %%%==================================================================================== %%% The state of the global_group process @@ -97,11 +98,14 @@ %%% External exported %%%==================================================================================== --spec global_groups() -> {group_name(), [group_name()]} | 'undefined'. +-spec global_groups() -> {GroupName, GroupNames} | undefined when + GroupName :: group_name(), + GroupNames :: [GroupName]. global_groups() -> request(global_groups). --spec monitor_nodes(boolean()) -> 'ok'. +-spec monitor_nodes(Flag) -> 'ok' when + Flag :: boolean(). monitor_nodes(Flag) -> case Flag of true -> request({monitor_nodes, Flag}); @@ -109,30 +113,41 @@ monitor_nodes(Flag) -> _ -> {error, not_boolean} end. --spec own_nodes() -> [node()]. +-spec own_nodes() -> Nodes when + Nodes :: [Node :: node()]. own_nodes() -> request(own_nodes). -type name() :: atom(). -type where() :: {'node', node()} | {'group', group_name()}. --spec registered_names(where()) -> [name()]. +-spec registered_names(Where) -> Names when + Where :: where(), + Names :: [Name :: name()]. registered_names(Arg) -> request({registered_names, Arg}). --spec send(name(), term()) -> pid() | {'badarg', {name(), term()}}. +-spec send(Name, Msg) -> pid() | {'badarg', {Name, Msg}} when + Name :: name(), + Msg :: term(). send(Name, Msg) -> request({send, Name, Msg}). --spec send(where(), name(), term()) -> pid() | {'badarg', {name(), term()}}. +-spec send(Where, Name, Msg) -> pid() | {'badarg', {Name, Msg}} when + Where :: where(), + Name :: name(), + Msg :: term(). send(Group, Name, Msg) -> request({send, Group, Name, Msg}). --spec whereis_name(name()) -> pid() | 'undefined'. +-spec whereis_name(Name) -> pid() | 'undefined' when + Name :: name(). whereis_name(Name) -> request({whereis_name, Name}). --spec whereis_name(where(), name()) -> pid() | 'undefined'. +-spec whereis_name(Where, Name) -> pid() | 'undefined' when + Where :: where(), + Name :: name(). whereis_name(Group, Name) -> request({whereis_name, Group, Name}). @@ -155,14 +170,14 @@ ng_add_check(Node, OthersNG) -> ng_add_check(Node, PubType, OthersNG) -> request({ng_add_check, Node, PubType, OthersNG}). --type info_item() :: {'state', sync_state()} - | {'own_group_name', group_name()} - | {'own_group_nodes', [node()]} - | {'synched_nodes', [node()]} - | {'sync_error', [node()]} - | {'no_contact', [node()]} - | {'other_groups', [group_tuple()]} - | {'monitoring', [pid()]}. +-type info_item() :: {'state', State :: sync_state()} + | {'own_group_name', GroupName :: group_name()} + | {'own_group_nodes', Nodes :: [node()]} + | {'synched_nodes', Nodes :: [node()]} + | {'sync_error', Nodes :: [node()]} + | {'no_contact', Nodes :: [node()]} + | {'other_groups', Groups :: [group_tuple()]} + | {'monitoring', Pids :: [pid()]}. -spec info() -> [info_item()]. info() -> @@ -1012,6 +1027,7 @@ grp_tuple({Name, normal, Nodes}) -> %%% The special process which checks that all nodes in the own global group %%% agrees on the configuration. %%%==================================================================================== +-spec sync_init(_, _, _, _) -> no_return(). sync_init(Type, Cname, PubType, Nodes) -> {Up, Down} = sync_check_node(lists:delete(node(), Nodes), [], []), sync_check_init(Type, Up, Cname, Nodes, Down, PubType). @@ -1032,9 +1048,11 @@ sync_check_node([Node|Nodes], Up, Down) -> %%% Check that all nodes are in agreement of the global %%% group configuration. %%%------------------------------------------------------------- +-spec sync_check_init(_, _, _, _, _, _) -> no_return(). sync_check_init(Type, Up, Cname, Nodes, Down, PubType) -> sync_check_init(Type, Up, Cname, Nodes, 3, [], Down, PubType). +-spec sync_check_init(_, _, _, _, _, _, _, _) -> no_return(). sync_check_init(_Type, NoContact, _Cname, _Nodes, 0, ErrorNodes, Down, _PubType) -> case ErrorNodes of [] -> diff --git a/lib/kernel/src/global_search.erl b/lib/kernel/src/global_search.erl index b723e18a1b..0bf53e29b8 100644 --- a/lib/kernel/src/global_search.erl +++ b/lib/kernel/src/global_search.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 @@ -69,6 +69,7 @@ start(Flag, Arg) -> %%%==================================================================================== %%%==================================================================================== +-spec init_send(_) -> no_return(). init_send({any, NodesList, Name, Msg, From}) -> case whereis_any_loop(NodesList, Name) of undefined -> @@ -115,6 +116,7 @@ init_send({node, Node, Name, Msg, From}) -> %%%==================================================================================== %%%==================================================================================== +-spec init_whereis(_) -> no_return(). init_whereis({any, NodesList, Name, From}) -> R = whereis_any_loop(NodesList, Name), gen_server:cast(global_group, {find_name_res, R, self(), From}), @@ -146,6 +148,7 @@ init_whereis({node, Node, Name, From}) -> %%%==================================================================================== %%%==================================================================================== %%%==================================================================================== +-spec init_names(_) -> no_return(). init_names({group, Nodes, From}) -> case names_group_loop(Nodes) of group_down -> diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl index e78acfc7a6..255ae4e51b 100644 --- a/lib/kernel/src/heart.erl +++ b/lib/kernel/src/heart.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 @@ -85,19 +85,21 @@ init(Starter, Parent) -> Starter ! {start_error, self()} end. --spec set_cmd(string()) -> 'ok' | {'error', {'bad_cmd', string()}}. +-spec set_cmd(Cmd) -> 'ok' | {'error', {'bad_cmd', Cmd}} when + Cmd :: string(). set_cmd(Cmd) -> heart ! {self(), set_cmd, Cmd}, wait(). --spec get_cmd() -> 'ok'. +-spec get_cmd() -> {ok, Cmd} when + Cmd :: string(). get_cmd() -> heart ! {self(), get_cmd}, wait(). --spec clear_cmd() -> {'ok', string()}. +-spec clear_cmd() -> ok. clear_cmd() -> heart ! {self(), clear_cmd}, diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 327e0f93f1..5649188c38 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.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 @@ -63,7 +63,8 @@ %% timer interface -export([start_timer/1, timeout/1, timeout/2, stop_timer/1]). --export_type([ip_address/0, socket/0]). +-export_type([family_option/0, hostent/0, hostname/0, ip4_address/0, + ip6_address/0, ip_address/0, posix/0, socket/0]). %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). @@ -79,8 +80,16 @@ %%% --------------------------------- %%% Contract type definitions + +-type hostent() :: #hostent{}. +-type hostname() :: atom() | string(). +-type ip4_address() :: {0..255,0..255,0..255,0..255}. +-type ip6_address() :: {0..65535,0..65535,0..65535,0..65535, + 0..65535,0..65535,0..65535,0..65535}. +-type ip_address() :: ip4_address() | ip6_address(). +-type ip_port() :: 0..65535. +-type posix() :: exbadport | exbadseq | file:posix(). -type socket() :: port(). --type posix() :: atom(). -type socket_setopt() :: {'raw', non_neg_integer(), non_neg_integer(), binary()} | @@ -106,7 +115,7 @@ {'packet', 0 | 1 | 2 | 4 | 'raw' | 'sunrm' | 'asn1' | 'cdr' | 'fcgi' | 'line' | 'tpkt' | 'http' | 'httph' | 'http_bin' | 'httph_bin' } | - {'mode', list() | binary()} | + {'mode', 'list' | 'binary'} | {'port', 'port', 'term'} | {'exit_on_close', boolean()} | {'low_watermark', non_neg_integer()} | @@ -195,12 +204,13 @@ %%% --------------------------------- --spec get_rc() -> [{any(),any()}]. +-spec get_rc() -> [{Par :: any(), Val :: any()}]. get_rc() -> inet_db:get_rc(). --spec close(Socket :: socket()) -> 'ok'. +-spec close(Socket) -> 'ok' when + Socket :: socket(). close(Socket) -> prim_inet:close(Socket), @@ -211,8 +221,10 @@ close(Socket) -> ok end. --spec peername(Socket :: socket()) -> - {'ok', {ip_address(), non_neg_integer()}} | {'error', posix()}. +-spec peername(Socket) -> {ok, {Address, Port}} | {error, posix()} when + Socket :: socket(), + Address :: ip_address(), + Port :: non_neg_integer(). peername(Socket) -> prim_inet:peername(Socket). @@ -226,8 +238,10 @@ setpeername(Socket, undefined) -> prim_inet:setpeername(Socket, undefined). --spec sockname(Socket :: socket()) -> - {'ok', {ip_address(), non_neg_integer()}} | {'error', posix()}. +-spec sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} when + Socket :: socket(), + Address :: ip_address(), + Port :: non_neg_integer(). sockname(Socket) -> prim_inet:sockname(Socket). @@ -260,8 +274,10 @@ send(Socket, Packet) -> setopts(Socket, Opts) -> prim_inet:setopts(Socket, Opts). --spec getopts(Socket :: socket(), Opts :: [socket_getopt()]) -> - {'ok', [socket_setopt()]} | {'error', posix()}. +-spec getopts(Socket, Options) -> + {'ok', [socket_setopt()]} | {'error', posix()} when + Socket :: socket(), + Options :: [socket_getopt()]. getopts(Socket, Opts) -> prim_inet:getopts(Socket, Opts). @@ -272,7 +288,19 @@ getopts(Socket, Opts) -> getifaddrs(Socket) -> prim_inet:getifaddrs(Socket). --spec getifaddrs() -> {'ok', [string()]} | {'error', posix()}. +-spec getifaddrs() -> {ok, Iflist} | {error, posix()} when + Iflist :: [{Ifname,[Ifopt]}], + Ifname :: string(), + Ifopt :: {flag,[Flag]} | {addr,Addr} | {netmask,Netmask} + | {broadaddr,Broadaddr} | {dstaddr,Dstaddr} + | {hwaddr,Hwaddr}, + Flag :: up | broadcast | loopback | pointtopoint + | running | multicast, + Addr :: ip_address(), + Netmask :: ip_address(), + Broadaddr :: ip_address(), + Dstaddr :: ip_address(), + Hwaddr :: [byte()]. getifaddrs() -> withsocket(fun(S) -> prim_inet:getifaddrs(S) end). @@ -371,7 +399,8 @@ popf(_Socket) -> % use of the DHCP-protocol % should never fail --spec gethostname() -> {'ok', string()}. +-spec gethostname() -> {'ok', Hostname} when + Hostname :: string(). gethostname() -> case inet_udp:open(0,[]) of @@ -402,19 +431,23 @@ getstat(Socket) -> getstat(Socket,What) -> prim_inet:getstat(Socket, What). --spec gethostbyname(Name :: string() | atom()) -> - {'ok', #hostent{}} | {'error', posix()}. +-spec gethostbyname(Hostname) -> {ok, Hostent} | {error, posix()} when + Hostname :: hostname(), + Hostent :: hostent(). gethostbyname(Name) -> gethostbyname_tm(Name, inet, false). --spec gethostbyname(Name :: string() | atom(), Family :: family_option()) -> - {'ok', #hostent{}} | {'error', posix()}. +-spec gethostbyname(Hostname, Family) -> + {ok, Hostent} | {error, posix()} when + Hostname :: hostname(), + Family :: family_option(), + Hostent :: hostent(). gethostbyname(Name,Family) -> gethostbyname_tm(Name, Family, false). --spec gethostbyname(Name :: string() | atom(), +-spec gethostbyname(Name :: hostname(), Family :: family_option(), Timeout :: non_neg_integer() | 'infinity') -> {'ok', #hostent{}} | {'error', posix()}. @@ -439,8 +472,9 @@ gethostbyname_tm(Name,Family,Timer) -> gethostbyname_tm(Name, Family, Timer, Opts). --spec gethostbyaddr(Address :: string() | ip_address()) -> - {'ok', #hostent{}} | {'error', posix()}. +-spec gethostbyaddr(Address) -> {ok, Hostent} | {error, posix()} when + Address :: string() | ip_address(), + Hostent :: hostent(). gethostbyaddr(Address) -> gethostbyaddr_tm(Address, false). @@ -491,14 +525,15 @@ getfd(Socket) -> %% Lookup an ip address %% --spec getaddr(Host :: ip_address() | string() | atom(), - Family :: family_option()) -> - {'ok', ip_address()} | {'error', posix()}. +-spec getaddr(Host, Family) -> {ok, Address} | {error, posix()} when + Host :: ip_address() | hostname(), + Family :: family_option(), + Address :: ip_address(). getaddr(Address, Family) -> getaddr(Address, Family, infinity). --spec getaddr(Host :: ip_address() | string() | atom(), +-spec getaddr(Host :: ip_address() | hostname(), Family :: family_option(), Timeout :: non_neg_integer() | 'infinity') -> {'ok', ip_address()} | {'error', posix()}. @@ -515,9 +550,11 @@ getaddr_tm(Address, Family, Timer) -> Error -> Error end. --spec getaddrs(Host :: ip_address() | string() | atom(), - Family :: family_option()) -> - {'ok', [ip_address()]} | {'error', posix()}. +-spec getaddrs(Host, Family) -> + {ok, Addresses} | {error, posix()} when + Host :: ip_address() | hostname(), + Family :: family_option(), + Addresses :: [ip_address()]. getaddrs(Address, Family) -> getaddrs(Address, Family, infinity). @@ -1237,7 +1274,8 @@ port_list(Name) -> %% utils %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec format_error(posix()) -> string(). +-spec format_error(Posix) -> string() when + Posix :: posix(). format_error(exbadport) -> "invalid port state"; format_error(exbadseq) -> "bad command sequence"; diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index 93563c6011..d5a8a2f134 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -47,18 +47,93 @@ false -> ok end). +-type res_option() :: + {alt_nameservers, [nameserver()]} + | {edns, 0 | false} + | {inet6, boolean()} + | {nameservers, [nameserver()]} + | {recurse, boolean()} + | {retry, integer()} + | {timeout, integer()} + | {udp_payload_size, integer()} + | {usevc, boolean()}. + +-type nameserver() :: {inet:ip_address(), Port :: 1..65535}. + +-type res_error() :: formerr | qfmterror | servfail | nxdomain | + notimp | refused | badvers | timeout. + +-type dns_name() :: string(). + +-type rr_type() :: a | aaaa | cname | gid | hinfo | ns | mb | md | mg | mf + | minfo | mx | naptr | null | ptr | soa | spf | srv | txt + | uid | uinfo | unspec | wks. + +-type dns_class() :: in | chaos | hs | any. + +-opaque dns_msg() :: term(). + +-type dns_data() :: + dns_name() + | inet:ip4_address() + | inet:ip6_address() + | {MName :: dns_name(), + RName :: dns_name(), + Serial :: integer(), + Refresh :: integer(), + Retry :: integer(), + Expiry :: integer(), + Minimum :: integer()} + | {inet:ip4_address(), Proto :: integer(), BitMap :: binary()} + | {CpuString :: string(), OsString :: string()} + | {RM :: dns_name(), EM :: dns_name()} + | {Prio :: integer(), dns_name()} + | {Prio :: integer(),Weight :: integer(),Port :: integer(),dns_name()} + | {Order :: integer(),Preference :: integer(),Flags :: string(), + Services :: string(),Regexp :: string(), dns_name()} + | [string()] + | binary(). + %% -------------------------------------------------------------------------- %% resolve: %% %% Nameserver query %% +-spec resolve(Name, Class, Type) -> {ok, dns_msg()} | Error when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Error :: {error, Reason} | {error,{Reason,dns_msg()}}, + Reason :: inet:posix() | res_error(). + resolve(Name, Class, Type) -> resolve(Name, Class, Type, [], infinity). +-spec resolve(Name, Class, Type, Opts) -> + {ok, dns_msg()} | Error when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [Opt], + Opt :: res_option() | verbose | atom(), + Error :: {error, Reason} | {error,{Reason,dns_msg()}}, + Reason :: inet:posix() | res_error(). + resolve(Name, Class, Type, Opts) -> resolve(Name, Class, Type, Opts, infinity). +-spec resolve(Name, Class, Type, Opts, Timeout) -> + {ok, dns_msg()} | Error when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [Opt], + Opt :: res_option() | verbose | atom(), + Timeout :: timeout(), + Error :: {error, Reason} | {error,{Reason,dns_msg()}}, + Reason :: inet:posix() | res_error(). + resolve(Name, Class, Type, Opts, Timeout) -> case nsdname(Name) of {ok, Nm} -> @@ -76,12 +151,30 @@ resolve(Name, Class, Type, Opts, Timeout) -> %% Convenience wrapper to resolve/3,4,5 that filters out all answer data %% fields of the class and type asked for. +-spec lookup(Name, Class, Type) -> [dns_data()] when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(). + lookup(Name, Class, Type) -> lookup(Name, Class, Type, []). +-spec lookup(Name, Class, Type, Opts) -> [dns_data()] when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [res_option() | verbose]. + lookup(Name, Class, Type, Opts) -> lookup(Name, Class, Type, Opts, infinity). +-spec lookup(Name, Class, Type, Opts, Timeout) -> [dns_data()] when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Opts :: [res_option() | verbose], + Timeout :: timeout(). + lookup(Name, Class, Type, Opts, Timeout) -> lookup_filter(resolve(Name, Class, Type, Opts, Timeout), Class, Type). @@ -101,17 +194,55 @@ lookup_filter({error,_}, _, _) -> []. %% %% To be deprecated +-spec nslookup(Name, Class, Type) -> {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Reason :: inet:posix() | res_error(). + nslookup(Name, Class, Type) -> do_nslookup(Name, Class, Type, [], infinity). +-spec nslookup(Name, Class, Type, Timeout) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Timeout :: timeout(), + Reason :: inet:posix() | res_error(); + (Name, Class, Type, Nameservers) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Nameservers :: [nameserver()], + Reason :: inet:posix() | res_error(). + nslookup(Name, Class, Type, Timeout) when is_integer(Timeout), Timeout >= 0 -> do_nslookup(Name, Class, Type, [], Timeout); nslookup(Name, Class, Type, NSs) -> % For backwards compatibility nnslookup(Name, Class, Type, NSs). % with OTP R6B only +-spec nnslookup(Name, Class, Type, Nameservers) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Nameservers :: [nameserver()], + Reason :: inet:posix(). + nnslookup(Name, Class, Type, NSs) -> nnslookup(Name, Class, Type, NSs, infinity). +-spec nnslookup(Name, Class, Type, Nameservers, Timeout) -> + {ok, dns_msg()} | {error, Reason} when + Name :: dns_name() | inet:ip_address(), + Class :: dns_class(), + Type :: rr_type(), + Timeout :: timeout(), + Nameservers :: [nameserver()], + Reason :: inet:posix(). + nnslookup(Name, Class, Type, NSs, Timeout) -> do_nslookup(Name, Class, Type, [{nameservers,NSs}], Timeout). @@ -192,8 +323,19 @@ make_options(Opts, [Name|Names]) -> %% %% -------------------------------------------------------------------------- +-spec gethostbyaddr(Address) -> {ok, Hostent} | {error, Reason} when + Address :: inet:ip_address(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + gethostbyaddr(IP) -> gethostbyaddr_tm(IP,false). +-spec gethostbyaddr(Address, Timeout) -> {ok, Hostent} | {error, Reason} when + Address :: inet:ip_address(), + Timeout :: timeout(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + gethostbyaddr(IP,Timeout) -> Timer = inet:start_timer(Timeout), Res = gethostbyaddr_tm(IP,Timer), @@ -249,6 +391,11 @@ res_gethostbyaddr(Addr, IP, Timer) -> %% Caches the answer. %% -------------------------------------------------------------------------- +-spec gethostbyname(Name) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + gethostbyname(Name) -> case inet_db:res_option(inet6) of true -> @@ -257,9 +404,23 @@ gethostbyname(Name) -> gethostbyname_tm(Name, inet, false) end. +-spec gethostbyname(Name, Family) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Hostent :: inet:hostent(), + Family :: inet:family_option(), + Reason :: inet:posix() | res_error(). + gethostbyname(Name,Family) -> gethostbyname_tm(Name,Family,false). +-spec gethostbyname(Name, Family, Timeout) -> + {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Hostent :: inet:hostent(), + Timeout :: timeout(), + Family :: inet:family_option(), + Reason :: inet:posix() | res_error(). + gethostbyname(Name,Family,Timeout) -> Timer = inet:start_timer(Timeout), Res = gethostbyname_tm(Name,Family,Timer), @@ -298,14 +459,27 @@ gethostbyname_tm(_Name, _Family, _Timer) -> %% %% getbyname(domain_name(), Type) => {ok, hostent()} | {error, Reason} %% -%% where domain_name() is domain string or atom and Type is ?S_A, ?S_MX ... +%% where domain_name() is domain string and Type is ?S_A, ?S_MX ... %% %% Caches the answer. %% -------------------------------------------------------------------------- +-spec getbyname(Name, Type) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Type :: rr_type(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + getbyname(Name, Type) -> getbyname_tm(Name,Type,false). +-spec getbyname(Name, Type, Timeout) -> {ok, Hostent} | {error, Reason} when + Name :: dns_name(), + Type :: rr_type(), + Timeout :: timeout(), + Hostent :: inet:hostent(), + Reason :: inet:posix() | res_error(). + getbyname(Name, Type, Timeout) -> Timer = inet:start_timer(Timeout), Res = getbyname_tm(Name, Type, Timer), diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 9a4089ab19..60bd96f332 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.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 @@ -39,8 +39,10 @@ getserv(Name) when is_atom(Name) -> inet:getservbyname(Name,udp). getaddr(Address) -> inet:getaddr(Address, inet). getaddr(Address,Timer) -> inet:getaddr_tm(Address, inet, Timer). +-spec open(_) -> {ok, inet:socket()} | {error, atom()}. open(Port) -> open(Port, []). +-spec open(_, _) -> {ok, inet:socket()} | {error, atom()}. open(Port, Opts) -> case inet:udp_options( [{port,Port}, {recbuf, ?RECBUF} | Opts], @@ -69,6 +71,8 @@ recv(S,Len) -> recv(S,Len,Time) -> prim_inet:recvfrom(S, Len, Time). +-spec close(inet:socket()) -> ok. + close(S) -> inet:udp_close(S). diff --git a/lib/kernel/src/net_adm.erl b/lib/kernel/src/net_adm.erl index 737b1ecee9..9b2dac9544 100644 --- a/lib/kernel/src/net_adm.erl +++ b/lib/kernel/src/net_adm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -35,7 +35,11 @@ %% Try to read .hosts.erlang file in %% 1. cwd , 2. $HOME 3. init:root_dir() --spec host_file() -> [atom()] | {'error',atom() | {integer(),atom(),_}}. +-spec host_file() -> Hosts | {error, Reason} when + Hosts :: [Host :: atom()], + %% Copied from file:path_consult/2: + Reason :: file:posix() | badarg | terminated | system_limit + | {Line :: integer(), Mod :: module(), Term :: term()}. host_file() -> Home = case init:get_argument(home) of @@ -50,7 +54,8 @@ host_file() -> %% Check whether a node is up or down %% side effect: set up a connection to Node if there not yet is one. --spec ping(atom()) -> 'pang' | 'pong'. +-spec ping(Node) -> pong | pang when + Node :: atom(). ping(Node) when is_atom(Node) -> case catch gen:call({net_kernel, Node}, @@ -63,7 +68,8 @@ ping(Node) when is_atom(Node) -> pang end. --spec localhost() -> string(). +-spec localhost() -> Name when + Name :: string(). localhost() -> {ok, Host} = inet:gethostname(), @@ -73,12 +79,20 @@ localhost() -> end. --spec names() -> {'ok', [{string(), integer()}]} | {'error', _}. +-spec names() -> {ok, [{Name, Port}]} | {error, Reason} when + Name :: string(), + Port :: non_neg_integer(), + Reason :: address | file:posix(). names() -> names(localhost()). --spec names(atom() | string()) -> {'ok', [{string(), integer()}]} | {'error', _}. + +-spec names(Host) -> {ok, [{Name, Port}]} | {error, Reason} when + Host :: atom() | string(), + Name :: string(), + Port :: non_neg_integer(), + Reason :: address | file:posix(). names(Hostname) -> case inet:gethostbyname(Hostname) of @@ -88,8 +102,9 @@ names(Hostname) -> Else end. --spec dns_hostname(atom() | string()) -> - {'ok', string()} | {'error', atom() | string()}. +-spec dns_hostname(Host) -> {ok, Name} | {error, Host} when + Host :: atom() | string(), + Name :: string(). dns_hostname(Hostname) -> case inet:gethostbyname(Hostname) of @@ -164,7 +179,8 @@ collect_new(Sofar, Nodelist) -> world() -> world(silent). --spec world(verbosity()) -> [node()]. +-spec world(Arg) -> [node()] when + Arg :: verbosity(). world(Verbose) -> case net_adm:host_file() of @@ -172,12 +188,15 @@ world(Verbose) -> Hosts -> expand_hosts(Hosts, Verbose) end. --spec world_list([atom()]) -> [node()]. +-spec world_list(Hosts) -> [node()] when + Hosts :: [atom()]. world_list(Hosts) when is_list(Hosts) -> expand_hosts(Hosts, silent). --spec world_list([atom()], verbosity()) -> [node()]. +-spec world_list(Hosts, Arg) -> [node()] when + Hosts :: [atom()], + Arg :: verbosity(). world_list(Hosts, Verbose) when is_list(Hosts) -> expand_hosts(Hosts, Verbose). diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl index 5228d4fe01..9e3d730cee 100644 --- a/lib/kernel/src/net_kernel.erl +++ b/lib/kernel/src/net_kernel.erl @@ -145,8 +145,15 @@ %% Interface functions kernel_apply(M,F,A) -> request({apply,M,F,A}). + +-spec allow(Nodes) -> ok | error when + Nodes :: [node()]. allow(Nodes) -> request({allow, Nodes}). + longnames() -> request(longnames). + +-spec stop() -> ok | {error, Reason} when + Reason :: not_allowed | not_found. stop() -> erl_distribution:stop(). node_info(Node) -> get_node_info(Node). @@ -158,10 +165,28 @@ i(Node) -> print_info(Node). verbose(Level) when is_integer(Level) -> request({verbose, Level}). +-spec set_net_ticktime(NetTicktime, TransitionPeriod) -> Res when + NetTicktime :: pos_integer(), + TransitionPeriod :: non_neg_integer(), + Res :: unchanged + | change_initiated + | {ongoing_change_to, NewNetTicktime}, + NewNetTicktime :: pos_integer(). set_net_ticktime(T, TP) when is_integer(T), T > 0, is_integer(TP), TP >= 0 -> ticktime_res(request({new_ticktime, T*250, TP*1000})). + +-spec set_net_ticktime(NetTicktime) -> Res when + NetTicktime :: pos_integer(), + Res :: unchanged + | change_initiated + | {ongoing_change_to, NewNetTicktime}, + NewNetTicktime :: pos_integer(). set_net_ticktime(T) when is_integer(T) -> set_net_ticktime(T, ?DEFAULT_TRANSITION_PERIOD). + +-spec get_net_ticktime() -> Res when + Res :: NetTicktime | {ongoing_change_to, NetTicktime} | ignored, + NetTicktime :: pos_integer(). get_net_ticktime() -> ticktime_res(request(ticktime)). @@ -171,6 +196,9 @@ get_net_ticktime() -> %% flags (we may want to move it elsewhere later). In order to easily %% be backward compatible, errors are created here when process_flag() %% fails. +-spec monitor_nodes(Flag) -> ok | Error when + Flag :: boolean(), + Error :: error | {error, term()}. monitor_nodes(Flag) -> case catch process_flag(monitor_nodes, Flag) of true -> ok; @@ -178,6 +206,13 @@ monitor_nodes(Flag) -> _ -> mk_monitor_nodes_error(Flag, []) end. +-spec monitor_nodes(Flag, Options) -> ok | Error when + Flag :: boolean(), + Options :: [Option], + Option :: {node_type, NodeType} + | nodedown_reason, + NodeType :: visible | hidden | all, + Error :: error | {error, term()}. monitor_nodes(Flag, Opts) -> case catch process_flag({monitor_nodes, Opts}, Flag) of true -> ok; @@ -209,6 +244,8 @@ publish_on_node(Node) when is_atom(Node) -> update_publish_nodes(Ns) -> request({update_publish_nodes, Ns}). +-spec connect_node(Node) -> boolean() | ignored when + Node :: node(). %% explicit connects connect_node(Node) when is_atom(Node) -> request({connect, normal, Node}). diff --git a/lib/kernel/src/os.erl b/lib/kernel/src/os.erl index d1feae771d..f6769df585 100644 --- a/lib/kernel/src/os.erl +++ b/lib/kernel/src/os.erl @@ -24,7 +24,10 @@ -include("file.hrl"). --spec type() -> 'vxworks' | {'unix',atom()} | {'win32',atom()} | {'ose',atom()}. +-spec type() -> vxworks | {Osfamily, Osname} when + Osfamily :: unix | win32, + Osname :: atom(). + type() -> case erlang:system_info(os_type) of {vxworks, _} -> @@ -32,18 +35,27 @@ type() -> Else -> Else end. --spec version() -> string() | {non_neg_integer(),non_neg_integer(),non_neg_integer()}. +-spec version() -> VersionString | {Major, Minor, Release} when + VersionString :: string(), + Major :: non_neg_integer(), + Minor :: non_neg_integer(), + Release :: non_neg_integer(). version() -> erlang:system_info(os_version). --spec find_executable(string()) -> string() | 'false'. +-spec find_executable(Name) -> Filename | 'false' when + Name :: string(), + Filename :: string(). find_executable(Name) -> case os:getenv("PATH") of false -> find_executable(Name, []); Path -> find_executable(Name, Path) end. --spec find_executable(string(), string()) -> string() | 'false'. +-spec find_executable(Name, Path) -> Filename | 'false' when + Name :: string(), + Path :: string(), + Filename :: string(). find_executable(Name, Path) -> Extensions = extensions(), case filename:pathtype(Name) of @@ -147,7 +159,8 @@ extensions() -> end. %% Executes the given command in the default shell for the operating system. --spec cmd(atom() | string() | [string()]) -> string(). +-spec cmd(Command) -> string() when + Command :: atom() | io_lib:chars(). cmd(Cmd) -> validate(Cmd), case type() of diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl index 956a900adc..0d5838716e 100644 --- a/lib/kernel/src/pg2.erl +++ b/lib/kernel/src/pg2.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 @@ -30,17 +30,19 @@ %%% Exported functions %%% --spec start_link() -> {'ok', pid()} | {'error', term()}. +-spec start_link() -> {'ok', pid()} | {'error', any()}. start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). --spec start() -> {'ok', pid()} | {'error', term()}. +-spec start() -> {'ok', pid()} | {'error', any()}. start() -> ensure_started(). --spec create(term()) -> 'ok'. +-type name() :: any(). + +-spec create(Name :: name()) -> 'ok'. create(Name) -> ensure_started(), @@ -55,9 +57,7 @@ create(Name) -> ok end. --type name() :: term(). - --spec delete(name()) -> 'ok'. +-spec delete(Name :: name()) -> 'ok'. delete(Name) -> ensure_started(), @@ -67,7 +67,8 @@ delete(Name) -> end), ok. --spec join(name(), pid()) -> 'ok' | {'error', {'no_such_group', term()}}. +-spec join(Name, Pid :: pid()) -> 'ok' | {'error', {'no_such_group', Name}} + when Name :: name(). join(Name, Pid) when is_pid(Pid) -> ensure_started(), @@ -83,7 +84,8 @@ join(Name, Pid) when is_pid(Pid) -> ok end. --spec leave(name(), pid()) -> 'ok' | {'error', {'no_such_group', name()}}. +-spec leave(Name, Pid :: pid()) -> 'ok' | {'error', {'no_such_group', Name}} + when Name :: name(). leave(Name, Pid) when is_pid(Pid) -> ensure_started(), @@ -99,10 +101,9 @@ leave(Name, Pid) when is_pid(Pid) -> ok end. --type get_members_ret() :: [pid()] | {'error', {'no_such_group', name()}}. +-spec get_members(Name) -> [pid()] | {'error', {'no_such_group', Name}} + when Name :: name(). --spec get_members(name()) -> get_members_ret(). - get_members(Name) -> ensure_started(), case ets:member(pg2_table, {group, Name}) of @@ -112,7 +113,8 @@ get_members(Name) -> {error, {no_such_group, Name}} end. --spec get_local_members(name()) -> get_members_ret(). +-spec get_local_members(Name) -> [pid()] | {'error', {'no_such_group', Name}} + when Name :: name(). get_local_members(Name) -> ensure_started(), @@ -123,15 +125,15 @@ get_local_members(Name) -> {error, {no_such_group, Name}} end. --spec which_groups() -> [name()]. +-spec which_groups() -> [Name :: name()]. which_groups() -> ensure_started(), all_groups(). --type gcp_error_reason() :: {'no_process', term()} | {'no_such_group', term()}. - --spec get_closest_pid(term()) -> pid() | {'error', gcp_error_reason()}. +-spec get_closest_pid(Name) -> pid() | {'error', Reason} when + Name :: name(), + Reason :: {'no_process', Name} | {'no_such_group', Name}. get_closest_pid(Name) -> case get_local_members(Name) of @@ -157,7 +159,9 @@ get_closest_pid(Name) -> -record(state, {}). --spec init([]) -> {'ok', #state{}}. +-opaque state() :: #state{}. + +-spec init(Arg :: []) -> {'ok', state()}. init([]) -> Ns = nodes(), @@ -169,13 +173,13 @@ init([]) -> pg2_table = ets:new(pg2_table, [ordered_set, protected, named_table]), {ok, #state{}}. --type call() :: {'create', name()} - | {'delete', name()} - | {'join', name(), pid()} - | {'leave', name(), pid()}. - --spec handle_call(call(), _, #state{}) -> - {'reply', 'ok', #state{}}. +-spec handle_call(Call :: {'create', Name} + | {'delete', Name} + | {'join', Name, Pid :: pid()} + | {'leave', Name, Pid :: pid()}, + From :: {pid(),Tag :: any()}, + State :: state()) -> {'reply', 'ok', state()} + when Name :: name(). handle_call({create, Name}, _From, S) -> assure_group(Name), @@ -195,11 +199,10 @@ handle_call(Request, From, S) -> [Request, From]), {noreply, S}. --type all_members() :: [[name(),...]]. --type cast() :: {'exchange', node(), all_members()} - | {'del_member', name(), pid()}. - --spec handle_cast(cast(), #state{}) -> {'noreply', #state{}}. +-spec handle_cast(Cast :: {'exchange', node(), Names :: [[Name,...]]} + | {'del_member', Name, Pid :: pid()}, + State :: state()) -> {'noreply', state()} + when Name :: name(). handle_cast({exchange, _Node, List}, S) -> store(List), @@ -208,7 +211,8 @@ handle_cast(_, S) -> %% Ignore {del_member, Name, Pid}. {noreply, S}. --spec handle_info(tuple(), #state{}) -> {'noreply', #state{}}. +-spec handle_info(Tuple :: tuple(), State :: state()) -> + {'noreply', state()}. handle_info({'DOWN', MonitorRef, process, _Pid, _Info}, S) -> member_died(MonitorRef), @@ -222,7 +226,7 @@ handle_info({new_pg2, Node}, S) -> handle_info(_, S) -> {noreply, S}. --spec terminate(term(), #state{}) -> 'ok'. +-spec terminate(Reason :: any(), State :: state()) -> 'ok'. terminate(_Reason, _S) -> true = ets:delete(pg2_table), diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index e09acb5024..be35f99ed2 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.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 @@ -263,14 +263,28 @@ proxy_user_flush() -> %% THE rpc client interface --spec call(node(), atom(), atom(), [term()]) -> term(). +-spec call(Node, Module, Function, Args) -> Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(). call(N,M,F,A) when node() =:= N -> %% Optimize local call local_call(M, F, A); call(N,M,F,A) -> do_call(N, {call,M,F,A,group_leader()}, infinity). --spec call(node(), atom(), atom(), [term()], timeout()) -> term(). +-spec call(Node, Module, Function, Args, Timeout) -> + Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(), + Timeout :: timeout(). call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call local_call(M,F,A); @@ -279,14 +293,28 @@ call(N,M,F,A,infinity) -> call(N,M,F,A,Timeout) when is_integer(Timeout), Timeout >= 0 -> do_call(N, {call,M,F,A,group_leader()}, Timeout). --spec block_call(node(), atom(), atom(), [term()]) -> term(). +-spec block_call(Node, Module, Function, Args) -> Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(). block_call(N,M,F,A) when node() =:= N -> %% Optimize local call local_call(M,F,A); block_call(N,M,F,A) -> do_call(N, {block_call,M,F,A,group_leader()}, infinity). --spec block_call(node(), atom(), atom(), [term()], timeout()) -> term(). +-spec block_call(Node, Module, Function, Args, Timeout) -> + Res | {badrpc, Reason} when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Res :: term(), + Reason :: term(), + Timeout :: timeout(). block_call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call local_call(M, F, A); @@ -339,7 +367,13 @@ rpc_check(X) -> X. %% The entire call is packed into an atomic transaction which %% either succeeds or fails, i.e. never hangs (unless the server itself hangs). --spec server_call(node(), atom(), term(), term()) -> term() | {'error', 'nodedown'}. +-spec server_call(Node, Name, ReplyWrapper, Msg) -> Reply | {error, Reason} when + Node :: node(), + Name :: atom(), + ReplyWrapper :: term(), + Msg :: term(), + Reply :: term(), + Reason :: nodedown. server_call(Node, Name, ReplyWrapper, Msg) when is_atom(Node), is_atom(Name) -> @@ -362,7 +396,11 @@ server_call(Node, Name, ReplyWrapper, Msg) end end. --spec cast(node(), atom(), atom(), [term()]) -> 'true'. +-spec cast(Node, Module, Function, Args) -> true when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. cast(Node, Mod, Fun, Args) when Node =:= node() -> catch spawn(Mod, Fun, Args), @@ -373,12 +411,17 @@ cast(Node, Mod, Fun, Args) -> %% Asynchronous broadcast, returns nothing, it's just send'n prey --spec abcast(atom(), term()) -> 'abcast'. +-spec abcast(Name, Msg) -> abcast when + Name :: atom(), + Msg :: term(). abcast(Name, Mess) -> abcast([node() | nodes()], Name, Mess). --spec abcast([node()], atom(), term()) -> 'abcast'. +-spec abcast(Nodes, Name, Msg) -> abcast when + Nodes :: [node()], + Name :: atom(), + Msg :: term(). abcast([Node|Tail], Name, Mess) -> Dest = {Name,Node}, @@ -396,23 +439,39 @@ abcast([], _,_) -> abcast. %% message when we return from the call, we can't know that they have %% processed the message though. --spec sbcast(atom(), term()) -> {[node()], [node()]}. +-spec sbcast(Name, Msg) -> {GoodNodes, BadNodes} when + Name :: atom(), + Msg :: term(), + GoodNodes :: [node()], + BadNodes :: [node()]. sbcast(Name, Mess) -> sbcast([node() | nodes()], Name, Mess). --spec sbcast([node()], atom(), term()) -> {[node()], [node()]}. +-spec sbcast(Nodes, Name, Msg) -> {GoodNodes, BadNodes} when + Name :: atom(), + Msg :: term(), + Nodes :: [node()], + GoodNodes :: [node()], + BadNodes :: [node()]. sbcast(Nodes, Name, Mess) -> Monitors = send_nodes(Nodes, ?NAME, {sbcast, Name, Mess}, []), rec_nodes(?NAME, Monitors). --spec eval_everywhere(atom(), atom(), [term()]) -> 'abcast'. +-spec eval_everywhere(Module, Function, Args) -> abcast when + Module :: module(), + Function :: atom(), + Args :: [term()]. eval_everywhere(Mod, Fun, Args) -> eval_everywhere([node() | nodes()] , Mod, Fun, Args). --spec eval_everywhere([node()], atom(), atom(), [term()]) -> 'abcast'. +-spec eval_everywhere(Nodes, Module, Function, Args) -> abcast when + Nodes :: [node()], + Module :: module(), + Function :: atom(), + Args :: [term()]. eval_everywhere(Nodes, Mod, Fun, Args) -> gen_server:abcast(Nodes, ?NAME, {cast,Mod,Fun,Args,group_leader()}). @@ -453,20 +512,45 @@ unmonitor(Ref) when is_reference(Ref) -> %% Call apply(M,F,A) on all nodes in parallel --spec multicall(atom(), atom(), [term()]) -> {[_], [node()]}. +-spec multicall(Module, Function, Args) -> {ResL, BadNodes} when + Module :: module(), + Function :: atom(), + Args :: [term()], + ResL :: [term()], + BadNodes :: [node()]. multicall(M, F, A) -> multicall(M, F, A, infinity). --spec multicall([node()], atom(), atom(), [term()]) -> {[_], [node()]} - ; (atom(), atom(), [term()], timeout()) -> {[_], [node()]}. +-spec multicall(Nodes, Module, Function, Args) -> {ResL, BadNodes} when + Nodes :: [node()], + Module :: module(), + Function :: atom(), + Args :: [term()], + ResL :: [term()], + BadNodes :: [node()]; + (Module, Function, Args, Timeout) -> {ResL, BadNodes} when + Module :: module(), + Function :: atom(), + Args :: [term()], + Timeout :: timeout(), + ResL :: [term()], + BadNodes :: [node()]. multicall(Nodes, M, F, A) when is_list(Nodes) -> multicall(Nodes, M, F, A, infinity); multicall(M, F, A, Timeout) -> multicall([node() | nodes()], M, F, A, Timeout). --spec multicall([node()], atom(), atom(), [term()], timeout()) -> {[_], [node()]}. +-spec multicall(Nodes, Module, Function, Args, Timeout) -> + {ResL, BadNodes} when + Nodes :: [node()], + Module :: module(), + Function :: atom(), + Args :: [term()], + Timeout :: timeout(), + ResL :: [term()], + BadNodes :: [node()]. multicall(Nodes, M, F, A, infinity) when is_list(Nodes), is_atom(M), is_atom(F), is_list(A) -> @@ -495,12 +579,21 @@ do_multicall(Nodes, M, F, A, Timeout) -> %% %% There is no apparent order among the replies. --spec multi_server_call(atom(), term()) -> {[_], [node()]}. +-spec multi_server_call(Name, Msg) -> {Replies, BadNodes} when + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. multi_server_call(Name, Msg) -> multi_server_call([node() | nodes()], Name, Msg). --spec multi_server_call([node()], atom(), term()) -> {[_], [node()]}. +-spec multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes} when + Nodes :: [node()], + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. multi_server_call(Nodes, Name, Msg) when is_list(Nodes), is_atom(Name) -> @@ -509,9 +602,22 @@ multi_server_call(Nodes, Name, Msg) %% Deprecated functions. Were only needed when communicating with R6 nodes. +-spec safe_multi_server_call(Name, Msg) -> {Replies, BadNodes} when + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. + safe_multi_server_call(Name, Msg) -> multi_server_call(Name, Msg). +-spec safe_multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes} when + Nodes :: [node()], + Name :: atom(), + Msg :: term(), + Replies :: [Reply :: term()], + BadNodes :: [node()]. + safe_multi_server_call(Nodes, Name, Msg) -> multi_server_call(Nodes, Name, Msg). @@ -539,7 +645,14 @@ rec_nodes(Name, [{N,R} | Tail], Badnodes, Replies) -> %% rpc's towards the same node. I.e. it returns immediately and %% it returns a Key that can be used in a subsequent yield(Key). --spec async_call(node(), atom(), atom(), [term()]) -> pid(). +-opaque key() :: pid(). + +-spec async_call(Node, Module, Function, Args) -> Key when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + Key :: key(). async_call(Node, Mod, Fun, Args) -> ReplyTo = self(), @@ -549,20 +662,27 @@ async_call(Node, Mod, Fun, Args) -> ReplyTo ! {self(), {promise_reply, R}} %% self() is key end). --spec yield(pid()) -> term(). +-spec yield(Key) -> {value, Val} | timeout when + Key :: key(), + Val :: (Res :: term()) | {badrpc, Reason :: term()}. yield(Key) when is_pid(Key) -> {value,R} = do_yield(Key, infinity), R. --spec nb_yield(pid(), timeout()) -> {'value', _} | 'timeout'. +-spec nb_yield(Key, Timeout) -> {value, Val} | timeout when + Key :: key(), + Timeout :: timeout(), + Val :: (Res :: term()) | {badrpc, Reason :: term()}. nb_yield(Key, infinity=Inf) when is_pid(Key) -> do_yield(Key, Inf); nb_yield(Key, Timeout) when is_pid(Key), is_integer(Timeout), Timeout >= 0 -> do_yield(Key, Timeout). --spec nb_yield(pid()) -> {'value', _} | 'timeout'. +-spec nb_yield(Key) -> {value, Val} | timeout when + Key :: key(), + Val :: (Res :: term()) | {badrpc, Reason :: term()}. nb_yield(Key) when is_pid(Key) -> do_yield(Key, 0). @@ -582,7 +702,12 @@ do_yield(Key, Timeout) -> %% ArgL === [{M,F,Args},........] %% Returns a lists of the evaluations in the same order as %% given to ArgL --spec parallel_eval([{atom(), atom(), [_]}]) -> [_]. +-spec parallel_eval(FuncCalls) -> ResL when + FuncCalls :: [{Module, Function, Args}], + Module :: module(), + Function :: atom(), + Args :: [term()], + ResL :: [term()]. parallel_eval(ArgL) -> Nodes = [node() | nodes()], @@ -599,7 +724,13 @@ map_nodes([{M,F,A}|Tail],[Node|MoreNodes], Original) -> %% Parallel version of lists:map/3 with exactly the same %% arguments and return value as lists:map/3, %% except that it calls exit/1 if a network error occurs. --spec pmap({atom(),atom()}, [term()], [term()]) -> [term()]. +-spec pmap(FuncSpec, ExtraArgs, List1) -> List2 when + FuncSpec :: {Module,Function}, + Module :: module(), + Function :: atom(), + ExtraArgs :: [term()], + List1 :: [Elem :: term()], + List2 :: [term()]. pmap({M,F}, As, List) -> check(parallel_eval(build_args(M,F,As, List, [])), []). @@ -616,15 +747,20 @@ check([], Ack) -> Ack. %% location transparent version of process_info --spec pinfo(pid()) -> [{atom(), _}] | 'undefined'. +-spec pinfo(Pid) -> [{Item, Info}] | undefined when + Pid :: pid(), + Item :: atom(), + Info :: term(). pinfo(Pid) when node(Pid) =:= node() -> process_info(Pid); pinfo(Pid) -> call(node(Pid), erlang, process_info, [Pid]). --spec pinfo(pid(), Item) -> {Item, _} | 'undefined' | [] - when is_subtype(Item, atom()). +-spec pinfo(Pid, Item) -> {Item, Info} | undefined | [] when + Pid :: pid(), + Item :: atom(), + Info :: term(). pinfo(Pid, Item) when node(Pid) =:= node() -> process_info(Pid, Item); diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 78c3040f21..ea5da2de1c 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.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 @@ -38,15 +38,17 @@ -type flag() :: 'send' | 'receive' | 'print' | 'timestamp'. -type component() :: 'label' | 'serial' | flag(). --type value() :: non_neg_integer() - | {non_neg_integer(), non_neg_integer()} - | boolean(). --type token_pair() :: {component(), value()}. +-type value() :: (Integer :: non_neg_integer()) + | {Previous :: non_neg_integer(), + Current :: non_neg_integer()} + | (Bool :: boolean()). %%--------------------------------------------------------------------------- --type token() :: [] | {integer(), boolean(), _, _, _}. --spec set_token(token()) -> token() | 'ok'. +-opaque token() :: {integer(), boolean(), _, _, _}. +-spec set_token(Token) -> PreviousToken | 'ok' when + Token :: [] | token(), + PreviousToken :: [] | token(). set_token([]) -> erlang:seq_trace(sequential_trace_token,[]); @@ -58,28 +60,35 @@ set_token({Flags,Label,Serial,_From,Lastcnt}) -> %% expects that, the BIF can however "unofficially" handle atoms as well, and %% atoms can be used if only Erlang nodes are involved --spec set_token(component(), value()) -> token_pair(). +-spec set_token(Component, Val) -> {Component, OldVal} when + Component :: component(), + Val :: value(), + OldVal :: value(). set_token(Type, Val) -> erlang:seq_trace(Type, Val). --spec get_token() -> term(). +-spec get_token() -> [] | token(). get_token() -> element(2,process_info(self(),sequential_trace_token)). --spec get_token(component()) -> token_pair(). - +-spec get_token(Component) -> {Component, Val} when + Component :: component(), + Val :: value(). get_token(Type) -> erlang:seq_trace_info(Type). --spec print(term()) -> 'ok'. +-spec print(TraceInfo) -> 'ok' when + TraceInfo :: term(). print(Term) -> erlang:seq_trace_print(Term), ok. --spec print(integer(), term()) -> 'ok'. +-spec print(Label, TraceInfo) -> 'ok' when + Label :: integer(), + TraceInfo :: term(). print(Label, Term) when is_atom(Label) -> erlang:error(badarg, [Label, Term]); @@ -94,14 +103,17 @@ reset_trace() -> %% reset_trace(Pid) -> % this might be a useful function too --type tracer() :: pid() | port() | 'false'. +-type tracer() :: (Pid :: pid()) | port() | 'false'. --spec set_system_tracer(tracer()) -> tracer(). +-spec set_system_tracer(Tracer) -> OldTracer when + Tracer :: tracer(), + OldTracer :: tracer(). set_system_tracer(Pid) -> erlang:system_flag(sequential_tracer, Pid). --spec get_system_tracer() -> tracer(). +-spec get_system_tracer() -> Tracer when + Tracer :: tracer(). get_system_tracer() -> element(2, erlang:system_info(sequential_tracer)). diff --git a/lib/kernel/src/wrap_log_reader.erl b/lib/kernel/src/wrap_log_reader.erl index fabaa07752..c41e0091e4 100644 --- a/lib/kernel/src/wrap_log_reader.erl +++ b/lib/kernel/src/wrap_log_reader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. 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 @@ -41,6 +41,8 @@ first_no :: non_neg_integer() | 'one' % first read file number }). +-opaque continuation() :: #wrap_reader{}. + %% %% Exported functions %% @@ -50,9 +52,11 @@ %% is not yet reached, we are on the first 'round' of filling the wrap %% files. --type open_ret() :: {'ok', #wrap_reader{}} | {'error', tuple()}. +-type open_ret() :: {'ok', Continuation :: continuation()} + | {'error', Reason :: tuple()}. --spec open(atom() | string()) -> open_ret(). +-spec open(Filename) -> open_ret() when + Filename :: string() | atom(). open(File) when is_atom(File) -> open(atom_to_list(File)); @@ -77,7 +81,9 @@ open(File) when is_list(File) -> Error end. --spec open(atom() | string(), integer()) -> open_ret(). +-spec open(Filename, N) -> open_ret() when + Filename :: string() | atom(), + N :: integer(). open(File, FileNo) when is_atom(File), is_integer(FileNo) -> open(atom_to_list(File), FileNo); @@ -100,22 +106,29 @@ open(File, FileNo) when is_list(File), is_integer(FileNo) -> Error end. --spec close(#wrap_reader{}) -> 'ok' | {'error', atom()}. +-spec close(Continuation) -> 'ok' | {'error', Reason} when + Continuation :: continuation(), + Reason :: file:posix(). close(#wrap_reader{fd = FD}) -> file:close(FD). --type chunk_ret() :: {#wrap_reader{}, [term()]} - | {#wrap_reader{}, [term()], non_neg_integer()} - | {#wrap_reader{}, 'eof'} - | {'error', term()}. +-type chunk_ret() :: {Continuation2, Terms :: [term()]} + | {Continuation2, + Terms :: [term()], + Badbytes :: non_neg_integer()} + | {Continuation2, 'eof'} + | {'error', Reason :: term()}. --spec chunk(#wrap_reader{}) -> chunk_ret(). +-spec chunk(Continuation) -> chunk_ret() when + Continuation :: continuation(). chunk(WR = #wrap_reader{}) -> chunk(WR, ?MAX_CHUNK_SIZE, 0). --spec chunk(#wrap_reader{}, 'infinity' | pos_integer()) -> chunk_ret(). +-spec chunk(Continuation, N) -> chunk_ret() when + Continuation :: continuation(), + N :: infinity | pos_integer(). chunk(WR = #wrap_reader{}, infinity) -> chunk(WR, ?MAX_CHUNK_SIZE, 0); -- cgit v1.2.3 From 87b389731e5d9862e0bfac2708ffb5170f9b98d8 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Thu, 21 Apr 2011 11:00:28 +0200 Subject: Expand the use of high memory allocation in halfword emulator Also add 'low' field in system_info(allocator) SHORT_LIVED is still in low memory --- erts/configure.in | 1 + erts/emulator/beam/erl_alloc.c | 58 +++++++++++++++++++++++++++++++++---- erts/emulator/beam/erl_alloc.types | 57 ++++++++++++++++++++++++++++-------- erts/emulator/beam/erl_alloc_util.c | 28 +++++++++++++----- erts/emulator/beam/erl_alloc_util.h | 3 ++ erts/emulator/beam/erl_lock_check.c | 10 ++++--- erts/emulator/beam/erl_lock_check.h | 2 +- erts/emulator/sys/common/erl_mseg.c | 35 +++++++++++----------- erts/emulator/sys/common/erl_mseg.h | 11 +++---- 9 files changed, 151 insertions(+), 54 deletions(-) diff --git a/erts/configure.in b/erts/configure.in index 31d1d55b8a..e4c6a7852f 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -769,6 +769,7 @@ if test "$enable_halfword_emulator" = "yes"; then if test "$ARCH" = "amd64"; then AC_DEFINE(HALFWORD_HEAP_EMULATOR, [1], [Define if building a halfword-heap 64bit emulator]) + ENABLE_ALLOC_TYPE_VARS="$ENABLE_ALLOC_TYPE_VARS halfword" AC_MSG_RESULT([yes]) else AC_MSG_ERROR(no; halfword emulator not supported on this architecture) diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 673eac7fea..cde38398e6 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -90,6 +90,10 @@ typedef union { static ErtsAllocatorState_t sl_alloc_state; static ErtsAllocatorState_t std_alloc_state; static ErtsAllocatorState_t ll_alloc_state; +#if HALFWORD_HEAP +static ErtsAllocatorState_t std_alloc_low_state; +static ErtsAllocatorState_t ll_alloc_low_state; +#endif static ErtsAllocatorState_t temp_alloc_state; static ErtsAllocatorState_t eheap_alloc_state; static ErtsAllocatorState_t binary_alloc_state; @@ -166,6 +170,10 @@ typedef struct { struct au_init binary_alloc; struct au_init ets_alloc; struct au_init driver_alloc; +#if HALFWORD_HEAP + struct au_init std_alloc_low; + struct au_init ll_alloc_low; +#endif } erts_alc_hndl_args_init_t; #define ERTS_AU_INIT__ {0, 0, GOODFIT, DEFAULT_ALLCTR_INIT, {1,1,1,1}} @@ -193,6 +201,10 @@ set_default_sl_alloc_opts(struct au_init *ip) #endif ip->init.util.ts = ERTS_ALC_MTA_SHORT_LIVED; ip->init.util.rsbcst = 80; +#if HALFWORD_HEAP + ip->init.util.low_mem = 1; +#endif + } static void @@ -256,6 +268,9 @@ set_default_temp_alloc_opts(struct au_init *ip) ip->init.util.ts = ERTS_ALC_MTA_TEMPORARY; ip->init.util.rsbcst = 90; ip->init.util.rmbcmt = 100; +#if HALFWORD_HEAP + ip->init.util.low_mem = 1; +#endif } static void @@ -275,6 +290,9 @@ set_default_eheap_alloc_opts(struct au_init *ip) #endif ip->init.util.ts = ERTS_ALC_MTA_EHEAP; ip->init.util.rsbcst = 50; +#if HALFWORD_HEAP + ip->init.util.low_mem = 1; +#endif } static void @@ -379,7 +397,7 @@ adjust_tpref(struct au_init *ip, int no_sched) static void handle_args(int *, char **, erts_alc_hndl_args_init_t *); static void -set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init); +set_au_allocator(ErtsAlcType_t alctr_n, const struct au_init *init); static void start_au_allocator(ErtsAlcType_t alctr_n, @@ -531,6 +549,16 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_allctrs[ERTS_ALC_A_SYSTEM].free = erts_sys_free; erts_allctrs_info[ERTS_ALC_A_SYSTEM].enabled = 1; +#if HALFWORD_HEAP + init.std_alloc_low = init.std_alloc; + init.std_alloc_low.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW; + init.std_alloc_low.init.util.low_mem = 1; + set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_alloc_low); + init.ll_alloc_low = init.ll_alloc; + init.ll_alloc_low.init.util.alloc_no = ERTS_ALC_A_LONG_LIVED_LOW; + init.ll_alloc_low.init.util.low_mem = 1; + set_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, &init.ll_alloc_low); +#endif set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc); set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc); set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc); @@ -576,7 +604,14 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) start_au_allocator(ERTS_ALC_A_LONG_LIVED, &init.ll_alloc, &ll_alloc_state); - +#if HALFWORD_HEAP + start_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, + &init.ll_alloc_low, + &ll_alloc_low_state); + start_au_allocator(ERTS_ALC_A_STANDARD_LOW, + &init.std_alloc_low, + &std_alloc_low_state); +#endif start_au_allocator(ERTS_ALC_A_EHEAP, &init.eheap_alloc, &eheap_alloc_state); @@ -612,11 +647,9 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_set_fix_size(ERTS_ALC_T_PROC, sizeof(Process)); erts_set_fix_size(ERTS_ALC_T_DB_TABLE, sizeof(DbTable)); erts_set_fix_size(ERTS_ALC_T_ATOM, sizeof(Atom)); - erts_set_fix_size(ERTS_ALC_T_EXPORT, sizeof(Export)); + erts_set_fix_size(ERTS_ALC_T_MODULE, sizeof(Module)); erts_set_fix_size(ERTS_ALC_T_REG_PROC, sizeof(RegProc)); - erts_set_fix_size(ERTS_ALC_T_MONITOR_SH, ERTS_MONITOR_SH_SIZE*sizeof(Uint)); - erts_set_fix_size(ERTS_ALC_T_NLINK_SH, ERTS_LINK_SH_SIZE*sizeof(Uint)); erts_set_fix_size(ERTS_ALC_T_FUN_ENTRY, sizeof(ErlFunEntry)); #ifdef ERTS_ALC_T_DRV_EV_D_STATE erts_set_fix_size(ERTS_ALC_T_DRV_EV_D_STATE, @@ -626,13 +659,18 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_set_fix_size(ERTS_ALC_T_DRV_SEL_D_STATE, sizeof(ErtsDrvSelectDataState)); #endif +#if !HALFWORD_HEAP + erts_set_fix_size(ERTS_ALC_T_EXPORT, sizeof(Export)); + erts_set_fix_size(ERTS_ALC_T_MONITOR_SH, ERTS_MONITOR_SH_SIZE*sizeof(Uint)); + erts_set_fix_size(ERTS_ALC_T_NLINK_SH, ERTS_LINK_SH_SIZE*sizeof(Uint)); +#endif #endif #endif } static void -set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init) +set_au_allocator(ErtsAlcType_t alctr_n, const struct au_init *init) { ErtsAllocatorFunctions_t *af = &erts_allctrs[alctr_n]; ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n]; @@ -1836,6 +1874,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) size.processes = size.processes_used = tmp; +#if HALFWORD_HEAP + /* BUG: We ignore link and monitor memory */ +#else erts_fix_info(ERTS_ALC_T_NLINK_SH, &efi); size.processes += efi.total; size.processes_used += efi.used; @@ -1843,6 +1884,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) erts_fix_info(ERTS_ALC_T_MONITOR_SH, &efi); size.processes += efi.total; size.processes_used += efi.used; +#endif erts_fix_info(ERTS_ALC_T_PROC, &efi); size.processes += efi.total; @@ -1879,8 +1921,12 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) erts_fix_info(ERTS_ALC_T_MODULE, &efi); size.code += efi.used; size.code += export_table_sz(); +#if HALFWORD_HEAP + size.code += export_list_size() * sizeof(Export); +#else erts_fix_info(ERTS_ALC_T_EXPORT, &efi); size.code += efi.used; +#endif size.code += erts_fun_table_sz(); erts_fix_info(ERTS_ALC_T_FUN_ENTRY, &efi); size.code += efi.used; diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index ca71798917..c6cc0e1fac 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -75,6 +75,11 @@ allocator EHEAP true eheap_alloc allocator ETS true ets_alloc allocator FIXED_SIZE true fix_alloc ++if halfword +allocator LONG_LIVED_LOW true ll_alloc_low +allocator STANDARD_LOW true std_alloc_low ++endif + +else # Non smp build allocator TEMPORARY false temp_alloc @@ -85,12 +90,18 @@ allocator EHEAP false eheap_alloc allocator ETS false ets_alloc allocator FIXED_SIZE false fix_alloc ++if halfword +allocator LONG_LIVED_LOW false ll_alloc_low +allocator STANDARD_LOW false std_alloc_low ++endif + +endif allocator BINARY true binary_alloc allocator DRIVER true driver_alloc + # --- Class declarations ----------------------------------------------------- # # Syntax: class @@ -125,14 +136,9 @@ class SYSTEM system_data type PROC FIXED_SIZE PROCESSES proc type ATOM FIXED_SIZE ATOM atom_entry -type EXPORT FIXED_SIZE CODE export_entry type MODULE FIXED_SIZE CODE module_entry type REG_PROC FIXED_SIZE PROCESSES reg_proc type LINK_LH STANDARD PROCESSES link_lh -type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh -type MONITOR_LH STANDARD PROCESSES monitor_lh -type NLINK_SH FIXED_SIZE PROCESSES nlink_sh -type NLINK_LH STANDARD PROCESSES nlink_lh type SUSPEND_MON STANDARD PROCESSES suspend_monitor type PEND_SUSPEND SHORT_LIVED PROCESSES pending_suspend type PROC_LIST SHORT_LIVED PROCESSES proc_list @@ -175,7 +181,6 @@ type DRIVER STANDARD SYSTEM driver type NIF DRIVER SYSTEM nif_internal type BINARY BINARY BINARIES binary type NBIF_TABLE SYSTEM SYSTEM nbif_tab -type CODE LONG_LIVED CODE code type ARG_REG STANDARD PROCESSES arg_reg type PROC_DICT STANDARD PROCESSES proc_dict type CALLS_BUF STANDARD PROCESSES calls_buf @@ -193,10 +198,8 @@ type DB_FIXATION SHORT_LIVED ETS db_fixation type DB_FIX_DEL SHORT_LIVED ETS fixed_del type DB_TABLES LONG_LIVED ETS db_tabs type DB_NTAB_ENT STANDARD ETS db_named_table_entry -type DB_HEIR_DATA STANDARD ETS db_heir_data type DB_TMP TEMPORARY ETS db_tmp type DB_MC_STK TEMPORARY ETS db_mc_stack -type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc type DB_MS_RUN_HEAP SHORT_LIVED ETS db_match_spec_run_heap type DB_MS_CMPL_HEAP TEMPORARY ETS db_match_spec_cmpl_heap type DB_SEG ETS ETS db_segment @@ -213,10 +216,8 @@ type LOGGER_DSBUF TEMPORARY SYSTEM logger_dsbuf type TMP_DSBUF TEMPORARY SYSTEM tmp_dsbuf type INFO_DSBUF SYSTEM SYSTEM info_dsbuf # INFO_DSBUF have to use the SYSTEM allocator; otherwise, a deadlock might occur -type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data type SCHDLR_SLP_INFO LONG_LIVED SYSTEM scheduler_sleep_info type RUNQS LONG_LIVED SYSTEM run_queues -type DDLL_PROCESS STANDARD SYSTEM ddll_processes type DDLL_HANDLE STANDARD SYSTEM ddll_handle type DDLL_ERRCODES LONG_LIVED SYSTEM ddll_errcodes type DDLL_TMP_BUF TEMPORARY SYSTEM ddll_tmp_buf @@ -327,13 +328,45 @@ type SSB SHORT_LIVED PROCESSES ssb +endif ++if halfword + +type DDLL_PROCESS STANDARD_LOW SYSTEM ddll_processes +type MONITOR_LH STANDARD_LOW PROCESSES monitor_lh +type NLINK_LH STANDARD_LOW PROCESSES nlink_lh +type CODE LONG_LIVED_LOW CODE code +type DB_HEIR_DATA STANDARD_LOW ETS db_heir_data +type DB_MS_PSDO_PROC LONG_LIVED_LOW ETS db_match_pseudo_proc +type SCHDLR_DATA LONG_LIVED_LOW SYSTEM scheduler_data +type LL_TEMP_TERM LONG_LIVED_LOW SYSTEM ll_temp_term + +# no FIXED_SIZE for low memory +type EXPORT STANDARD_LOW CODE export_entry +type MONITOR_SH STANDARD_LOW PROCESSES monitor_sh +type NLINK_SH STANDARD_LOW PROCESSES nlink_sh + ++else # "fullword" + +type DDLL_PROCESS STANDARD SYSTEM ddll_processes +type MONITOR_LH STANDARD PROCESSES monitor_lh +type NLINK_LH STANDARD PROCESSES nlink_lh +type CODE LONG_LIVED CODE code +type DB_HEIR_DATA STANDARD ETS db_heir_data +type DB_MS_PSDO_PROC LONG_LIVED ETS db_match_pseudo_proc +type SCHDLR_DATA LONG_LIVED SYSTEM scheduler_data +type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term + +type EXPORT FIXED_SIZE CODE export_entry +type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh +type NLINK_SH FIXED_SIZE PROCESSES nlink_sh + ++endif + + # # Types used by system specific code # type TEMP_TERM TEMPORARY SYSTEM temp_term -type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term - type DRV_TAB LONG_LIVED SYSTEM drv_tab type DRV_EV_STATE LONG_LIVED SYSTEM driver_event_state type DRV_EV_D_STATE FIXED_SIZE SYSTEM driver_event_data_state diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index 84c72439a3..cc04ef65bf 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -1639,6 +1639,9 @@ static struct { Eterm e; Eterm t; Eterm ramv; +#if HALFWORD_HEAP + Eterm low; +#endif Eterm sbct; #if HAVE_ERTS_MSEG Eterm asbcst; @@ -1724,6 +1727,9 @@ init_atoms(Allctr_t *allctr) AM_INIT(e); AM_INIT(t); AM_INIT(ramv); +#if HALFWORD_HEAP + AM_INIT(low); +#endif AM_INIT(sbct); #if HAVE_ERTS_MSEG AM_INIT(asbcst); @@ -2168,6 +2174,9 @@ info_options(Allctr_t *allctr, "option e: true\n" "option t: %s\n" "option ramv: %s\n" +#if HALFWORD_HEAP + "option low: %s\n" +#endif "option sbct: %beu\n" #if HAVE_ERTS_MSEG "option asbcst: %bpu\n" @@ -2185,6 +2194,9 @@ info_options(Allctr_t *allctr, "option mbcgs: %beu\n", topt, allctr->ramv ? "true" : "false", +#if HALFWORD_HEAP + allctr->mseg_opt.low_mem ? "true" : "false", +#endif allctr->sbc_threshold, #if HAVE_ERTS_MSEG allctr->mseg_opt.abs_shrink_th, @@ -2243,6 +2255,9 @@ info_options(Allctr_t *allctr, add_2tup(hpp, szp, &res, am.sbct, bld_uint(hpp, szp, allctr->sbc_threshold)); +#if HALFWORD_HEAP + add_2tup(hpp, szp, &res, am.low, allctr->mseg_opt.low_mem ? am_true : am_false); +#endif add_2tup(hpp, szp, &res, am.ramv, allctr->ramv ? am_true : am_false); add_2tup(hpp, szp, &res, am.t, (allctr->t ? bld_uint(hpp, szp, (Uint) allctr->t) @@ -3105,13 +3120,12 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) goto error; #if HAVE_ERTS_MSEG - { - ErtsMsegOpt_t mseg_opt = ERTS_MSEG_DEFAULT_OPT_INITIALIZER; - - sys_memcpy((void *) &allctr->mseg_opt, - (void *) &mseg_opt, - sizeof(ErtsMsegOpt_t)); - } + sys_memcpy((void *) &allctr->mseg_opt, + (void *) &erts_mseg_default_opt, + sizeof(ErtsMsegOpt_t)); +# if HALFWORD_HEAP + allctr->mseg_opt.low_mem = init->low_mem; +# endif #endif allctr->name_prefix = init->name_prefix; diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index d296081714..ddf84c086c 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -38,6 +38,7 @@ typedef struct { int tspec; int tpref; int ramv; + int low_mem; /* HALFWORD only */ UWord sbct; UWord asbcst; UWord rsbcst; @@ -70,6 +71,7 @@ typedef struct { 0, /* (bool) tspec: thread specific */\ 0, /* (bool) tpref: thread preferred */\ 0, /* (bool) ramv: realloc always moves */\ + 0, /* (bool) low_mem: HALFWORD only */\ 512*1024, /* (bytes) sbct: sbc threshold */\ 2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\ 20, /* (%) rsbcst: rel sbc shrink threshold */\ @@ -97,6 +99,7 @@ typedef struct { 0, /* (bool) tspec: thread specific */\ 0, /* (bool) tpref: thread preferred */\ 0, /* (bool) ramv: realloc always moves */\ + 0, /* (bool) low_mem: HALFWORD only */\ 64*1024, /* (bytes) sbct: sbc threshold */\ 2*1024*2024, /* (amount) asbcst: abs sbc shrink threshold */\ 20, /* (%) rsbcst: rel sbc shrink threshold */\ diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 9e18997890..9180508a49 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -240,7 +240,7 @@ typedef struct erts_lc_locked_lock_t_ erts_lc_locked_lock_t; struct erts_lc_locked_lock_t_ { erts_lc_locked_lock_t *next; erts_lc_locked_lock_t *prev; - Eterm extra; + UWord extra; Sint16 id; Uint16 flags; }; @@ -441,12 +441,12 @@ new_locked_lock(erts_lc_lock_t *lck, Uint16 op_flags) } static void -print_lock2(char *prefix, Sint16 id, Eterm extra, Uint16 flags, char *suffix) +print_lock2(char *prefix, Sint16 id, Wterm extra, Uint16 flags, char *suffix) { char *lname = (0 <= id && id < ERTS_LOCK_ORDER_SIZE ? erts_lock_order[id].name : "unknown"); - if (is_boxed(extra)) + if (is_not_immed(extra)) erts_fprintf(stderr, "%s'%s:%p%s'%s%s", prefix, @@ -1260,7 +1260,8 @@ erts_lc_init_lock(erts_lc_lock_t *lck, char *name, Uint16 flags) { lck->id = erts_lc_get_lock_order_id(name); - lck->extra = make_boxed(&lck->extra); + lck->extra = &lck->extra; + ASSERT(is_not_immed(lck->extra)); lck->flags = flags; lck->inited = ERTS_LC_INITITALIZED; } @@ -1270,6 +1271,7 @@ erts_lc_init_lock_x(erts_lc_lock_t *lck, char *name, Uint16 flags, Eterm extra) { lck->id = erts_lc_get_lock_order_id(name); lck->extra = extra; + ASSERT(is_immed(lck->extra)); lck->flags = flags; lck->inited = ERTS_LC_INITITALIZED; } diff --git a/erts/emulator/beam/erl_lock_check.h b/erts/emulator/beam/erl_lock_check.h index cdb06d4458..b67f36fa06 100644 --- a/erts/emulator/beam/erl_lock_check.h +++ b/erts/emulator/beam/erl_lock_check.h @@ -39,7 +39,7 @@ typedef struct { int inited; Sint16 id; Uint16 flags; - Eterm extra; + UWord extra; } erts_lc_lock_t; #define ERTS_LC_INITITALIZED 0x7f7f7f7f diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index ffa3a6328c..eaef6680dd 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -134,7 +134,16 @@ static int mmap_fd; #define CAN_PARTLY_DESTROY 0 #endif -static const ErtsMsegOpt_t default_opt = ERTS_MSEG_DEFAULT_OPT_INITIALIZER; +const ErtsMsegOpt_t erts_mseg_default_opt = { + 1, /* Use cache */ + 1, /* Preserv data */ + 0, /* Absolute shrink threshold */ + 0 /* Relative shrink threshold */ +#if HALFWORD_HEAP + ,0 /* need low memory */ +#endif +}; + typedef struct cache_desc_t_ { void *seg; @@ -605,18 +614,10 @@ mseg_clear_cache(MemKind* mk) INC_CC(clear_cache); } -static ERTS_INLINE MemKind* type2mk(ErtsAlcType_t atype) +static ERTS_INLINE MemKind* memkind(const ErtsMsegOpt_t *opt) { #if HALFWORD_HEAP - switch (atype) { - case ERTS_ALC_A_ETS: - case ERTS_ALC_A_BINARY: - case ERTS_ALC_A_FIXED_SIZE: - case ERTS_ALC_A_DRIVER: - return &hi_mem; - default: - return &low_mem; - } + return opt->low_mem ? &low_mem : &hi_mem; #else return &the_mem; #endif @@ -628,7 +629,7 @@ mseg_alloc(ErtsAlcType_t atype, Uint *size_p, const ErtsMsegOpt_t *opt) Uint max, min, diff_size, size; cache_desc_t *cd, *cand_cd; void *seg; - MemKind* mk = type2mk(atype); + MemKind* mk = memkind(opt); INC_CC(alloc); @@ -742,7 +743,7 @@ static void mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size, const ErtsMsegOpt_t *opt) { - MemKind* mk = type2mk(atype); + MemKind* mk = memkind(opt); cache_desc_t *cd; ERTS_MSEG_DEALLOC_STAT(mk,size); @@ -800,7 +801,7 @@ static void * mseg_realloc(ErtsAlcType_t atype, void *seg, Uint old_size, Uint *new_size_p, const ErtsMsegOpt_t *opt) { - MemKind* mk = type2mk(atype); + MemKind* mk = memkind(opt); void *new_seg; Uint new_size; @@ -1372,7 +1373,7 @@ erts_mseg_alloc_opt(ErtsAlcType_t atype, Uint *size_p, const ErtsMsegOpt_t *opt) void * erts_mseg_alloc(ErtsAlcType_t atype, Uint *size_p) { - return erts_mseg_alloc_opt(atype, size_p, &default_opt); + return erts_mseg_alloc_opt(atype, size_p, &erts_mseg_default_opt); } void @@ -1387,7 +1388,7 @@ erts_mseg_dealloc_opt(ErtsAlcType_t atype, void *seg, Uint size, void erts_mseg_dealloc(ErtsAlcType_t atype, void *seg, Uint size) { - erts_mseg_dealloc_opt(atype, seg, size, &default_opt); + erts_mseg_dealloc_opt(atype, seg, size, &erts_mseg_default_opt); } void * @@ -1405,7 +1406,7 @@ void * erts_mseg_realloc(ErtsAlcType_t atype, void *seg, Uint old_size, Uint *new_size_p) { - return erts_mseg_realloc_opt(atype, seg, old_size, new_size_p, &default_opt); + return erts_mseg_realloc_opt(atype, seg, old_size, new_size_p, &erts_mseg_default_opt); } void diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index d8053eb0d9..fbb66ee33b 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -60,15 +60,12 @@ typedef struct { int preserv; UWord abs_shrink_th; UWord rel_shrink_th; +#if HALFWORD_HEAP + int low_mem; +#endif } ErtsMsegOpt_t; -#define ERTS_MSEG_DEFAULT_OPT_INITIALIZER \ -{ \ - 1, /* Use cache */ \ - 1, /* Preserv data */ \ - 0, /* Absolute shrink threshold */ \ - 0 /* Relative shrink threshold */ \ -} +extern const ErtsMsegOpt_t erts_mseg_default_opt; void *erts_mseg_alloc(ErtsAlcType_t, Uint *); void *erts_mseg_alloc_opt(ErtsAlcType_t, Uint *, const ErtsMsegOpt_t *); -- cgit v1.2.3 From d0c23a772b1f297ab67fb377e2b17f9186cbc13b Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 2 May 2011 18:15:51 +0200 Subject: Allow allocator disable for high memory (better valgrind for halfword) --- erts/emulator/beam/erl_alloc.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index cde38398e6..8af2f555a8 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -397,7 +397,7 @@ adjust_tpref(struct au_init *ip, int no_sched) static void handle_args(int *, char **, erts_alc_hndl_args_init_t *); static void -set_au_allocator(ErtsAlcType_t alctr_n, const struct au_init *init); +set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init); static void start_au_allocator(ErtsAlcType_t alctr_n, @@ -553,12 +553,15 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) init.std_alloc_low = init.std_alloc; init.std_alloc_low.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW; init.std_alloc_low.init.util.low_mem = 1; - set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_alloc_low); + init.ll_alloc_low = init.ll_alloc; init.ll_alloc_low.init.util.alloc_no = ERTS_ALC_A_LONG_LIVED_LOW; init.ll_alloc_low.init.util.low_mem = 1; + + set_au_allocator(ERTS_ALC_A_STANDARD_LOW, &init.std_alloc_low); set_au_allocator(ERTS_ALC_A_LONG_LIVED_LOW, &init.ll_alloc_low); -#endif +#endif /* HALFWORD */ + set_au_allocator(ERTS_ALC_A_TEMPORARY, &init.temp_alloc); set_au_allocator(ERTS_ALC_A_SHORT_LIVED, &init.sl_alloc); set_au_allocator(ERTS_ALC_A_STANDARD, &init.std_alloc); @@ -670,12 +673,21 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) } static void -set_au_allocator(ErtsAlcType_t alctr_n, const struct au_init *init) +set_au_allocator(ErtsAlcType_t alctr_n, struct au_init *init) { ErtsAllocatorFunctions_t *af = &erts_allctrs[alctr_n]; ErtsAllocatorInfo_t *ai = &erts_allctrs_info[alctr_n]; ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[alctr_n]; +#if HALFWORD_HEAP + /* If halfword heap, silently ignore any disabling of internal + * allocators for low memory + */ + if (init->init.util.low_mem) { + init->enable = 1; + } +#endif + if (!init->enable) { af->alloc = erts_sys_alloc; af->realloc = erts_sys_realloc; @@ -1386,14 +1398,6 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) argv[j++] = argv[i]; } *argc = j; -#if HALFWORD_HEAP - /* If halfword heap, silently ignore any disabling of internal - allocators */ - for (i = 0; i < aui_sz; ++i) - aui[i]->enable = 1; -#endif - - } static char *type_no_str(ErtsAlcType_t n) -- cgit v1.2.3 From 4d84edb06d01f30eb0afedcd6296bb45cc410f9f Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Mon, 9 May 2011 18:49:32 +0200 Subject: Fix faulty values from erlang:memory() on halfword-vm Use of Uint instead of UWord could cause overflow errors on systems with large memory use. --- erts/emulator/beam/erl_alloc.c | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 8af2f555a8..d2d7c6da8e 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1570,10 +1570,10 @@ erts_realloc_n_enomem(ErtsAlcType_t n, void *ptr, Uint size) erts_alc_fatal_error(ERTS_ALC_E_NOMEM, ERTS_ALC_O_REALLOC, n, size); } -static ERTS_INLINE Uint +static ERTS_INLINE UWord alcu_size(ErtsAlcType_t ai) { - Uint res = 0; + UWord res = 0; ASSERT(erts_allctrs_info[ai].enabled); ASSERT(erts_allctrs_info[ai].alloc_util); @@ -1623,20 +1623,20 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) int maximum; } want = {0}; struct { - Uint total; - Uint processes; - Uint processes_used; - Uint system; - Uint atom; - Uint atom_used; - Uint binary; - Uint code; - Uint ets; - Uint maximum; + UWord total; + UWord processes; + UWord processes_used; + UWord system; + UWord atom; + UWord atom_used; + UWord binary; + UWord code; + UWord ets; + UWord maximum; } size = {0}; - Eterm atoms[sizeof(size)/sizeof(Uint)]; - Uint *uintps[sizeof(size)/sizeof(Uint)]; - Eterm euints[sizeof(size)/sizeof(Uint)]; + Eterm atoms[sizeof(size)/sizeof(UWord)]; + UWord *uintps[sizeof(size)/sizeof(UWord)]; + Eterm euints[sizeof(size)/sizeof(UWord)]; int want_tot_or_sys; int length; Eterm res = THE_NON_VALUE; @@ -1811,7 +1811,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) ASSERT(length <= sizeof(atoms)/sizeof(Eterm)); ASSERT(length <= sizeof(euints)/sizeof(Eterm)); - ASSERT(length <= sizeof(uintps)/sizeof(Uint)); + ASSERT(length <= sizeof(uintps)/sizeof(UWord)); if (proc) { @@ -1830,8 +1830,8 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) for (ai = ERTS_ALC_A_MIN; ai <= ERTS_ALC_A_MAX; ai++) { if (erts_allctrs_info[ai].alloc_util) { - Uint *save; - Uint asz; + UWord *save; + UWord asz; switch (ai) { case ERTS_ALC_A_TEMPORARY: /* @@ -1863,7 +1863,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (want_tot_or_sys || want.processes || want.processes_used) { - Uint tmp; + UWord tmp; if (ERTS_MEM_NEED_ALL_ALCU) tmp = size.processes; @@ -1963,7 +1963,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) /* Print result... */ erts_print(to, arg, "=memory\n"); for (i = 0; i < length; i++) - erts_print(to, arg, "%T: %beu\n", atoms[i], *uintps[i]); + erts_print(to, arg, "%T: %bpu\n", atoms[i], *uintps[i]); } if (proc) { @@ -1976,9 +1976,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (only_one_value) { ASSERT(length == 1); hsz = 0; - erts_bld_uint(NULL, &hsz, *uintps[0]); + erts_bld_uword(NULL, &hsz, *uintps[0]); hp = hsz ? HAlloc((Process *) proc, hsz) : NULL; - res = erts_bld_uint(&hp, NULL, *uintps[0]); + res = erts_bld_uword(&hp, NULL, *uintps[0]); } else { Uint **hpp = NULL; @@ -1988,7 +1988,7 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) while (1) { int i; for (i = 0; i < length; i++) - euints[i] = erts_bld_uint(hpp, hszp, *uintps[i]); + euints[i] = erts_bld_uword(hpp, hszp, *uintps[i]); res = erts_bld_2tup_list(hpp, hszp, length, atoms, euints); if (hpp) break; -- cgit v1.2.3 From 6db87840174c80225bac5328ffe5e74dad5242f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Fri, 6 May 2011 12:28:13 +0200 Subject: Replace io_list_len() with erts_iolist_size() The io_list_len() function returns an int, where a negative return value indicates a type error. One problem is that an int only consists of 32 bits in a 64-bit emulator. Changing the return type to Sint will solve that problem, but in the 32-bit emulator, a large iolist and a iolist with a type error will both return a negative number. (Noticed by Jon Meredith.) Another problem is that for iolists whose total size exceed the word size, the result would be truncated, leading to a subsequent buffer overflow and emulator crash. Therefore, introduce the new erts_iolist_size() function which returns a status indication and writes the result size through a passed pointer. If the result size does not fit in a word, return an overflow indication. --- erts/emulator/beam/binary.c | 13 ++++--- erts/emulator/beam/erl_bif_ddll.c | 13 +++---- erts/emulator/beam/erl_bif_info.c | 6 ++-- erts/emulator/beam/erl_bif_port.c | 9 ++--- erts/emulator/beam/erl_bif_re.c | 18 +++++----- erts/emulator/beam/erl_nif.c | 4 +-- erts/emulator/beam/global.h | 6 +++- erts/emulator/beam/io.c | 72 ++++++++++++++++++++++--------------- erts/emulator/beam/utils.c | 37 ++++++++++++++----- erts/emulator/test/binary_SUITE.erl | 19 ++++++++++ erts/emulator/test/driver_SUITE.erl | 58 +++++++++++++++++++++++++++++- 11 files changed, 185 insertions(+), 70 deletions(-) diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 9486602633..91eb2114b1 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -355,21 +355,24 @@ BIF_RETTYPE bitstring_to_list_1(BIF_ALIST_1) BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg) { Eterm bin; - int i; + Uint size; int offset; byte* bytes; + if (is_nil(arg)) { BIF_RET(new_binary(p,(byte*)"",0)); } if (is_not_list(arg)) { goto error; } - if ((i = io_list_len(arg)) < 0) { - goto error; + switch (erts_iolist_size(arg, &size)) { + case ERTS_IOLIST_OVERFLOW: BIF_ERROR(p, SYSTEM_LIMIT); + case ERTS_IOLIST_TYPE: goto error; + default: ; } - bin = new_binary(p, (byte *)NULL, i); + bin = new_binary(p, (byte *)NULL, size); bytes = binary_bytes(bin); - offset = io_list_to_buf(arg, (char*) bytes, i); + offset = io_list_to_buf(arg, (char*) bytes, size); ASSERT(offset == 0); BIF_RET(bin); diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index c9cdcb87a6..9631fb50db 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -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 @@ -146,7 +146,7 @@ BIF_RETTYPE erl_ddll_try_load_3(Process *p, Eterm path_term, Eterm name_term, Eterm options) { char *path = NULL; - int path_len; + Uint path_len; char *name = NULL; DE_Handle *dh; erts_driver_t *drv; @@ -221,9 +221,7 @@ BIF_RETTYPE erl_ddll_try_load_3(Process *p, Eterm path_term, goto error; } - path_len = io_list_len(path_term); - - if (path_len <= 0) { + if (erts_iolist_size(path_term, &path_len)) { goto error; } path = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, path_len + 1 /* might need path separator */ + sys_strlen(name) + 1); @@ -1878,7 +1876,7 @@ static Eterm mkatom(char *str) static char *pick_list_or_atom(Eterm name_term) { char *name = NULL; - int name_len; + Uint name_len; if (is_atom(name_term)) { Atom *ap = atom_tab(atom_val(name_term)); if (ap->len == 0) { @@ -1890,8 +1888,7 @@ static char *pick_list_or_atom(Eterm name_term) memcpy(name,ap->name,ap->len); name[ap->len] = '\0'; } else { - name_len = io_list_len(name_term); - if (name_len <= 0) { + if (erts_iolist_size(name_term, &name_len)) { goto error; } name = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, name_len + 1); diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index e50fc18e64..f264bf44df 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1732,14 +1732,14 @@ info_1_tuple(Process* BIF_P, /* Pointer to current process. */ # define ERTS_ERROR_CHECKER_PRINTF_XML VALGRIND_PRINTF_XML # endif #endif - int buf_size = 8*1024; /* Try with 8KB first */ + Uint buf_size = 8*1024; /* Try with 8KB first */ char *buf = erts_alloc(ERTS_ALC_T_TMP, buf_size); int r = io_list_to_buf(*tp, (char*) buf, buf_size - 1); if (r < 0) { erts_free(ERTS_ALC_T_TMP, (void *) buf); - buf_size = io_list_len(*tp); - if (buf_size < 0) + if (erts_iolist_size(*tp, &buf_size)) { goto badarg; + } buf_size++; buf = erts_alloc(ERTS_ALC_T_TMP, buf_size); r = io_list_to_buf(*tp, (char*) buf, buf_size - 1); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index fbc92b9730..3fd35dd963 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2010. All Rights Reserved. + * Copyright Ericsson AB 2001-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 @@ -996,6 +996,7 @@ static byte* convert_environment(Process* p, Eterm env) Eterm* hp; Uint heap_size; int n; + Uint size; byte* bytes; if ((n = list_length(env)) < 0) { @@ -1039,15 +1040,15 @@ static byte* convert_environment(Process* p, Eterm env) if (is_not_nil(env)) { goto done; } - if ((n = io_list_len(all)) < 0) { + if (erts_iolist_size(all, &size)) { goto done; } /* * Put the result in a binary (no risk for a memory leak that way). */ - (void) erts_new_heap_binary(p, NULL, n, &bytes); - io_list_to_buf(all, (char*)bytes, n); + (void) erts_new_heap_binary(p, NULL, size, &bytes); + io_list_to_buf(all, (char*)bytes, size); done: erts_free(ERTS_ALC_T_TMP, temp_heap); diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index d4a8a3aaa7..26891c4348 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2008-2010. All Rights Reserved. + * Copyright Ericsson AB 2008-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 @@ -417,7 +417,7 @@ build_compile_result(Process *p, Eterm error_tag, pcre *result, int errcode, con BIF_RETTYPE re_compile_2(BIF_ALIST_2) { - int slen; + Uint slen; char *expr; pcre *result; int errcode = 0; @@ -444,7 +444,7 @@ re_compile_2(BIF_ALIST_2) BIF_TRAP2(ucompile_trap_exportp, BIF_P, BIF_ARG_1, BIF_ARG_2); } - if ((slen = io_list_len(BIF_ARG_1)) < 0) { + if (erts_iolist_size(BIF_ARG_1, &slen)) { BIF_ERROR(BIF_P,BADARG); } expr = erts_alloc(ERTS_ALC_T_RE_TMP_BUF, slen + 1); @@ -795,8 +795,8 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) memcpy(tmpb,ap->name,ap->len); tmpb[ap->len] = '\0'; } else { - int slen = io_list_len(val); - if (slen < 0) { + Uint slen; + if (erts_iolist_size(val, &slen)) { goto error; } if ((slen + 1) > tmpbsiz) { @@ -851,7 +851,7 @@ re_run_3(BIF_ALIST_3) const pcre *code_tmp; RestartContext restart; byte *temp_alloc = NULL; - int slength; + Uint slength; int startoffset = 0; int options = 0, comp_options = 0; int ovsize; @@ -875,7 +875,7 @@ re_run_3(BIF_ALIST_3) if (is_not_tuple(BIF_ARG_2) || (arityval(*tuple_val(BIF_ARG_2)) != 4)) { if (is_binary(BIF_ARG_2) || is_list(BIF_ARG_2) || is_nil(BIF_ARG_2)) { /* Compile from textual RE */ - int slen; + Uint slen; char *expr; pcre *result; int errcode = 0; @@ -889,7 +889,7 @@ re_run_3(BIF_ALIST_3) BIF_TRAP3(urun_trap_exportp, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); } - if ((slen = io_list_len(BIF_ARG_2)) < 0) { + if (erts_iolist_size(BIF_ARG_2, &slen)) { BIF_ERROR(BIF_P,BADARG); } @@ -1027,7 +1027,7 @@ re_run_3(BIF_ALIST_3) restart.flags |= RESTART_FLAG_SUBJECT_IN_BINARY; } else { handle_iolist: - if ((slength = io_list_len(BIF_ARG_1)) < 0) { + if (erts_iolist_size(BIF_ARG_1, &slength)) { erts_free(ERTS_ALC_T_RE_SUBJECT, restart.ovector); erts_free(ERTS_ALC_T_RE_SUBJECT, restart.code); if (restart.ret_info != NULL) { diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 8b48444904..68421b4387 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -472,7 +472,7 @@ static void tmp_alloc_dtor(struct enif_tmp_obj_t* obj) int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin) { struct enif_tmp_obj_t* tobj; - int sz; + Uint sz; if (is_binary(term)) { return enif_inspect_binary(env,term,bin); } @@ -483,7 +483,7 @@ int enif_inspect_iolist_as_binary(ErlNifEnv* env, Eterm term, ErlNifBinary* bin) bin->ref_bin = NULL; return 1; } - if ((sz = io_list_len(term)) < 0) { + if (erts_iolist_size(term, &sz)) { return 0; } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 96da894d90..7d5b1853e6 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1653,10 +1653,14 @@ struct Sint_buf { }; char* Sint_to_buf(Sint, struct Sint_buf*); +#define ERTS_IOLIST_OK 0 +#define ERTS_IOLIST_OVERFLOW 1 +#define ERTS_IOLIST_TYPE 2 + Eterm buf_to_intlist(Eterm**, char*, int, Eterm); /* most callers pass plain char*'s */ int io_list_to_buf(Eterm, char*, int); int io_list_to_buf2(Eterm, char*, int); -int io_list_len(Eterm); +int erts_iolist_size(Eterm, Uint *); int is_string(Eterm); void erl_at_exit(void (*) (void*), void*); Eterm collect_memory(Process *); diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index bf49417c3f..d9df90fe7d 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -961,6 +961,7 @@ do { \ if (thing_subtag(*binary_val(_real)) == REFC_BINARY_SUBTAG && \ _bitoffs == 0) { \ b_size += _size; \ + if (b_size < _size) goto L_overflow_error; \ in_clist = 0; \ v_size++; \ if (_size >= ERL_SMALL_IO_BIN_LIMIT) { \ @@ -975,6 +976,7 @@ do { \ } \ } else { \ c_size += _size; \ + if (c_size < _size) goto L_overflow_error; \ if (!in_clist) { \ in_clist = 1; \ v_size++; \ @@ -989,28 +991,30 @@ do { \ /* -** Size of a io list in bytes -** return -1 if error -** returns: - Total size of io list -** vsize - SysIOVec size needed for a writev -** csize - Number of bytes not in binary (in the common binary) -** pvsize - SysIOVec size needed if packing small binaries -** pcsize - Number of bytes in the common binary if packing -*/ + * Returns 0 if successful and a non-zero value otherwise. + * + * Return values through pointers: + * *vsize - SysIOVec size needed for a writev + * *csize - Number of bytes not in binary (in the common binary) + * *pvsize - SysIOVec size needed if packing small binaries + * *pcsize - Number of bytes in the common binary if packing + * *total_size - Total size of iolist in bytes + */ static int -io_list_vec_len(Eterm obj, int* vsize, int* csize, - int * pvsize, int * pcsize) +io_list_vec_len(Eterm obj, Uint* vsize, Uint* csize, + Uint* pvsize, Uint* pcsize, Uint* total_size) { DECLARE_ESTACK(s); Eterm* objp; - int v_size = 0; - int c_size = 0; - int b_size = 0; - int in_clist = 0; - int p_v_size = 0; - int p_c_size = 0; - int p_in_clist = 0; + Uint v_size = 0; + Uint c_size = 0; + Uint b_size = 0; + Uint in_clist = 0; + Uint p_v_size = 0; + Uint p_c_size = 0; + Uint p_in_clist = 0; + Uint total; goto L_jump_start; /* avoid a push */ @@ -1024,6 +1028,9 @@ io_list_vec_len(Eterm obj, int* vsize, int* csize, if (is_byte(obj)) { c_size++; + if (c_size == 0) { + goto L_overflow_error; + } if (!in_clist) { in_clist = 1; v_size++; @@ -1063,16 +1070,23 @@ io_list_vec_len(Eterm obj, int* vsize, int* csize, } } + total = c_size + b_size; + if (total < c_size) { + goto L_overflow_error; + } + *total_size = total; + DESTROY_ESTACK(s); *vsize = v_size; *csize = c_size; *pvsize = p_v_size; *pcsize = p_c_size; - return c_size + b_size; + return 0; L_type_error: + L_overflow_error: DESTROY_ESTACK(s); - return -1; + return 1; } /* write data to a port */ @@ -1080,7 +1094,7 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) { char *buf; erts_driver_t *drv = p->drv_ptr; - int size; + Uint size; int fpe_was_unmasked; ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); @@ -1088,10 +1102,10 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) p->caller = caller_id; if (drv->outputv != NULL) { - int vsize; - int csize; - int pvsize; - int pcsize; + Uint vsize; + Uint csize; + Uint pvsize; + Uint pcsize; int blimit; SysIOVec iv[SMALL_WRITE_VEC]; ErlDrvBinary* bv[SMALL_WRITE_VEC]; @@ -1100,8 +1114,8 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) ErlDrvBinary* cbin; ErlIOVec ev; - if ((size = io_list_vec_len(list, &vsize, &csize, - &pvsize, &pcsize)) < 0) { + if (io_list_vec_len(list, &vsize, &csize, + &pvsize, &pcsize, &size)) { goto bad_value; } /* To pack or not to pack (small binaries) ...? */ @@ -1176,7 +1190,7 @@ int erts_write_to_port(Eterm caller_id, Port *p, Eterm list) else { ASSERT(r == -1); /* Overflow */ erts_free(ERTS_ALC_T_TMP, buf); - if ((size = io_list_len(list)) < 0) { + if (erts_iolist_size(list, &size)) { goto bad_value; } @@ -2140,7 +2154,7 @@ erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist) byte* to_port = NULL; /* Buffer to write to port. */ /* Initialization is for shutting up warning about use before set. */ - int to_len = 0; /* Length of buffer. */ + Uint to_len = 0; /* Length of buffer. */ int must_free = 0; /* True if the buffer should be freed. */ char port_result[ERL_ONHEAP_BIN_LIMIT]; /* Default buffer for result from port. */ char* port_resp; /* Pointer to result buffer. */ @@ -2185,7 +2199,7 @@ erts_port_control(Process* p, Port* prt, Uint command, Eterm iolist) } else { ASSERT(r == -1); /* Overflow */ erts_free(ERTS_ALC_T_TMP, (void *) to_port); - if ((to_len = io_list_len(iolist)) < 0) { /* Type error */ + if (erts_iolist_size(iolist, &to_len)) { /* Type error */ return THE_NON_VALUE; } must_free = 1; diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index f531d1430b..afea51c44b 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3021,13 +3021,25 @@ int io_list_to_buf(Eterm obj, char* buf, int len) return -1; } -int io_list_len(Eterm obj) +/* + * Return 0 if successful, and non-zero if unsuccessful. + */ +int erts_iolist_size(Eterm obj, Uint* sizep) { Eterm* objp; - Sint len = 0; + Uint size = 0; DECLARE_ESTACK(s); goto L_again; +#define SAFE_ADD(Var, Val) \ + do { \ + Uint valvar = (Val); \ + Var += valvar; \ + if (Var < valvar) { \ + goto L_overflow_error; \ + } \ + } while (0) + while (!ESTACK_ISEMPTY(s)) { obj = ESTACK_POP(s); L_again: @@ -3037,9 +3049,12 @@ int io_list_len(Eterm obj) /* Head */ obj = CAR(objp); if (is_byte(obj)) { - len++; + size++; + if (size == 0) { + goto L_overflow_error; + } } else if (is_binary(obj) && binary_bitsize(obj) == 0) { - len += binary_size(obj); + SAFE_ADD(size, binary_size(obj)); } else if (is_list(obj)) { ESTACK_PUSH(s, CDR(objp)); goto L_iter_list; /* on head */ @@ -3051,23 +3066,29 @@ int io_list_len(Eterm obj) if (is_list(obj)) goto L_iter_list; /* on tail */ else if (is_binary(obj) && binary_bitsize(obj) == 0) { - len += binary_size(obj); + SAFE_ADD(size, binary_size(obj)); } else if (is_not_nil(obj)) { goto L_type_error; } } else if (is_binary(obj) && binary_bitsize(obj) == 0) { /* Tail was binary */ - len += binary_size(obj); + SAFE_ADD(size, binary_size(obj)); } else if (is_not_nil(obj)) { goto L_type_error; } } +#undef SAFE_ADD DESTROY_ESTACK(s); - return len; + *sizep = size; + return ERTS_IOLIST_OK; + + L_overflow_error: + DESTROY_ESTACK(s); + return ERTS_IOLIST_OVERFLOW; L_type_error: DESTROY_ESTACK(s); - return -1; + return ERTS_IOLIST_TYPE; } /* return 0 if item is not a non-empty flat list of bytes */ diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 401b3c23b6..67f0ae9408 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -275,6 +275,25 @@ bad_list_to_binary(Config) when is_list(Config) -> ?line test_bad_bin(fun(X, Y) -> X*Y end), ?line test_bad_bin([1,fun(X) -> X + 1 end,2|fun() -> 0 end]), ?line test_bad_bin([fun(X) -> X + 1 end]), + + %% Test iolists that do not fit in the address space. + %% Unfortunately, it would be too slow to test in a 64-bit emulator. + case erlang:system_info(wordsize) of + 4 -> huge_iolists(); + _ -> ok + end. + +huge_iolists() -> + FourGigs = 1 bsl 32, + ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ + [1 bsl N || N <- lists:seq(33, 37)], + ?line Base = <<0:(1 bsl 20)/unit:8>>, + [begin + L = build_iolist(Sz, Base), + ?line {'EXIT',{system_limit,_}} = (catch list_to_binary([L])), + ?line {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])), + ?line {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L)) + end || Sz <- Sizes], ok. test_bad_bin(List) -> diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index ffd08f2214..520e3e8c76 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -179,7 +179,13 @@ outputv_errors(Config) when is_list(Config) -> ?line outputv_errors_1([L|T]) end), outputv_errors_1(42), - ok. + + %% Test iolists that do not fit in the address space. + %% Unfortunately, it would be too slow to test in a 64-bit emulator. + case erlang:system_info(wordsize) of + 4 -> outputv_huge_iolists(); + _ -> ok + end. outputv_bad_types(Test) -> Types = [-1,256,atom,42.0,{a,b,c},make_ref(),fun() -> 42 end, @@ -187,11 +193,61 @@ outputv_bad_types(Test) -> _ = [Test(Type) || Type <- Types], ok. +outputv_huge_iolists() -> + FourGigs = 1 bsl 32, + ?line Sizes = [FourGigs+N || N <- lists:seq(0, 64)] ++ + [1 bsl N || N <- lists:seq(33, 37)], + ?line Base = <<0:(1 bsl 20)/unit:8>>, + [begin + ?line L = build_iolist(Sz, Base), + ?line outputv_errors_1(L) + end || Sz <- Sizes], + ok. + outputv_errors_1(Term) -> Port = open_port({spawn_driver,outputv_drv}, []), {'EXIT',{badarg,_}} = (catch port_command(Port, Term)), port_close(Port). +build_iolist(N, Base) when N < 16 -> + case random:uniform(3) of + 1 -> + <> = Base, + Bin; + _ -> + lists:seq(1, N) + end; +build_iolist(N, Base) when N =< byte_size(Base) -> + case random:uniform(3) of + 1 -> + <> = Base, + Bin; + 2 -> + <> = Base, + [Bin]; + 3 -> + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L]; + 1 -> + L = build_iolist(N div 2, Base), + [L,L,45] + end + end; +build_iolist(N0, Base) -> + Small = random:uniform(15), + Seq = lists:seq(1, Small), + N = N0 - Small, + case N rem 2 of + 0 -> + L = build_iolist(N div 2, Base), + [L,L|Seq]; + 1 -> + L = build_iolist(N div 2, Base), + [47,L,L|Seq] + end. + outputv_echo(doc) -> ["Test echoing data with a driver that supports outputv."]; outputv_echo(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:minutes(10)), -- cgit v1.2.3 From 875104fa418b2ead0f19250e75074002ae3e3c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 10 May 2011 08:59:21 +0200 Subject: Fix overflow in list_to_bitstring/1 Noticed-by: Jon Meredith --- erts/emulator/beam/binary.c | 90 +++++++++++++++++++++++++++---------- erts/emulator/test/binary_SUITE.erl | 5 ++- 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 91eb2114b1..1fb39c6c67 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -32,11 +32,11 @@ #include "erl_bits.h" #ifdef DEBUG -static int list_to_bitstr_buf(Eterm obj, char* buf, int len); +static int list_to_bitstr_buf(Eterm obj, char* buf, Uint len); #else static int list_to_bitstr_buf(Eterm obj, char* buf); #endif -static Sint bitstr_list_len(Eterm obj); +static int bitstr_list_len(Eterm obj, Uint* num_bytes); void erts_init_binary(void) @@ -399,7 +399,8 @@ BIF_RETTYPE iolist_to_binary_1(BIF_ALIST_1) BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) { Eterm bin; - int i,offset; + Uint sz; + int offset; byte* bytes; ErlSubBin* sb1; Eterm* hp; @@ -408,15 +409,19 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) BIF_RET(new_binary(BIF_P,(byte*)"",0)); } if (is_not_list(BIF_ARG_1)) { - goto error; + error: + BIF_ERROR(BIF_P, BADARG); } - if ((i = bitstr_list_len(BIF_ARG_1)) < 0) { + switch (bitstr_list_len(BIF_ARG_1, &sz)) { + case ERTS_IOLIST_TYPE: goto error; + case ERTS_IOLIST_OVERFLOW: + BIF_ERROR(BIF_P, SYSTEM_LIMIT); } - bin = new_binary(BIF_P, (byte *)NULL, i); + bin = new_binary(BIF_P, (byte *)NULL, sz); bytes = binary_bytes(bin); #ifdef DEBUG - offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes, i); + offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes, sz); #else offset = list_to_bitstr_buf(BIF_ARG_1, (char*) bytes); #endif @@ -425,20 +430,16 @@ BIF_RETTYPE list_to_bitstring_1(BIF_ALIST_1) hp = HAlloc(BIF_P, ERL_SUB_BIN_SIZE); sb1 = (ErlSubBin *) hp; sb1->thing_word = HEADER_SUB_BIN; - sb1->size = i-1; + sb1->size = sz-1; sb1->offs = 0; sb1->orig = bin; sb1->bitoffs = 0; sb1->bitsize = offset; sb1->is_writable = 0; - hp += ERL_SUB_BIN_SIZE; bin = make_binary(sb1); } BIF_RET(bin); - - error: - BIF_ERROR(BIF_P, BADARG); } BIF_RETTYPE split_binary_2(BIF_ALIST_2) @@ -502,7 +503,7 @@ BIF_RETTYPE split_binary_2(BIF_ALIST_2) */ static int #ifdef DEBUG -list_to_bitstr_buf(Eterm obj, char* buf, int len) +list_to_bitstr_buf(Eterm obj, char* buf, Uint len) #else list_to_bitstr_buf(Eterm obj, char* buf) #endif @@ -605,8 +606,8 @@ list_to_bitstr_buf(Eterm obj, char* buf) return offset; } -static Sint -bitstr_list_len(Eterm obj) +static int +bitstr_list_len(Eterm obj, Uint* num_bytes) { Eterm* objp; Uint len = 0; @@ -614,6 +615,26 @@ bitstr_list_len(Eterm obj) DECLARE_ESTACK(s); goto L_again; +#define SAFE_ADD(Var, Val) \ + do { \ + Uint valvar = (Val); \ + Var += valvar; \ + if (Var < valvar) { \ + goto L_overflow_error; \ + } \ + } while (0) + +#define SAFE_ADD_BITSIZE(Var, Bin) \ + do { \ + if (*binary_val(Bin) == HEADER_SUB_BIN) { \ + Uint valvar = ((ErlSubBin *) binary_val(Bin))->bitsize; \ + Var += valvar; \ + if (Var < valvar) { \ + goto L_overflow_error; \ + } \ + } \ + } while (0) + while (!ESTACK_ISEMPTY(s)) { obj = ESTACK_POP(s); L_again: @@ -624,9 +645,12 @@ bitstr_list_len(Eterm obj) obj = CAR(objp); if (is_byte(obj)) { len++; + if (len == 0) { + goto L_overflow_error; + } } else if (is_binary(obj)) { - len += binary_size(obj); - offs += binary_bitsize(obj); + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); } else if (is_list(obj)) { ESTACK_PUSH(s, CDR(objp)); goto L_iter_list; /* on head */ @@ -638,24 +662,44 @@ bitstr_list_len(Eterm obj) if (is_list(obj)) goto L_iter_list; /* on tail */ else if (is_binary(obj)) { - len += binary_size(obj); - offs += binary_bitsize(obj); + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); } else if (is_not_nil(obj)) { goto L_type_error; } } else if (is_binary(obj)) { - len += binary_size(obj); - offs += binary_bitsize(obj); + SAFE_ADD(len, binary_size(obj)); + SAFE_ADD_BITSIZE(offs, obj); } else if (is_not_nil(obj)) { goto L_type_error; } } +#undef SAFE_ADD +#undef SAFE_ADD_BITSIZE DESTROY_ESTACK(s); - return (Sint) (len + (offs/8) + ((offs % 8) != 0)); + + /* + * Make sure that the number of bits in the bitstring will fit + * in an Uint to ensure that the binary can be matched using + * the binary syntax. + */ + if (len << 3 < len) { + goto L_overflow_error; + } + len += (offs >> 3) + ((offs & 7) != 0); + if (len << 3 < len) { + goto L_overflow_error; + } + *num_bytes = len; + return ERTS_IOLIST_OK; L_type_error: DESTROY_ESTACK(s); - return (Sint) -1; + return ERTS_IOLIST_TYPE; + + L_overflow_error: + DESTROY_ESTACK(s); + return ERTS_IOLIST_OVERFLOW; } diff --git a/erts/emulator/test/binary_SUITE.erl b/erts/emulator/test/binary_SUITE.erl index 67f0ae9408..4e82381fba 100644 --- a/erts/emulator/test/binary_SUITE.erl +++ b/erts/emulator/test/binary_SUITE.erl @@ -23,12 +23,12 @@ %% Tests binaries and the BIFs: %% list_to_binary/1 %% iolist_to_binary/1 -%% bitstr_to_list/1 +%% list_to_bitstring/1 %% binary_to_list/1 %% binary_to_list/3 %% binary_to_term/1 %% binary_to_term/2 -%% bitstr_to_list/1 +%% bitstring_to_list/1 %% term_to_binary/1 %% erlang:external_size/1 %% size(Binary) @@ -291,6 +291,7 @@ huge_iolists() -> [begin L = build_iolist(Sz, Base), ?line {'EXIT',{system_limit,_}} = (catch list_to_binary([L])), + ?line {'EXIT',{system_limit,_}} = (catch list_to_bitstring([L])), ?line {'EXIT',{system_limit,_}} = (catch binary:list_to_bin([L])), ?line {'EXIT',{system_limit,_}} = (catch iolist_to_binary(L)) end || Sz <- Sizes], -- cgit v1.2.3 From 20c9d6e23f3572873c32daf7053819a844427a4b Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 10 May 2011 12:30:33 +0200 Subject: Link OpenSSL libraries static on Windows --- erts/configure.in | 34 +++++++++++++++++++++++++++++----- lib/crypto/c_src/Makefile.in | 8 ++++---- lib/ssl/c_src/Makefile.in | 8 +++++--- otp_build | 15 ++++++++------- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/erts/configure.in b/erts/configure.in index 31d1d55b8a..3cd6f0b6bf 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3523,6 +3523,8 @@ dnl use "PATH/include" and "PATH/lib". AC_SUBST(SSL_INCLUDE) AC_SUBST(SSL_ROOT) AC_SUBST(SSL_LIBDIR) +AC_SUBST(SSL_CRYPTO_LIBNAME) +AC_SUBST(SSL_SSL_LIBNAME) AC_SUBST(SSL_CC_RUNTIME_LIBRARY_PATH) AC_SUBST(SSL_LD_RUNTIME_LIBRARY_PATH) AC_SUBST(SSL_DED_LD_RUNTIME_LIBRARY_PATH) @@ -3686,19 +3688,41 @@ case "$erl_xcomp_without_sysroot-$with_ssl" in CRYPTO_APP=crypto SSH_APP=ssh - AC_MSG_CHECKING(for OpenSSL >= 0.9.7 in standard locations) + SSL_CRYPTO_LIBNAME=crypto + SSL_SSL_LIBNAME=ssl + + AC_MSG_CHECKING(for OpenSSL >= 0.9.7 in standard locations) for rdir in $extra_dir /cygdrive/c/OpenSSL $std_ssl_locations; do dir="$erl_xcomp_sysroot$rdir" if test -f "$erl_xcomp_isysroot$rdir/include/openssl/opensslv.h"; then is_real_ssl=yes SSL_ROOT="$dir" if test "x$MIXED_CYGWIN" = "xyes" ; then - if test -f "$dir/lib/VC/ssleay32.lib" || \ - test -f "$dir/lib/VC/openssl.lib"; then + if test -f "$dir/lib/VC/libeay32.lib"; then + SSL_RUNTIME_LIBDIR="$rdir/lib/VC" + SSL_LIBDIR="$dir/lib/VC" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_SSL_LIBNAME=ssleay32 + elif test -f "$dir/lib/VC/openssl.lib"; then SSL_RUNTIME_LIBDIR="$rdir/lib/VC" SSL_LIBDIR="$dir/lib/VC" - elif test -f "$dir/lib/ssleay32.lib" || \ - test -f "$dir/lib/openssl.lib"; then + elif test -f $dir/lib/VC/libeay32MD.lib; then + SSL_CRYPTO_LIBNAME=libeay32MD + SSL_SSL_LIBNAME=ssleay32MD + if test "x$enable_dynamic_ssl" = "xno" && \ + test -f $dir/lib/VC/static/libeay32MD.lib; then + SSL_RUNTIME_LIBDIR="$rdir/lib/VC/static" + SSL_LIBDIR="$dir/lib/VC/static" + else + SSL_RUNTIME_LIBDIR="$rdir/lib/VC" + SSL_LIBDIR="$dir/lib/VC" + fi + elif test -f "$dir/lib/libeay32.lib"; then + SSL_RUNTIME_LIBDIR="$rdir/lib" + SSL_LIBDIR="$dir/lib" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_CRYPTO_LIBNAME=ssleay32 + elif test -f "$dir/lib/openssl.lib"; then SSL_RUNTIME_LIBDIR="$rdir/lib" SSL_LIBDIR="$dir/lib" else diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 040adcfd09..3ace10403e 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -40,7 +40,7 @@ CFLAGS = $(DED_CFLAGS) # From erts/configure SSL_LIBDIR = @SSL_LIBDIR@ SSL_INCLUDE = @SSL_INCLUDE@ - +SSL_CRYPTO_LIBNAME = @SSL_CRYPTO_LIBNAME@ INCLUDES = $(SSL_INCLUDE) $(DED_INCLUDES) @@ -84,10 +84,10 @@ DYNAMIC_CRYPTO_LIB=@SSL_DYNAMIC_ONLY@ ifeq ($(DYNAMIC_CRYPTO_LIB),yes) SSL_DED_LD_RUNTIME_LIBRARY_PATH = @SSL_DED_LD_RUNTIME_LIBRARY_PATH@ -CRYPTO_LINK_LIB=$(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -lcrypto +CRYPTO_LINK_LIB=$(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -l$(SSL_CRYPTO_LIBNAME) else SSL_DED_LD_RUNTIME_LIBRARY_PATH= -CRYPTO_LINK_LIB=$(SSL_LIBDIR)/libcrypto.a +CRYPTO_LINK_LIB=$(SSL_LIBDIR)/lib$(SSL_CRYPTO_LIBNAME).a endif # ---------------------------------------------------- @@ -112,7 +112,7 @@ $(LIBDIR)/crypto$(TYPEMARKER).so: $(OBJS) $(LIBDIR)/crypto$(TYPEMARKER).dll: $(OBJS) $(INSTALL_DIR) $(LIBDIR) - $(LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(OBJS) -llibeay32 + $(LD) $(LDFLAGS) -o $@ $(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) $(OBJS) -l$(SSL_CRYPTO_LIBNAME) clean: ifeq ($(findstring win32,$(TARGET)), win32) diff --git a/lib/ssl/c_src/Makefile.in b/lib/ssl/c_src/Makefile.in index 49a209f2eb..da716f7c40 100644 --- a/lib/ssl/c_src/Makefile.in +++ b/lib/ssl/c_src/Makefile.in @@ -28,6 +28,8 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # ---------------------------------------------------- SSL_LIBDIR = @SSL_LIBDIR@ SSL_INCLUDE = @SSL_INCLUDE@ +SSL_CRYPTO_LIBNAME = @SSL_CRYPTO_LIBNAME@ +SSL_SSL_LIBNAME = @SSL_SSL_LIBNAME@ # ---------------------------------------------------- # Application version @@ -134,7 +136,7 @@ ifeq ($(findstring @,$(SSL_CC_RUNTIME_LIBRARY_PATH)),@) SSL_CC_RUNTIME_LIBRARY_PATH = $(CC_R_OPT) endif -SSL_LINK_LIB=-L$(SSL_LIBDIR) -lssl -lcrypto +SSL_LINK_LIB=-L$(SSL_LIBDIR) -l$(SSL_SSL_LIBNAME) -l$(SSL_CRYPTO_LIBNAME) else # not dynamic crypto lib (default from R11B-5) NEED_KERBEROS=@SSL_LINK_WITH_KERBEROS@ @@ -142,7 +144,7 @@ NEED_ZLIB=@SSL_LINK_WITH_ZLIB@ SSL_MAKEFILE = CC_R_OPT = SSL_CC_RUNTIME_LIBRARY_PATH= -SSL_LINK_LIB = $(SSL_LIBDIR)/libssl.a $(SSL_LIBDIR)/libcrypto.a +SSL_LINK_LIB = $(SSL_LIBDIR)/lib$(SSL_SSL_LIBNAME).a $(SSL_LIBDIR)/lib$(SSL_CRYPTO_LIBNAME).a ifeq ($(NEED_KERBEROS),yes) SSL_LINK_LIB += @STATIC_KERBEROS_LIBS@ endif @@ -175,7 +177,7 @@ $(BINDIR)/ssl_esock: $(OBJS) # Win32/Cygwin $(BINDIR)/ssl_esock.exe: $(OBJS) - $(LD) $(SSL_CC_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -o $@ $^ -lwsock32 -llibeay32 -lssleay32 + $(LD) $(SSL_CC_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -o $@ $^ -lwsock32 -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME) # Unix only, and only when linking statically $(SSL_MAKEFILE): diff --git a/otp_build b/otp_build index 1172592415..aebb91372c 100755 --- a/otp_build +++ b/otp_build @@ -186,15 +186,16 @@ set_config_flags () if target_contains free_source; then CONFIG_FLAGS="$CONFIG_FLAGS --host=$TARGET" fi + # Link SSL static for all binary distributions if not overridden + # Even for win32 starting with R14B03 + XX=`echo $* | grep -v dynamic-ssl-lib` + if [ "$*" = "$XX" ]; then + CONFIG_FLAGS="--disable-dynamic-ssl-lib $CONFIG_FLAGS" + fi if target_contains win32; then - CONFIG_FLAGS="--build=$BUILDSYS build_alias=win32 --host=win32 --target=win32 $CONFIG_FLAGS" - else - # Link SSL static for all binary distributions if not overridden - XX=`echo $* | grep -v dynamic-ssl-lib` - if [ "$*" = "$XX" ]; then - CONFIG_FLAGS="--disable-dynamic-ssl-lib $CONFIG_FLAGS" - fi + CONFIG_FLAGS="--build=$BUILDSYS build_alias=win32 --host=win32 --target=win32 $CONFIG_FLAGS" fi + if [ "x$OVERRIDE_CONFIG_CACHE" = "x" ]; then CONFIG_FLAGS="$CONFIG_FLAGS --cache-file=/dev/null" else -- cgit v1.2.3 From 168535441f6be241adaba34b4971e7a580ad17d8 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 10 May 2011 12:31:13 +0200 Subject: Update OpenSSL license text in crypto --- lib/crypto/doc/src/licenses.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crypto/doc/src/licenses.xml b/lib/crypto/doc/src/licenses.xml index bae87a373e..e851655aa5 100644 --- a/lib/crypto/doc/src/licenses.xml +++ b/lib/crypto/doc/src/licenses.xml @@ -37,7 +37,7 @@ This chapter contains in extenso versions OpenSSL License /* ==================================================================== - * Copyright (c) 1998-2002 The OpenSSL Project. All rights reserved. + * Copyright (c) 1998-2011 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions -- cgit v1.2.3 From 742722ec551b7cd4f9ee53190443ee422bb8c794 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 10 May 2011 15:40:27 +0200 Subject: Present 'low' memory count for halfword-vm with erlang:memory() --- erts/doc/src/erlang.xml | 8 +++++ erts/emulator/beam/erl_alloc.c | 70 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 19f501391f..f98e15cb52 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2356,6 +2356,14 @@ os_prompt% instrument(3) and/or erl(1).

+ low + +

Only on 64-bit halfword emulator.

+

The total amount of memory allocated in low memory areas + that are restricted to less than 4 Gb even though + the system may have more physical memory.

+

May be removed in future releases of halfword emulator.

+

The system value is not complete. Some allocated diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index d2d7c6da8e..cda404af5e 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -550,6 +550,7 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) erts_allctrs_info[ERTS_ALC_A_SYSTEM].enabled = 1; #if HALFWORD_HEAP + /* Init low memory variants by cloning */ init.std_alloc_low = init.std_alloc; init.std_alloc_low.init.util.alloc_no = ERTS_ALC_A_STANDARD_LOW; init.std_alloc_low.init.util.low_mem = 1; @@ -1605,6 +1606,49 @@ alcu_size(ErtsAlcType_t ai) return res; } +#if HALFWORD_HEAP +static ERTS_INLINE int +alcu_is_low(ErtsAlcType_t ai) +{ + int is_low = 0; + ASSERT(erts_allctrs_info[ai].enabled); + ASSERT(erts_allctrs_info[ai].alloc_util); + + if (!erts_allctrs_info[ai].thr_spec) { + Allctr_t *allctr = erts_allctrs_info[ai].extra; + is_low = allctr->mseg_opt.low_mem; + } + else { + ErtsAllocatorThrSpec_t *tspec = &erts_allctr_thr_spec[ai]; + int i; +# ifdef DEBUG + int found_one = 0; +# endif + + ASSERT(tspec->all_thr_safe); + ASSERT(tspec->enabled); + + for (i = tspec->size - 1; i >= 0; i--) { + Allctr_t *allctr = tspec->allctr[i]; + if (allctr) { +# ifdef DEBUG + if (!found_one) { + is_low = allctr->mseg_opt.low_mem; + found_one = 1; + } + else ASSERT(is_low == allctr->mseg_opt.low_mem); +# else + is_low = allctr->mseg_opt.low_mem; + break; +# endif + } + } + ASSERT(found_one); + } + return is_low; +} +#endif /* HALFWORD */ + Eterm erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) { @@ -1621,6 +1665,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) int code; int ets; int maximum; +#if HALFWORD_HEAP + int low; +#endif } want = {0}; struct { UWord total; @@ -1633,6 +1680,9 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) UWord code; UWord ets; UWord maximum; +#if HALFWORD_HEAP + UWord low; +#endif } size = {0}; Eterm atoms[sizeof(size)/sizeof(UWord)]; UWord *uintps[sizeof(size)/sizeof(UWord)]; @@ -1688,7 +1738,11 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) atoms[length] = am_maximum; uintps[length++] = &size.maximum; } - +#if HALFWORD_HEAP + want.low = 1; + atoms[length] = am_low; + uintps[length++] = &size.low; +#endif } else { DeclareTmpHeapNoproc(tmp_heap,2); @@ -1782,6 +1836,15 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) return am_badarg; } break; +#if HALFWORD_HEAP + case am_low: + if (!want.low) { + want.low = 1; + atoms[length] = am_low; + uintps[length++] = &size.low; + } + break; +#endif default: UnUseTmpHeapNoproc(2); return am_badarg; @@ -1856,6 +1919,11 @@ erts_memory(int *print_to_p, void *print_to_arg, void *proc, Eterm earg) if (save) *save = asz; size.total += asz; +#if HALFWORD_HEAP + if (alcu_is_low(ai)) { + size.low += asz; + } +#endif } } } -- cgit v1.2.3 From 8d1dd7079fdcd22765b30a1268649022360f6f56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 3 May 2011 14:20:22 +0200 Subject: erl_trace: Eliminate alias warning Avoid creating two variable names referring to the same memory area, because that can cause aliasing warnings in some versions of gcc. --- erts/emulator/beam/erl_trace.c | 22 ++++++++++------------ erts/emulator/beam/global.h | 6 ++++++ 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index c0397ca6c3..8833137112 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -1538,8 +1538,7 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, Eterm tracee; #endif Eterm transformed_args[MAX_ARG]; - DeclareTmpHeap(sub_bin_heap_et,ERL_SUB_BIN_SIZE,p); - ErlSubBin *sub_bin_heap = (ErlSubBin *) sub_bin_heap_et; + DeclareTypedTmpHeap(ErlSubBin,sub_bin_heap,p); ASSERT(tracer_pid); if (*tracer_pid == am_true) { @@ -1600,21 +1599,20 @@ erts_call_trace(Process* p, BeamInstr mfa[3], Binary *match_spec, if (is_boxed(arg) && header_is_bin_matchstate(*boxed_val(arg))) { ErlBinMatchState* ms = (ErlBinMatchState *) boxed_val(arg); ErlBinMatchBuffer* mb = &ms->mb; - ErlSubBin* sb = sub_bin_heap; Uint bit_size; ASSERT(sub_bin_heap->thing_word == 0); /* At most one of match context */ bit_size = mb->size - mb->offset; - sb->thing_word = HEADER_SUB_BIN; - sb->size = BYTE_OFFSET(bit_size); - sb->bitsize = BIT_OFFSET(bit_size); - sb->offs = BYTE_OFFSET(mb->offset); - sb->bitoffs = BIT_OFFSET(mb->offset); - sb->is_writable = 0; - sb->orig = mb->orig; - - arg = make_binary(sb); + sub_bin_heap->thing_word = HEADER_SUB_BIN; + sub_bin_heap->size = BYTE_OFFSET(bit_size); + sub_bin_heap->bitsize = BIT_OFFSET(bit_size); + sub_bin_heap->offs = BYTE_OFFSET(mb->offset); + sub_bin_heap->bitoffs = BIT_OFFSET(mb->offset); + sub_bin_heap->is_writable = 0; + sub_bin_heap->orig = mb->orig; + + arg = make_binary(sub_bin_heap); } transformed_args[i] = arg; } diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 96da894d90..857aab2126 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1890,6 +1890,8 @@ erts_alloc_message_heap(Uint size, # if defined(DEBUG) # define DeclareTmpHeap(VariableName,Size,Process) \ Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,Process) +# define DeclareTypedTmpHeap(Type,VariableName,Process) \ + Type *VariableName = (Type *) erts_debug_allocate_tmp_heap(sizeof(Type)/sizeof(Eterm),Process) # define DeclareTmpHeapNoproc(VariableName,Size) \ Eterm *VariableName = erts_debug_allocate_tmp_heap(Size,NULL) # define UseTmpHeap(Size,Proc) \ @@ -1911,6 +1913,8 @@ erts_alloc_message_heap(Uint size, # else # define DeclareTmpHeap(VariableName,Size,Process) \ Eterm *VariableName = (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used) +# define DeclareTypedTmpHeap(Type,VariableName,Process) \ + Type *VariableName = (Type *) (ERTS_PROC_GET_SCHDATA(Process)->tmp_heap)+(ERTS_PROC_GET_SCHDATA(Process)->num_tmp_heap_used) # define DeclareTmpHeapNoproc(VariableName,Size) \ Eterm *VariableName = (erts_get_scheduler_data()->tmp_heap)+(erts_get_scheduler_data()->num_tmp_heap_used) # define UseTmpHeap(Size,Proc) \ @@ -1936,6 +1940,8 @@ erts_alloc_message_heap(Uint size, #else # define DeclareTmpHeap(VariableName,Size,Process) \ Eterm VariableName[Size] +# define DeclareTypedTmpHeap(Type,VariableName,Process) \ + Type VariableName[1] # define DeclareTmpHeapNoproc(VariableName,Size) \ Eterm VariableName[Size] # define UseTmpHeap(Size,Proc) /* Nothing */ -- cgit v1.2.3 From 54b677e80cdd1da685c69d349ca83e68dce5b991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Tue, 3 May 2011 14:35:27 +0200 Subject: Eliminate alias warning in gcc 4.5.2 --- erts/emulator/beam/beam_emu.c | 9 ++++++--- erts/emulator/beam/erl_process.c | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 32ea8588d2..d5d113200a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1911,13 +1911,15 @@ void process_main(void) * Note that for the halfword emulator, the two first elements * of the array are used. */ - *((BeamInstr **) (UWord) c_p->def_arg_reg) = I+3; + BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg; + *pi = I+3; set_timer(c_p, unsigned_val(timeout_value)); } else if (timeout_value == am_infinity) { c_p->flags |= F_TIMO; #if !defined(ARCH_64) || HALFWORD_HEAP } else if (term_to_Uint(timeout_value, &time_val)) { - *((BeamInstr **) (UWord) c_p->def_arg_reg) = I+3; + BeamInstr** pi = (BeamInstr**) c_p->def_arg_reg; + *pi = I+3; set_timer(c_p, time_val); #endif } else { /* Wrong time */ @@ -1974,7 +1976,8 @@ void process_main(void) * we must test the F_INSLPQUEUE flag as well as the F_TIMO flag. */ if ((c_p->flags & (F_INSLPQUEUE | F_TIMO)) == 0) { - *((BeamInstr **) (UWord) c_p->def_arg_reg) = I+3; + BeamInstr** p = (BeamInstr **) c_p->def_arg_reg; + *p = I+3; set_timer(c_p, Arg(1)); } goto wait2; diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 31f23d3978..7a578ec882 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -7603,7 +7603,8 @@ continue_exit_process(Process *p static void timeout_proc(Process* p) { - p->i = *((BeamInstr **) (UWord) p->def_arg_reg); + BeamInstr** pi = (BeamInstr **) p->def_arg_reg; + p->i = *pi; p->flags |= F_TIMO; p->flags &= ~F_INSLPQUEUE; -- cgit v1.2.3 From b841f4fd4d6693ad91621c92839c2b36d7de472a Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 May 2011 10:55:10 +0200 Subject: Dialyzer cleanup (removed unused function clauses). --- lib/snmp/src/app/snmp.appup.src | 4 ++++ lib/snmp/src/misc/snmp_log.erl | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 5bc48a43d0..f931b14338 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -28,6 +28,7 @@ {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -59,6 +60,7 @@ {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_conf, soft_purge, soft_purge, []}, @@ -111,6 +113,7 @@ {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -144,6 +147,7 @@ {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_log, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_config, soft_purge, soft_purge, []}, diff --git a/lib/snmp/src/misc/snmp_log.erl b/lib/snmp/src/misc/snmp_log.erl index f9aa911817..7930e37c66 100644 --- a/lib/snmp/src/misc/snmp_log.erl +++ b/lib/snmp/src/misc/snmp_log.erl @@ -266,9 +266,6 @@ validate_loop(eof, _Log, _Validatior, _PrevTS, _PrevSN) -> ok; validate_loop({error, _} = Error, _Log, _Validator, _PrevTS, _PrevSN) -> Error; -validate_loop({corrupt_log_file, _} = Reason, - _Log, _Validator, _PrevTS, _PrevSN) -> - {error, Reason}; validate_loop({Cont, Terms}, Log, Validator, PrevTS, PrevSN) -> ?vtrace("validate_loop -> entry with" "~n Terms: ~p" @@ -508,8 +505,6 @@ loop(eof, _Log, _Write) -> ok; loop({error, _} = Error, _Log, _Write) -> Error; -loop({corrupt_log_file, _} = Reason, _Log, _Write) -> - {error, Reason}; loop({Cont, Terms}, Log, Write) -> case (catch lists:foreach(Write, Terms)) of {'EXIT', Reason} -> -- cgit v1.2.3 From b8f38a71e2c7768b6af6fd877f9bf5ceb0dbbaef Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Wed, 11 May 2011 10:56:16 +0200 Subject: Allow Dets tablenames to be arbitrary terms --- lib/stdlib/src/dets.erl | 4 ++-- lib/stdlib/test/dets_SUITE.erl | 26 ++++++++++++++++++++++++-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 6c91f1efb7..89beeea1bb 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.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 @@ -99,7 +99,7 @@ -type object() :: tuple(). -type pattern() :: atom() | tuple(). --type tab_name() :: atom() | reference(). +-type tab_name() :: term(). %%% This is the implementation of the mnesia file storage. Each (non %%% ram-copy) table is maintained in a corresponding .DAT file. The diff --git a/lib/stdlib/test/dets_SUITE.erl b/lib/stdlib/test/dets_SUITE.erl index 9fcc9e6aaf..698070368f 100644 --- a/lib/stdlib/test/dets_SUITE.erl +++ b/lib/stdlib/test/dets_SUITE.erl @@ -53,7 +53,7 @@ simultaneous_open/1, insert_new/1, repair_continuation/1, otp_5487/1, otp_6206/1, otp_6359/1, otp_4738/1, otp_7146/1, otp_8070/1, otp_8856/1, otp_8898/1, otp_8899/1, otp_8903/1, - otp_8923/1]). + otp_8923/1, otp_9282/1]). -export([dets_dirty_loop/0]). @@ -112,7 +112,7 @@ all() -> many_clients, otp_4906, otp_5402, simultaneous_open, insert_new, repair_continuation, otp_5487, otp_6206, otp_6359, otp_4738, otp_7146, otp_8070, otp_8856, otp_8898, - otp_8899, otp_8903, otp_8923] + otp_8899, otp_8903, otp_8923, otp_9282] end. groups() -> @@ -3857,6 +3857,28 @@ otp_8923(Config) when is_list(Config) -> file:delete(File), ok. +otp_9282(doc) -> + ["OTP-9282. The name of a table can be an arbitrary term"]; +otp_9282(suite) -> + []; +otp_9282(Config) when is_list(Config) -> + some_calls(make_ref(), Config), + some_calls({a,typical,name}, Config), + some_calls(fun() -> a_funny_name end, Config), + ok. + +some_calls(Tab, Config) -> + File = filename(ref, Config), + ?line {ok,T} = dets:open_file(Tab, [{file,File}]), + ?line T = Tab, + ?line false = dets:info(T, safe_fixed), + ?line File = dets:info(T, filename), + ?line ok = dets:insert(Tab, [{3,0}]), + ?line [{3,0}] = dets:lookup(Tab, 3), + ?line [{3,0}] = dets:traverse(Tab, fun(X) -> {continue, X} end), + ?line ok = dets:close(T), + file:delete(File). + %% %% Parts common to several test cases %% -- cgit v1.2.3 From 5bcdb3ac4ea27ca47e18628aa147e7544043fa84 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 11:45:30 +0200 Subject: Homogenize memory barriers on atomics Atomic operations with specified barriers have specified barrier semantics. Set and read operations have undefined barrier semantics. All other atomic operations implied full memory barriers, except when using the libatomic_ops library and the tilera atomics api. Some code in the runtime system assumed that all operations used (except for set, read and specified) implied full memory barriers. The use of the libatomic_ops library and the tilera atomics api have therefore been modified to behave as the other implementations. Some atomic operations with specified barrier semantics on sparc32 have also been been relaxed in this commit. --- erts/aclocal.m4 | 2 +- erts/include/internal/libatomic_ops/ethr_atomic.h | 26 ++++++++---------- erts/include/internal/sparc32/atomic.h | 29 ++++++++------------ erts/include/internal/tile/atomic.h | 33 ++++++++++++++++------- 4 files changed, 47 insertions(+), 43 deletions(-) diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index a1211bbf0c..f7e5c31910 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -1153,7 +1153,7 @@ case "$THR_LIB_NAME" in AO_nop_full(); AO_store(&x, (AO_t) 0); z = AO_load(&x); - z = AO_compare_and_swap(&x, (AO_t) 0, (AO_t) 1); + z = AO_compare_and_swap_full(&x, (AO_t) 0, (AO_t) 1); ], [ethr_have_native_atomics=yes ethr_have_libatomic_ops=yes]) diff --git a/erts/include/internal/libatomic_ops/ethr_atomic.h b/erts/include/internal/libatomic_ops/ethr_atomic.h index d56693dbf8..2fc82c99a8 100644 --- a/erts/include/internal/libatomic_ops/ethr_atomic.h +++ b/erts/include/internal/libatomic_ops/ethr_atomic.h @@ -146,13 +146,13 @@ ETHR_NATMC_FUNC__(read)(ETHR_ATMC_T__ *var) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { -#ifdef AO_HAVE_fetch_and_add - return ((ETHR_AINT_T__) AO_fetch_and_add(&var->counter, (AO_t) incr)) + incr; +#ifdef AO_HAVE_fetch_and_add_full + return ((ETHR_AINT_T__) AO_fetch_and_add_full(&var->counter, (AO_t) incr)) + incr; #else while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp + (AO_t) incr; - if (AO_compare_and_swap(&var->counter, exp, new)) + if (AO_compare_and_swap_full(&var->counter, exp, new)) return (ETHR_AINT_T__) new; } #endif @@ -167,8 +167,8 @@ ETHR_NATMC_FUNC__(add)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(inc_return)(ETHR_ATMC_T__ *var) { -#ifdef AO_HAVE_fetch_and_add1 - return ((ETHR_AINT_T__) AO_fetch_and_add1(&var->counter)) + 1; +#ifdef AO_HAVE_fetch_and_add1_full + return ((ETHR_AINT_T__) AO_fetch_and_add1_full(&var->counter)) + 1; #else return ETHR_NATMC_FUNC__(add_return)(var, 1); #endif @@ -183,8 +183,8 @@ ETHR_NATMC_FUNC__(inc)(ETHR_ATMC_T__ *var) static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(dec_return)(ETHR_ATMC_T__ *var) { -#ifdef AO_HAVE_fetch_and_sub1 - return ((ETHR_AINT_T__) AO_fetch_and_sub1(&var->counter)) - 1; +#ifdef AO_HAVE_fetch_and_sub1_full + return ((ETHR_AINT_T__) AO_fetch_and_sub1_full(&var->counter)) - 1; #else return ETHR_NATMC_FUNC__(add_return)(var, -1); #endif @@ -202,7 +202,7 @@ ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp & ((AO_t) mask); - if (AO_compare_and_swap(&var->counter, exp, new)) + if (AO_compare_and_swap_full(&var->counter, exp, new)) return (ETHR_AINT_T__) exp; } } @@ -213,7 +213,7 @@ ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) while (1) { AO_t exp = AO_load(&var->counter); AO_t new = exp | ((AO_t) mask); - if (AO_compare_and_swap(&var->counter, exp, new)) + if (AO_compare_and_swap_full(&var->counter, exp, new)) return (ETHR_AINT_T__) exp; } } @@ -225,7 +225,7 @@ ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, { ETHR_AINT_T__ act; do { - if (AO_compare_and_swap(&var->counter, (AO_t) exp, (AO_t) new)) + if (AO_compare_and_swap_full(&var->counter, (AO_t) exp, (AO_t) new)) return exp; act = (ETHR_AINT_T__) AO_load(&var->counter); } while (act == exp); @@ -237,7 +237,7 @@ ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new) { while (1) { AO_t exp = AO_load(&var->counter); - if (AO_compare_and_swap(&var->counter, exp, (AO_t) new)) + if (AO_compare_and_swap_full(&var->counter, exp, (AO_t) new)) return (ETHR_AINT_T__) exp; } } @@ -265,7 +265,6 @@ ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) return ((ETHR_AINT_T__) AO_fetch_and_add1_acquire(&var->counter)) + 1; #else ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(add_return)(var, 1); - ETHR_MEMORY_BARRIER; return res; #endif } @@ -287,7 +286,6 @@ ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) #ifdef AO_HAVE_fetch_and_sub1_release return ((ETHR_AINT_T__) AO_fetch_and_sub1_release(&var->counter)) - 1; #else - ETHR_MEMORY_BARRIER; return ETHR_NATMC_FUNC__(dec_return)(var); #endif } @@ -314,7 +312,6 @@ ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, return act; #else ETHR_AINT_T__ act = ETHR_NATMC_FUNC__(cmpxchg)(var, new, exp); - ETHR_MEMORY_BARRIER; return act; #endif } @@ -333,7 +330,6 @@ ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, } while (act == exp); return act; #else - ETHR_MEMORY_BARRIER; return ETHR_NATMC_FUNC__(cmpxchg)(var, new, exp); #endif } diff --git a/erts/include/internal/sparc32/atomic.h b/erts/include/internal/sparc32/atomic.h index 00380dbf07..16182f8b01 100644 --- a/erts/include/internal/sparc32/atomic.h +++ b/erts/include/internal/sparc32/atomic.h @@ -95,7 +95,7 @@ ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) { ETHR_AINT_T__ old, tmp; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); do { old = var->counter; tmp = old+incr; @@ -105,7 +105,7 @@ ETHR_NATMC_FUNC__(add_return)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ incr) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); } while (__builtin_expect(old != tmp, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old+incr; } @@ -144,7 +144,7 @@ ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { ETHR_AINT_T__ old, tmp; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); do { old = var->counter; tmp = old & mask; @@ -154,7 +154,7 @@ ETHR_NATMC_FUNC__(and_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); } while (__builtin_expect(old != tmp, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old; } @@ -163,7 +163,7 @@ ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) { ETHR_AINT_T__ old, tmp; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); do { old = var->counter; tmp = old | mask; @@ -173,7 +173,7 @@ ETHR_NATMC_FUNC__(or_retold)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ mask) : "r"(old), "r"(&var->counter), "0"(tmp) : "memory"); } while (__builtin_expect(old != tmp, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old; } @@ -182,7 +182,7 @@ ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ val) { ETHR_AINT_T__ old, new; - __asm__ __volatile__("membar #LoadLoad|#StoreLoad"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad" : : : "memory"); do { old = var->counter; new = val; @@ -192,20 +192,20 @@ ETHR_NATMC_FUNC__(xchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ val) : "r"(old), "r"(&var->counter), "0"(new) : "memory"); } while (__builtin_expect(old != new, 0)); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return old; } static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { - __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n"); + __asm__ __volatile__("membar #LoadLoad|#StoreLoad\n" : : : "memory"); __asm__ __volatile__( ETHR_CAS__ " [%2], %1, %0" : "=&r"(new) : "r"(old), "r"(&var->counter), "0"(new) : "memory"); - __asm__ __volatile__("membar #StoreLoad|#StoreStore"); + __asm__ __volatile__("membar #StoreLoad|#StoreStore" : : : "memory"); return new; } @@ -213,13 +213,11 @@ ETHR_NATMC_FUNC__(cmpxchg)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ * Atomic ops with at least specified barriers. */ -/* TODO: relax acquire barriers */ - static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(read_acqb)(ETHR_ATMC_T__ *var) { ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(read)(var); - __asm__ __volatile__("membar #LoadLoad|#LoadStore|#StoreLoad|#StoreStore" : : : "memory"); + __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } @@ -234,21 +232,18 @@ static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(inc_return_acqb)(ETHR_ATMC_T__ *var) { ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(inc_return)(var); - __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } static ETHR_INLINE void ETHR_NATMC_FUNC__(dec_relb)(ETHR_ATMC_T__ *var) { - __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); ETHR_NATMC_FUNC__(dec)(var); } static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(dec_return_relb)(ETHR_ATMC_T__ *var) { - __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); return ETHR_NATMC_FUNC__(dec_return)(var); } @@ -256,14 +251,12 @@ static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(cmpxchg_acqb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { ETHR_AINT_T__ res = ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); - __asm__ __volatile__("membar #LoadLoad|#LoadStore" : : : "memory"); return res; } static ETHR_INLINE ETHR_AINT_T__ ETHR_NATMC_FUNC__(cmpxchg_relb)(ETHR_ATMC_T__ *var, ETHR_AINT_T__ new, ETHR_AINT_T__ old) { - __asm__ __volatile__("membar #LoadStore|#StoreStore" : : : "memory"); return ETHR_NATMC_FUNC__(cmpxchg)(var, new, old); } diff --git a/erts/include/internal/tile/atomic.h b/erts/include/internal/tile/atomic.h index 48e4c0c6c8..0c7b597a6b 100644 --- a/erts/include/internal/tile/atomic.h +++ b/erts/include/internal/tile/atomic.h @@ -65,25 +65,35 @@ ethr_native_atomic32_read(ethr_native_atomic32_t *var) static ETHR_INLINE void ethr_native_atomic32_add(ethr_native_atomic32_t *var, ethr_sint32_t incr) { + ETHR_MEMORY_BARRIER; atomic_add(&var->counter, incr); + ETHR_MEMORY_BARRIER; } static ETHR_INLINE void ethr_native_atomic32_inc(ethr_native_atomic32_t *var) { + ETHR_MEMORY_BARRIER; atomic_increment(&var->counter); + ETHR_MEMORY_BARRIER; } static ETHR_INLINE void ethr_native_atomic32_dec(ethr_native_atomic32_t *var) { + ETHR_MEMORY_BARRIER; atomic_decrement(&var->counter); + ETHR_MEMORY_BARRIER; } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_add_return(ethr_native_atomic32_t *var, ethr_sint32_t incr) { - return atomic_exchange_and_add(&var->counter, incr) + incr; + ethr_sint32_t res; + ETHR_MEMORY_BARRIER; + res = atomic_exchange_and_add(&var->counter, incr) + incr; + ETHR_MEMORY_BARRIER; + return res; } static ETHR_INLINE ethr_sint32_t @@ -101,18 +111,27 @@ ethr_native_atomic32_dec_return(ethr_native_atomic32_t *var) static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_and_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { - return atomic_and_val(&var->counter, mask); + ethr_sint32_t res; + ETHR_MEMORY_BARRIER; + res = atomic_and_val(&var->counter, mask); + ETHR_MEMORY_BARRIER; + return res; } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_or_retold(ethr_native_atomic32_t *var, ethr_sint32_t mask) { - return atomic_or_val(&var->counter, mask); + ethr_sint32_t res; + ETHR_MEMORY_BARRIER; + res = atomic_or_val(&var->counter, mask); + ETHR_MEMORY_BARRIER; + return res; } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_xchg(ethr_native_atomic32_t *var, ethr_sint32_t val) { + ETHR_MEMORY_BARRIER; return atomic_exchange_acq(&var->counter, val); } @@ -121,6 +140,7 @@ ethr_native_atomic32_cmpxchg(ethr_native_atomic32_t *var, ethr_sint32_t new, ethr_sint32_t expected) { + ETHR_MEMORY_BARRIER; return atomic_compare_and_exchange_val_acq(&var->counter, new, expected); } @@ -139,9 +159,7 @@ ethr_native_atomic32_read_acqb(ethr_native_atomic32_t *var) static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_inc_return_acqb(ethr_native_atomic32_t *var) { - ethr_sint32_t res = ethr_native_atomic32_inc_return(var); - ETHR_MEMORY_BARRIER; - return res; + return ethr_native_atomic32_inc_return(var); } static ETHR_INLINE void @@ -154,14 +172,12 @@ ethr_native_atomic32_set_relb(ethr_native_atomic32_t *var, ethr_sint32_t val) static ETHR_INLINE void ethr_native_atomic32_dec_relb(ethr_native_atomic32_t *var) { - ETHR_MEMORY_BARRIER; ethr_native_atomic32_dec(var); } static ETHR_INLINE ethr_sint32_t ethr_native_atomic32_dec_return_relb(ethr_native_atomic32_t *var) { - ETHR_MEMORY_BARRIER; return ethr_native_atomic32_dec_return(var); } @@ -178,7 +194,6 @@ ethr_native_atomic32_cmpxchg_relb(ethr_native_atomic32_t *var, ethr_sint32_t new, ethr_sint32_t exp) { - ETHR_MEMORY_BARRIER; return ethr_native_atomic32_cmpxchg(var, new, exp); } -- cgit v1.2.3 From 4dde37d7d40d213ba052a4f6c18b7c257bf93960 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 May 2011 12:11:57 +0200 Subject: Release notes, test case and some minor decode fixes (allow only 32 bit values when decoding). --- lib/snmp/doc/src/notes.xml | 9 +++++++ lib/snmp/src/misc/snmp_pdus.erl | 11 ++------ lib/snmp/test/snmp_pdus_test.erl | 55 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index c507965cd8..105a977d92 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -43,6 +43,15 @@

-

--> + +

Fixed endode/decode of values of type Counter32.

+

This type (Counter32) is an unsigned integer 32, + but is actually encoded as an signed integer 32. + The encode/decode functions however, treated it as if it was + encodeded as an unsigned integer 32.

+

Own Id: OTP-9022

+
+

[agent] Added support for sending traps to IPv6 targets.

See the diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl index 9c273bc0e0..213d0226b1 100644 --- a/lib/snmp/src/misc/snmp_pdus.erl +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -268,14 +268,6 @@ dec_value([4 | Bytes]) -> dec_value([64 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'IpAddress', Value}, Rest}; -%% dec_value([65 | Bytes]) -> -%% {Value, Rest} = dec_integer_notag(Bytes), -%% if -%% (Value >= 0) andalso (Value =< 4294967295) -> -%% {{'Counter32', Value}, Rest}; -%% true -> -%% exit({error, {bad_counter32, Value}}) -%% end; dec_value([65 | Bytes]) -> %% Counter32 is an unsigned 32 but is actually encoded as %% a signed integer 32 (INTEGER). @@ -284,12 +276,13 @@ dec_value([65 | Bytes]) -> if (Value >= 0) andalso (Value =< 16#ffffffff) -> %% We accept value above 16#7fffffff + %% in order to be backward bug-compatible Value; (Value < 0) -> 16#ffffffff + Value + 1; true -> exit({error, {bad_counter32, Value}}) end, - {{'Counter64', Value2}, Rest}; + {{'Counter32', Value2}, Rest}; dec_value([66 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), if diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl index ef510ad62e..197797c816 100644 --- a/lib/snmp/test/snmp_pdus_test.erl +++ b/lib/snmp/test/snmp_pdus_test.erl @@ -38,6 +38,8 @@ otp7575/1, otp8563/1, + otp9022/1, + init_per_testcase/2, end_per_testcase/2 ]). @@ -75,7 +77,7 @@ all() -> [{group, tickets}]. groups() -> - [{tickets, [], [otp7575, otp8563]}]. + [{tickets, [], [otp7575, otp8563, otp9022]}]. init_per_group(_GroupName, Config) -> Config. @@ -169,7 +171,56 @@ otp8563(Config) when is_list(Config) -> Unexpected7 -> exit({unexpected_encode_result, Unexpected7, Val7}) end, - + + ok. + + +otp9022(suite) -> []; +otp9022(doc) -> ["OTP-9022"]; +otp9022(Config) when is_list(Config) -> + Val1 = 16#7fffffff, + io:format("try encode and decode ~w~n", [Val1]), + Enc1 = snmp_pdus:enc_value('Counter32', Val1), + {{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1), + + Val2 = Val1 + 1, + io:format("try encode and decode ~w~n", [Val2]), + Enc2 = snmp_pdus:enc_value('Counter32', Val2), + {{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2), + + Val3 = Val2 + 1, + io:format("try encode and decode ~w~n", [Val3]), + Enc3 = snmp_pdus:enc_value('Counter32', Val3), + {{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3), + + Val4 = 16#fffffffe, + io:format("try encode and decode ~w~n", [Val4]), + Enc4 = snmp_pdus:enc_value('Counter32', Val4), + {{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4), + + Val5 = Val4 + 1, + io:format("try encode and decode ~w~n", [Val5]), + Enc5 = snmp_pdus:enc_value('Counter32', Val5), + {{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5), + + Val6 = 16#ffffffff + 1, + io:format("try and fail to encode ~w~n", [Val6]), + case (catch snmp_pdus:enc_value('Counter32', Val6)) of + {'EXIT', {error, {bad_counter32, Val6}}} -> + ok; + Unexpected6 -> + exit({unexpected_encode_result, Unexpected6, Val6}) + end, + + Val7 = -1, + io:format("try and fail to encode ~w~n", [Val7]), + case (catch snmp_pdus:enc_value('Counter32', Val7)) of + {'EXIT', {error, {bad_counter32, Val7}}} -> + ok; + Unexpected7 -> + exit({unexpected_encode_result, Unexpected7, Val7}) + end, + ok. -- cgit v1.2.3 From 3122d9125723578e140082a499c08a27480de67b Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 May 2011 12:14:07 +0200 Subject: Cosmetics. --- lib/snmp/src/misc/snmp_pdus.erl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl index 213d0226b1..82618a0b86 100644 --- a/lib/snmp/src/misc/snmp_pdus.erl +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -281,7 +281,8 @@ dec_value([65 | Bytes]) -> (Value < 0) -> 16#ffffffff + Value + 1; true -> - exit({error, {bad_counter32, Value}}) end, + exit({error, {bad_counter32, Value}}) + end, {{'Counter32', Value2}, Rest}; dec_value([66 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), -- cgit v1.2.3 From 8ca1414cf245e53e638ef53f28661ac574fee6cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 5 May 2011 10:00:58 +0200 Subject: cerl: Remove ancient obsolete options The -frag and -smp_frag options was used to start an (at the time) experimental build of the emulator. The -shared and -hybrid option was used to start experimental versions of the emulator with alternate heap architectures. --- erts/etc/unix/cerl.src | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index 0355f2629f..e31a95f27e 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -135,26 +135,11 @@ while [ $# -gt 0 ]; do cargs="$cargs -lcnt" TYPE=.lcnt ;; - "-frag") - shift - cargs="$cargs -frag" - EMU_TYPE=.frag - ;; - "-smp_frag") - shift - cargs="$cargs -smp_frag" - EMU_TYPE=.smp_frag - ;; "-gprof") shift cargs="$cargs -gprof" TYPE=.gprof ;; - "-hybrid") - shift - cargs="$cargs -hybrid" - EMU_TYPE=.hybrid - ;; "-debug") shift cargs="$cargs -debug" @@ -180,11 +165,6 @@ while [ $# -gt 0 ]; do # shift # GDB=xxgdb # ;; - "-shared") - shift - cargs="$cargs -shared" - TYPE=.shared - ;; "-purify") shift cargs="$cargs -purify" -- cgit v1.2.3 From f587cbbb739fc2318d3e2f824d3df271028f9c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 5 May 2011 10:49:26 +0200 Subject: Teach erlexec the -emu_name_exit option It is difficult/impossible to find out whether an invocation of 'erl' will start the smp emulator or the non-smp emulator (short of actually starting the emulator). Therefore we will need a way to ask 'erlexec'. --- erts/etc/common/erlexec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 60b3af7db7..90c19f66f8 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -394,6 +394,7 @@ int main(int argc, char **argv) int print_args_exit = 0; int print_qouted_cmd_exit = 0; erts_cpu_info_t *cpuinfo = NULL; + char* emu_name; #ifdef __WIN32__ this_module_handle = module; @@ -566,6 +567,7 @@ int main(int argc, char **argv) usage("+MYm"); } emu = add_extra_suffixes(emu, emu_type); + emu_name = strsave(emu); erts_snprintf(tmpStr, sizeof(tmpStr), "%s" DIRSEP "%s" BINARY_EXT, bindir, emu); emu = strsave(tmpStr); @@ -682,6 +684,9 @@ int main(int argc, char **argv) verbose = 1; } else if (strcmp(argv[i], "-emu_args_exit") == 0) { print_args_exit = 1; + } else if (strcmp(argv[i], "-emu_name_exit") == 0) { + printf("%s\n", emu_name); + exit(0); } else if (strcmp(argv[i], "-emu_qouted_cmd_exit") == 0) { print_qouted_cmd_exit = 1; } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */ -- cgit v1.2.3 From ea7b192ef727ad5dac64df22f3c4a74288608b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Thu, 5 May 2011 10:55:08 +0200 Subject: cerl: Fix several incompatibilities with 'erl' Invoking 'cerl' like 'cerl -smp' would only start the smp emulator if default was to start the non-smp emulator, otherwise 'erlexec' would try to start 'beam.smp.smp'. Furthermore 'cerl -smp disable' would be handled in the same way as 'cerl -smp'. --- erts/etc/unix/cerl.src | 46 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src index e31a95f27e..0b2d6512ea 100644 --- a/erts/etc/unix/cerl.src +++ b/erts/etc/unix/cerl.src @@ -62,13 +62,21 @@ cxargs_add() { done } +eeargs= +eeargs_add() { + while [ $# -gt 0 ]; do + cargs="$cargs $1" + eeargs="$eeargs $1" + shift + done +} + core= GDB= GDBBP= GDBARGS= TYPE= -EMU_TYPE= debug= run_valgrind=no @@ -127,8 +135,26 @@ while [ $# -gt 0 ]; do ;; "-smp") shift - cargs="$cargs -smp" - EMU_TYPE=.smp + if [ $# -le 0 ]; then + eeargs_add -smp + else + case $1 in + disable) + shift + eeargs_add -smpdisable + ;; + enable) + shift + eeargs_add -smp + ;; + *) + eeargs_add -smp + esac + fi + ;; + "-smpdisable") + shift + eeargs_add -smpdisable ;; "-lcnt") shift @@ -202,7 +228,9 @@ PATH=$BINDIR:$ROOTDIR/bin:$PATH EXEC=$BINDIR/erlexec PROGNAME="$PROGNAME $cargs" -EMU=$EMU$TYPE$EMU_TYPE +EMU="$EMU$TYPE" +EMU_NAME=`$EXEC -emu_name_exit $eeargs` + if [ $run_valgrind != yes ]; then xargs="$xargs -pz $PRELOADED --" fi @@ -228,9 +256,9 @@ if [ "x$GDB" = "x" ]; then valgrind_log= else if [ $valmajor -gt 2 -a $valminor -gt 4 ]; then - valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU.log.$$" + valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log.$$" else - valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU.log" + valgrind_log="$log_file_prefix$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU_NAME.log" fi fi if [ "x$VALGRIND_MISC_FLAGS" = "x" ]; then @@ -243,9 +271,9 @@ if [ "x$GDB" = "x" ]; then early_beam_args=`echo $beam_args | sed "s|^\(.*-progname\).*$|\1|g"` late_beam_args=`echo $beam_args | sed "s|^$pre_beam_args.*\(-- -home.*\)$|\1|g"` - exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU $emu_xargs $early_beam_args "$PROGNAME" $late_beam_args -pz $PRELOADED + exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU_NAME $emu_xargs $early_beam_args "$PROGNAME" $late_beam_args -pz $PRELOADED else - exec $EXEC $xargs ${1+"$@"} + exec $EXEC $eeargs $xargs ${1+"$@"} fi else if [ "x$EMACS" = "x" ]; then @@ -280,5 +308,5 @@ else (insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \ (comint-send-input)" # Fire up gdb in emacs... - exec $EMACS --eval "(progn (gdb \"gdb $GDBARGS$EMU\") $gdbcmd)" + exec $EMACS --eval "(progn (gdb \"gdb $GDBARGS$EMU_NAME\") $gdbcmd)" fi -- cgit v1.2.3 From 418d08ee5ac108d47940516e62580c9393395c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= Date: Mon, 9 May 2011 13:30:59 +0200 Subject: erlexec: Make ERL__FLAGS behave like ERL_AFLAGS The contents of the ERL__FLAGS undocumented environment variable would be added at the end of the command line passed to the BEAM emualator (similar to ERL_ZFLAGS), making it impossible to override (for example) "-smp" with "-smp disable" on the command line. To allow overriding, put the contents of ERL__FLAGS at the beginning of the command line (similar to ERL_AFLAGS). Since the ERL__FLAGS variable is undocumented and unsupported, it is OK to change its behaviour in a minor release. --- erts/etc/common/erlexec.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index 90c19f66f8..90d3be9448 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -1975,6 +1975,11 @@ initial_argv_massage(int *argc, char ***argv) */ vix = 0; + + av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS"); + if (av) + avv[vix++].argv = av; + av = build_args_from_env("ERL_AFLAGS"); if (av) avv[vix++].argv = av; @@ -1989,10 +1994,6 @@ initial_argv_massage(int *argc, char ***argv) if (av) avv[vix++].argv = av; - av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS"); - if (av) - avv[vix++].argv = av; - av = build_args_from_env("ERL_ZFLAGS"); if (av) avv[vix++].argv = av; -- cgit v1.2.3 From 5a485461a1157fef1bb3ce8426bfd1ad57b5ca52 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 6 May 2011 16:01:56 +0200 Subject: Use Erlang specs and types for documentation --- lib/kernel/doc/specs/.gitignore | 1 + lib/kernel/doc/src/Makefile | 40 +- lib/kernel/doc/src/application.xml | 234 +++------ lib/kernel/doc/src/auth.xml | 51 +- lib/kernel/doc/src/code.xml | 6 + lib/kernel/doc/src/disk_log.xml | 451 ++++++++--------- lib/kernel/doc/src/erl_boot_server.xml | 44 +- lib/kernel/doc/src/erl_ddll.xml | 129 ++--- lib/kernel/doc/src/error_handler.xml | 44 +- lib/kernel/doc/src/error_logger.xml | 135 ++--- lib/kernel/doc/src/file.xml | 895 +++++++++++++-------------------- lib/kernel/doc/src/gen_sctp.xml | 237 ++++----- lib/kernel/doc/src/gen_tcp.xml | 163 +++--- lib/kernel/doc/src/gen_udp.xml | 101 ++-- lib/kernel/doc/src/global.xml | 176 +++---- lib/kernel/doc/src/global_group.xml | 157 +++--- lib/kernel/doc/src/heart.xml | 16 +- lib/kernel/doc/src/inet.xml | 193 +++---- lib/kernel/doc/src/inet_res.xml | 271 +++------- lib/kernel/doc/src/net_adm.xml | 74 +-- lib/kernel/doc/src/net_kernel.xml | 90 ++-- lib/kernel/doc/src/os.xml | 44 +- lib/kernel/doc/src/pg2.xml | 66 +-- lib/kernel/doc/src/rpc.xml | 330 ++++-------- lib/kernel/doc/src/seq_trace.xml | 95 ++-- lib/kernel/doc/src/specs.xml | 33 ++ lib/kernel/doc/src/wrap_log_reader.xml | 66 ++- 27 files changed, 1585 insertions(+), 2557 deletions(-) create mode 100644 lib/kernel/doc/specs/.gitignore create mode 100644 lib/kernel/doc/src/specs.xml diff --git a/lib/kernel/doc/specs/.gitignore b/lib/kernel/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/kernel/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index f8c1cac8b3..de10e31d36 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -1,19 +1,20 @@ -# ``The contents of this file are subject to the Erlang Public License, +# +# %CopyrightBegin% +# +# 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 # 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 via the world wide web at http://www.erlang.org/. -# +# 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. -# -# The Initial Developer of the Original Code is Ericsson Utvecklings AB. -# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings -# AB. All Rights Reserved.'' -# -# $Id$ +# +# %CopyrightEnd% # include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk @@ -94,19 +95,24 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include + # ---------------------------------------------------- # Targets # ---------------------------------------------------- $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -125,8 +131,22 @@ clean clean_docs: rm -f $(MAN4DIR)/* rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ +$(SPECDIR)/specs_erl_prim_loader_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_prim_loader_stub +$(SPECDIR)/specs_erlang_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erlang_stub +$(SPECDIR)/specs_init_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module init_stub +$(SPECDIR)/specs_zlib_stub.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module zlib_stub + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index 47d578a339..51a3311ec2 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -4,7 +4,7 @@

- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -50,20 +50,27 @@

Refer to OTP Design Principles for more information about applications and behaviours.

+ + + + + + + + + + tuple_of(T) +

A tuple where the elements are of type T.

+
+
- get_all_env() -> Env - get_all_env(Application) -> Env + + Get the configuration parameters for an application - - Application = atom() - Env = [{Par,Val}] -  Par = atom() -  Val = term() -

Returns the configuration parameters and their values for - Application. If the argument is omitted, it defaults to + Application. If the argument is omitted, it defaults to the application of the calling process.

If the specified application is not loaded, or if the process executing the call does not belong to any application, @@ -71,18 +78,12 @@ - get_all_key() -> {ok, Keys} | [] - get_all_key(Application) -> {ok, Keys} | undefined + + Get the application specification keys - - Application = atom() - Keys = [{Key,Val}] -  Key = atom() -  Val = term() -

Returns the application specification keys and their values - for Application. If the argument is omitted, it + for Application. If the argument is omitted, it defaults to the application of the calling process.

If the specified application is not loaded, the function returns undefined. If the process executing the call @@ -91,17 +92,12 @@ - get_application() -> {ok, Application} | undefined - get_application(Pid | Module) -> {ok, Application} | undefined + + Get the name of an application containing a certain process or module - - Pid = pid() - Module = atom() - Application = atom() -

Returns the name of the application to which the process - Pid or the module Module belongs. Providing no + Pid or the module Module belongs. Providing no argument is the same as calling get_application(self()).

If the specified process does not belong to any application, @@ -110,17 +106,12 @@ - get_env(Par) -> {ok, Val} | undefined - get_env(Application, Par) -> {ok, Val} | undefined + + Get the value of a configuration parameter - - Application = atom() - Par = atom() - Val = term() - -

Returns the value of the configuration parameter Par - for Application. If the application argument is +

Returns the value of the configuration parameter Par + for Application. If the application argument is omitted, it defaults to the application of the calling process.

If the specified application is not loaded, or @@ -130,17 +121,12 @@ - get_key(Key) -> {ok, Val} | undefined - get_key(Application, Key) -> {ok, Val} | undefined + + Get the value of an application specification key - - Application = atom() - Key = atom() - Val = term() -

Returns the value of the application specification key - Key for Application. If the application + Key for Application. If the application argument is omitted, it defaults to the application of the calling process.

If the specified application is not loaded, or @@ -150,45 +136,35 @@ - load(AppDescr) -> ok | {error, Reason} - load(AppDescr, Distributed) -> ok | {error, Reason} + + Load an application - - AppDescr = Application | AppSpec -  Application = atom() -  AppSpec = {application,Application,AppSpecKeys} -   AppSpec = [{Key,Val}] -    Key = atom() -    Val = term() - Distributed = {Application,Nodes} | {Application,Time,Nodes} | default -  Nodes = [node() | {node(),..,node()}] -  Time = integer() > 0 - Reason = term() - + +

Loads the application specification for an application into the application controller. It will also load the application specifications for any included applications. Note that the function does not load the actual Erlang object code.

-

The application can be given by its name Application. +

The application can be given by its name Application. In this case the application controller will search the code - path for the application resource file Application.app + path for the application resource file Application.app and load the specification it contains.

The application specification can also be given directly as a - tuple AppSpec. This tuple should have the format and + tuple AppSpec. This tuple should have the format and contents as described in app(4).

-

If Distributed == {Application,[Time,]Nodes}, +

If Distributed == {Application,[Time,]Nodes}, the application will be distributed. The argument overrides the value for the application in the Kernel configuration - parameter distributed. Application must be + parameter distributed. Application must be the name of the application (same as in the first argument). - If a node crashes and Time has been specified, then - the application controller will wait for Time + If a node crashes and Time has been specified, then + the application controller will wait for Time milliseconds before attempting to restart the application on - another node. If Time is not specified, it will + another node. If Time is not specified, it will default to 0 and the application will be restarted immediately.

-

Nodes is a list of node names where the application +

Nodes is a list of node names where the application may run, in priority from left to right. Node names can be grouped using tuples to indicate that they have the same priority. Example:

@@ -204,32 +180,22 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]
- loaded_applications() -> [{Application, Description, Vsn}] + Get the currently loaded applications - - Application = atom() - Description = string() - Vsn = string() -

Returns a list with information about the applications which have been loaded using load/1,2, also included - applications. Application is the application name. - Description and Vsn are the values of its + applications. Application is the application name. + Description and Vsn are the values of its description and vsn application specification keys, respectively.

- permit(Application, Bool) -> ok | {error, Reason} + Change an application's permission to run on a node. - - Application = atom() - Bool = bool() - Reason = term() - -

Changes the permission for Application to run at +

Changes the permission for Application to run at the current node. The application must have been loaded using load/1,2 for the function to have effect.

If the permission of a loaded, but not started, application @@ -258,20 +224,14 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] - set_env(Application, Par, Val) -> ok - set_env(Application, Par, Val, Timeout) -> ok + + Set the value of a configuration parameter - - Application = atom() - Par = atom() - Val = term() - Timeout = int() | infinity - -

Sets the value of the configuration parameter Par for - Application.

+

Sets the value of the configuration parameter Par for + Application.

set_env/3 uses the standard gen_server timeout - value (5000 ms). A Timeout argument can be provided + value (5000 ms). A Timeout argument can be provided if another timeout value is useful, for example, in situations where the application controller is heavily loaded.

@@ -285,20 +245,15 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]
- start(Application) -> ok | {error, Reason} - start(Application, Type) -> ok | {error, Reason} + + Load and start an application - - Application = atom() - Type = permanent | transient | temporary - Reason = term() - -

Starts Application. If it is not loaded, +

Starts Application. If it is not loaded, the application controller will first load it using load/1. It will make sure any included applications are loaded, but will not start them. That is assumed to be - taken care of in the code for Application.

+ taken care of in the code for Application.

The application controller checks the value of the application specification key applications, to ensure that all applications that should be started before @@ -310,7 +265,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] The application master starts the application by calling the application callback function Module:start/2 as defined by the application specification key mod.

-

The Type argument specifies the type of +

The Type argument specifies the type of the application. If omitted, it defaults to temporary.

If a permanent application terminates, all other @@ -331,19 +286,15 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]
- start_type() -> StartType | local | undefined + Get the start type of an ongoing application startup. - - StartType = normal | {takeover,Node} | {failover,Node} -  Node = node() -

This function is intended to be called by a process belonging to an application, when the application is being started, to - determine the start type which is either StartType or + determine the start type which is either StartType or local.

-

See Module:start/2 for a description of - StartType.

+

See Module:start/2 for a description of + StartType.

local is returned if only parts of the application is being restarted (by a supervisor), or if the function is called outside a startup.

@@ -352,14 +303,10 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]
- stop(Application) -> ok | {error, Reason} + Stop an application - - Application = atom() - Reason = term() - -

Stops Application. The application master calls +

Stops Application. The application master calls Module:prep_stop/1, if such a function is defined, and then tells the top supervisor of the application to shutdown (see supervisor(3)). This means that the entire @@ -384,16 +331,11 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] - takeover(Application, Type) -> ok | {error, Reason} + Take over a distributed application - - Application = atom() - Type = permanent | transient | temporary - Reason = term() -

Performs a takeover of the distributed application - Application, which executes at another node + Application, which executes at another node Node. At the current node, the application is restarted by calling Module:start({takeover,Node},StartArgs). Module @@ -413,14 +355,10 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] - unload(Application) -> ok | {error, Reason} + Unload an application - - Application = atom() - Reason = term() - -

Unloads the application specification for Application +

Unloads the application specification for Application from the application controller. It will also unload the application specifications for any included applications. Note that the function does not purge the actual Erlang @@ -428,19 +366,14 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] - unset_env(Application, Par) -> ok - unset_env(Application, Par, Timeout) -> ok + + Unset the value of a configuration parameter - - Application = atom() - Par = atom() - Timeout = int() | infinity - -

Removes the configuration parameter Par and its value - for Application.

+

Removes the configuration parameter Par and its value + for Application.

unset_env/2 uses the standard gen_server - timeout value (5000 ms). A Timeout argument can be + timeout value (5000 ms). A Timeout argument can be provided if another timeout value is useful, for example, in situations where the application controller is heavily loaded.

@@ -454,23 +387,17 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]
- which_applications() -> [{Application, Description, Vsn}] - which_applications(Timeout) -> [{Application, Description, Vsn}] + + Get the currently running applications - - Application = atom() - Description = string() - Vsn = string() - Timeout = int() | infinity -

Returns a list with information about the applications which - are currently running. Application is the application - name. Description and Vsn are the values of its + are currently running. Application is the application + name. Description and Vsn are the values of its description and vsn application specification keys, respectively.

which_applications/0 uses the standard - gen_server timeout value (5000 ms). A Timeout + gen_server timeout value (5000 ms). A Timeout argument can be provided if another timeout value is useful, for example, in situations where the application controller is heavily loaded.

@@ -501,7 +428,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] structured according to the OTP design principles as a supervision tree, this means starting the top supervisor of the tree.

-

StartType defines the type of start:

+

StartType defines the type of start:

normal if it's a normal startup. normal also if the application is distributed and @@ -532,8 +459,7 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}] Extended start of an application Phase = atom() - StartType = normal | {takeover,Node} | {failover,Node} -  Node = node() + StartType = start_type() PhaseArgs = term() Pid = pid() State = state() diff --git a/lib/kernel/doc/src/auth.xml b/lib/kernel/doc/src/auth.xml index f53fc8b29a..15d9ef0fe4 100644 --- a/lib/kernel/doc/src/auth.xml +++ b/lib/kernel/doc/src/auth.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -34,29 +34,28 @@ Cookie system, refer to Distributed Erlang in the Erlang Reference Manual.

+ + + + + - is_auth(Node) -> yes | no + Status of communication authorization (deprecated) - - Node = node() - -

Returns yes if communication with Node is - authorized. Note that a connection to Node will - be established in this case. Returns no if Node +

Returns yes if communication with Node is + authorized. Note that a connection to Node will + be established in this case. Returns no if Node does not exist or communication is not authorized (it has another cookie than auth thinks it has).

-

Use net_adm:ping(Node) +

Use net_adm:ping(Node) instead.

- cookie() -> Cookie + Magic cookie for local node (deprecated) - - Cookie = atom() -

Use erlang:get_cookie() @@ -64,16 +63,14 @@ - cookie(TheCookie) -> true + Set the magic for the local node (deprecated) - - TheCookie = Cookie | [Cookie] - The cookie may also be given as a list with a single atom element -  Cookie = atom() - + + The cookie may also be given as a list with a single atom element. +

Use - erlang:set_cookie(node(), Cookie) + erlang:set_cookie(node(), Cookie) instead.

@@ -82,7 +79,7 @@ Set the magic cookie for a node and verify authorization (deprecated) Node = node() - Cookie = atom() + Cookie = cookie()

Equivalent to @@ -90,18 +87,14 @@ - node_cookie(Node, Cookie) -> yes | no + Set the magic cookie for a node and verify authorization (deprecated) - - Node = node() - Cookie = atom() - -

Sets the magic cookie of Node to Cookie, and +

Sets the magic cookie of Node to Cookie, and verifies the status of the authorization. Equivalent to calling - erlang:set_cookie(Node, Cookie), followed by - auth:is_auth(Node).

+ erlang:set_cookie(Node, Cookie), followed by + auth:is_auth(Node).

diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 4b8f934df1..6f85388c22 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -286,6 +286,12 @@ given to set_path/1.

+ + + + + + set_path(Path) -> true | {error, What} diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 324d4264cf..9721907162 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -179,13 +179,48 @@ reopen the log simultaneously.

+ + + + + + + + + + + + + + + + + + + + + + + + +

Chunk continuation returned by + chunk/2,3, bchunk/2,3, or chunk_step/3.

+
+
+ + + + + + + + + +
- accessible_logs() -> {[LocalLog], [DistributedLog]} + Return the accessible disk logs on the current node. - - LocalLog = DistributedLog = term() -

The accessible_logs/0 function returns the names of the disk logs accessible on the current node. @@ -195,16 +230,13 @@ - alog(Log, Term) - balog(Log, Bytes) -> ok | {error, Reason} + + + + + + Asynchronously log an item onto a disk log. - - Log = term() - Term = term() - Bytes = binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Reason = no_such_log -

The alog/2 and balog/2 functions asynchronously append an item to a disk log. The function alog/2 is @@ -225,17 +257,13 @@ - alog_terms(Log, TermList) - balog_terms(Log, BytesList) -> ok | {error, Reason} + + Asynchronously log several items onto a disk log. - - Log = term() - TermList = [term()] - BytesList = [Bytes] - Bytes = binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Reason = no_such_log - + + + +

The alog_terms/2 and balog_terms/2 functions asynchronously append a list of items to a disk log. @@ -257,14 +285,10 @@ - block(Log) - block(Log, QueueLogRecords) -> ok | {error, Reason} + + + Block a disk log. - - Log = term() - QueueLogRecords = bool() - Reason = no_such_log | nonode | {blocked_log, Log} -

With a call to block/1,2 a process can block a log. If the blocking process is not an owner of the log, a temporary @@ -280,52 +304,32 @@ affected by the block. Any other attempt than those hitherto mentioned to update or read a blocked log suspends the calling process until the log is unblocked or returns an - error message {blocked_log, Log}, depending on - whether the value of QueueLogRecords is true - or false. The default value of QueueLogRecords + error message {blocked_log, Log}, depending on + whether the value of QueueLogRecords is true + or false. The default value of QueueLogRecords is true, which is used by block/1.

- change_header(Log, Header) -> ok | {error, Reason} + Change the head or head_func option for an owner of a disk log. - - Log = term() - Header = {head, Head} | {head_func, {M,F,A}} - Head = none | term() | binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {badarg, head} -

The change_header/2 function changes the value of the head or head_func option of a disk log.

- change_notify(Log, Owner, Notify) -> ok | {error, Reason} + Change the notify option for an owner of a disk log. - - Log = term() - Owner = pid() - Notify = bool() - Reason = no_such_log | nonode | {blocked_log, Log} | {badarg, notify} | {not_owner, Owner} -

The change_notify/3 function changes the value of the notify option for an owner of a disk log.

- change_size(Log, Size) -> ok | {error, Reason} + Change the size of an open disk log. - - Log = term() - Size = integer() > 0 | infinity | {MaxNoBytes, MaxNoFiles} - MaxNoBytes = integer() > 0 - MaxNoFiles = integer() > 0 - Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {new_size_too_small, CurrentSize} | {badarg, size} | {file_error, FileName, FileError} -

The change_size/2 function changes the size of an open log. For a halt log it is always possible to increase the size, @@ -363,21 +367,17 @@ - chunk(Log, Continuation) - chunk(Log, Continuation, N) -> {Continuation2, Terms} | {Continuation2, Terms, Badbytes} | eof | {error, Reason} - bchunk(Log, Continuation) - bchunk(Log, Continuation, N) -> {Continuation2, Binaries} | {Continuation2, Binaries, Badbytes} | eof | {error, Reason} + + + + Read a chunk of items written to a disk log. - - Log = term() - Continuation = start | cont() - N = integer() > 0 | infinity - Continuation2 = cont() - Terms = [term()] - Badbytes = integer() - Reason = no_such_log | {format_external, Log} | {blocked_log, Log} | {badarg, continuation} | {not_internal_wrap, Log} | {corrupt_log_file, FileName} | {file_error, FileName, FileError} - Binaries = [binary()] - + + + + + +

The chunk/2,3 and bchunk/2,3 functions make it possible to efficiently read the terms which have been @@ -394,31 +394,31 @@ individual distributed log on some other node is chosen, if such a log exists.

-

When chunk/3 is called, N controls the +

When chunk/3 is called, N controls the maximum number of terms that are read from the log in each chunk. Default is infinity, which means that all the terms contained in the 64 kilobyte chunk are read. If less than - N terms are returned, this does not necessarily mean + N terms are returned, this does not necessarily mean that the end of the file has been reached.

The chunk function returns a tuple - {Continuation2, Terms}, where Terms is a list - of terms found in the log. Continuation2 is yet + {Continuation2, Terms}, where Terms is a list + of terms found in the log. Continuation2 is yet another continuation which must be passed on to any subsequent calls to chunk. With a series of calls to chunk it is possible to extract all terms from a log.

The chunk function returns a tuple - {Continuation2, Terms, Badbytes} if the log is opened - in read-only mode and the read chunk is corrupt. Badbytes + {Continuation2, Terms, Badbytes} if the log is opened + in read-only mode and the read chunk is corrupt. Badbytes is the number of bytes in the file which were found not to be Erlang terms in the chunk. Note also that the log is not repaired. When trying to read chunks from a log opened in read-write mode, - the tuple {corrupt_log_file, FileName} is returned if the + the tuple {corrupt_log_file, FileName} is returned if the read chunk is corrupt.

chunk returns eof when the end of the log is - reached, or {error, Reason} if an error occurs. Should + reached, or {error, Reason} if an error occurs. Should a wrap log file be missing, a message is output on the error log.

When chunk/2,3 is used with wrap logs, the returned @@ -431,12 +431,8 @@ - chunk_info(Continuation) -> InfoList | {error, Reason} + Return information about a chunk continuation of a disk log. - - Continuation = cont() - Reason = {no_continuation, Continuation} -

The chunk_info/1 function returns the following pair describing the chunk continuation returned by @@ -444,29 +440,22 @@

-

{node, Node}. Terms are read from - the disk log running on Node.

+

{node, Node}. Terms are read from + the disk log running on Node.

- chunk_step(Log, Continuation, Step) -> {ok, Continuation2} | {error, Reason} + Step forward or backward among the wrap log files of a disk log. - - Log = term() - Continuation = start | cont() - Step = integer() - Continuation2 = cont() - Reason = no_such_log | end_of_log | {format_external, Log} | {blocked_log, Log} | {badarg, continuation} | {file_error, FileName, FileError} -

The function chunk_step can be used in conjunction with chunk/2,3 and bchunk/2,3 to search through an internally formatted wrap log. It takes as argument a continuation as returned by chunk/2,3, bchunk/2,3, or chunk_step/3, and steps forward - (or backward) Step files in the wrap log. The + (or backward) Step files in the wrap log. The continuation returned points to the first log item in the new current file.

@@ -482,11 +471,9 @@
- close(Log) -> ok | {error, Reason} + Close a disk log. - - Reason = no_such_log | nonode | {file_error, FileName, FileError} - +

The function close/1 closes a @@ -511,11 +498,8 @@ The function close/1 closes a - format_error(Error) -> Chars + Return an English description of a disk log error reply. - - Chars = [char() | Chars] -

Given the error returned by any function in this module, the function format_error returns a descriptive string @@ -524,11 +508,10 @@ The function close/1 closes a - inc_wrap_file(Log) -> ok | {error, Reason} + Change to the next wrap log file of a disk log. - - Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {halt_log, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError} - + +

The inc_wrap_file/1 function forces the internally formatted disk log to start logging to the @@ -543,8 +526,9 @@ The function close/1 closes a - info(Log) -> InfoList | {error, no_such_log} + Return information about a disk log. +

The info/1 function returns a list of {Tag, Value} pairs describing the log. If there is a disk log process running @@ -556,55 +540,55 @@ The function close/1 closes a

-

{name, Log}, where Log is the name of +

{name, Log}, where Log is the name of the log as given by the open/1 option name.

-

{file, File}. For halt logs File is the - filename, and for wrap logs File is the base name.

+

{file, File}. For halt logs File is the + filename, and for wrap logs File is the base name.

-

{type, Type}, where Type is the type of +

{type, Type}, where Type is the type of the log as given by the open/1 option type.

-

{format, Format}, where Format is the format +

{format, Format}, where Format is the format of the log as given by the open/1 option format.

-

{size, Size}, where Size is the size +

{size, Size}, where Size is the size of the log as given by the open/1 option size, or the size set by change_size/2. The value set by change_size/2 is reflected immediately.

-

{mode, Mode}, where Mode is the mode +

{mode, Mode}, where Mode is the mode of the log as given by the open/1 option mode.

-

{owners, [{pid(), Notify}]} where Notify +

{owners, [{pid(), Notify}]} where Notify is the value set by the open/1 option notify or the function change_notify/3 for the owners of the log.

-

{users, Users} where Users is the number +

{users, Users} where Users is the number of anonymous users of the log, see the open/1 option linkto.

-

{status, Status}, where Status is ok - or {blocked, QueueLogRecords} as set by the functions +

{status, Status}, where Status is ok + or {blocked, QueueLogRecords} as set by the functions block/1,2 and unblock/1.

-

{node, Node}. The information returned by the +

{node, Node}. The information returned by the current invocation of the info/1 function has been - gathered from the disk log process running on Node.

+ gathered from the disk log process running on Node.

-

{distributed, Dist}. If the log is local on - the current node, then Dist has the value local, +

{distributed, Dist}. If the log is local on + the current node, then Dist has the value local, otherwise all nodes where the log is distributed are returned as a list.

@@ -614,16 +598,16 @@ The function close/1 closes a

-

{head, Head}. Depending of the value of +

{head, Head}. Depending of the value of the open/1 options head and head_func or set by the function change_header/2, the value - of Head is none (default), + of Head is none (default), {head, H} (head option) or {M,F,A} (head_func option).

-

{no_written_items, NoWrittenItems}, where - NoWrittenItems is the number of items +

{no_written_items, NoWrittenItems}, where + NoWrittenItems is the number of items written to the log since the disk log process was created.

@@ -632,7 +616,7 @@ The function close/1 closes a

-

{full, Full}, where Full is true or +

{full, Full}, where Full is true or false depending on whether the halt log is full or not.

@@ -660,8 +644,8 @@ The function close/1 closes a size or set by change_size/2.

-

{no_overflows, {SinceLogWasOpened, SinceLastInfo}}, - where SinceLogWasOpened (SinceLastInfo) is +

{no_overflows, {SinceLogWasOpened, SinceLastInfo}}, + where SinceLogWasOpened (SinceLastInfo) is the number of times a wrap log file has been filled up and a new one opened or inc_wrap_file/1 has been called since the disk log was last opened (info/1 @@ -677,21 +661,18 @@ The function close/1 closes a - lclose(Log) - lclose(Log, Node) -> ok | {error, Reason} + + + Close a disk log on one node. - - Node = node() - Reason = no_such_log | {file_error, FileName, FileError} -

The function lclose/1 closes a local log or an individual distributed log on the current node. The function lclose/2 closes an individual distributed log on the specified node if the node is not the current one. - lclose(Log) is equivalent to - lclose(Log, node()). + lclose(Log) is equivalent to + lclose(Log, node()). See also close/1.

If there is no log with the given name @@ -700,20 +681,17 @@ The function close/1 closes a - log(Log, Term) - blog(Log, Bytes) -> ok | {error, Reason} + + Log an item onto a disk log. - - Log = term() - Term = term() - Bytes = binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Reason = no_such_log | nonode | {read_only_mode, Log} | {format_external, Log} | {blocked_log, Log} | {full, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError} - + + + +

The log/2 and blog/2 functions synchronously append a term to a disk log. They return ok or - {error, Reason} when the term has been written to + {error, Reason} when the term has been written to disk. If the log is distributed, ok is always returned, unless all nodes are down. Terms are written by means of the ordinary write() function of the @@ -736,17 +714,13 @@ The function close/1 closes a - log_terms(Log, TermList) - blog_terms(Log, BytesList) -> ok | {error, Reason} + + Log several items onto a disk log. - - Log = term() - TermList = [term()] - BytesList = [Bytes] - Bytes = binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Reason = no_such_log | nonode | {read_only_mode, Log} | {format_external, Log} | {blocked_log, Log} | {full, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError} - + + + +

The log_terms/2 and blog_terms/2 functions synchronously append a list of items to the log. The benefit @@ -769,47 +743,33 @@ The function close/1 closes a - open(ArgL) -> OpenRet | DistOpenRet + + + + + + + + + + Open a disk log file. - - ArgL = [Opt] - Opt = {name, term()} | {file, FileName}, {linkto, LinkTo} | {repair, Repair} | {type, Type} | {format, Format} | {size, Size} | {distributed, [Node]} | {notify, bool()} | {head, Head} | {head_func, {M,F,A}} | {mode, Mode} - FileName = string() | atom() - LinkTo = pid() | none - Repair = true | false | truncate - Type = halt | wrap - Format = internal | external - Size = integer() > 0 | infinity | {MaxNoBytes, MaxNoFiles} - MaxNoBytes = integer() > 0 - MaxNoFiles = 0 < integer() < 65000 - Rec = integer() - Bad = integer() - Head = none | term() | binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Mode = read_write | read_only - OpenRet = Ret | {error, Reason} - DistOpenRet = {[{Node, Ret}], [{BadNode, {error, DistReason}}]} - Node = BadNode = atom() - Ret = {ok, Log} | {repaired, Log, {recovered, Rec}, {badbytes, Bad}} - DistReason = nodedown | Reason - Reason = no_such_log | {badarg, Arg} | {size_mismatch, CurrentSize, NewSize} | {arg_mismatch, OptionName, CurrentValue, Value} | {name_already_open, Log} | {open_read_write, Log} | {open_read_only, Log} | {need_repair, Log} | {not_a_log_file, FileName} | {invalid_index_file, FileName} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError} | {node_already_open, Log} - -

The ArgL parameter is a list of options which have +

The ArgL parameter is a list of options which have the following meanings:

-

{name, Log} specifies the name of the log. +

{name, Log} specifies the name of the log. This is the name which must be passed on as a parameter in all subsequent logging operations. A name must always be supplied.

-

{file, FileName} specifies the name of the +

{file, FileName} specifies the name of the file which will be used for logged terms. If this value is omitted and the name of the log is either an atom or a string, - the file name will default to lists:concat([Log, ".LOG"]) for halt logs. For wrap logs, this will be + the file name will default to lists:concat([Log, ".LOG"]) for halt logs. For wrap logs, this will be the base name of the files. Each file in a wrap log will be called .N]]>, where N is an integer. Each wrap log will also have two files called @@ -817,22 +777,22 @@ The function close/1 closes a

-

{linkto, LinkTo}. +

{linkto, LinkTo}. If - LinkTo is a pid, that pid becomes an owner of the - log. If LinkTo is none the log records + LinkTo is a pid, that pid becomes an owner of the + log. If LinkTo is none the log records that it is used anonymously by some process by incrementing the users counter. By default, the process which calls open/1 owns the log.

-

{repair, Repair}. If Repair is true, +

{repair, Repair}. If Repair is true, the current log file will be repaired, if needed. As the restoration is initiated, a message is output on the error log. If false is given, no automatic repair will be attempted. Instead, the - tuple {error, {need_repair, Log}} is returned if an + tuple {error, {need_repair, Log}} is returned if an attempt is made to open a corrupt log file. If truncate is given, the log file will be truncated, creating an empty log. Default is @@ -841,41 +801,41 @@ If

-

{type, Type} is the type of the log. Default +

{type, Type} is the type of the log. Default is halt.

-

{format, Format} specifies the format of the +

{format, Format} specifies the format of the disk log. Default is internal.

-

{size, Size} specifies the size of the log. +

{size, Size} specifies the size of the log. When a halt log has reached its maximum size, all attempts to log more items are rejected. The default size is infinity, which for halt implies that there is no - maximum size. For wrap logs, the Size parameter + maximum size. For wrap logs, the Size parameter may be either a pair - {MaxNoBytes, MaxNoFiles} or infinity. In the + {MaxNoBytes, MaxNoFiles} or infinity. In the latter case, if the files of an already existing wrap log with the same name can be found, the size is read from the existing wrap log, otherwise an error is returned. - Wrap logs write at most MaxNoBytes bytes on each file - and use MaxNoFiles files before starting all over with - the first wrap log file. Regardless of MaxNoBytes, + Wrap logs write at most MaxNoBytes bytes on each file + and use MaxNoFiles files before starting all over with + the first wrap log file. Regardless of MaxNoBytes, at least the header (if there is one) and one item is written on each wrap log file before wrapping to the next file. When opening an existing wrap log, it is not necessary to supply a value for the option Size, but any supplied value must equal the current size of the log, otherwise - the tuple {error, {size_mismatch, CurrentSize, NewSize}} + the tuple {error, {size_mismatch, CurrentSize, NewSize}} is returned.

-

{distributed, Nodes}. This option can be used for +

{distributed, Nodes}. This option can be used for adding members to a distributed disk log. The default value is [], which means that the log is local on the current node. @@ -946,10 +906,10 @@ If -

{head, Head} specifies a header to be +

{head, Head} specifies a header to be written first on the log file. If the log is a wrap - log, the item Head is written first in each new file. - Head should be a term if the format is + log, the item Head is written first in each new file. + Head should be a term if the format is internal, and a deep list of bytes (or a binary) otherwise. Default is none, which means that no header is written first on the file. @@ -966,17 +926,17 @@ If

-

{mode, Mode} specifies if the log is to be +

{mode, Mode} specifies if the log is to be opened in read-only or read-write mode. It defaults to read_write.

-

The open/1 function returns {ok, Log} if the +

The open/1 function returns {ok, Log} if the log file was successfully opened. If the file was - successfully repaired, the tuple {repaired, Log, {recovered, Rec}, {badbytes, Bad}} is returned, where - Rec is the number of whole Erlang terms found in the - file and Bad is the number of bytes in the file which + successfully repaired, the tuple {repaired, Log, {recovered, Rec}, {badbytes, Bad}} is returned, where + Rec is the number of whole Erlang terms found in the + file and Bad is the number of bytes in the file which were non-Erlang terms. If the distributed parameter was given, open/1 returns a list of successful replies and a list of erroneous replies. Each @@ -988,7 +948,7 @@ If position after the last logged item, and the logging of items will commence from there. If the format is internal and the existing file is not recognized as an internally - formatted log, a tuple {error, {not_a_log_file, FileName}} + formatted log, a tuple {error, {not_a_log_file, FileName}} is returned.

The open/1 function cannot be used for changing the @@ -1000,15 +960,15 @@ If or change_size/2. As a consequence, none of the options except name is mandatory. If some given value differs from the current value, a tuple - {error, {arg_mismatch, OptionName, CurrentValue, Value}} + {error, {arg_mismatch, OptionName, CurrentValue, Value}} is returned. Caution: an owner's attempt to open a log as owner once again is acknowledged with the return value - {ok, Log}, but the state of the disk log is not + {ok, Log}, but the state of the disk log is not affected in any way.

If a log with a given name is local on some node, and one tries to open the log distributed on the same node, - then the tuple {error, {node_already_open, Name}} is + then the tuple {error, {node_already_open, Log}} is returned. The same tuple is returned if the log is distributed on some node, and one tries to open the log locally on the same node. Opening individual distributed disk logs for the first time @@ -1036,12 +996,8 @@ If - pid2name(Pid) -> {ok, Log} | undefined + Return the name of the disk log handled by a pid. - - Log = term() - Pid = pid() -

The pid2name/1 function returns the name of the log given the pid of a disk log process on the current node, or @@ -1052,26 +1008,23 @@ If - reopen(Log, File) - reopen(Log, File, Head) - breopen(Log, File, BHead) -> ok | {error, Reason} + + + Reopen a disk log and save the old log. - - Log = term() - File = string() - Head = term() - BHead = binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {same_file_name, Log} | {invalid_index_file, FileName} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError} - + + + + +

The reopen functions first rename the log file - to File and then re-create a new log file. - In case of a wrap log, File is used as the base name + to File and then re-create a new log file. + In case of a wrap log, File is used as the base name of the renamed files. By default the header given to open/1 is written first in - the newly opened log file, but if the Head or the - BHead argument is given, this item is used instead. + the newly opened log file, but if the Head or the + BHead argument is given, this item is used instead. The header argument is used once only; next time a wrap log file is opened, the header given to open/1 is used.

@@ -1089,12 +1042,9 @@ If
- sync(Log) -> ok | {error, Reason} + + Flush the contents of a disk log to the disk. - - Log = term() - Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {file_error, FileName, FileError} -

The sync/1 function ensures that the contents of the log are actually written to the disk. @@ -1103,20 +1053,17 @@ If - truncate(Log) - truncate(Log, Head) - btruncate(Log, BHead) -> ok | {error, Reason} + + + Truncate a disk log. - - Log = term() - Head = term() - BHead = binary() | [Byte] - Byte = [Byte] | 0 =< integer() =< 255 - Reason = no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {invalid_header, InvalidHeader} | {file_error, FileName, FileError} - + + + +

The truncate functions remove all items from a disk log. - If the Head or the BHead argument is + If the Head or the BHead argument is given, this item is written first in the newly truncated log, otherwise the header given to open/1 is used. The header argument is only used once; next time a wrap log file @@ -1138,12 +1085,9 @@ If - unblock(Log) -> ok | {error, Reason} + + Unblock a disk log. - - Log = term() - Reason = no_such_log | nonode | {not_blocked, Log} | {not_blocked_by_pid, Log} -

The unblock/1 function unblocks a log. A log can only be unblocked by the blocking process. @@ -1159,4 +1103,3 @@ If wrap_log_reader(3)

- diff --git a/lib/kernel/doc/src/erl_boot_server.xml b/lib/kernel/doc/src/erl_boot_server.xml index 4e7533810e..472671a80e 100644 --- a/lib/kernel/doc/src/erl_boot_server.xml +++ b/lib/kernel/doc/src/erl_boot_server.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -49,29 +49,17 @@ - start(Slaves) -> {ok, Pid} | {error, What} + Start the boot server - - Slaves = [Host] - Host = atom() - Pid = pid() - What = term() - -

Starts the boot server. Slaves is a list of IP +

Starts the boot server. Slaves is a list of IP addresses for hosts which are allowed to use this server as a boot server.

- start_link(Slaves) -> {ok, Pid} | {error, What} + Start the boot server and links the caller - - Slaves = [Host] - Host = atom() - Pid = pid() - What = term()() -

Starts the boot server and links to the caller. This function is used to start the server if it is included in a supervision @@ -79,37 +67,23 @@ - add_slave(Slave) -> ok | {error, What} + Add a slave to the list of allowed slaves - - Slave = Host - Host = atom() - What = term() - -

Adds a Slave node to the list of allowed slave hosts.

+

Adds a Slave node to the list of allowed slave hosts.

- delete_slave(Slave) -> ok | {error, What} + Delete a slave from the list of allowed slaves - - Slave = Host - Host = atom() - What = void() - -

Deletes a Slave node from the list of allowed slave +

Deletes a Slave node from the list of allowed slave hosts.

- which_slaves() -> Slaves + Return the current list of allowed slave hosts - - Slaves = [Host] - Host = atom() -

Returns the current list of allowed slave hosts.

diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml index 9a62b45d63..f9514dda2f 100644 --- a/lib/kernel/doc/src/erl_ddll.xml +++ b/lib/kernel/doc/src/erl_ddll.xml @@ -4,7 +4,7 @@
- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -172,6 +172,14 @@ + + + + + + + + demonitor(MonitorRef) -> ok @@ -189,37 +197,21 @@ - info() -> AllInfoList + Retrieve information about all drivers - - AllInfoList = [ DriverInfo ] - DriverInfo = {DriverName, InfoList} - DriverName = string() - InfoList = [ InfoItem ] - InfoItem = {Tag, Value} - Tag = atom() - Value = term() - -

Returns a list of tuples {DriverName, InfoList}, where - InfoList is the result of calling info/1 for that - DriverName. Only dynamically linked in drivers are +

Returns a list of tuples {DriverName, InfoList}, where + InfoList is the result of calling info/1 for that + DriverName. Only dynamically linked in drivers are included in the list.

- info(Name) -> InfoList + Retrieve information about one driver - - Name = string() | atom() - InfoList = [ InfoItem ] - InfoItem = {Tag, Value} - Tag = atom() - Value = term() - -

Returns a list of tuples {Tag, Value}, where - Tag is the information item and Value is the result +

Returns a list of tuples {Tag, Value}, where + Tag is the information item and Value is the result of calling info/2 with this driver name and this tag. The result being a tuple list containing all information available about a driver.

@@ -305,22 +297,18 @@
- load(Path, Name) -> ok | {error, ErrorDesc} + Load a driver - - Path = Name = string() | atom() - ErrorDesc = term() - -

Loads and links the dynamic driver Name. Path +

Loads and links the dynamic driver Name. Path is a file path to the directory containing the driver. - Name must be a sharable object/dynamic library. Two - drivers with different Path parameters cannot be - loaded under the same name. The Name is a string or + Name must be a sharable object/dynamic library. Two + drivers with different Path parameters cannot be + loaded under the same name. The Name is a string or atom containing at least one character.

-

The Name given should correspond to the filename +

The Name given should correspond to the filename of the actual dynamically loadable object file residing in - the directory given as Path, but without the + the directory given as Path, but without the extension (i.e. .so). The driver name provided in the driver initialization routine must correspond with the filename, in much the same way as erlang module names @@ -328,14 +316,14 @@

If the driver has been previously unloaded, but is still present due to open ports against it, a call to load/2 will stop the unloading and keep the driver - (as long as the Path is the same) and ok is + (as long as the Path is the same) and ok is returned. If one actually wants the object code to be reloaded, one uses reload/2 or the low-level interface try_load/3 instead. Please refer to the description of different scenarios for loading/unloading in the introduction.

If more than one process tries to load an already loaded - driver withe the same Path, or if the same process + driver withe the same Path, or if the same process tries to load it several times, the function will return ok. The emulator will keep track of the load/2 calls, so that a corresponding number of @@ -349,16 +337,16 @@ several drivers with the same name but with different Path parameters.

-

Note especially that the Path is interpreted +

Note especially that the Path is interpreted literally, so that all loaders of the same driver needs to - give the same literalPath string, even + give the same literalPath string, even though different paths might point out the same directory in the filesystem (due to use of relative paths and links).

On success, the function returns ok. On - failure, the return value is {error,ErrorDesc}, - where ErrorDesc is an opaque term to be + failure, the return value is {error,ErrorDesc}, + where ErrorDesc is an opaque term to be translated into human readable form by the format_error/1 function.

For more control over the error handling, again use the @@ -369,20 +357,16 @@ - load_driver(Path, Name) -> ok | {error, ErrorDesc} + Load a driver - - Path = Name = string() | atom() - ErrorDesc = term() -

Works essentially as load/2, but will load the driver - with options other options. All ports that are using the + with other options. All ports that are using the driver will get killed with the reason driver_unloaded when the driver is to be unloaded.

The number of loads and unloads by different users influence the actual loading and unloading of a driver file. The port killing will - therefore only happen when the lastuser unloads the driver, or the + therefore only happen when the last user unloads the driver, or the last process having loaded the driver exits.

This interface (or at least the name of the functions) is kept for backward compatibility. Using try_load/3 with @@ -551,16 +535,11 @@ - reload(Path, Name) -> ok | {error, ErrorDesc} + Replace a driver - - Path = Name = string() | atom() - ErrorDesc = pending_process | OpaqueError - OpaqueError = term() - -

Reloads the driver named Name from a possibly - different Path than was previously used. This +

Reloads the driver named Name from a possibly + different Path than was previously used. This function is used in the code change scenario described in the introduction.

If there are other users @@ -574,7 +553,7 @@

If one wants to avoid hanging on open ports, one should use the try_load/3 function instead.

-

The Name and Path parameters have exactly the +

The Name and Path parameters have exactly the same meaning as when calling the plain load/2 function.

Avoid mixing @@ -594,13 +573,8 @@ - reload_driver(Path, Name) -> ok | {error, ErrorDesc} + Replace a driver - - Path = Name = string() | atom() - ErrorDesc = pending_process | OpaqueError - OpaqueError = term() -

Works exactly as reload/2, but for drivers loaded with the load_driver/2 interface.

@@ -1066,15 +1040,11 @@
- unload(Name) -> ok | {error, ErrorDesc} + Unload a driver - - Name = string() | atom() - ErrorDesc = term() -

Unloads, or at least dereferences the driver named - Name. If the caller is the last user of the driver, and there + Name. If the caller is the last user of the driver, and there are no more open ports using the driver, the driver will actually get unloaded. In all other cases, actual unloading will be delayed until all ports are closed and there are no @@ -1084,7 +1054,7 @@ is no longer considered a user of the driver. For usage scenarios, see the description in the beginning of this document.

-

The ErrorDesc returned is an opaque value to be +

The ErrorDesc returned is an opaque value to be passed further on to the format_error/1 function. For more control over the operation, use the try_unload/2 @@ -1094,15 +1064,11 @@ - unload_driver(Name) -> ok | {error, ErrorDesc} + Unload a driver - - Name = string() | atom() - ErrorDesc = term() -

Unloads, or at least dereferences the driver named - Name. If the caller is the last user of the driver, all + Name. If the caller is the last user of the driver, all remaining open ports using the driver will get killed with the reason driver_unloaded and the driver will eventually get unloaded.

@@ -1112,7 +1078,7 @@ user. For usage scenarios, see the description in the beginning of this document.

-

The ErrorDesc returned is an opaque value to be +

The ErrorDesc returned is an opaque value to be passed further on to the format_error/1 function. For more control over the operation, use the try_unload/2 @@ -1125,7 +1091,7 @@ loaded_drivers() -> {ok, Drivers} List loaded drivers - Drivers = [Driver()] + Drivers = [Driver] Driver = string() @@ -1138,13 +1104,10 @@ - format_error(ErrorDesc) -> string() + Format an error descriptor - - ErrorDesc -- see below - -

Takes an ErrorDesc returned by load, unload or +

Takes an ErrorDesc returned by load, unload or reload functions and returns a string which describes the error or warning.

diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml index 7f78322472..acbf9a2c6e 100644 --- a/lib/kernel/doc/src/error_handler.xml +++ b/lib/kernel/doc/src/error_handler.xml @@ -37,48 +37,44 @@ - undefined_function(Module, Function, Args) -> term() + Called when an undefined function is encountered - - Module = Function = atom() - Args = [term()] - A (possibly empty) list of arguments Arg1,..,ArgN - + + A (possibly empty) list of arguments Arg1,..,ArgN +

This function is evaluated if a call is made to - Module:Function(Arg1,.., ArgN) and - Module:Function/N is undefined. Note that + Module:Function(Arg1,.., ArgN) and + Module:Function/N is undefined. Note that undefined_function/3 is evaluated inside the process making the original call.

-

If Module is interpreted, the interpreter is invoked +

If Module is interpreted, the interpreter is invoked and the return value of the interpreted - Function(Arg1,.., ArgN) call is returned.

+ Function(Arg1,.., ArgN) call is returned.

Otherwise, it returns, if possible, the value of - apply(Module, Function, Args) after an attempt has been - made to autoload Module. If this is not possible, the - call to Module:Function(Arg1,.., ArgN) fails with + apply(Module, Function, Args) after an attempt has been + made to autoload Module. If this is not possible, the + call to Module:Function(Arg1,.., ArgN) fails with exit reason undef.

- undefined_lambda(Module, Fun, Args) -> term() + Called when an undefined lambda (fun) is encountered - - Module = Function = atom() - Args = [term()] - A (possibly empty) list of arguments Arg1,..,ArgN - + + A (possibly empty) list of arguments Arg1,..,ArgN +

This function is evaluated if a call is made to - Fun(Arg1,.., ArgN) when the module defining the fun is + Fun(Arg1,.., ArgN) when the module defining the fun is not loaded. The function is evaluated inside the process making the original call.

-

If Module is interpreted, the interpreter is invoked +

If Module is interpreted, the interpreter is invoked and the return value of the interpreted - Fun(Arg1,.., ArgN) call is returned.

+ Fun(Arg1,.., ArgN) call is returned.

Otherwise, it returns, if possible, the value of - apply(Fun, Args) after an attempt has been made to - autoload Module. If this is not possible, the call + apply(Fun, Args) after an attempt has been made to + autoload Module. If this is not possible, the call fails with exit reason undef.

diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index e107d9b746..2d95f96ac7 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -65,19 +65,20 @@ be tagged as warnings or info. Tagging them as warnings may require rewriting existing user defined event handlers.

+ + + + + - error_msg(Format) -> ok - error_msg(Format, Data) -> ok - format(Format, Data) -> ok + + + Send an standard error event to the error logger - - Format = string() - Data = [term()] -

Sends a standard error event to the error logger. - The Format and Data arguments are the same as + The Format and Data arguments are the same as the arguments of io:format/2. The event is handled by the standard event handler.

@@ -94,12 +95,8 @@ ok
- error_report(Report) -> ok + Send a standard error report event to the error logger - - Report = [{Tag, Data} | term()] | string() | term() -  Tag = Data = term() -

Sends a standard error report event to the error logger. The event is handled by the standard event handler.

@@ -119,18 +116,13 @@ ok
- error_report(Type, Report) -> ok + Send a user defined error report event to the error logger - - Type = term() - Report = [{Tag, Data} | term()] | string() | term() -  Tag = Data = term() -

Sends a user defined error report event to the error logger. An event handler to handle the event is supposed to have been added. The event is ignored by the standard event handler.

-

It is recommended that Report follows the same +

It is recommended that Report follows the same structure as for error_report/1.

@@ -174,16 +166,12 @@ ok - warning_msg(Format) -> ok - warning_msg(Format, Data) -> ok + + Send a standard warning event to the error logger - - Format = string() - Data = [term()] -

Sends a standard warning event to the error logger. - The Format and Data arguments are the same as + The Format and Data arguments are the same as the arguments of io:format/2. The event is handled by the standard event handler. It is tagged either as an error, warning or info, see @@ -196,12 +184,8 @@ ok - warning_report(Report) -> ok + Send a standard warning report event to the error logger - - Report = [{Tag, Data} | term()] | string() | term() -  Tag = Data = term() -

Sends a standard warning report event to the error logger. The event is handled by the standard event handler. It is @@ -210,13 +194,8 @@ ok - warning_report(Type, Report) -> ok + Send a user defined warning report event to the error logger - - Type = term() - Report = [{Tag, Data} | term()] | string() | term() -  Tag = Data = term() -

Sends a user defined warning report event to the error logger. An event handler to handle the event is supposed to @@ -227,16 +206,12 @@ ok - info_msg(Format) -> ok - info_msg(Format, Data) -> ok + + Send a standard information event to the error logger - - Format = string() - Data = [term()] -

Sends a standard information event to the error logger. - The Format and Data arguments are the same as + The Format and Data arguments are the same as the arguments of io:format/2. The event is handled by the standard event handler.

@@ -253,12 +228,8 @@ ok
- info_report(Report) -> ok + Send a standard information report event to the error logger - - Report = [{Tag, Data} | term()] | string() | term() -  Tag = Data = term() -

Sends a standard information report event to the error logger. The event is handled by the standard event handler.

@@ -278,63 +249,49 @@ ok
- info_report(Type, Report) -> ok + Send a user defined information report event to the error logger - - Type = term() - Report = [{Tag, Data} | term()] | string() | term() -  Tag = Data = term() -

Sends a user defined information report event to the error logger. An event handler to handle the event is supposed to have been added. The event is ignored by the standard event handler.

-

It is recommended that Report follows the same +

It is recommended that Report follows the same structure as for info_report/1.

- add_report_handler(Handler) -> Result - add_report_handler(Handler, Args) -> Result + + Add an event handler to the error logger - - Handler, Args, Result -- see gen_event:add_handler/3 -

Adds a new event handler to the error logger. The event handler must be implemented as a gen_event callback module, see gen_event(3).

-

Handler is typically the name of the callback module - and Args is an optional term (defaults to []) passed - to the initialization callback function Module:init/1. +

Handler is typically the name of the callback module + and Args is an optional term (defaults to []) passed + to the initialization callback function Handler:init/1. The function returns ok if successful.

The event handler must be able to handle the events described below.

- delete_report_handler(Handler) -> Result + Delete an event handler from the error logger - - Handler, Result -- see gen_event:delete_handler/3 -

Deletes an event handler from the error logger by calling - gen_event:delete_handler(error_logger, Handler, []), + gen_event:delete_handler(error_logger, Handler, []), see gen_event(3).

- tty(Flag) -> ok + Enable or disable printouts to the tty - - Flag = bool() - -

Enables (Flag == true) or disables - (Flag == false) printout of standard events to the tty.

+

Enables (Flag == true) or disables + (Flag == false) printout of standard events to the tty.

This is done by adding or deleting the standard event handler for output to tty, thus calling this function overrides the value of the Kernel error_logger configuration @@ -342,13 +299,15 @@ ok - logfile(Request) -> ok | Filename | {error, What} + + + + + + + + Enable or disable error printouts to a file - - Request = {open, Filename} | close | filename -  Filename = atom() | string() - What = allready_have_logfile | no_log_file | term() -

Enables or disables printout of standard events to a file.

This is done by adding or deleting the standard event handler @@ -361,22 +320,22 @@ ok There can only be one active log file at a time.

Request is one of:

- {open, Filename} + {open, Filename} -

Opens the log file Filename. Returns ok if +

Opens the log file Filename. Returns ok if successful, or {error, allready_have_logfile} if logging to file is already enabled, or an error tuple if - another error occurred. For example, if Filename + another error occurred. For example, if Filename could not be opened.

close

Closes the current log file. Returns ok, or - {error, What}.

+ {error, module_not_found}.

filename -

Returns the name of the log file Filename, or +

Returns the name of the log file Filename, or {error, no_log_file} if logging to file is not enabled.

diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index 36fce464c5..e0feaf6ee7 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -93,47 +93,76 @@ is UTF-8...

-
- DATA TYPES - -iodata() = iolist() | binary() - iolist() = [char() | binary() | iolist()] - -io_device() - as returned by file:open/2, a process handling IO protocols - -name() = string() | atom() | DeepList | RawFilename - DeepList = [char() | atom() | DeepList] - RawFilename = binary() - If VM is in unicode filename mode, string() and char() are allowed to be > 255. - RawFilename is a filename not subject to Unicode translation, meaning that it - can contain characters not conforming to the Unicode encoding expected from the - filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode - filename mode). - -posix() - an atom which is named from the POSIX error codes used in - Unix, and in the runtime libraries of most C compilers - -ext_posix() = posix() | badarg + + + + + + + + + + + + + + + + +

As returned by + file:open/2, + a process handling IO protocols.

+
+
+ + + +

If VM is in Unicode filename mode, string() and char() + are allowed to be > 255. + RawFilename is a filename not subject to + Unicode translation, + meaning that it can contain characters not conforming to + the Unicode encoding expected from the filesystem + (i.e. non-UTF-8 characters although the VM is started + in Unicode filename mode). +

+
+
+ + + +

An atom which is named from the POSIX error codes used in + Unix, and in the runtime libraries of most C compilers.

+
+
+ + + + + + + + + +

Must denote a valid date and time.

+
+
+ + + + + + + + + +
-time() = {{Year, Month, Day}, {Hour, Minute, Second}} - Year = Month = Day = Hour = Minute = Second = int() - Must denote a valid date and time
-
- advise(IoDevice, Offset, Length, Advise) -> ok | {error, Reason} + Predeclare an access pattern for file data - - IoDevice = io_device() - Offset = int() - Length = int() - Advise = posix_file_advise() - posix_file_advise() = normal | sequential | random | no_reuse - | will_need | dont_need - Reason = ext_posix() - +

advise/4 can be used to announce an intention to access file data in a specific pattern in the future, thus allowing the @@ -142,93 +171,58 @@ time() = {{Year, Month, Day}, {Hour, Minute, Second}} - change_group(Filename, Gid) -> ok | {error, Reason} + Change group of a file - - Filename = name() - Gid = int() - Reason = ext_posix() -

Changes group of a file. See write_file_info/2.

- change_mode(Filename, Mode) -> ok | {error, Reason} + Change permissions of a file - - Filename = name() - Mode = int() - Reason = ext_posix() -

Changes permissions of a file. See write_file_info/2.

- change_owner(Filename, Uid) -> ok | {error, Reason} + Change owner of a file - - Filename = name() - Uid = int() - Reason = ext_posix() -

Changes owner of a file. See write_file_info/2.

- change_owner(Filename, Uid, Gid) -> ok | {error, Reason} + Change owner and group of a file - - Filename = name() - Uid = int() - Gid = int() - Reason = ext_posix() -

Changes owner and group of a file. See write_file_info/2.

- change_time(Filename, Mtime) -> ok | {error, Reason} + Change the modification time of a file - - Filename = name() - Mtime = time() - Reason = ext_posix() -

Changes the modification and access times of a file. See write_file_info/2.

- change_time(Filename, Mtime, Atime) -> ok | {error, Reason} + Change the modification and last access time of a file - - Filename = name() - Mtime = Atime = time() - Reason = ext_posix() -

Changes the modification and last access times of a file. See write_file_info/2.

- close(IoDevice) -> ok | {error, Reason} + Close a file - - IoDevice = io_device() - Reason = ext_posix() | terminated - -

Closes the file referenced by IoDevice. It mostly +

Closes the file referenced by IoDevice. It mostly returns ok, expect for some severe errors such as out of memory.

Note that if the option delayed_write was @@ -238,20 +232,13 @@ time() = {{Year, Month, Day}, {Hour, Minute, Second}} - consult(Filename) -> {ok, Terms} | {error, Reason} + Read Erlang terms from a file - - Filename = name() - Terms = [term()] - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see below - -

Reads Erlang terms, separated by '.', from Filename. - Returns one of the following:

+

Reads Erlang terms, separated by '.', from + Filename. Returns one of the following:

- {ok, Terms} + {ok, Terms}

The file was successfully read.

@@ -261,7 +248,8 @@ time() = {{Year, Month, Day}, {Hour, Minute, Second}} See open/2 for a list of typical error codes.

- {error, {Line, Mod, Term}} + {error, {Line, Mod, + Term}}

An error occurred when interpreting the Erlang terms in the file. Use format_error/1 to convert @@ -279,53 +267,46 @@ f.txt: {person, "kalle", 25}. - copy(Source, Destination) -> - copy(Source, Destination, ByteCount) -> {ok, BytesCopied} | {error, Reason} + + Copy file contents - - Source = Destination = io_device() | Filename | {Filename, Modes} -  Filename = name() -  Modes = [Mode] -- see open/2 - ByteCount = int() >= 0 | infinity - BytesCopied = int() - -

Copies ByteCount bytes from Source to - Destination. Source and Destination refer +

Copies ByteCount bytes from + Source to Destination. + Source and Destination refer to either filenames or IO devices from e.g. open/2. - ByteCount defaults infinity, denoting an + ByteCount defaults to infinity, denoting an infinite number of bytes.

-

The argument Modes is a list of possible modes, see - open/2, and defaults to +

The argument Modes is a list of possible modes, + see open/2, and defaults to [].

-

If both Source and Destination refer to +

If both Source and + Destination refer to filenames, the files are opened with [read, binary] and [write, binary] prepended to their mode lists, respectively, to optimize the copy.

-

If Source refers to a filename, it is opened with +

If Source refers to a filename, it is opened with read mode prepended to the mode list before the copy, and closed when done.

-

If Destination refers to a filename, it is opened +

If Destination refers to a filename, it is opened with write mode prepended to the mode list before the copy, and closed when done.

-

Returns {ok, BytesCopied} where BytesCopied is +

Returns {ok, BytesCopied} where + BytesCopied is the number of bytes that actually was copied, which may be - less than ByteCount if end of file was encountered on - the source. If the operation fails, {error, Reason} is - returned.

+ less than ByteCount if end of file was + encountered on the source. If the operation fails, + {error, Reason} is returned.

Typical error reasons: As for open/2 if a file had to be opened, and as for read/2 and write/2.

- del_dir(Dir) -> ok | {error, Reason} + Delete a directory - - Dir = name() - Reason = ext_posix() - -

Tries to delete the directory Dir. The directory must +

Tries to delete the directory Dir. + The directory must be empty before it can be deleted. Returns ok if successful.

Typical error reasons are:

@@ -333,7 +314,7 @@ f.txt: {person, "kalle", 25}. eacces

Missing search or write permissions for the parent - directories of Dir.

+ directories of Dir.

eexist @@ -345,8 +326,8 @@ f.txt: {person, "kalle", 25}. enotdir -

A component of Dir is not a directory. On some - platforms, enoent is returned instead.

+

A component of Dir is not a directory. + On some platforms, enoent is returned instead.

einval @@ -357,15 +338,11 @@ f.txt: {person, "kalle", 25}.
- delete(Filename) -> ok | {error, Reason} + Delete a file - - Filename = name() - Reason = ext_posix() - -

Tries to delete the file Filename. Returns ok - if successful.

+

Tries to delete the file Filename. + Returns ok if successful.

Typical error reasons are:

enoent @@ -387,30 +364,25 @@ f.txt: {person, "kalle", 25}. einval -

Filename had an improper type, such as tuple.

+

Filename had an improper type, such as tuple.

-

In a future release, a bad type for the Filename - argument will probably generate an exception.

+

In a future release, a bad type for the + Filename argument will probably generate + an exception.

- eval(Filename) -> ok | {error, Reason} + Evaluate Erlang expressions in a file - - Filename = name() - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see below -

Reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from - Filename. The actual result of the evaluation is not - returned; any expression sequence in the file must be there + Filename. The actual result of the evaluation + is not returned; any expression sequence in the file must be there for its side effect. Returns one of the following:

ok @@ -422,7 +394,8 @@ f.txt: {person, "kalle", 25}.

An error occurred when opening the file or reading it. See open/2 for a list of typical error codes.

- {error, {Line, Mod, Term}} + {error, {Line, Mod, + Term}}

An error occurred when interpreting the Erlang expressions in the file. Use format_error/1 to @@ -433,18 +406,11 @@ f.txt: {person, "kalle", 25}. - eval(Filename, Bindings) -> ok | {error, Reason} + Evaluate Erlang expressions in a file - - Filename = name() - Bindings -- see erl_eval(3) - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see eval/1 -

The same as eval/1 but the variable bindings - Bindings are used in the evaluation. See + Bindings are used in the evaluation. See erl_eval(3) about variable bindings.

@@ -458,27 +424,19 @@ f.txt: {person, "kalle", 25}.
- format_error(Reason) -> Chars + Return a descriptive string for an error reason - - Reason = atom() | {Line, Mod, Term} -  Line, Mod, Term -- see eval/1 - Chars = [char() | Chars] -

Given the error reason returned by any function in this module, returns a descriptive string of the error in English.

- get_cwd() -> {ok, Dir} | {error, Reason} + Get the current working directory - - Dir = string() - Reason = posix() - -

Returns {ok, Dir}, where Dir is the current +

Returns {ok, Dir}, where Dir + is the current working directory of the file server.

In rare circumstances, this function can fail on Unix. @@ -496,17 +454,14 @@ f.txt: {person, "kalle", 25}. - get_cwd(Drive) -> {ok, Dir} | {error, Reason} + Get the current working directory for the drive specified - - Drive = string() -- see below - Dir = string() - Reason = ext_posix() - -

Drive should be of the form "Letter:", - for example "c:". Returns {ok, Dir} or - {error, Reason}, where Dir is the current +

Drive should be of the form + "Letter:", + for example "c:". Returns {ok, Dir} or + {error, Reason}, where Dir + is the current working directory of the drive specified.

This function returns {error, enotsup} on platforms which have no concept of current drive (Unix, for example).

@@ -514,7 +469,7 @@ f.txt: {person, "kalle", 25}. enotsup -

The operating system have no concept of drives.

+

The operating system has no concept of drives.

eacces @@ -522,32 +477,27 @@ f.txt: {person, "kalle", 25}. einval -

The format of Drive is invalid.

+

The format of Drive is invalid.

- list_dir(Dir) -> {ok, Filenames} | {error, Reason} + List files in a directory - - Dir = name() - Filenames = [Filename] -  Filename = string() - Reason = ext_posix() -

Lists all the files in a directory. Returns - {ok, Filenames} if successful. Otherwise, it returns - {error, Reason}. Filenames is a list of + {ok, Filenames} if successful. + Otherwise, it returns {error, Reason}. + Filenames is a list of the names of all the files in the directory. The names are not sorted.

Typical error reasons are:

eacces -

Missing search or write permissions for Dir or - one of its parent directories.

+

Missing search or write permissions for Dir + or one of its parent directories.

enoent @@ -557,14 +507,10 @@ f.txt: {person, "kalle", 25}.
- make_dir(Dir) -> ok | {error, Reason} + Make a directory - - Dir = name() - Reason = ext_posix() - -

Tries to create the directory Dir. Missing parent +

Tries to create the directory Dir. Missing parent directories are not created. Returns ok if successful.

Typical error reasons are:

@@ -572,15 +518,15 @@ f.txt: {person, "kalle", 25}. eacces

Missing search or write permissions for the parent - directories of Dir.

+ directories of Dir.

eexist -

There is already a file or directory named Dir.

+

There is already a file or directory named Dir.

enoent -

A component of Dir does not exist.

+

A component of Dir does not exist.

enospc @@ -588,35 +534,33 @@ f.txt: {person, "kalle", 25}. enotdir -

A component of Dir is not a directory. On some - platforms, enoent is returned instead.

+

A component of Dir is not a directory. + On some platforms, enoent is returned instead.

- make_link(Existing, New) -> ok | {error, Reason} + Make a hard link to a file - - Existing = New = name() - Reason = ext_posix() - -

Makes a hard link from Existing to New, on +

Makes a hard link from Existing to + New, on platforms that support links (Unix). This function returns ok if the link was successfully created, or - {error, Reason}. On platforms that do not support + {error, Reason}. On platforms that do not support links, {error,enotsup} is returned.

Typical error reasons:

eacces

Missing read or write permissions for the parent - directories of Existing or New.

+ directories of Existing or + New.

eexist -

New already exists.

+

New already exists.

enotsup @@ -626,30 +570,28 @@ f.txt: {person, "kalle", 25}.
- make_symlink(Name1, Name2) -> ok | {error, Reason} + Make a symbolic link to a file or directory - - Name1 = Name2 = name() - Reason = ext_posix() - -

This function creates a symbolic link Name2 to - the file or directory Name1, on platforms that support - symbolic links (most Unix systems). Name1 need not +

This function creates a symbolic link Name2 to + the file or directory Name1, on platforms that + support + symbolic links (most Unix systems). Name1 need not exist. This function returns ok if the link was - successfully created, or {error, Reason}. On platforms + successfully created, or {error, Reason}. + On platforms that do not support symbolic links, {error, enotsup} is returned.

Typical error reasons:

eacces -

Missing read or write permissions for the parent - directories of Name1 or Name2.

+

Missing read or write permissions for the parent directories + of Name1 or Name2.

eexist -

Name2 already exists.

+

Name2 already exists.

enotsup @@ -668,22 +610,12 @@ f.txt: {person, "kalle", 25}.
- open(Filename, Modes) -> {ok, IoDevice} | {error, Reason} + Open a file - - Filename = name() - Modes = [Mode] -  Mode = read | write | append | exclusive | raw | binary | {delayed_write, Size, Delay} | delayed_write | {read_ahead, Size} | read_ahead | compressed | {encoding, Encoding} -   Size = Delay = int() -   Encoding = latin1 | unicode | utf8 | utf16 | {utf16, Endian} | utf32 | {utf32, Endian} -     Endian = big | little - IoDevice = io_device() - Reason = ext_posix() | system_limit - -

Opens the file Filename in the mode determined by - Modes, which may contain one or more of the following - items:

+

Opens the file Filename in the mode determined + by Modes, which may contain one or more of the + following items:

read @@ -841,23 +773,23 @@ f.txt: {person, "kalle", 25}.

Returns:

- {ok, IoDevice} + {ok, IoDevice}

The file has been opened in the requested mode. - IoDevice is a reference to the file.

+ IoDevice is a reference to the file.

- {error, Reason} + {error, Reason}

The file could not be opened.

-

IoDevice is really the pid of the process which +

IoDevice is really the pid of the process which handles the file. This process is linked to the process which originally opened the file. If any process to which - the IoDevice is linked terminates, the file will be - closed and the process itself will be terminated. - An IoDevice returned from this call can be used as an - argument to the IO functions (see + the IoDevice is linked terminates, the file will + be closed and the process itself will be terminated. + An IoDevice returned from this call can be used + as an argument to the IO functions (see io(3)).

In previous versions of file, modes were given @@ -897,34 +829,25 @@ f.txt: {person, "kalle", 25}. - path_consult(Path, Filename) -> {ok, Terms, FullName} | {error, Reason} + Read Erlang terms from a file - - Path = [Dir] -  Dir = name() - Filename = name() - Terms = [term()] - FullName = string() - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see below - -

Searches the path Path (a list of directory names) - until the file Filename is found. If Filename - is an absolute filename, Path is ignored. +

Searches the path Path (a list of directory + names) until the file Filename is found. + If Filename + is an absolute filename, Path is ignored. Then reads Erlang terms, separated by '.', from the file. Returns one of the following:

- {ok, Terms, FullName} + {ok, Terms, FullName} -

The file was successfully read. FullName is +

The file was successfully read. FullName is the full name of the file.

{error, enoent}

The file could not be found in any of the directories in - Path.

+ Path.

{error, atom()} @@ -932,7 +855,8 @@ f.txt: {person, "kalle", 25}. See open/2 for a list of typical error codes.

- {error, {Line, Mod, Term}} + {error, {Line, Mod, + Term}}

An error occurred when interpreting the Erlang terms in the file. Use format_error/1 to convert @@ -943,36 +867,28 @@ f.txt: {person, "kalle", 25}. - path_eval(Path, Filename) -> {ok, FullName} | {error, Reason} + Evaluate Erlang expressions in a file - - Path = [Dir] -  Dir = name() - Filename = name() - FullName = string() - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see below - -

Searches the path Path (a list of directory names) - until the file Filename is found. If Filename - is an absolute file name, Path is ignored. Then reads +

Searches the path Path (a list of directory + names) until the file Filename is found. + If Filename is an absolute file name, + Path is ignored. Then reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from the file. The actual result of evaluation is not returned; any expression sequence in the file must be there for its side effect. Returns one of the following:

- {ok, FullName} + {ok, FullName} -

The file was read and evaluated. FullName is +

The file was read and evaluated. FullName is the full name of the file.

{error, enoent}

The file could not be found in any of the directories in - Path.

+ Path.

{error, atom()} @@ -980,7 +896,8 @@ f.txt: {person, "kalle", 25}. See open/2 for a list of typical error codes.

- {error, {Line, Mod, Term}} + {error, {Line, Mod, + Term}}

An error occurred when interpreting the Erlang expressions in the file. Use format_error/1 to @@ -991,34 +908,26 @@ f.txt: {person, "kalle", 25}. - path_open(Path, Filename, Modes) -> {ok, IoDevice, FullName} | {error, Reason} + Open a file - - Path = [Dir] -  Dir = name() - Filename = name() - Modes = [Mode] -- see open/2 - IoDevice = io_device() - FullName = string() - Reason = ext_posix() | system_limit - -

Searches the path Path (a list of directory names) - until the file Filename is found. If Filename - is an absolute file name, Path is ignored. - Then opens the file in the mode determined by Modes. +

Searches the path Path (a list of directory + names) until the file Filename is found. + If Filename + is an absolute file name, Path is ignored. + Then opens the file in the mode determined by Modes. Returns one of the following:

- {ok, IoDevice, FullName} + {ok, IoDevice, FullName}

The file has been opened in the requested mode. - IoDevice is a reference to the file and - FullName is the full name of the file.

+ IoDevice is a reference to the file and + FullName is the full name of the file.

{error, enoent}

The file could not be found in any of the directories in - Path.

+ Path.

{error, atom()} @@ -1028,36 +937,27 @@ f.txt: {person, "kalle", 25}.
- path_script(Path, Filename) -> {ok, Value, FullName} | {error, Reason} + Evaluate and return the value of Erlang expressions in a file - - Path = [Dir] -  Dir = name() - Filename = name() - Value = term() - FullName = string() - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see below - -

Searches the path Path (a list of directory names) - until the file Filename is found. If Filename - is an absolute file name, Path is ignored. Then reads +

Searches the path Path (a list of directory + names) until the file Filename is found. + If Filename is an absolute file name, + Path is ignored. Then reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from the file. Returns one of the following:

- {ok, Value, FullName} + {ok, Value, FullName} -

The file was read and evaluated. FullName is - the full name of the file and Value the value of +

The file was read and evaluated. FullName is + the full name of the file and Value the value of the last expression.

{error, enoent}

The file could not be found in any of the directories in - Path.

+ Path.

{error, atom()} @@ -1065,7 +965,8 @@ f.txt: {person, "kalle", 25}. See open/2 for a list of typical error codes.

- {error, {Line, Mod, Term}} + {error, {Line, Mod, + Term}}

An error occurred when interpreting the Erlang expressions in the file. Use format_error/1 to @@ -1076,42 +977,28 @@ f.txt: {person, "kalle", 25}. - path_script(Path, Filename, Bindings) -> {ok, Value, FullName} | {error, Reason} + Evaluate and return the value of Erlang expressions in a file - - Path = [Dir] -  Dir = name() - Filename = name() - Bindings -- see erl_eval(3) - Value = term() - FullName = string() - Reason = posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see path_script/2 -

The same as path_script/2 but the variable bindings - Bindings are used in the evaluation. See + Bindings are used in the evaluation. See erl_eval(3) about variable bindings.

- pid2name(Pid) -> string() | undefined + Return the name of the file handled by a pid - - Pid = pid() - -

If Pid is an IO device, that is, a pid returned from +

If Pid is an IO device, that is, a pid returned from open/2, this function returns the filename, or rather:

- {ok, Filename} + {ok, Filename}

If this node's file server is not a slave, the file was opened by this node's file server, (this implies that - Pid must be a local pid) and the file is not - closed. Filename is the filename in flat string + Pid must be a local pid) and the file is not + closed. Filename is the filename in flat string format.

undefined @@ -1125,21 +1012,15 @@ f.txt: {person, "kalle", 25}.
- position(IoDevice, Location) -> {ok, NewPosition} | {error, Reason} + Set position in a file - - IoDevice = io_device() - Location = Offset | {bof, Offset} | {cur, Offset} | {eof, Offset} | bof | cur | eof -  Offset = int() - NewPosition = int() - Reason = ext_posix() | terminated - -

Sets the position of the file referenced by IoDevice - to Location. Returns {ok, NewPosition} (as +

Sets the position of the file referenced by IoDevice + to Location. Returns + {ok, NewPosition} (as absolute offset) if successful, otherwise - {error, Reason}. Location is one of - the following:

+ {error, Reason}. Location is + one of the following:

Offset @@ -1167,7 +1048,8 @@ f.txt: {person, "kalle", 25}. einval -

Either Location was illegal, or it evaluated to a +

Either Location was illegal, or it + evaluated to a negative offset in the file. Note that if the resulting position is a negative value, the result is an error, and after the call the file position is undefined.

@@ -1176,22 +1058,14 @@ f.txt: {person, "kalle", 25}.
- pread(IoDevice, LocNums) -> {ok, DataL} | eof | {error, Reason} + Read from a file at certain positions - - IoDevice = io_device() - LocNums = [{Location, Number}] -  Location -- see position/2 -  Number = int() - DataL = [Data] -  Data = [char()] | binary() - Reason = ext_posix() | terminated -

Performs a sequence of pread/3 in one operation, which is more efficient than calling them one at a time. - Returns {ok, [Data, ...]} or {error, Reason}, - where each Data, the result of the corresponding + Returns {ok, [Data, ...]} or + {error, Reason}, + where each Data, the result of the corresponding pread, is either a list or a binary depending on the mode of the file, or eof if the requested position was beyond end of file.

@@ -1199,76 +1073,53 @@ f.txt: {person, "kalle", 25}.
- pread(IoDevice, Location, Number) -> {ok, Data} | eof | {error, Reason} + Read from a file at a certain position - - IoDevice = io_device() - Location -- see position/2 - Number = int() - Data = [char()] | binary() - Reason = ext_posix() | terminated -

Combines position/2 and read/2 in one operation, which is more efficient than calling them one at a - time. If IoDevice has been opened in raw mode, some - restrictions apply: Location is only allowed to be an + time. If IoDevice has been opened in raw mode, + some restrictions apply: Location is only allowed + to be an integer; and the current position of the file is undefined after the operation.

As the position is given as a byte-offset, special caution has to be taken when working with files where encoding is set to something else than latin1, as not every byte position will be a valid character boundary on such a file.

- pwrite(IoDevice, LocBytes) -> ok | {error, {N, Reason}} + Write to a file at certain positions - - IoDevice = io_device() - LocBytes = [{Location, Bytes}] -  Location -- see position/2 -  Bytes = iodata() - N = int() - Reason = ext_posix() | terminated -

Performs a sequence of pwrite/3 in one operation, which is more efficient than calling them one at a time. - Returns ok or {error, {N, Reason}}, where - N is the number of successful writes that was done + Returns ok or {error, {N, + Reason}}, where + N is the number of successful writes that was done before the failure.

When positioning in a file with other encoding than latin1, caution must be taken to set the position on a correct character boundary, see position/2 for details.

- pwrite(IoDevice, Location, Bytes) -> ok | {error, Reason} + Write to a file at a certain position - - IoDevice = io_device() - Location -- see position/2 - Bytes = iodata() - Reason = ext_posix() | terminated -

Combines position/2 and write/2 in one operation, which is more efficient than calling them one at a - time. If IoDevice has been opened in raw mode, some - restrictions apply: Location is only allowed to be an + time. If IoDevice has been opened in raw mode, + some restrictions apply: Location is only allowed + to be an integer; and the current position of the file is undefined after the operation.

When positioning in a file with other encoding than latin1, caution must be taken to set the position on a correct character boundary, see position/2 for details.

- read(IoDevice, Number) -> {ok, Data} | eof | {error, Reason} + Read from a file - - IoDevice = io_device() - Number = int() - Data = [char()] | binary() - Reason = ext_posix() | terminated - -

Reads Number bytes/characters from the file referenced by - IoDevice. The functions read/2, pread/3 +

Reads Number bytes/characters from the file + referenced by IoDevice. The functions + read/2, pread/3 and read_line/1 are the only ways to read from a file opened in raw mode (although they work for normally opened files, too).

@@ -1276,7 +1127,7 @@ f.txt: {person, "kalle", 25}.

Also if encoding is set to something else than latin1, the read/3 call will fail if the data contains characters larger than 255, why the io(3) module is to be preferred when reading such a file.

The function returns:

- {ok, Data} + {ok, Data}

If the file was opened in binary mode, the read bytes are returned in a binary, otherwise in a list. The list or @@ -1285,10 +1136,10 @@ f.txt: {person, "kalle", 25}. eof -

Returned if Number>0 and end of file was reached - before anything at all could be read.

+

Returned if Number>0 and end of file was + reached before anything at all could be read.

- {error, Reason} + {error, Reason}

An error occurred.

@@ -1307,17 +1158,14 @@ f.txt: {person, "kalle", 25}.
- read_file(Filename) -> {ok, Binary} | {error, Reason} + Read a file - - Filename = name() - Binary = binary() - Reason = ext_posix() | terminated | system_limit - -

Returns {ok, Binary}, where Binary is a binary - data object that contains the contents of Filename, or - {error, Reason} if an error occurs.

+

Returns {ok, Binary}, where + Binary is a binary + data object that contains the contents of + Filename, or + {error, Reason} if an error occurs.

Typical error reasons:

enoent @@ -1346,17 +1194,13 @@ f.txt: {person, "kalle", 25}.
- read_file_info(Filename) -> {ok, FileInfo} | {error, Reason} + Get information about a file - - Filename = name() - FileInfo = #file_info{} - Reason = ext_posix() -

Retrieves information about a file. Returns - {ok, FileInfo} if successful, otherwise - {error, Reason}. FileInfo is a record + {ok, FileInfo} if successful, otherwise + {error, Reason}. FileInfo + is a record file_info, defined in the Kernel include file file.hrl. Include the following directive in the module from which the function is called:

@@ -1364,7 +1208,7 @@ f.txt: {person, "kalle", 25}. -include_lib("kernel/include/file.hrl").

The record file_info contains the following fields.

- size = int() + size = integer()

Size of file in bytes.

@@ -1391,7 +1235,7 @@ f.txt: {person, "kalle", 25}. the file or the inode was changed. In Windows, it is the create time.

- mode = int() + mode = integer()

The file permissions as the sum of the following bit values:

@@ -1422,33 +1266,33 @@ f.txt: {person, "kalle", 25}.

On Unix platforms, other bits than those listed above may be set.

- links = int() + links = integer()

Number of links to the file (this will always be 1 for file systems which have no concept of links).

- major_device = int() + major_device = integer()

Identifies the file system where the file is located. In Windows, the number indicates a drive as follows: 0 means A:, 1 means B:, and so on.

- minor_device = int() + minor_device = integer()

Only valid for character devices on Unix. In all other cases, this field is zero.

- inode = int() + inode = integer()

Gives the inode number. On non-Unix file systems, this field will be zero.

- uid = int() + uid = integer()

Indicates the owner of the file. Will be zero for non-Unix file systems.

- gid = int() + gid = integer()

Gives the group that the owner of the file belongs to. Will be zero for non-Unix file systems.

@@ -1474,21 +1318,16 @@ f.txt: {person, "kalle", 25}.
- read_line(IoDevice) -> {ok, Data} | eof | {error, Reason} + Read a line from a file - - IoDevice = io_device() - Data = [char()] | binary() - Reason = ext_posix() | terminated -

Reads a line of bytes/characters from the file referenced by - IoDevice. Lines are defined to be delimited by the linefeed (LF, \n) character, but any carriage return (CR, \r) followed by a newline is also treated as a single LF character (the carriage return is silently ignored). The line is returned including the LF, but excluding any CR immediately followed by a LF. This behaviour is consistent with the behaviour of io:get_line/2. If end of file is reached without any LF ending the last line, a line with no trailing LF is returned.

+ IoDevice. Lines are defined to be delimited by the linefeed (LF, \n) character, but any carriage return (CR, \r) followed by a newline is also treated as a single LF character (the carriage return is silently ignored). The line is returned including the LF, but excluding any CR immediately followed by a LF. This behaviour is consistent with the behaviour of io:get_line/2. If end of file is reached without any LF ending the last line, a line with no trailing LF is returned.

The function can be used on files opened in raw mode. It is however inefficient to use it on raw files if the file is not opened with the option {read_ahead, Size} specified, why combining raw and {read_ahead, Size} is highly recommended when opening a text file for raw line oriented reading.

If encoding is set to something else than latin1, the read_line/1 call will fail if the data contains characters larger than 255, why the io(3) module is to be preferred when reading such a file.

The function returns:

- {ok, Data} + {ok, Data}

One line from the file is returned, including the trailing LF, but with CRLF sequences replaced by a single LF (see above).

If the file was opened in binary mode, the read bytes are @@ -1499,7 +1338,7 @@ f.txt: {person, "kalle", 25}.

Returned if end of file was reached before anything at all could be read.

- {error, Reason} + {error, Reason}

An error occurred.

@@ -1518,23 +1357,19 @@ f.txt: {person, "kalle", 25}.
- read_link(Name) -> {ok, Filename} | {error, Reason} + See what a link is pointing to - - Name = name() - Filename = string() - Reason = ext_posix() - -

This function returns {ok, Filename} if Name - refers to a symbolic link or {error, Reason} otherwise. +

This function returns {ok, Filename} if + Name refers to a symbolic link or + {error, Reason} otherwise. On platforms that do not support symbolic links, the return value will be {error,enotsup}.

Typical error reasons:

einval -

Linkname does not refer to a symbolic link.

+

Name does not refer to a symbolic link.

enoent @@ -1548,34 +1383,26 @@ f.txt: {person, "kalle", 25}.
- read_link_info(Name) -> {ok, FileInfo} | {error, Reason} + Get information about a link or file - - Name = name() - FileInfo = #file_info{}, see read_file_info/1 - Reason = ext_posix() -

This function works like read_file_info/1, except that - if Name is a symbolic link, information about the link - will be returned in the file_info record and + if Name is a symbolic link, information about + the link will be returned in the file_info record and the type field of the record will be set to symlink.

-

If Name is not a symbolic link, this function returns +

If Name is not a symbolic link, this function returns exactly the same result as read_file_info/1. On platforms that do not support symbolic links, this function is always equivalent to read_file_info/1.

- rename(Source, Destination) -> ok | {error, Reason} + Rename a file - - Source = Destination = name() - Reason = ext_posix() - -

Tries to rename the file Source to Destination. +

Tries to rename the file Source to + Destination. It can be used to move files (and directories) between directories, but it is not sufficient to specify the destination only. The destination file name must also be @@ -1593,25 +1420,28 @@ f.txt: {person, "kalle", 25}. eacces

Missing read or write permissions for the parent - directories of Source or Destination. On + directories of Source or + Destination. On some platforms, this error is given if either - Source or Destination is open.

+ Source or Destination + is open.

eexist -

Destination is not an empty directory. On some - platforms, also given when Source and - Destination are not of the same type.

+

Destination is not an empty directory. + On some platforms, also given when Source and + Destination are not of the same type.

einval -

Source is a root directory, or Destination - is a sub-directory of Source.

+

Source is a root directory, or + Destination + is a sub-directory of Source.

eisdir -

Destination is a directory, but Source is - not.

+

Destination is a directory, but + Source is not.

enoent @@ -1619,35 +1449,28 @@ f.txt: {person, "kalle", 25}. enotdir -

Source is a directory, but Destination is - not.

+

Source is a directory, but + Destination is not.

exdev -

Source and Destination are on different - file systems.

+

Source and Destination + are on different file systems.

- script(Filename) -> {ok, Value} | {error, Reason} + Evaluate and return the value of Erlang expressions in a file - - Filename = name() - Value = term() - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see below -

Reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from the file. Returns one of the following:

- {ok, Value} + {ok, Value} -

The file was read and evaluated. Value is +

The file was read and evaluated. Value is the value of the last expression.

{error, atom()} @@ -1656,7 +1479,8 @@ f.txt: {person, "kalle", 25}. See open/2 for a list of typical error codes.

- {error, {Line, Mod, Term}} + {error, {Line, Mod, + Term}}

An error occurred when interpreting the Erlang expressions in the file. Use format_error/1 to @@ -1667,33 +1491,21 @@ f.txt: {person, "kalle", 25}. - script(Filename, Bindings) -> {ok, Value} | {error, Reason} + Evaluate and return the value of Erlang expressions in a file - - Filename = name() - Bindings -- see erl_eval(3) - Value = term() - Reason = ext_posix() | terminated | system_limit - | {Line, Mod, Term} -  Line, Mod, Term -- see below -

The same as script/1 but the variable bindings - Bindings are used in the evaluation. See + Bindings are used in the evaluation. See erl_eval(3) about variable bindings.

- set_cwd(Dir) -> ok | {error,Reason} + Set the current working directory - - Dir = name() - Reason = ext_posix() -

Sets the current working directory of the file server to - Dir. Returns ok if successful.

+ Dir. Returns ok if successful.

Typical error reasons are:

enoent @@ -1702,8 +1514,8 @@ f.txt: {person, "kalle", 25}. enotdir -

A component of Dir is not a directory. On some - platforms, enoent is returned.

+

A component of Dir is not a directory. + On some platforms, enoent is returned.

eacces @@ -1712,23 +1524,21 @@ f.txt: {person, "kalle", 25}. badarg -

Filename had an improper type, such as tuple.

+

Dir had an improper type, + such as tuple.

-

In a future release, a bad type for the Filename +

In a future release, a bad type for the + Dir argument will probably generate an exception.

- sync(IoDevice) -> ok | {error, Reason} + Synchronizes the in-memory state of a file with that on the physical medium - - IoDevice = io_device() - Reason = ext_posix() | terminated -

Makes sure that any buffers kept by the operating system (not by the Erlang runtime system) are written to disk. On @@ -1743,12 +1553,8 @@ f.txt: {person, "kalle", 25}. - datasync(IoDevice) -> ok | {error, Reason} + Synchronizes the in-memory data of a file, ignoring most of its metadata, with that on the physical medium - - IoDevice = io_device() - Reason = ext_posix() | terminated -

Makes sure that any buffers kept by the operating system (not by the Erlang runtime system) are written to disk. In @@ -1770,32 +1576,23 @@ f.txt: {person, "kalle", 25}. - truncate(IoDevice) -> ok | {error, Reason} + Truncate a file - - IoDevice = io_device() - Reason = ext_posix() | terminated - -

Truncates the file referenced by IoDevice at +

Truncates the file referenced by IoDevice at the current position. Returns ok if successful, - otherwise {error, Reason}.

+ otherwise {error, Reason}.

- write(IoDevice, Bytes) -> ok | {error, Reason} + Write to a file - - IoDevice = io_device() - Bytes = iodata() - Reason = ext_posix() | terminated - -

Writes Bytes to the file referenced by - IoDevice. This function is the only way to write to a +

Writes Bytes to the file referenced by + IoDevice. This function is the only way to write to a file opened in raw mode (although it works for normally opened files, too). Returns ok if successful, and - {error, Reason} otherwise.

+ {error, Reason} otherwise.

If the file is opened with encoding set to something else than latin1, each byte written might result in several bytes actually being written to the file, as the byte range 0..255 might represent anything between one and four bytes depending on value and UTF encoding type.

Typical error reasons are:

@@ -1811,18 +1608,14 @@ f.txt: {person, "kalle", 25}.
- write_file(Filename, Bytes) -> ok | {error, Reason} + Write a file - - Filename = name() - Bytes = iodata() - Reason = ext_posix() | terminated | system_limit - -

Writes the contents of the iodata term Bytes to the - file Filename. The file is created if it does not +

Writes the contents of the iodata term Bytes + to the file Filename. + The file is created if it does not exist. If it exists, the previous contents are - overwritten. Returns ok, or {error, Reason}.

+ overwritten. Returns ok, or {error, Reason}.

Typical error reasons are:

enoent @@ -1851,33 +1644,23 @@ f.txt: {person, "kalle", 25}.
- write_file(Filename, Bytes, Modes) -> ok | {error, Reason} + Write a file - - Filename = name() - Bytes = iodata() - Modes = [Mode] -- see open/2 - Reason = ext_posix() | terminated | system_limit -

Same as write_file/2, but takes a third argument - Modes, a list of possible modes, see + Modes, a list of possible modes, see open/2. The mode flags binary and write are implicit, so they should not be used.

- write_file_info(Filename, FileInfo) -> ok | {error, Reason} + Change information about a file - - Filename = name() - FileInfo = #file_info{} -- see also read_file_info/1 - Reason = ext_posix() -

Change file information. Returns ok if successful, - otherwise {error, Reason}. FileInfo is a record + otherwise {error, Reason}. + FileInfo is a record file_info, defined in the Kernel include file file.hrl. Include the following directive in the module from which the function is called:

@@ -1901,7 +1684,7 @@ f.txt: {person, "kalle", 25}. time). On Windows, this field is the new creation time to set for the file.

- mode = int() + mode = integer()

The file permissions as the sum of the following bit values:

@@ -1932,12 +1715,12 @@ f.txt: {person, "kalle", 25}.

On Unix platforms, other bits than those listed above may be set.

- uid = int() + uid = integer()

Indicates the owner of the file. Ignored for non-Unix file systems.

- gid = int() + gid = integer()

Gives the group that the owner of the file belongs to. Ignored non-Unix file systems.

diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index fb09092f1c..5ceb82ae41 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -4,7 +4,7 @@
- 20072010 + 20072011 Ericsson AB. All Rights Reserved. @@ -65,77 +65,71 @@ SEE ALSO AUTHORS +
-
- - DATA TYPES - - - assoc_id() - + + + +

An opaque term returned in for example #sctp_paddr_change{} that identifies an association for an SCTP socket. The term is opaque except for the special value 0 that has a - meaning such as "the whole endpoint" or "all future associations".

- -
- charlist() = [char()] - - - iolist() = [char() | binary()] - - - ip_address() - + meaning such as "the whole endpoint" or "all future associations". +

+ + + + + + + +

Represents an address of an SCTP socket. It is a tuple as explained in inet(3).

- -
- port_number() = 0 .. 65535 - - - posix() - -

See - inet(3); POSIX Error Codes.

- -
- sctp_option() - + + + + + + + + +

See + inet(3); POSIX Error Codes.

+
+
+ + +

One of the SCTP Socket Options.

-
- sctp_socket() - + + + + +

Socket identifier returned from open/*.

- -
- timeout() = int() | infinity - -

Timeout used in SCTP connect and receive calls.

-
-
- -
+ + + + + - abort(sctp_socket(), Assoc) -> ok | {error, posix()} + Abnormally terminate the association given by Assoc, without flushing of unsent data - - Assoc = #sctp_assoc_change{} - -

Abnormally terminates the association given by Assoc, without +

Abnormally terminates the association given by Assoc, without flushing of unsent data. The socket itself remains open. Other associations opened on this socket are still valid, and it can be used in new associations.

- close(sctp_socket()) -> ok | {error, posix()} + Completely close the socket and all associations on it

Completely closes the socket and all associations on it. The unsent @@ -148,35 +142,26 @@ - connect(Socket, Addr, Port, Opts) -> {ok,Assoc} | {error, posix()} + Same as connect(Socket, Addr, Port, Opts, infinity). -

Same as connect(Socket, Addr, Port, Opts, infinity).

+

Same as connect(Socket, Addr, Port, Opts, infinity).

- connect(Socket, Addr, Port, [Opt], Timeout) -> {ok, Assoc} | {error, posix()} + Establish a new association for the socket Socket, with a peer (SCTP server socket) - - Socket = sctp_socket() - Addr = ip_address() | Host - Port = port_number() - Opt = sctp_option() - Timeout = timeout() - Host = atom() | string() - Assoc = #sctp_assoc_change{} - -

Establishes a new association for the socket Socket, +

Establishes a new association for the socket Socket, with the peer (SCTP server socket) given by - Addr and Port. The Timeout, + Addr and Port. The Timeout, is expressed in milliseconds. A socket can be associated with multiple peers.

-

WARNING:Using a value of Timeout less than +

WARNING:Using a value of Timeout less than the maximum time taken by the OS to establish an association (around 4.5 minutes if the default values from RFC 4960 are used) can result in inconsistent or incorrect return values. This is especially - relevant for associations sharing the same Socket + relevant for associations sharing the same Socket (i.e. source address and port) since the controlling process blocks until connect/* returns. connect_init/* @@ -185,7 +170,7 @@

The result of connect/* is an #sctp_assoc_change{} event which contains, in particular, the new - Association ID:

+ Association ID.

   #sctp_assoc_change{
         state             = atom(),
@@ -198,13 +183,13 @@
           giving an sctp_initmsg option to connect
           as in:

-  connect(Socket, Ip, Port,
+  connect(Socket, Ip, Port,
         [{sctp_initmsg,#sctp_initmsg{num_ostreams=OutStreams,
                                      max_instreams=MaxInStreams}}])        
-

All options Opt are set on the socket before the +

All options Opt are set on the socket before the association is attempted. If an option record has got undefined field values, the options record is first read from the socket - for those values. In effect, Opt option records only + for those values. In effect, Opt option records only define field values to change before connecting.

The returned outbound_streams and inbound_streams are the actual stream numbers on the socket, which may be different @@ -242,27 +227,19 @@ - connect_init(Socket, Addr, Port, Opts) -> ok | {error, posix()} + Same as connect_init(Socket, Addr, Port, Opts, infinity). -

Same as connect_init(Socket, Addr, Port, Opts, infinity).

+

Same as connect_init(Socket, Addr, Port, Opts, infinity).

- connect_init(Socket, Addr, Port, [Opt], Timeout) -> ok | {error, posix()} + Initiate a new association for the socket Socket, with a peer (SCTP server socket) - - Socket = sctp_socket() - Addr = ip_address() | Host - Port = port_number() - Opt = sctp_option() - Timeout = timeout() - Host = atom() | string() - -

Initiates a new association for the socket Socket, +

Initiates a new association for the socket Socket, with the peer (SCTP server socket) given by - Addr and Port.

+ Addr and Port.

The fundamental difference between this API and connect/* is that the return value is that of the underlying OS connect(2) system call. If ok is returned @@ -275,64 +252,52 @@ active option.

The parameters are as described in connect/*, with the - exception of the Timeout value.

-

The timer associated with Timeout only supervises - IP resolution of Addr

+ exception of the Timeout value.

+

The timer associated with Timeout only supervises + IP resolution of Addr

- controlling_process(sctp_socket(), pid()) -> ok + Assign a new controlling process pid to the socket -

Assigns a new controlling process Pid to Socket. Same implementation +

Assigns a new controlling process Pid to Socket. Same implementation as gen_udp:controlling_process/2.

- eof(Socket, Assoc) -> ok | {error, Reason} + Gracefully terminate the association given by Assoc, with flushing of all unsent data - - Socket = sctp_socket() - Assoc = #sctp_assoc_change{} - -

Gracefully terminates the association given by Assoc, with +

Gracefully terminates the association given by Assoc, with flushing of all unsent data. The socket itself remains open. Other associations opened on this socket are still valid, and it can be used in new associations.

- listen(Socket, IsServer) -> ok | {error, Reason} + Set up a socket to listen. - - Socket = sctp_socket() - IsServer = bool() -

Sets up a socket to listen on the IP address and port number - it is bound to. IsServer must be 'true' or 'false'. + it is bound to. IsServer must be true + or false. In the contrast to TCP, in SCTP there is no listening queue length. - If IsServer is 'true' the socket accepts new associations, i.e. + If IsServer is true the socket accepts new associations, i.e. it will become an SCTP server socket.

- open() -> {ok, Socket} | {error, posix()} - open(Port) -> {ok, Socket} | {error, posix()} - open([Opt]) -> {ok, Socket} | {error, posix()} - open(Port, [Opt]) -> {ok, Socket} | {error, posix()} + + + + Create an SCTP socket and bind it to local addresses - - Opt = {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option() - IP = ip_address() | any | loopback - Port = port_number() -

Creates an SCTP socket and binds it to the local addresses - specified by all {ip,IP} (or synonymously {ifaddr,IP}) + specified by all {ip,IP} (or synonymously {ifaddr,IP}) options (this feature is called SCTP multi-homing). - The default IP and Port are any + The default IP and Port are any and 0, meaning bind to all local addresses on any one free port.

A default set of socket options @@ -345,27 +310,16 @@ - recv(sctp_socket()) -> {ok, {FromIP, FromPort, AncData, BinMsg}} | {error, Reason} - recv(sctp_socket(), timeout()) -> {ok, {FromIP, FromPort, AncData, Data}} | {error, Reason} + + Receive a message from a socket - - FromIP = ip_address() - FromPort = port_number() - AncData = [#sctp_sndrcvinfo{}] - Data = binary() | charlist() | #sctp_sndrcvinfo{} | - #sctp_assoc_change{} | #sctp_paddr_change{} | - #sctp_adaptation_event{} - Reason = posix() | #sctp_send_failed{} | #scpt_paddr_change{} | - #sctp_pdapi_event{} | #sctp_remote_error{} | - #sctp_shutdown_event{} - -

Receives the Data message from any association of the socket. +

Receives the Data message from any association of the socket. If the receive times out {error,timeout is returned. The default timeout is infinity. - FromIP and FromPort indicate the sender's address.

-

AncData is a list of Ancillary Data items which - may be received along with the main Data. + FromIP and FromPort indicate the sender's address.

+

AncData is a list of Ancillary Data items which + may be received along with the main Data. This list can be empty, or contain a single #sctp_sndrcvinfo{} record, if receiving of such ancillary data is enabled @@ -375,10 +329,10 @@ provide an easy way of determining the association and stream over which the message has been received. (An alternative way would be to get the Association ID from the - FromIP and FromPort using the + FromIP and FromPort using the sctp_get_peer_addr_info socket option, but this would still not produce the Stream number).

-

The actual Data received may be a binary(), +

The actual Data received may be a binary(), or list() of bytes (integers in the range 0 through 255) depending on the socket mode, or an SCTP Event. @@ -476,15 +430,10 @@ - send(Socket, SndRcvInfo, Data) -> ok | {error, Reason} + Send a message using an #sctp_sndrcvinfo{}record - - Socket = sctp_socket() - SndRcvInfo = #sctp_sndrcvinfo{} - Data = binary() | iolist() - -

Sends the Data message with all sending parameters from a +

Sends the Data message with all sending parameters from a #sctp_sndrcvinfo{} record. This way, the user can specify the PPID (passed to the remote end) and Context (passed to the local SCTP layer) which can be used @@ -494,21 +443,15 @@ - send(Socket, Assoc, Stream, Data) -> ok | {error, Reason} + Send a message over an existing association and given stream - - Socket = sctp_socket() - Assoc = #sctp_assoc_change{} | assoc_id() - Stream = integer() - Data = binary() | iolist() - -

Sends Data message over an existing association and given +

Sends Data message over an existing association and given stream.

- error_string(integer()) -> ok | string() | undefined + Translate an SCTP error number into a string

Translates an SCTP error number from for example diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index aa171c77c2..f1d42d9faa 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -4,7 +4,7 @@

- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -63,36 +63,45 @@ do_recv(Sock, Bs) ->

For more examples, see the examples section.

-
- DATA TYPES - -ip_address() - see inet(3) - -posix() - see inet(3) + + + + + + + +

Represents an address of a TCP socket. + It is a tuple as explained in + inet(3).

+
+
+ + + + + + +

See + inet(3); POSIX Error Codes.

+
+
+ + socket() + +

As returned by accept/1,2 and connect/3,4.

+ +
+
+
-socket() - as returned by accept/1,2 and connect/3,4
- -
- connect(Address, Port, Options) -> {ok, Socket} | {error, Reason} - connect(Address, Port, Options, Timeout) -> {ok, Socket} | {error, Reason} + + Connect to a TCP port - - Address = string() | atom() | ip_address() - Port = 0..65535 - Options = [Opt] -  Opt -- see below - Timeout = int() | infinity - Socket = socket() - Reason = posix() - -

Connects to a server on TCP port Port on the host - with IP address Address. The Address argument +

Connects to a server on TCP port Port on the host + with IP address Address. The Address argument can be either a hostname, or an IP address.

The available options are:

@@ -127,13 +136,13 @@ socket()

Set up the socket for IPv4.

- Opt + Opt

See inet:setopts/2.

-

Packets can be sent to the returned socket Socket +

Packets can be sent to the returned socket Socket using send/2. Packets sent from the peer are delivered as messages:

@@ -148,7 +157,7 @@ socket()

unless {active, false} is specified in the option list for the socket, in which case packets are retrieved by calling recv/2.

-

The optional Timeout parameter specifies a timeout in +

The optional Timeout parameter specifies a timeout in milliseconds. The default value is infinity.

The default values for options given to connect can @@ -159,19 +168,12 @@ socket() - listen(Port, Options) -> {ok, ListenSocket} | {error, Reason} + Set up a socket to listen on a port - - Port = 0..65535 - Options = [Opt] -  Opt -- see below - ListenSocket -- see below - Reason = posix() - -

Sets up a socket to listen on the port Port on +

Sets up a socket to listen on the port Port on the local host.

-

If Port == 0, the underlying OS assigns an available +

If Port == 0, the underlying OS assigns an available port number, use inet:port/1 to retrieve it.

The available options are:

@@ -214,7 +216,7 @@ socket() inet:setopts/2.

-

The returned socket ListenSocket can only be used in +

The returned socket ListenSocket can only be used in calls to accept/1,2.

The default values for options given to listen can @@ -225,27 +227,23 @@ socket() - accept(ListenSocket) -> {ok, Socket} | {error, Reason} - accept(ListenSocket, Timeout) -> {ok, Socket} | {error, Reason} + + Accept an incoming connection request on a listen socket - - ListenSocket -- see listen/2 - Timeout = int() | infinity - Socket = socket() - Reason = closed | timeout | posix() - + Returned by listen/2. +

Accepts an incoming connection request on a listen socket. - Socket must be a socket returned from listen/2. - Timeout specifies a timeout value in ms, defaults to + Socket must be a socket returned from listen/2. + Timeout specifies a timeout value in ms, defaults to infinity.

-

Returns {ok, Socket} if a connection is established, - or {error, closed} if ListenSocket is closed, +

Returns {ok, Socket} if a connection is established, + or {error, closed} if ListenSocket is closed, or {error, timeout} if no connection is established within the specified time. May also return a POSIX error value if something else goes wrong, see inet(3) for possible error values.

-

Packets can be sent to the returned socket Socket +

Packets can be sent to the returned socket Socket using send/2. Packets sent from the peer are delivered as messages:

@@ -264,13 +262,8 @@ socket()
- send(Socket, Packet) -> ok | {error, Reason} + Send a packet - - Socket = socket() - Packet = [char()] | binary() - Reason = posix() -

Sends a packet on a socket.

There is no send call with timeout option, you use the @@ -279,70 +272,52 @@ socket() - recv(Socket, Length) -> {ok, Packet} | {error, Reason} - recv(Socket, Length, Timeout) -> {ok, Packet} | {error, Reason} + + Receive a packet from a passive socket - - Socket = socket() - Length = int() - Packet = [char()] | binary() | HttpPacket - Timeout = int() | infinity - Reason = closed | posix() - HttpPacket = see the description of HttpPacket in erlang:decode_packet/3 - + See the description of + HttpPacket in + erlang:decode_packet/3. +

This function receives a packet from a socket in passive mode. A closed socket is indicated by a return value {error, closed}.

-

The Length argument is only meaningful when +

The Length argument is only meaningful when the socket is in raw mode and denotes the number of - bytes to read. If Length = 0, all available bytes are - returned. If Length > 0, exactly Length + bytes to read. If Length = 0, all available bytes are + returned. If Length > 0, exactly Length bytes are returned, or an error; possibly discarding less - than Length bytes of data when the socket gets closed + than Length bytes of data when the socket gets closed from the other side.

-

The optional Timeout parameter specifies a timeout in +

The optional Timeout parameter specifies a timeout in milliseconds. The default value is infinity.

- controlling_process(Socket, Pid) -> ok | {error, Reason} + Change controlling process of a socket - - Socket = socket() - Pid = pid() - Reason = closed | not_owner | posix() - -

Assigns a new controlling process Pid to - Socket. The controlling process is the process which +

Assigns a new controlling process Pid to + Socket. The controlling process is the process which receives messages from the socket. If called by any other process than the current controlling process, {error, eperm} is returned.

- close(Socket) -> ok | {error, Reason} + Close a TCP socket - - Socket = socket() - Reason = posix() -

Closes a TCP socket.

- shutdown(Socket, How) -> ok | {error, Reason} + Immediately close a socket - - Socket = socket() - How = read | write | read_write - Reason = posix() -

Immediately close a socket in one or two directions.

-

How == write means closing the socket for writing, +

How == write means closing the socket for writing, reading from it is still possible.

To be able to handle that the peer has done a shutdown on the write side, the {exit_on_close, false} option diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 71f2e9bd83..c0e783f508 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -4,7 +4,7 @@

- 19972009 + 19972011 Ericsson AB. All Rights Reserved. @@ -34,32 +34,43 @@ with sockets using the UDP protocol.

-
- DATA TYPES - -ip_address() - see inet(3) - -posix() - see inet(3) + + + + + + + +

Represents an address of a TCP socket. + It is a tuple as explained in + inet(3).

+
+
+ + + + + + +

See + inet(3); POSIX Error Codes.

+
+
+ + socket() + +

As returned by open/1,2.

+
+
+
-socket() - as returned by open/1,2
-
- open(Port) -> {ok, Socket} | {error, Reason} - open(Port, Options) -> {ok, Socket} | {error, Reason} + + Associate a UDP port number with the process calling it - - Port = 0..65535 - Options = [Opt] -  Opt -- see below - Socket = socket() - Reason = posix() - -

Associates a UDP port number (Port) with the calling +

Associates a UDP port number (Port) with the calling process.

The available options are:

@@ -96,7 +107,7 @@ socket() inet:setopts/2.

-

The returned socket Socket is used to send packets +

The returned socket Socket is used to send packets from this port with send/4. When UDP packets arrive at the opened port, they are delivered as messages:

@@ -110,66 +121,42 @@ socket() binary if the option binary was specified.

Default value for the receive buffer option is {recbuf, 8192}.

-

If Port == 0, the underlying OS assigns a free UDP +

If Port == 0, the underlying OS assigns a free UDP port, use inet:port/1 to retrieve it.

- send(Socket, Address, Port, Packet) -> ok | {error, Reason} + Send a packet - - Socket = socket() - Address = string() | atom() | ip_address() - Port = 0..65535 - Packet = [char()] | binary() - Reason = not_owner | posix() -

Sends a packet to the specified address and port. - The Address argument can be either a hostname, or an + The Address argument can be either a hostname, or an IP address.

- recv(Socket, Length) -> {ok, {Address, Port, Packet}} | {error, Reason} - recv(Socket, Length, Timeout) -> {ok, {Address, Port, Packet}} | {error, Reason} + + Receive a packet from a passive socket - - Socket = socket() - Length = int() - Address = ip_address() - Port = 0..65535 - Packet = [char()] | binary() - Timeout = int() | infinity - Reason = not_owner | posix() -

This function receives a packet from a socket in passive mode.

-

The optional Timeout parameter specifies a timeout in +

The optional Timeout parameter specifies a timeout in milliseconds. The default value is infinity.

- controlling_process(Socket, Pid) -> ok + Change controlling process of a socket - - Socket = socket() - Pid = pid() - -

Assigns a new controlling process Pid to - Socket. The controlling process is the process which +

Assigns a new controlling process Pid to + Socket. The controlling process is the process which receives messages from the socket.

- close(Socket) -> ok | {error, Reason} + Close a UDP socket - - Socket = socket() - Reason = not_owner | posix() -

Closes a UDP socket.

diff --git a/lib/kernel/doc/src/global.xml b/lib/kernel/doc/src/global.xml index 077109d6c9..304a9b1d88 100644 --- a/lib/kernel/doc/src/global.xml +++ b/lib/kernel/doc/src/global.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -106,45 +106,37 @@ + + + + + + - del_lock(Id) - del_lock(Id, Nodes) -> void() + + Delete a lock - - Id = {ResourceId, LockRequesterId} -  ResourceId = term() -  LockRequesterId = term() - Nodes = [node()] - -

Deletes the lock Id synchronously.

+

Deletes the lock Id synchronously.

- notify_all_name(Name, Pid1, Pid2) -> none + Name resolving function that notifies both pids - - Name = term() - Pid1 = Pid2 = pid() -

This function can be used as a name resolving function for register_name/3 and re_register_name/3. It unregisters both pids, and sends the message - {global_name_conflict, Name, OtherPid} to both + {global_name_conflict, Name, OtherPid} to both processes.

- random_exit_name(Name, Pid1, Pid2) -> Pid1 | Pid2 + Name resolving function that kills one pid - - Name = term() - Pid1 = Pid2 = pid() -

This function can be used as a name resolving function for register_name/3 and re_register_name/3. It @@ -154,33 +146,27 @@ - random_notify_name(Name, Pid1, Pid2) -> Pid1 | Pid2 + Name resolving function that notifies one pid - - Name = term() - Pid1 = Pid2 = pid() -

This function can be used as a name resolving function for register_name/3 and re_register_name/3. It randomly chooses one of the pids for registration, and sends - the message {global_name_conflict, Name} to the other + the message {global_name_conflict, Name} to the other pid.

- register_name(Name, Pid) - register_name(Name, Pid, Resolve) -> yes | no + + Globally register a name for a pid - - Name = term() - Pid = pid() - Resolve = fun() or {Module, Function} where -   Resolve(Name, Pid, Pid2) -> Pid | Pid2 | none - + + {Module, Function} + is also allowed + -

Globally associates the name Name with a pid, that is, +

Globally associates the name Name with a pid, that is, Globally notifies all nodes of a new global name in a network of Erlang nodes.

@@ -188,7 +174,7 @@ of the globally registered names that already exist. The network is also informed of any global names in newly connected nodes. If any name clashes are discovered, - the Resolve function is called. Its purpose is to + the Resolve function is called. Its purpose is to decide which pid is correct. If the function crashes, or returns anything other than one of the pids, the name is unregistered. This function is called once for each name @@ -196,7 +182,7 @@

There are three pre-defined resolve functions: random_exit_name/3, random_notify_name/3, and - notify_all_name/3. If no Resolve function is + notify_all_name/3. If no Resolve function is defined, random_exit_name is used. This means that one of the two registered processes will be selected as correct while the other is killed.

@@ -225,78 +211,63 @@
- registered_names() -> [Name] + All globally registered names - - Name = term() -

Returns a lists of all globally registered names.

- re_register_name(Name, Pid) - re_register_name(Name, Pid, Resolve) -> void() + + Atomically re-register a name - - Name = term() - Pid = pid() - Resolve = fun() or {Module, Function} where -   Resolve(Name, Pid, Pid2) -> Pid | Pid2 | none - + + {Module, Function} + is also allowed + -

Atomically changes the registered name Name on all - nodes to refer to Pid.

+

Atomically changes the registered name Name on all + nodes to refer to Pid.

-

The Resolve function has the same behavior as in +

The Resolve function has the same behavior as in register_name/2,3.

- send(Name, Msg) -> Pid + Send a message to a globally registered pid - - Name = term() - Msg = term() - Pid = pid() - -

Sends the message Msg to the pid globally registered - as Name.

+

Sends the message Msg to the pid globally registered + as Name.

-

Failure: If Name is not a globally registered +

Failure: If Name is not a globally registered name, the calling function will exit with reason - {badarg, {Name, Msg}}.

+ {badarg, {Name, Msg}}.

- set_lock(Id) - set_lock(Id, Nodes) - set_lock(Id, Nodes, Retries) -> boolean() + + + Set a lock on the specified nodes - - Id = {ResourceId, LockRequesterId} -  ResourceId = term() -  LockRequesterId = term() - Nodes = [node()] - Retries = int() >= 0 | infinity - + +

Sets a lock on the specified nodes (or on all nodes if none - are specified) on ResourceId for - LockRequesterId. If a lock already exists on - ResourceId for another requester than - LockRequesterId, and Retries is not equal to 0, + are specified) on ResourceId for + LockRequesterId. If a lock already exists on + ResourceId for another requester than + LockRequesterId, and Retries is not equal to 0, the process sleeps for a while and will try to execute - the action later. When Retries attempts have been made, + the action later. When Retries attempts have been made, false is returned, otherwise true. If - Retries is infinity, true is eventually + Retries is infinity, true is eventually returned (unless the lock is never released).

-

If no value for Retries is given, infinity is +

If no value for Retries is given, infinity is used.

This function is completely synchronous.

@@ -315,7 +286,7 @@ application to detect and rectify a deadlock.

-

Some values of ResourceId should be avoided or +

Some values of ResourceId should be avoided or Erlang/OTP will not work properly. A list of resources to avoid: global, dist_ac, mnesia_table_lock, mnesia_adjust_log_writes, @@ -326,7 +297,7 @@ - sync() -> void() + Synchronize the global name server

Synchronizes the global name server with all nodes known to @@ -335,56 +306,45 @@ the global name server will receive global information from all nodes. This function can be called when new nodes are added to the network.

+

The only possible error reason Reason is + {"global_groups definition error", Error}.

- trans(Id, Fun) - trans(Id, Fun, Nodes) - trans(Id, Fun, Nodes, Retries) -> Res | aborted + + + Micro transaction facility - - Id = {ResourceId, LockRequesterId} -  ResourceId = term() -  LockRequesterId = term() - Fun = fun() | {M, F} - Nodes = [node()] - Retries = int() >= 0 | infinity - Res = term() - + + -

Sets a lock on Id (using set_lock/3). If this - succeeds, Fun() is evaluated and the result Res +

Sets a lock on Id (using set_lock/3). If this + succeeds, Fun() is evaluated and the result Res is returned. Returns aborted if the lock attempt - failed. If Retries is set to infinity, + failed. If Retries is set to infinity, the transaction will not abort.

infinity is the default setting and will be used if - no value is given for Retries.

+ no value is given for Retries.

- unregister_name(Name) -> void() + Remove a globally registered name for a pid - - Name = term() - -

Removes the globally registered name Name from +

Removes the globally registered name Name from the network of Erlang nodes.

- whereis_name(Name) -> pid() | undefined + Get the pid with a given globally registered name - - Name = term() -

Returns the pid with the globally registered name - Name. Returns undefined if the name is not + Name. Returns undefined if the name is not globally registered.

diff --git a/lib/kernel/doc/src/global_group.xml b/lib/kernel/doc/src/global_group.xml index 4facf4a4aa..abf6178fc4 100644 --- a/lib/kernel/doc/src/global_group.xml +++ b/lib/kernel/doc/src/global_group.xml @@ -4,7 +4,7 @@
- 19982009 + 19982011 Ericsson AB. All Rights Reserved. @@ -42,26 +42,7 @@ kernel(6), config(4):

-{global_groups, [GroupTuple]} -

Types:

- - GroupTuple = {GroupName, [Node]} | {GroupName, PublishType, [Node]} - GroupName = atom() (naming a global group) - PublishType = normal | hidden - Node = atom() (naming a node) - -

A GroupTuple without PublishType is the same as a - GroupTuple with PublishType == normal.

-

A node started with the command line flag -hidden, see - erl(1), is said to be a - hidden node. A hidden node will establish hidden - connections to nodes not part of the same global group, but - normal (visible) connections to nodes part of the same global - group.

-

A global group defined with PublishType == hidden, is - said to be a hidden global group. All nodes in a hidden global - group are hidden nodes, regardless if they are started with - the -hidden command line flag or not.

+{global_groups, [GroupTuple :: group_tuple()]}

For the processes and nodes to run smoothly using the global group functionality, the following criteria must be met:

@@ -82,14 +63,44 @@

In the following description, a group node is a node belonging to the same global group as the local node.

+ + + + +

A GroupTuple without PublishType is the same as a + GroupTuple with PublishType == normal.

+
+
+ + + + + + +

A node started with the command line flag -hidden, see + erl(1), is said to be a + hidden node. A hidden node will establish hidden + connections to nodes not part of the same global group, but + normal (visible) connections to nodes part of the same global + group.

+

A global group defined with PublishType == hidden, is + said to be a hidden global group. All nodes in a hidden global + group are hidden nodes, regardless if they are started with + the -hidden command line flag or not.

+
+
+ + +

A registered name.

+
+ + + +
- global_groups() -> {GroupName, GroupNames} | undefined + Return the global group names - - GroupName = atom() - GroupNames = [GroupName] -

Returns a tuple containing the name of the global group the local node belongs to, and the list of all other known @@ -98,53 +109,52 @@ - info() -> [{Item, Info}] + Information about global groups - - Item, Info -- see below - + +

Returns a list containing information about the global groups. Each element of the list is a tuple. The order of the tuples is not defined.

- {state, State} + {state, State}

If the local node is part of a global group, - State == synced. If no global groups are defined, - State == no_conf.

+ State == synced. If no global groups are defined, + State == no_conf.

- {own_group_name, GroupName} + {own_group_name, GroupName}

The name (atom) of the group that the local node belongs to.

- {own_group_nodes, Nodes} + {own_group_nodes, Nodes}

A list of node names (atoms), the group nodes.

- {synced_nodes, Nodes} + {synced_nodes, Nodes}

A list of node names, the group nodes currently synchronized with the local node.

- {sync_error, Nodes} + {sync_error, Nodes}

A list of node names, the group nodes with which the local node has failed to synchronize.

- {no_contact, Nodes} + {no_contact, Nodes}

A list of node names, the group nodes to which there are currently no connections.

- {other_groups, Groups} + {other_groups, Groups} -

Groups is a list of tuples - {GroupName, Nodes}, specifying the name and nodes +

Groups is a list of tuples + {GroupName, Nodes}, specifying the name and nodes of the other global groups.

- {monitoring, Pids} + {monitoring, Pids}

A list of pids, specifying the processes which have subscribed to nodeup and nodedown messages.

@@ -153,73 +163,52 @@
- monitor_nodes(Flag) -> ok + Subscribe to node status changes - - Flag = bool() - -

Depending on Flag, the calling process starts - subscribing (Flag == true) or stops subscribing - (Flag == false) to node status change messages.

+

Depending on Flag, the calling process starts + subscribing (Flag == true) or stops subscribing + (Flag == false) to node status change messages.

A process which has subscribed will receive the messages {nodeup, Node} and {nodedown, Node} when a group node connects or disconnects, respectively.

- own_nodes() -> Nodes + Return the group nodes - - Nodes = [Node] -  Node = node() -

Returns the names of all group nodes, regardless of their current status.

- registered_names(Where) -> Names + Return globally registered names - - Where = {node, Node} | {group, GroupName} -  Node = node() -  GroupName = atom() - Names = [Name] -  Name = atom() -

Returns a list of all names which are globally registered on the specified node or in the specified global group.

- send(Name, Msg) -> pid() | {badarg, {Name, Msg}} - send(Where, Name, Msg) -> pid() | {badarg, {Name, Msg}} + + Send a message to a globally registered pid - - Where = {node, Node} | {group, GroupName} -  Node = node() -  GroupName = atom() - Name = atom() - Msg = term() - -

Searches for Name, globally registered on +

Searches for Name, globally registered on the specified node or in the specified global group, or -- - if the Where argument is not provided -- in any global + if the Where argument is not provided -- in any global group. The global groups are searched in the order in which they appear in the value of the global_groups configuration parameter.

-

If Name is found, the message Msg is sent to +

If Name is found, the message Msg is sent to the corresponding pid. The pid is also the return value of the function. If the name is not found, the function returns - {badarg, {Name, Msg}}.

+ {badarg, {Name, Msg}}.

- sync() -> ok + Synchronize the group nodes

Synchronizes the group nodes, that is, the global name @@ -235,23 +224,17 @@ - whereis_name(Name) -> pid() | undefined - whereis_name(Where, Name) -> pid() | undefined + + Get the pid with a given globally registered name - - Where = {node, Node} | {group, GroupName} -  Node = node() -  GroupName = atom() - Name = atom() - -

Searches for Name, globally registered on +

Searches for Name, globally registered on the specified node or in the specified global group, or -- if - the Where argument is not provided -- in any global + the Where argument is not provided -- in any global group. The global groups are searched in the order in which they appear in the value of the global_groups configuration parameter.

-

If Name is found, the corresponding pid is returned. +

If Name is found, the corresponding pid is returned. If the name is not found, the function returns undefined.

diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 0df699572d..e2dbcbe63d 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -76,11 +76,8 @@ - set_cmd(Cmd) -> ok | {error, {bad_cmd, Cmd}} + Set a temporary reboot command - - Cmd = string() -

Sets a temporary reboot command. This command is used if a HEART_COMMAND other than the one specified with @@ -88,12 +85,12 @@ the system. The new Erlang runtime system will (if it misbehaves) use the environment variable HEART_COMMAND to reboot.

-

Limitations: The length of the Cmd command string +

Limitations: The length of the Cmd command string must be less than 2047 characters.

- clear_cmd() -> ok + Clear the temporary boot command

Clears the temporary boot command. If the system terminates, @@ -101,11 +98,8 @@ - get_cmd() -> {ok, Cmd} + Get the temporary reboot command - - Cmd = string() -

Get the temporary reboot command. If the command is cleared, the empty string will be returned.

diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index f05a224f33..fd843b00d9 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -4,7 +4,7 @@
- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -54,33 +54,6 @@ $ erl -sname test -kernel \ inet_default_listen_options '[{delay_send,true}]'

Note that the default option {active, true} currently cannot be changed, for internal reasons.

- - -
- DATA TYPES - -#hostent{h_addr_list = [ip_address()] % list of addresses for this host - h_addrtype = inet | inet6 - h_aliases = [hostname()] % list of aliases - h_length = int() % length of address in bytes - h_name = hostname() % official name for host - The record is defined in the Kernel include file "inet.hrl" - Add the following directive to the module: - -include_lib("kernel/include/inet.hrl"). - -hostname() = atom() | string() - -ip_address() = {N1,N2,N3,N4} % IPv4 - | {K1,K2,K3,K4,K5,K6,K7,K8} % IPv6 - Ni = 0..255 - Ki = 0..65535 - -posix() - an atom which is named from the Posix error codes used in - Unix, and in the runtime libraries of most C compilers - -socket() - see gen_tcp(3), gen_udp(3)

Addresses as inputs to functions can be either a string or a tuple. For instance, the IP address 150.236.20.73 can be passed to gethostbyaddr/1 either as the string "150.236.20.73" @@ -109,24 +82,58 @@ fe80::204:acff:fe17:bf38 {ok,{192,168,42,2}} 2> inet_parse:address("FFFF::192.168.42.2"). {ok,{65535,0,0,0,0,0,49320,10754}} -

+ + + + + + +

The record is defined in the Kernel include file "inet.hrl". + Add the following directive to the module:

+-include_lib("kernel/include/inet.hrl").
+
+ + + + + + + + + + + + + + +

An atom which is named from the Posix error codes + used in Unix, and in the runtime libraries of most + C compilers. See + POSIX Error Codes.

+
+
+ + socket() +

See gen_tcp(3) + and gen_udp(3).

+
+
+ + + +
+ - close(Socket) -> ok + Close a socket of any type - - Socket = socket() -

Closes a socket of any type.

- get_rc() -> [{Par, Val}] + Return a list of IP configuration parameters - - Par, Val -- see below -

Returns the state of the Inet configuration database in form of a list of recorded configuration parameters. (See the @@ -135,116 +142,74 @@ fe80::204:acff:fe17:bf38 - format_error(Posix) -> string() + Return a descriptive string for an error reason - - Posix = posix() -

Returns a diagnostic error string. See the section below - for possible Posix values and the corresponding + for possible Posix values and the corresponding strings.

- getaddr(Host, Family) -> {ok, Address} | {error, posix()} + Return the IP-address for a host - - Host = ip_address() | string() | atom() - Family = inet | inet6 - Address = ip_address() - posix() = term() - -

Returns the IP-address for Host as a tuple of - integers. Host can be an IP-address, a single hostname +

Returns the IP-address for Host as a tuple of + integers. Host can be an IP-address, a single hostname or a fully qualified hostname.

- getaddrs(Host, Family) -> {ok, Addresses} | {error, posix()} + Return the IP-addresses for a host - - Host = ip_address() | string() | atom() - Addresses = [ip_address()] - Family = inet | inet6 - -

Returns a list of all IP-addresses for Host. - Host can be an IP-address, a single hostname or a fully +

Returns a list of all IP-addresses for Host. + Host can be an IP-address, a single hostname or a fully qualified hostname.

- gethostbyaddr(Address) -> {ok, Hostent} | {error, posix()} + Return a hostent record for the host with the given address - - Address = string() | ip_address() - Hostent = #hostent{} -

Returns a hostent record given an address.

- gethostbyname(Name) -> {ok, Hostent} | {error, posix()} + Return a hostent record for the host with the given name - - Hostname = hostname() - Hostent = #hostent{} -

Returns a hostent record given a hostname.

- gethostbyname(Name, Family) -> {ok, Hostent} | {error, posix()} + Return a hostent record for the host with the given name - - Hostname = hostname() - Family = inet | inet6 - Hostent = #hostent{} -

Returns a hostent record given a hostname, restricted to the given address family.

- gethostname() -> {ok, Hostname} + Return the local hostname - - Hostname = string() -

Returns the local hostname. Will never fail.

- getifaddrs() -> {ok,Iflist} | {error,posix} + Return a list of interfaces and their addresses - - Iflist = {Ifname,[Ifopt]} - Ifname = string() - Ifopt = {flag,[Flag]} | {addr,Addr} | {netmask,Netmask} - | {broadaddr,Broadaddr} | {dstaddr,Dstaddr} - | {hwaddr,Hwaddr} - Flag = up | broadcast | loopback | pointtopoint - | running | multicast - Addr = Netmask = Broadadddr = Dstaddr = ip_address() - Hwaddr = [byte()] - -

Returns a list of 2-tuples containing interface names and the - interface's addresses. Ifname is a Unicode string. - Hwaddr is hardware dependent, e.g on Ethernet interfaces + interface's addresses. Ifname is a Unicode string. + Hwaddr is hardware dependent, e.g on Ethernet interfaces it is the 6-byte Ethernet address (MAC address (EUI-48 address)).

- The {addr,Addr}, {netmask,_} and {broadaddr,_} + The {addr,Addr}, {netmask,_} and {broadaddr,_} tuples are repeated in the result list iff the interface has multiple addresses. If you come across an interface that has multiple {flag,_} or {hwaddr,_} tuples you have @@ -252,8 +217,8 @@ fe80::204:acff:fe17:bf38 The {flag,_} tuple is mandatory, all other optional.

- Do not rely too much on the order of Flag atoms or - Ifopt tuples. There are some rules, though: + Do not rely too much on the order of Flag atoms or + Ifopt tuples. There are some rules, though: Immediately after {addr,_} follows {netmask,_} @@ -261,7 +226,7 @@ fe80::204:acff:fe17:bf38 Immediately thereafter follows {broadaddr,_} if the broadcast flag is not set and the - pointtopointflag is set. + pointtopoint flag is set. Any {netmask,_}, {broadaddr,_} or @@ -277,11 +242,12 @@ fe80::204:acff:fe17:bf38

On Windows, the data is fetched from quite different OS API - functions, so the Netmask and Broadaddr - values may be calculated, just as some Flag values. + functions, so the Netmask and Broadaddr + values may be calculated, just as some Flag values. You have been warned. Report flagrant bugs.

+
getopts(Socket, Options) -> {ok, OptionValues} | {error, posix()} @@ -291,13 +257,14 @@ fe80::204:acff:fe17:bf38 Options = [Opt | RawOptReq] Opt = atom() RawOptReq = {raw, Protocol, OptionNum, ValueSpec} - Protocol = int() - OptionNum = int() + Protocol = integer() + OptionNum = integer() ValueSpec = ValueSize | ValueBin - ValueSize = int() + ValueSize = integer() ValueBin = binary() OptionValues = [{Opt, Val} | {raw, Protocol, OptionNum, ValueBin}] +

Gets one or more options for a socket. See setopts/2 @@ -419,13 +386,8 @@ fe80::204:acff:fe17:bf38 - peername(Socket) -> {ok, {Address, Port}} | {error, posix()} + Return the address and port for the other end of a connection - - Socket = socket() - Address = ip_address() - Port = int() -

Returns the address and port for the other end of a connection.

@@ -436,20 +398,15 @@ fe80::204:acff:fe17:bf38 Return the local port number for a socket Socket = socket() - Port = int() + Port = integer()

Returns the local port number for a socket.

- sockname(Socket) -> {ok, {Address, Port}} | {error, posix()} + Return the local address and port number for a socket - - Socket = socket() - Address = ip_address() - Port = int() -

Returns the local address and port number for a socket.

@@ -460,8 +417,8 @@ fe80::204:acff:fe17:bf38 Socket = term() Options = [{Opt, Val} | {raw, Protocol, Option, ValueBin}] - Protocol = int() - OptionNum = int() + Protocol = integer() + OptionNum = integer() ValueBin = binary()  Opt, Val -- see below diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml index d8fe23544b..bf73ccf13d 100644 --- a/lib/kernel/doc/src/inet_res.xml +++ b/lib/kernel/doc/src/inet_res.xml @@ -4,7 +4,7 @@
- 20092009 + 20092011 Ericsson AB. All Rights Reserved. @@ -76,64 +76,38 @@ query is tried for the alt_nameservers.

- - - -
- DATA TYPES -

As defined in the module - inet:

- -hostent() = #hostent{} -posix() = some atom()s -ip_address() = tuple of integers of arity 4 or 8 - +

Resolver types:

- These correspond to resolver options: - -res_option() = - [ {alt_nameservers, [ nameserver() ]} - | {edns, 0 | false} % Use EDNS - | {inet6, bool()} % Return IPv6 addresses - | {nameservers, [ nameserver() ]} % List of nameservers - | {recurse, bool()} % Request server recursion - | {retry, integer()} % UDP retries - | {timeout, integer()} % UDP query timeout - | {udp_payload_size, integer()} % EDNS payload size - | {usevc, bool()} ] % Use TCP (Virtual Circuit) - -nameserver() = {ip_address(),Port} - Port = integer(1..65535) - -res_error() = - formerr | - qfmterror | - servfail | - nxdomain | - notimp | - refused | - badvers | - timeout - - -

DNS types:

- - dns_name() = string() with no adjacent dots - -rr_type() = a | aaaa | cname | gid | hinfo | ns | mb | md | mg | mf - | minfo | mx | naptr | null | ptr | soa | spf | srv | txt - | uid | uinfo | unspec | wks - -query_type() = axfr | mailb | maila | any | rr_type() - -dns_class() = in | chaos | hs | any - + + + + + + + + + + +

DNS types:

+ + +

A string with no adjacent dots.

+
+ + + + + + + + + +

This is the start of a hiearchy of opaque data structures + that can be examined with access functions in inet_dns that + return lists of {Field,Value} tuples. The arity 2 functions + just return the value for a given field. +

 dns_msg() = DnsMsg
-    This is the start of a hiearchy of opaque data structures
-    that can be examined with access functions in inet_dns
-    that return lists of {Field,Value} tuples. The arity 2
-    functions just return the value for a given field.
-
     inet_dns:msg(DnsMsg) ->
         [ {header, dns_header()}
         | {qdlist, dns_query()}
@@ -143,19 +117,21 @@ dns_msg() = DnsMsg
     inet_dns:msg(DnsMsg, header) -> dns_header() % for example
     inet_dns:msg(DnsMsg, Field) -> Value
 
-dhs_header() = DnsHeader
+dns_header() = DnsHeader
     inet_dns:header(DnsHeader) ->
         [ {id, integer()}
-        | {qr, bool()}
+        | {qr, boolean()}
         | {opcode, 'query' | iquery | status | integer()}
-        | {aa, bool()}
-        | {tc, bool()}
-        | {rd, bool()}
-        | {ra, bool()}
-        | {pr, bool()}
+        | {aa, boolean()}
+        | {tc, boolean()}
+        | {rd, boolean()}
+        | {ra, boolean()}
+        | {pr, boolean()}
         | {rcode, integer(0..16)} ]
     inet_dns:header(DnsHeader, Field) -> Value
 
+query_type() = axfr | mailb | maila | any | rr_type()
+
 dns_query() = DnsQuery
     inet_dns:dns_query(DnsQuery) ->
         [ {domain, dns_name()}
@@ -179,32 +155,6 @@ dns_rr() = DnsRr
                      | {data, dns_data()} ]
     inet_dns:rr(DnsRr, Field) -> Value
 
-dns_data() =             % for dns_type()
-    [ dns_name()         % ns, md, mf, cname, mb, mg, mr, ptr
-    | ip_address(v4)     % a
-    | ip_address(v6)     % aaaa
-    | {MName,RName,Serial,Refresh,Retry,Expiry,Minimum} % soa
-    | {ip_address(v4),Proto,BitMap} % wks
-    | {CpuString,OsString} % hinfo
-    | {RM,EM}            % minfo
-    | {Prio,dns_name()}  % mx
-    | {Prio,Weight,Port,dns_name()} % srv
-    | {Order,Preference,Flags,Services,Regexp,dns_name()} % naptr
-    | [ string() ]         % txt, spf
-    | binary() ]           % null, integer()
-MName, RName = dns_name()
-Serial, Refresh, Retry, Expiry, Minimum = integer(),
-Proto = integer()
-BitMap = binary()
-CpuString, OsString = string()
-RM = EM = dns_name()
-Prio, Weight, Port = integer()
-Order, Preference = integer()
-Flags, Services = string(),
-Regexp = string(utf8)
-
-
-
 There is an info function for the types above:
 
 inet_dns:record_type(dns_msg()) -> msg;
@@ -214,26 +164,25 @@ inet_dns:record_type(dns_rr()) -> rr;
 inet_dns:record_type(_) -> undefined.
 
 So; inet_dns:(inet_dns:record_type(X))(X) will convert
-any of these data structures into a {Field,Value} list.
-  
- +any of these data structures into a {Field,Value} list.

+ + + + +

Regexp is a string with characters encoded in the + UTF-8 coding standard.

+
+
+ - getbyname(Name, Type) -> {ok,hostent()} | {error,Reason} - getbyname(Name, Type, Timeout) -> - {ok,hostent()} | {error,Reason} - + + Resolve a DNS record of the given type for the given host - - Name = dns_name() - Type = rr_type() - Timeout = integer() >= 0 | infinity - Reason = posix() | res_error() -

Resolve a DNS record of the given type for the given host, of class in. On success returns a hostent() record with @@ -252,17 +201,10 @@ any of these data structures into a {Field,Value} list. - gethostbyaddr(Address) -> {ok,hostent()} | {error,Reason} - gethostbyaddr(Address, Timeout) -> - {ok,hostent()} | {error,Reason} - + + Return a hostent record for the host with the given address - - Address = ip_address() - Timeout = integer() >= 0 | infinity - Reason = posix() | res_error() -

Backend functions used by @@ -273,20 +215,11 @@ any of these data structures into a {Field,Value} list. - gethostbyname(Name) -> {ok,hostent()} | Reason} - gethostbyname(Name, Family) -> - {ok,hostent()} | {error,Reason}} - - gethostbyname(Name, Family, Timeout) -> - {ok,hostent()} | {error,Reason} - + + + Return a hostent record for the host with the given name - - Name = dns_name() - Timeout = integer() >= 0 | infinity - Reason = posix() | res_error() -

Backend functions used by @@ -305,26 +238,16 @@ any of these data structures into a {Field,Value} list. - lookup(Name, Class, Type) -> [ dns_data() ] - - lookup(Name, Class, Type, Opts) -> [ dns_data() ] - - lookup(Name, Class, Type, Opts, Timeout) -> [ dns_data() ] - + + + Resolve the DNS data for the record of the given type and class for the given name - - Name = dns_name() | ip_address() - Type = rr_type() - Opts = res_option() | verbose - Timeout = integer() >= 0 | infinity - Reason = posix() | res_error() -

Resolve the DNS data for the record of the given type and class for the given name. On success filters out the answer records - with the correct Class and Type and returns + with the correct Class and Type and returns a list of their data fields. So a lookup for type any will give an empty answer since the answer records have specific types that are not any. An empty answer @@ -332,44 +255,33 @@ any of these data structures into a {Field,Value} list.

Calls resolve/2..4 with the same arguments and filters the result, so - Opts is explained there. + Opts is explained there.

- resolve(Name, Class, Type) -> {ok,dns_msg()} | Error - - resolve(Name, Class, Type, Opts) -> {ok,dns_msg()} | Error - - resolve(Name, Class, Type, Opts, Timeout) -> {ok,dns_msg()} | Error - + + + Resolve a DNS record of the given type and class for the given name - - Name = dns_name() | ip_address() - Type = rr_type() - Opts = res_option() | verbose | atom() - Timeout = integer() >= 0 | infinity - Error = {error,Reason} | {error,{Reason,dns_msg()}} - Reason = posix() | res_error() -

Resolve a DNS record of the given type and class for the given name. The returned dns_msg() can be examined using access functions in inet_db as described in DNS types.

- If Name is an ip_address(), the domain name + If Name is an ip_address(), the domain name to query for is generated as the standard reverse ".IN-ADDR.ARPA." name for an IPv4 address, or the ".IP6.ARPA." name for an IPv6 address. In this case you most probably want to use - Class = in and Type = ptr but it + Class = in and Type = ptr but it is not done automatically.

- Opts override the corresponding resolver options. + Opts override the corresponding resolver options. If the option nameservers is given, it is also assumed that it is the complete list of nameserves, so the resolver option alt_nameserves is ignored. @@ -382,14 +294,14 @@ any of these data structures into a {Field,Value} list. of queries, replies retransmissions, etc, similar to from utilities like dig, nslookup et.al.

- If Opt is an arbitrary atom it is interpreted - as {Opt,true} unless the atom string starts with - "no" making the interpretation {Opt,false}. + If Opt is an arbitrary atom it is interpreted + as {Opt,true} unless the atom string starts with + "no" making the interpretation {Opt,false}. For example: usevc is an alias for {usevc,true}, and nousevc an alias for {usevc,false}.

The inet6 option currently has no effect on this function. - You probably want to use Type = a | aaaa instead. + You probably want to use Type = a | aaaa instead.

@@ -430,24 +342,18 @@ any of these data structures into a {Field,Value} list. - nslookup(Name, Class, Type) -> {ok,dns_msg()} | {error,Reason} - - nslookup(Name, Class, Type, Timeout) -> - {ok,dns_msg()} | {error,Reason} - - nslookup(Name, Class, Type, Nameservers) -> - {ok,dns_msg()} | {error,Reason} - + + + Resolve a DNS record of the given type and class for the given name - - Name = dns_name() | ip_address() - Type = rr_type() - Nameservers = [ nameserver() ] - Timeout = integer() >= 0 | infinity - Reason = posix() | res_error() - + + + + + +

Resolve a DNS record of the given type and class for the given name.

@@ -455,22 +361,11 @@ any of these data structures into a {Field,Value} list.
- nnslookup(Name, Class, Type, Nameservers) -> - {ok,dns_msg()} | {error,posix()} - - nnslookup(Name, Class, Type, Nameservers, Timeout) -> - {ok,dns_msg()} | {error,posix()} - + + Resolve a DNS record of the given type and class for the given name - - Name = dns_name() | ip_address() - Type = rr_type() - Nameservers = [ nameserver() ] - Timeout = integer() >= 0 | infinity - Reason = posix() | res_error() -

Resolve a DNS record of the given type and class for the given name.

diff --git a/lib/kernel/doc/src/net_adm.xml b/lib/kernel/doc/src/net_adm.xml index 7ec4f7f0e7..f2aac9282c 100644 --- a/lib/kernel/doc/src/net_adm.xml +++ b/lib/kernel/doc/src/net_adm.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -34,94 +34,70 @@ - dns_hostname(Host) -> {ok, Name} | {error, Host} + Official name of a host - - Host = atom() | string() - Name = string() - -

Returns the official name of Host, or - {error, Host} if no such name is found. See also +

Returns the official name of Host, or + {error, Host} if no such name is found. See also inet(3).

- host_file() -> Hosts | {error, Reason} + Read the .hosts.erlangfile - - Hosts = [Host] -  Host = atom() - Reason = term() -

Reads the .hosts.erlang file, see the section Files below. Returns the hosts in this file as a - list, or returns {error, Reason} if the file could not - be read. See file(3) for possible values of - Reason.

+ list, or returns {error, Reason} if the file could not + be read or the Erlang terms on the file could not be interpreted.

- localhost() -> Name + Name of the local host - - Name = string() -

Returns the name of the local host. If Erlang was started - with the -name command line flag, Name is + with the -name command line flag, Name is the fully qualified name.

- names() -> {ok, [{Name, Port}]} | {error, Reason} - names(Host) -> {ok, [{Name, Port}]} | {error, Reason} + + Names of Erlang nodes at a host - - Name = string() - Port = int() - Reason = address | term() -

Similar to epmd -names, see epmd(1). - Host defaults to the local host. Returns the names and + Host defaults to the local host. Returns the names and associated port numbers of the Erlang nodes that epmd at the specified host has registered.

Returns {error, address} if epmd is not - running. See inet(3) for other possible values of - Reason.

+ running.

 (arne@dunn)1> net_adm:names().
 {ok,[{"arne",40262}]}
- ping(Node) -> pong | pang + Set up a connection to a node - - Node = node() - -

Tries to set up a connection to Node. Returns +

Tries to set up a connection to Node. Returns pang if it fails, or pong if it is successful.

- world() -> [node()] - world(Arg) -> [node()] + + + Lookup and connect to all nodes at all hosts in .hosts.erlang - - Arg = silent | verbose -

This function calls names(Host) for all hosts which are specified in the Erlang host file .hosts.erlang, collects the replies and then evaluates ping(Node) on all those nodes. Returns the list of all nodes that were, successfully pinged.

-

Arg defaults to silent. - If Arg == verbose, the function writes information about which +

Arg defaults to silent. + If Arg == verbose, the function writes information about which nodes it is pinging to stdout.

This function can be useful when a node is started, and the names of the other nodes in the network are not initially @@ -131,14 +107,10 @@ - world_list(Hosts) -> [node()] - world_list(Hosts, Arg) -> [node()] + + + Lookup and connect to all nodes at specified hosts - - Hosts = [Host] -  Host = atom() - Arg = silent | verbose -

As world/0,1, but the hosts are given as argument instead of being read from .hosts.erlang.

diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index a18226e779..96e2aa665d 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -64,50 +64,38 @@ $ erl -sname foobar - allow(Nodes) -> ok | error + Limit access to a specified set of nodes - - Nodes = [node()] -

Limits access to the specified set of nodes. Any access - attempts made from (or to) nodes not in Nodes will be + attempts made from (or to) nodes not in Nodes will be rejected.

-

Returns error if any element in Nodes is not +

Returns error if any element in Nodes is not an atom.

- connect_node(Node) -> true | false | ignored + Establish a connection to a node - - Node = node() - -

Establishes a connection to Node. Returns true +

Establishes a connection to Node. Returns true if successful, false if not, and ignored if the local node is not alive.

- monitor_nodes(Flag) -> ok | Error - monitor_nodes(Flag, Options) -> ok | Error + + Subscribe to node status change messages - - Flag = true | false - Options = [Option] -  Option -- see below - Error = error | {error, term()} -

The calling process subscribes or unsubscribes to node status change messages. A nodeup message is delivered to all subscribing process when a new node is connected, and a nodedown message is delivered when a node is disconnected.

-

If Flag is true, a new subscription is started. - If Flag is false, all previous subscriptions -- - started with the same Options -- are stopped. Two +

If Flag is true, a new subscription is started. + If Flag is false, all previous subscriptions -- + started with the same Options -- are stopped. Two option lists are considered the same if they contain the same set of options.

As of kernel version 2.11.4, and erts version @@ -139,23 +127,23 @@ $ erl -sname foobar

Note, that this is not guaranteed for kernel versions before 2.13.

The format of the node status change messages depends on - Options. If Options is [], which is the default, + Options. If Options is [], which is the default, the format is:

{nodeup, Node} | {nodedown, Node} Node = node() -

If Options /= [], the format is:

+

If Options /= [], the format is:

{nodeup, Node, InfoList} | {nodedown, Node, InfoList} Node = node() InfoList = [{Tag, Val}]

InfoList is a list of tuples. Its contents depends on - Options, see below.

+ Options, see below.

Also, when OptionList == [] only visible nodes, that is, nodes that appear in the result of nodes/0, are monitored.

-

Option can be any of the following:

+

Option can be any of the following:

{node_type, NodeType} @@ -209,61 +197,51 @@ $ erl -sname foobar
- get_net_ticktime() -> Res + Get net_ticktime - - Res = NetTicktime | {ongoing_change_to, NetTicktime} -  NetTicktime = int() -

Gets net_ticktime (see kernel(6)).

-

Currently defined return values (Res):

+

Currently defined return values (Res):

- NetTicktime + NetTicktime -

net_ticktime is NetTicktime seconds.

+

net_ticktime is NetTicktime seconds.

- {ongoing_change_to, NetTicktime} + {ongoing_change_to, NetTicktime}

net_kernel is currently changing - net_ticktime to NetTicktime seconds.

+ net_ticktime to NetTicktime seconds.

- set_net_ticktime(NetTicktime) -> Res - set_net_ticktime(NetTicktime, TransitionPeriod) -> Res + + Set net_ticktime - - NetTicktime = int() > 0 - TransitionPeriod = int() >= 0 - Res = unchanged | change_initiated | {ongoing_change_to, NewNetTicktime} -  NewNetTicktime = int() > 0 -

Sets net_ticktime (see kernel(6)) to - NetTicktime seconds. TransitionPeriod defaults + NetTicktime seconds. TransitionPeriod defaults to 60.

Some definitions:

The minimum transition traffic interval (MTTI) -

minimum(NetTicktime, PreviousNetTicktime)*1000 div 4 milliseconds.

+

minimum(NetTicktime, PreviousNetTicktime)*1000 div 4 milliseconds.

The transition period

The time of the least number of consecutive MTTIs - to cover TransitionPeriod seconds following + to cover TransitionPeriod seconds following the call to set_net_ticktime/2 (i.e. - ((TransitionPeriod*1000 - 1) div MTTI + 1)*MTTI + ((TransitionPeriod*1000 - 1) div MTTI + 1)*MTTI milliseconds).

-

If , the actual +

If NetTicktime < PreviousNetTicktime]]>, the actual net_ticktime change will be done at the end of the transition period; otherwise, at the beginning. During the transition period, net_kernel will ensure that @@ -271,7 +249,7 @@ $ erl -sname foobar every MTTI millisecond.

The net_ticktime changes have to be initiated on all - nodes in the network (with the same NetTicktime) + nodes in the network (with the same NetTicktime) before the end of any transition period on any node; otherwise, connections may erroneously be disconnected.

@@ -280,18 +258,18 @@ $ erl -sname foobar unchanged

net_ticktime already had the value of - NetTicktime and was left unchanged.

+ NetTicktime and was left unchanged.

change_initiated

net_kernel has initiated the change of - net_ticktime to NetTicktime seconds.

+ net_ticktime to NetTicktime seconds.

- {ongoing_change_to, NewNetTicktime} + {ongoing_change_to, NewNetTicktime}

The request was ignored; because, net_kernel was busy changing net_ticktime to - NewTicktime seconds.

+ NewNetTicktime seconds.

@@ -315,7 +293,7 @@ $ erl -sname foobar
- stop() -> ok | {error, not_allowed | not_found} + Turn a node into a non-distributed Erlang runtime system

Turns a distributed node into a non-distributed node. For diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 2c9cc33eb7..56fc1834ec 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -4,7 +4,7 @@

- 19972009 + 19972011 Ericsson AB. All Rights Reserved. @@ -38,13 +38,10 @@ - cmd(Command) -> string() + Execute a command in a shell of the target OS - - Command = string() | atom() - -

Executes Command in a command shell of the target OS, +

Executes Command in a command shell of the target OS, captures the standard output of the command and returns this result as a string. This function is a replacement of the previous unix:cmd/1; on a Unix platform they are @@ -60,23 +57,18 @@ DirOut = os:cmd("dir"), % on Win32 platform - find_executable(Name) -> Filename | false - find_executable(Name, Path) -> Filename | false + + Absolute filename of a program - - Name = string() - Path = string() - Filename = string() -

These two functions look up an executable program given its name and a search path, in the same way as the underlying operating system. find_executable/1 uses the current execution path (that is, the environment variable PATH on Unix and Windows).

-

Path, if given, should conform to the syntax of +

Path, if given, should conform to the syntax of execution paths on the operating system. The absolute - filename of the executable program Name is returned, + filename of the executable program Name is returned, or false if the program was not found.

@@ -137,7 +129,7 @@ DirOut = os:cmd("dir"), % on Win32 platform timestamp() -> {MegaSecs, Secs, MicroSecs} Returna a timestamp from the OS in the erlang:now/0 format - MegaSecs = Secs = MicroSecs = int() + MegaSecs = Secs = MicroSecs = integer() >= 0

Returns a tuple in the same format as erlang:now/0. The difference is that this function returns what the operating system thinks (a.k.a. the wall clock time) without any attempts at time correction. The result of two different calls to this function is not guaranteed to be different.

@@ -165,19 +157,15 @@ format_utc_timestamp() ->
- type() -> {Osfamily, Osname} | Osfamily + Return the OS family and, in some cases, OS name of the current operating system - - Osfamily = win32 | unix | vxworks - Osname = atom() - -

Returns the Osfamily and, in some cases, Osname +

Returns the Osfamily and, in some cases, Osname of the current operating system.

-

On Unix, Osname will have same value as +

On Unix, Osname will have same value as uname -s returns, but in lower case. For example, on Solaris 1 and 2, it will be sunos.

-

In Windows, Osname will be either nt (on +

In Windows, Osname will be either nt (on Windows NT), or windows (on Windows 95).

On VxWorks the OS family alone is returned, that is vxworks.

@@ -185,17 +173,13 @@ format_utc_timestamp() ->

Think twice before using this function. Use the filename module if you want to inspect or build file names in a portable way. - Avoid matching on the Osname atom.

+ Avoid matching on the Osname atom.

- version() -> {Major, Minor, Release} | VersionString + Return the Operating System version - - Major = Minor = Release = integer() - VersionString = string() -

Returns the operating system version. On most systems, this function returns a tuple, but a string diff --git a/lib/kernel/doc/src/pg2.xml b/lib/kernel/doc/src/pg2.xml index 7463fd10f5..d26ff0fc6b 100644 --- a/lib/kernel/doc/src/pg2.xml +++ b/lib/kernel/doc/src/pg2.xml @@ -4,7 +4,7 @@

- 19972009 + 19972011 Ericsson AB. All Rights Reserved. @@ -60,13 +60,16 @@ to avoid name clashes.

+ + + +

The name of a process group.

+
+
- create(Name) -> void() + Create a new, empty process group - - Name = term() -

Creates a new, empty process group. The group is globally visible on all nodes. If the group exists, nothing happens. @@ -74,24 +77,16 @@ - delete(Name) -> void() + Delete a process group - - Name = term() -

Deletes a process group.

- get_closest_pid(Name) -> Pid | {error, Reason} + Common dispatch function - - Name = term() - Pid = pid() - Reason = {no_process, Name} | {no_such_group, Name} -

This is a useful dispatch function which can be used from client functions. It returns a process on the local node, if @@ -100,13 +95,8 @@ - get_members(Name) -> [Pid] | {error, Reason} + Return all processes in a group - - Name = term() - Pid = pid() - Reason = {no_such_group, Name} -

Returns all processes in the group Name. This function should be used from within a client function that @@ -115,13 +105,8 @@ - get_local_members(Name) -> [Pid] | {error, Reason} + Return all local processes in a group - - Name = term() - Pid = pid() - Reason = {no_such_group, Name} -

Returns all processes running on the local node in the group Name. This function should to be used from @@ -131,13 +116,8 @@ - join(Name, Pid) -> ok | {error, Reason} + Join a process to a group - - Name = term() - Pid = pid() - Reason = {no_such_group, Name} -

Joins the process Pid to the group Name. A process can join a group several times; it must then @@ -146,13 +126,8 @@ - leave(Name, Pid) -> ok | {error, Reason} + Make a process leave a group - - Name = term() - Pid = pid() - Reason = {no_such_group, Name} -

Makes the process Pid leave the group Name. If the process is not a member of the group, ok is @@ -161,24 +136,17 @@ - which_groups() -> [Name] + Return a list of all known groups - - Name = term() -

Returns a list of all known groups.

- start() - start_link() -> {ok, Pid} | {error, Reason} + + Start the pg2 server - - Pid = pid() - Reason = term() -

Starts the pg2 server. Normally, the server does not need to be started explicitly, as it is started dynamically if it diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index 2b81de170d..b01ff16c85 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -4,7 +4,7 @@

- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -37,40 +37,34 @@ for collecting information on a remote node, or for running a function with some specific side effects on the remote node.

+ + + + +

As returned by + async_call/4.

+
+
+
- call(Node, Module, Function, Args) -> Res | {badrpc, Reason} + Evaluate a function call on a node - - Node = node() - Module = Function = atom() - Args = [term()] - Res = term() - Reason = term() - -

Evaluates apply(Module, Function, Args) on the node - Node and returns the corresponding value Res, or - {badrpc, Reason} if the call fails.

+

Evaluates apply(Module, Function, Args) on the node + Node and returns the corresponding value Res, or + {badrpc, Reason} if the call fails.

- call(Node, Module, Function, Args, Timeout) -> Res | {badrpc, Reason} + Evaluate a function call on a node - - Node = node() - Module = Function = atom() - Args = [term()] - Res = term() - Reason = timeout | term() - Timeout = int() | infinity - -

Evaluates apply(Module, Function, Args) on the node - Node and returns the corresponding value Res, or - {badrpc, Reason} if the call fails. Timeout is +

Evaluates apply(Module, Function, Args) on the node + Node and returns the corresponding value Res, or + {badrpc, Reason} if the call fails. Timeout is a timeout value in milliseconds. If the call times out, - Reason is timeout.

+ Reason is timeout.

If the reply arrives after the call times out, no message will contaminate the caller's message queue, since this function spawns off a middleman process to act as (a void) @@ -80,17 +74,10 @@ - block_call(Node, Module, Function, Args) -> Res | {badrpc, Reason} + Evaluate a function call on a node in the RPC server's context - - Node = node() - Module = Function = atom() - Args = [term()] - Res = term() - Reason = term() - -

Like call/4, but the RPC server at Node does +

Like call/4, but the RPC server at Node does not create a separate process to handle the call. Thus, this function can be used if the intention of the call is to block the RPC server from any other incoming requests until @@ -101,50 +88,31 @@ - block_call(Node, Module, Function, Args, Timeout) -> Res | {badrpc, Reason} + Evaluate a function call on a node in the RPC server's context - - Node = node() - Module = Function = atom() - Args = [term()] - Timeout = int() | infinity - Res = term() - Reason = term() -

Like block_call/4, but with a timeout value in the same manner as call/5.

- async_call(Node, Module, Function, Args) -> Key + Evaluate a function call on a node, asynchronous version - - Node = node() - Module = Function = atom() - Args = [term()] - Key -- see below -

Implements call streams with promises, a type of RPC which does not suspend the caller until the result is finished. Instead, a key is returned which can be used at a later stage to collect the value. The key can be viewed as a promise to deliver the answer.

-

In this case, the key Key is returned, which can be +

In this case, the key Key is returned, which can be used in a subsequent call to yield/1 or nb_yield/1,2 to retrieve the value of evaluating - apply(Module, Function, Args) on the node Node.

+ apply(Module, Function, Args) on the node Node.

- yield(Key) -> Res | {badrpc, Reason} + Deliver the result of evaluating a function call on a node (blocking) - - Key -- see async_call/4 - Res = term() - Reason = term() -

Returns the promised answer from a previous async_call/4. If the answer is available, it is @@ -153,87 +121,46 @@ - nb_yield(Key) -> {value, Val} | timeout + Deliver the result of evaluating a function call on a node (non-blocking) - - Key -- see async_call/4 - Val = Res | {badrpc, Reason} -  Res = term() -  Reason = term() - -

Equivalent to nb_yield(Key, 0).

+

Equivalent to nb_yield(Key, 0).

- nb_yield(Key, Timeout) -> {value, Val} | timeout + Deliver the result of evaluating a function call on a node (non-blocking) - - Key -- see async_call/4 - Timeout = int() | infinity - Val = Res | {badrpc, Reason} -  Res = term() -  Reason = term() -

This is a non-blocking version of yield/1. It returns - the tuple {value, Val} when the computation has - finished, or timeout when Timeout milliseconds + the tuple {value, Val} when the computation has + finished, or timeout when Timeout milliseconds has elapsed.

- multicall(Module, Function, Args) -> {ResL, BadNodes} + Evaluate a function call on a number of nodes - - Module = Function = atom() - Args = [term()] - ResL = [term()] - BadNodes = [node()] - -

Equivalent to multicall([node()|nodes()], Module, Function, Args, infinity).

+

Equivalent to multicall([node()|nodes()], Module, Function, Args, infinity).

- multicall(Nodes, Module, Function, Args) -> {ResL, BadNodes} + Evaluate a function call on a number of nodes - - Nodes = [node()] - Module = Function = atom() - Args = [term()] - ResL = [term()] - BadNodes = [node()] - -

Equivalent to multicall(Nodes, Module, Function, Args, infinity).

+

Equivalent to multicall(Nodes, Module, Function, Args, infinity).

- multicall(Module, Function, Args, Timeout) -> {ResL, BadNodes} + Evaluate a function call on a number of nodes - - Module = Function = atom() - Args = [term()] - Timeout = int() | infinity - ResL = [term()] - BadNodes = [node()] - -

Equivalent to multicall([node()|nodes()], Module, Function, Args, Timeout).

+

Equivalent to multicall([node()|nodes()], Module, Function, Args, Timeout).

- multicall(Nodes, Module, Function, Args, Timeout) -> {ResL, BadNodes} + Evaluate a function call on a number of nodes - - Nodes = [node()] - Module = Function = atom() - Args = [term()] - Timeout = int() | infinity - ResL = [term()] - BadNodes = [node()] -

In contrast to an RPC, a multicall is an RPC which is sent concurrently from one client to multiple servers. This is @@ -243,12 +170,12 @@ making a series of RPCs on all the nodes, but the multicall is faster as all the requests are sent at the same time and are collected one by one as they come back.

-

The function evaluates apply(Module, Function, Args) +

The function evaluates apply(Module, Function, Args) on the specified nodes and collects the answers. It returns - {ResL, Badnodes}, where Badnodes is a list + {ResL, BadNodes}, where BadNodes is a list of the nodes that terminated or timed out during computation, - and ResL is a list of the return values. - Timeout is a time (integer) in milliseconds, or + and ResL is a list of the return values. + Timeout is a time (integer) in milliseconds, or infinity.

The following example is useful when new object code is to be loaded on all nodes in the network, and also indicates @@ -264,93 +191,60 @@ - cast(Node, Module, Function, Args) -> void() + Run a function on a node ignoring the result - - Node = node() - Module = Function = atom() - Args = [term()] - -

Evaluates apply(Module, Function, Args) on the node - Node. No response is delivered and the calling +

Evaluates apply(Module, Function, Args) on the node + Node. No response is delivered and the calling process is not suspended until the evaluation is complete, as is the case with call/4,5.

- eval_everywhere(Module, Funtion, Args) -> void() + Run a function on all nodes, ignoring the result - - Module = Function = atom() - Args = [term()] - -

Equivalent to eval_everywhere([node()|nodes()], Module, Function, Args).

+

Equivalent to eval_everywhere([node()|nodes()], Module, Function, Args).

- eval_everywhere(Nodes, Module, Function, Args) -> void() + Run a function on specific nodes, ignoring the result - - Nodes = [node()] - Module = Function = atom() - Args = [term()] - -

Evaluates apply(Module, Function, Args) on +

Evaluates apply(Module, Function, Args) on the specified nodes. No answers are collected.

- abcast(Name, Msg) -> void() + Broadcast a message asynchronously to a registered process on all nodes - - Name = atom() - Msg = term() - -

Equivalent to abcast([node()|nodes()], Name, Msg).

+

Equivalent to abcast([node()|nodes()], Name, Msg).

- abcast(Nodes, Name, Msg) -> void() + Broadcast a message asynchronously to a registered process on specific nodes - - Nodes = [node()] - Name = atom() - Msg = term() - -

Broadcasts the message Msg asynchronously to - the registered process Name on the specified nodes.

+

Broadcasts the message Msg asynchronously to + the registered process Name on the specified nodes.

- sbcast(Name, Msg) -> {GoodNodes, BadNodes} + Broadcast a message synchronously to a registered process on all nodes - - Name = atom() - Msg = term() - GoodNodes = BadNodes = [node()] - -

Equivalent to sbcast([node()|nodes()], Name, Msg).

+

Equivalent to sbcast([node()|nodes()], Name, Msg).

- sbcast(Nodes, Name, Msg) -> {GoodNodes, BadNodes} + Broadcast a message synchronously to a registered process on specific nodes - - Name = atom() - Msg = term() - Nodes = GoodNodes = BadNodes = [node()] - -

Broadcasts the message Msg synchronously to - the registered process Name on the specified nodes.

-

Returns {GoodNodes, BadNodes}, where GoodNodes - is the list of nodes which have Name as a registered +

Broadcasts the message Msg synchronously to + the registered process Name on the specified nodes.

+

Returns {GoodNodes, BadNodes}, where GoodNodes + is the list of nodes which have Name as a registered process.

The function is synchronous in the sense that it is known that all servers have received the message when the call @@ -362,67 +256,46 @@ - server_call(Node, Name, ReplyWrapper, Msg) -> Reply | {error, Reason} + Interact with a server on a node - - Node = node() - Name = atom() - ReplyWrapper = Msg = Reply = term() - Reason = term() -

This function can be used when interacting with a server - called Name at node Node. It is assumed that + called Name at node Node. It is assumed that the server receives messages in the format - {From, Msg} and replies using From ! {ReplyWrapper, Node, Reply}. This function makes such + {From, Msg} and replies using From ! {ReplyWrapper, Node, Reply}. This function makes such a server call and ensures that the entire call is packed into an atomic transaction which either succeeds or fails. It never hangs, unless the server itself hangs.

-

The function returns the answer Reply as produced by - the server Name, or {error, Reason}.

+

The function returns the answer Reply as produced by + the server Name, or {error, Reason}.

- multi_server_call(Name, Msg) -> {Replies, BadNodes} + Interact with the servers on a number of nodes - - Name = atom() - Msg = term() - Replies = [Reply] -  Reply = term() - BadNodes = [node()] - -

Equivalent to multi_server_call([node()|nodes()], Name, Msg).

+

Equivalent to multi_server_call([node()|nodes()], Name, Msg).

- multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes} + Interact with the servers on a number of nodes - - Nodes = [node()] - Name = atom() - Msg = term() - Replies = [Reply] -  Reply = term() - BadNodes = [node()] -

This function can be used when interacting with servers - called Name on the specified nodes. It is assumed that - the servers receive messages in the format {From, Msg} - and reply using From ! {Name, Node, Reply}, where + called Name on the specified nodes. It is assumed that + the servers receive messages in the format {From, Msg} + and reply using From ! {Name, Node, Reply}, where Node is the name of the node where the server is - located. The function returns {Replies, Badnodes}, - where Replies is a list of all Reply values and - BadNodes is a list of the nodes which did not exist, or + located. The function returns {Replies, BadNodes}, + where Replies is a list of all Reply values and + BadNodes is a list of the nodes which did not exist, or where the server did not exist, or where the server terminated before sending any reply.

- safe_multi_server_call(Name, Msg) -> {Replies, BadNodes} - safe_multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes} + + Interact with the servers on a number of nodes (deprecated) @@ -432,66 +305,47 @@

In Erlang/OTP R6B and earlier releases, multi_server_call/2,3 could not handle the case where the remote node exists, but there is no server called - Name. Instead this function had to be used. In + Name. Instead this function had to be used. In Erlang/OTP R7B and later releases, however, the functions are equivalent, except for this function being slightly slower.

- parallel_eval(FuncCalls) -> ResL + Evaluate several function calls on all nodes in parallel - - FuncCalls = [{Module, Function, Args}] -  Module = Function = atom() -  Args = [term()] - ResL = [term()] - -

For every tuple in FuncCalls, evaluates - apply(Module, Function, Args) on some node in +

For every tuple in FuncCalls, evaluates + apply(Module, Function, Args) on some node in the network. Returns the list of return values, in the same - order as in FuncCalls.

+ order as in FuncCalls.

- pmap({Module, Function}, ExtraArgs, List1) -> List2 + Parallell evaluation of mapping a function over a list - - Module = Function = atom() - ExtraArgs = [term()] - List1 = [Elem] -  Elem = term() - List2 = [term()] - -

Evaluates apply(Module, Function, [Elem|ExtraArgs]), - for every element Elem in List1, in parallel. +

Evaluates apply(Module, Function, [Elem|ExtraArgs]), + for every element Elem in List1, in parallel. Returns the list of return values, in the same order as in - List1.

+ List1.

- pinfo(Pid) -> [{Item, Info}] | undefined + Information about a process - - Pid = pid() - Item, Info -- see erlang:process_info/1 -

Location transparent version of the BIF - process_info/1.

+ + process_info/1.

- pinfo(Pid, Item) -> {Item, Info} | undefined | [] + Information about a process - - Pid = pid() - Item, Info -- see erlang:process_info/1 -

Location transparent version of the BIF - process_info/2.

+ + process_info/2.

diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index 6c043dd767..1ab955bd8a 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -4,7 +4,7 @@
- 19982009 + 19982011 Ericsson AB. All Rights Reserved. @@ -47,17 +47,22 @@ from users.

+ + + + +

An opaque term (a tuple) representing a trace token.

+
+
+
- set_token(Token) -> PreviousToken + Set the trace token - - Token = PreviousToken = term() | [] - -

Sets the trace token for the calling process to Token. - If Token == [] then tracing is disabled, otherwise - Token should be an Erlang term returned from +

Sets the trace token for the calling process to Token. + If Token == [] then tracing is disabled, otherwise + Token should be an Erlang term returned from get_token/0 or set_token/1. set_token/1 can be used to temporarily exclude message passing from the trace by setting the trace token to empty like this:

@@ -72,18 +77,16 @@ seq_trace:set_token(OldToken), % activate the trace token again
- set_token(Component, Val) -> {Component, OldVal} + Set a component of the trace token - - Component = label | serial | Flag -  Flag = send | 'receive' | print | timestamp - Val = OldVal -- see below - + + + -

Sets the individual Component of the trace token to - Val. Returns the previous value of the component.

+

Sets the individual Component of the trace token to + Val. Returns the previous value of the component.

- set_token(label, Int) + set_token(label, Integer)

The label component is an integer which identifies all events belonging to the same sequential @@ -93,31 +96,31 @@ seq_trace:set_token(OldToken), % activate the trace token again set_token(serial, SerialValue) -

SerialValue = {Previous, Current}. +

SerialValue = {Previous, Current}. The serial component contains counters which enables the traced messages to be sorted, should never be set explicitly by the user as these counters are updated automatically. Default is {0, 0}.

- set_token(send, Bool) + set_token(send, Bool)

A trace token flag (true | false) which enables/disables tracing on message sending. Default is false.

- set_token('receive', Bool) + set_token('receive', Bool)

A trace token flag (true | false) which enables/disables tracing on message reception. Default is false.

- set_token(print, Bool) + set_token(print, Bool)

A trace token flag (true | false) which enables/disables tracing on explicit calls to seq_trace:print/1. Default is false.

- set_token(timestamp, Bool) + set_token(timestamp, Bool)

A trace token flag (true | false) which enables/disables a timestamp to be generated for each @@ -127,11 +130,8 @@ seq_trace:set_token(OldToken), % activate the trace token again - get_token() -> TraceToken + Return the value of the trace token - - TraceToken = term() | [] -

Returns the value of the trace token for the calling process. If [] is returned, it means that tracing is not active. @@ -141,13 +141,11 @@ seq_trace:set_token(OldToken), % activate the trace token again - get_token(Component) -> {Component, Val} + Return the value of a trace token component - - Component = label | serial | Flag -  Flag = send | 'receive' | print | timestamp - Val -- see set_token/2 - + + +

Returns the value of the trace token component Component. See @@ -156,33 +154,26 @@ seq_trace:set_token(OldToken), % activate the trace token again - print(TraceInfo) -> void() + Put the Erlang term TraceInfointo the sequential trace output - - TraceInfo = term() - -

Puts the Erlang term TraceInfo into the sequential +

Puts the Erlang term TraceInfo into the sequential trace output if the calling process currently is executing within a sequential trace and the print flag of the trace token is set.

- print(Label, TraceInfo) -> void() + Put the Erlang term TraceInfointo the sequential trace output - - Label = int() - TraceInfo = term() -

Same as print/1 with the additional condition that - TraceInfo is output only if Label is equal to + TraceInfo is output only if Label is equal to the label component of the trace token.

- reset_trace() -> void() + Stop all sequential tracing on the local node

Sets the trace token to empty for all processes on the @@ -194,26 +185,22 @@ seq_trace:set_token(OldToken), % activate the trace token again - set_system_tracer(Tracer) -> OldTracer + Set the system tracer - - Tracer = OldTracer = pid() | port() | false - +

Sets the system tracer. The system tracer can be either a - process or port denoted by Tracer. Returns the previous + process or port denoted by Tracer. Returns the previous value (which can be false if no system tracer is active).

-

Failure: {badarg, Info}} if Pid is not an +

Failure: {badarg, Info}} if Pid is not an existing local pid.

- get_system_tracer() -> Tracer + Return the pid() or port() of the current system tracer. - - Tracer = pid() | port() | false - +

Returns the pid or port identifier of the current system tracer or false if no system tracer is activated.

diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml new file mode 100644 index 0000000000..b41addaa0c --- /dev/null +++ b/lib/kernel/doc/src/specs.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/kernel/doc/src/wrap_log_reader.xml b/lib/kernel/doc/src/wrap_log_reader.xml index 18664a029f..6cf480b532 100644 --- a/lib/kernel/doc/src/wrap_log_reader.xml +++ b/lib/kernel/doc/src/wrap_log_reader.xml @@ -4,7 +4,7 @@
- 19982009 + 19982011 Ericsson AB. All Rights Reserved. @@ -50,18 +50,20 @@ the called node, it is entirely up to the user to be sure that all items are read.

+ + + +

Continuation returned by + open/1,2 or chunk/1,2.

+
+
+
- chunk(Continuation) - chunk(Continuation, N) -> {Continuation2, Terms} | {Continuation2, Terms, Badbytes} | {Continuation2, eof} | {error, Reason} + + Read a chunk of objects written to a wrap log. - - Continuation = continuation() - N = int() > 0 | infinity - Continuation2 = continuation() - Terms= [term()] - Badbytes = integer() - +

This function makes it possible to efficiently read the terms which have been appended to a log. It minimises disk @@ -70,29 +72,29 @@

The first time chunk is called an initial continuation returned from the open/1, open/2 must be provided.

-

When chunk/3 is called, N controls the +

When chunk/3 is called, N controls the maximum number of terms that are read from the log in each chunk. Default is infinity, which means that all the terms contained in the 8K chunk are read. If less than - N terms are returned, this does not necessarily mean + N terms are returned, this does not necessarily mean that end of file is reached.

The chunk function returns a tuple - {Continuation2, Terms}, where Terms is a list - of terms found in the log. Continuation2 is yet + {Continuation2, Terms}, where Terms is a list + of terms found in the log. Continuation2 is yet another continuation which must be passed on into any subsequent calls to chunk. With a series of calls to chunk it is then possible to extract all terms from a log.

The chunk function returns a tuple - {Continuation2, Terms, Badbytes} if the log is opened - in read only mode and the read chunk is corrupt. Badbytes + {Continuation2, Terms, Badbytes} if the log is opened + in read only mode and the read chunk is corrupt. Badbytes indicates the number of non-Erlang terms found in the chunk. Note also that the log is not repaired.

-

chunk returns {Continuation2, eof} when the end of the log is - reached, and {error, Reason} if an error occurs. +

chunk returns {Continuation2, eof} when the end of the log is + reached, and {error, Reason} if an error occurs.

The returned continuation may or may not be valid in the next call to chunk. This is because the log may wrap and delete @@ -103,37 +105,29 @@ - close(Continuation) -> ok + Close a log - - Continuation = continuation() -

This function closes a log file properly.

- open(Filename) -> OpenRet - open(Filename, N) -> OpenRet + + Open a log file - - File = string() | atom() - N = integer() - OpenRet = {ok, Continuation} | {error, Reason} - Continuation = continuation() - + -

Filename specifies the name of the file which is to be read.

-

N specifies the index of the file which is to be read. - If N is omitted the whole wrap log file will be read; if it +

Filename specifies the name of the file which is to be read.

+

N specifies the index of the file which is to be read. + If N is omitted the whole wrap log file will be read; if it is specified only the specified index file will be read.

-

The open function returns {ok, Continuation} if the - log/index file was successfully opened. The Continuation +

The open function returns {ok, Continuation} if the + log/index file was successfully opened. The Continuation is to be used when chunking or closing the file.

-

The function returns {error, Reason} for all errors. +

The function returns {error, Reason} for all errors.

-- cgit v1.2.3 From 011b45b19a12cb29216883e117a314a251a74fb3 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 May 2011 14:43:41 +0200 Subject: Updated appup file also (to handle snmp_pdus). --- lib/snmp/src/app/snmp.appup.src | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 4c2de0fcf4..4d68d57553 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -27,6 +27,7 @@ {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -56,6 +57,7 @@ {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmp_config, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmpa_conf, soft_purge, soft_purge, []}, @@ -107,6 +109,7 @@ {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_misc, soft_purge, soft_purge, []}, @@ -138,6 +141,7 @@ {load_module, snmpa_usm, soft_purge, soft_purge, []}, {load_module, snmpm_usm, soft_purge, soft_purge, []}, {load_module, snmp_misc, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, {load_module, snmp_conf, soft_purge, soft_purge, []}, {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_conf]}, {load_module, snmp_config, soft_purge, soft_purge, []}, -- cgit v1.2.3 From 5d32eaf750cff98d242c8048eeb6b49c94fb6ee6 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Wed, 11 May 2011 12:05:11 +0200 Subject: Fixed httpc manager crash httpc manager crashes.When a request results in a retry, the request id will be "reused" in the previous implementation a race condition could occur causing the manager to crash. This is now avoided by using proc_lib:init_ack and gen_server:enter_loop to allow more advanced initialization of httpc_handlers without blocking the httpc_manger and eliminating extra processes that can cause race conditions. --- lib/inets/src/http_client/httpc_handler.erl | 256 ++++-------- lib/inets/src/http_client/httpc_handler_sup.erl | 8 +- lib/inets/src/http_client/httpc_manager.erl | 509 +++++------------------- lib/inets/src/http_client/httpc_request.erl | 8 +- lib/inets/src/inets_app/inets.appup.src | 60 +-- lib/inets/test/httpc_SUITE.erl | 211 ++++++---- 6 files changed, 324 insertions(+), 728 deletions(-) diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 5e22400fe0..1f0e012e7e 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.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 @@ -29,10 +29,10 @@ %%-------------------------------------------------------------------- %% Internal Application API -export([ - start_link/2, - connect_and_send/2, + start_link/4, + %% connect_and_send/2, send/2, - cancel/2, + cancel/3, stream/3, stream_next/1, info/1 @@ -51,7 +51,7 @@ -record(state, { request, % #request{} - session, % #tcp_session{} + session, % #session{} status_line, % {Version, StatusCode, ReasonPharse} headers, % #http_response_h{} body, % binary() @@ -94,13 +94,9 @@ %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- -start_link(Options, ProfileName) -> - Args = [Options, ProfileName], - gen_server:start_link(?MODULE, Args, []). - -connect_and_send(Request, HandlerPid) -> - call({connect_and_send, Request}, HandlerPid). - +start_link(Parent, Request, Options, ProfileName) -> + {ok, proc_lib:start_link(?MODULE, init, [[Parent, Request, Options, + ProfileName]])}. %%-------------------------------------------------------------------- %% Function: send(Request, Pid) -> ok @@ -122,8 +118,8 @@ send(Request, Pid) -> %% Description: Cancels a request. Intended to be called by the httpc %% manager process. %%-------------------------------------------------------------------- -cancel(RequestId, Pid) -> - cast({cancel, RequestId}, Pid). +cancel(RequestId, Pid, From) -> + cast({cancel, RequestId, From}, Pid). %%-------------------------------------------------------------------- @@ -229,16 +225,27 @@ stream(BodyPart, Request,_) -> % only 200 and 206 responses can be streamed %% but we do not want that so errors will be handled by the process %% sending an init_error message to itself. %%-------------------------------------------------------------------- -init([Options, ProfileName]) -> - ?hcrv("init - starting", [{options, Options}, {profile, ProfileName}]), +init([Parent, Request, Options, ProfileName]) -> process_flag(trap_exit, true), - handle_verbose(Options#options.verbose), - State = #state{status = undefined, - options = Options, - profile_name = ProfileName}, - ?hcrd("init - started", []), - {ok, State}. + %% Do not let initial tcp-connection block the manager-process + proc_lib:init_ack(Parent, self()), + handle_verbose(Options#options.verbose), + Address = handle_proxy(Request#request.address, Options#options.proxy), + {ok, State} = + case {Address /= Request#request.address, Request#request.scheme} of + {true, https} -> + Error = https_through_proxy_is_not_currently_supported, + self() ! {init_error, + Error, httpc_response:error(Request, Error)}, + {ok, #state{request = Request, options = Options, + status = ssl_tunnel}}; + {_, _} -> + connect_and_send_first_request(Address, Request, + #state{options = Options, + profile_name = ProfileName}) + end, + gen_server:enter_loop(?MODULE, [], State). %%-------------------------------------------------------------------- %% Function: handle_call(Request, From, State) -> {reply, Reply, State} | @@ -249,41 +256,6 @@ init([Options, ProfileName]) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- - - -%% This is the first request, the reason the proc was started -handle_call({connect_and_send, #request{address = Address0, - scheme = Scheme} = Request}, - _From, - #state{options = #options{proxy = Proxy}, - status = undefined, - session = undefined} = State) -> - ?hcrv("connect and send", [{address0, Address0}, {proxy, Proxy}]), - Address = handle_proxy(Address0, Proxy), - if - ((Address =/= Address0) andalso (Scheme =:= https)) -> - %% This is what we should do if and when ssl supports - %% "socket upgrading" - %%send_ssl_tunnel_request(Address, Request, - %% #state{options = Options, - %% status = ssl_tunnel}); - Reason = {failed_connecting, - https_through_proxy_is_not_currently_supported}, - %% Send a reply to the original caller - ErrorResponse = httpc_response:error(Request, Reason), - httpc_response:send(Request#request.from, ErrorResponse), - %% Reply to the manager - ErrorReply = {error, Reason}, - {stop, normal, ErrorReply, State}; - true -> - case connect_and_send_first_request(Address, Request, State) of - {ok, NewState} -> - {reply, ok, NewState}; - {stop, Error, NewState} -> - {stop, normal, Error, NewState} - end - end; - handle_call(#request{address = Addr} = Request, _, #state{status = Status, session = #session{type = pipeline} = Session, @@ -445,25 +417,27 @@ handle_call(info, _, State) -> %% handle_keep_alive_queue/2 on the other hand will just skip the %% request as if it was never issued as in this case the request will %% not have been sent. -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{request = #request{id = RequestId} = Request, profile_name = ProfileName, canceled = Canceled} = State) -> ?hcrv("cancel current request", [{request_id, RequestId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {stop, normal, State#state{canceled = [RequestId | Canceled], request = Request#request{from = answer_sent}}}; -handle_cast({cancel, RequestId}, +handle_cast({cancel, RequestId, From}, #state{profile_name = ProfileName, + request = #request{id = CurrId}, canceled = Canceled} = State) -> - ?hcrv("cancel", [{request_id, RequestId}, + ?hcrv("cancel", [{request_id, RequestId}, + {curr_req_id, CurrId}, {profile, ProfileName}, {canceled, Canceled}]), - httpc_manager:request_canceled(RequestId, ProfileName), + httpc_manager:request_canceled(RequestId, ProfileName, From), ?hcrv("canceled", []), {noreply, State#state{canceled = [RequestId | Canceled]}}; @@ -872,62 +846,55 @@ connect(SocketType, ToAddress, Opts3 = [IpFamily | Opts2], http_transport:connect(SocketType, ToAddress, Opts3, Timeout) end. - -connect_and_send_first_request(Address, - #request{settings = Settings, - headers = Headers, - address = OrigAddress, - scheme = Scheme} = Request, - #state{options = Options} = State) -> - - ?hcrd("connect", - [{address, Address}, {request, Request}, {options, Options}]), +connect_and_send_first_request(Address, Request, #state{options = Options} = State) -> SocketType = socket_type(Request), - ConnTimeout = Settings#http_options.connect_timeout, + ConnTimeout = (Request#request.settings)#http_options.connect_timeout, + ?hcri("connect", + [{address, Address}, {request, Request}, {options, Options}]), case connect(SocketType, Address, Options, ConnTimeout) of {ok, Socket} -> - Session = #session{id = {OrigAddress, self()}, - scheme = Scheme, - socket = Socket, - socket_type = SocketType}, - ?hcrd("connected - now send first request", [{socket, Socket}]), + ClientClose = + httpc_request:is_client_closing( + Request#request.headers), + SessionType = httpc_manager:session_type(Options), + SocketType = socket_type(Request), + Session = #session{id = {Request#request.address, self()}, + scheme = Request#request.scheme, + socket = Socket, + socket_type = SocketType, + client_close = ClientClose, + type = SessionType}, + ?hcri("connected - now send first request", [{socket, Socket}]), + case httpc_request:send(Address, Session, Request) of ok -> - ?hcrd("first request sent", []), - ClientClose = - httpc_request:is_client_closing(Headers), - SessionType = httpc_manager:session_type(Options), - Session2 = - Session#session{client_close = ClientClose, - type = SessionType}, - TmpState = - State#state{request = Request, - session = Session2, - mfa = init_mfa(Request, State), - status_line = init_status_line(Request), - headers = undefined, - body = undefined, - status = new}, - ?hcrt("activate socket", []), - activate_once(Session), + ?hcri("first request sent", []), + TmpState = State#state{request = Request, + session = Session, + mfa = init_mfa(Request, State), + status_line = + init_status_line(Request), + headers = undefined, + body = undefined, + status = new}, + http_transport:setopts(SocketType, + Socket, [{active, once}]), NewState = activate_request_timeout(TmpState), {ok, NewState}; - - {error, Reason} = Error -> - ?hcrv("failed sending request", [{reason, Reason}]), - {stop, Error, - State#state{session = {send_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_sending, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request, + session = + #session{socket = Socket}}} end; - - {error, Reason} = Error -> - ?hcri("connect failed", [{reason, Reason}]), - {stop, Error, State#state{session = {connect_failed, Reason}, - request = Request}} + {error, Reason} -> + self() ! {init_error, error_connecting, + httpc_response:error(Request, Reason)}, + {ok, State#state{request = Request}} end. - handler_info(#state{request = Request, session = Session, status_line = _StatusLine, @@ -1167,12 +1134,12 @@ handle_response(#state{request = Request, {ok, Msg, Data} -> ?hcrd("handle response - ok", []), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), handle_queue(NewState, Data); {stop, Msg} -> ?hcrd("handle response - stop", [{msg, Msg}]), end_stream(StatusLine, Request), - NewState = answer_request(Request, Msg, State), + NewState = maybe_send_answer(Request, Msg, State), {stop, normal, NewState} end. @@ -1242,7 +1209,8 @@ handle_pipeline(#state{status = pipeline, %% See comment for handle_cast({cancel, RequestId}) {stop, normal, State#state{request = - NextRequest#request{from = answer_sent}}}; + NextRequest#request{from = answer_sent}, + pipeline = Pipeline}}; false -> ?hcrv("next request", [{request, NextRequest}]), NewSession = @@ -1443,6 +1411,7 @@ answer_request(#request{id = RequestId, from = From} = Request, Msg, Timer = {RequestId, TimerRef}, cancel_timer(TimerRef, {timeout, Request#request.id}), httpc_manager:request_done(RequestId, ProfileName), + State#state{request = Request#request{from = answer_sent}, timers = Timers#timers{request_timers = @@ -1662,67 +1631,6 @@ handle_verbose(_) -> ok. -%%% Normaly I do not comment out code, I throw it away. But this might -%%% actually be used one day if ssl is improved. -%% send_ssl_tunnel_request(Address, Request = #request{address = {Host, Port}}, -%% State) -> -%% %% A ssl tunnel request is a special http request that looks like -%% %% CONNECT host:port HTTP/1.1 -%% SslTunnelRequest = #request{method = connect, scheme = http, -%% headers = -%% #http_request_h{ -%% host = Host, -%% address = Address, -%% path = Host ++ ":", -%% pquery = integer_to_list(Port), -%% other = [{ "Proxy-Connection", "keep-alive"}]}, -%% Ipv6 = (State#state.options)#options.ipv6, -%% SocketType = socket_type(SslTunnelRequest), -%% case http_transport:connect(SocketType, -%% SslTunnelRequest#request.address, Ipv6) of -%% {ok, Socket} -> -%% case httpc_request:send(Address, SslTunnelRequest, Socket) of -%% ok -> -%% Session = #tcp_session{id = -%% {SslTunnelRequest#request.address, -%% self()}, -%% scheme = -%% SslTunnelRequest#request.scheme, -%% socket = Socket}, -%% NewState = State#state{mfa = -%% {httpc_response, parse, -%% [State#state.max_header_size]}, -%% request = Request, -%% session = Session}, -%% http_transport:setopts(socket_type( -%% SslTunnelRequest#request.scheme), -%% Socket, -%% [{active, once}]), -%% {ok, NewState}; -%% {error, Reason} -> -%% self() ! {init_error, error_sending, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request, -%% session = #tcp_session{socket = -%% Socket}}} -%% end; -%% {error, Reason} -> -%% self() ! {init_error, error_connecting, -%% httpc_response:error(Request, Reason)}, -%% {ok, State#state{request = Request}} -%% end. - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. - send_raw(#session{socket = Socket, socket_type = SocketType}, {ProcessBody, Acc}) when is_function(ProcessBody, 1) -> @@ -1756,11 +1664,5 @@ call(Msg, Pid, Timeout) -> cast(Msg, Pid) -> gen_server:cast(Pid, Msg). - -%% to(To, Start) when is_integer(Start) andalso (Start >= 0) -> -%% http_util:timeout(To, Start); -%% to(To, _Start) -> -%% http_util:timeout(To, t()). - t() -> http_util:timestamp(). diff --git a/lib/inets/src/http_client/httpc_handler_sup.erl b/lib/inets/src/http_client/httpc_handler_sup.erl index 2a69fd15d0..f7a0b014b3 100644 --- a/lib/inets/src/http_client/httpc_handler_sup.erl +++ b/lib/inets/src/http_client/httpc_handler_sup.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 @@ -23,7 +23,7 @@ %% API -export([start_link/0]). --export([start_child/2]). +-export([start_child/1]). %% Supervisor callback -export([init/1]). @@ -34,11 +34,9 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []). -start_child(Options, Profile) -> - Args = [Options, Profile], +start_child(Args) -> supervisor:start_child(?MODULE, Args). - %%%========================================================================= %%% Supervisor callback %%%========================================================================= diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 591cb78c29..7f66b477eb 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.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 @@ -29,7 +29,7 @@ start_link/3, request/2, cancel_request/2, - request_canceled/2, + request_canceled/3, request_done/2, retry_request/2, redirect_request/2, @@ -66,6 +66,7 @@ state % State of the handler: initiating | started | operational | canceled }). +-define(DELAY, 500). %%==================================================================== %% Internal Application API @@ -158,7 +159,8 @@ cancel_request(RequestId, ProfileName) -> %% be called by the httpc handler process. %%-------------------------------------------------------------------- -request_canceled(RequestId, ProfileName) -> +request_canceled(RequestId, ProfileName, From) -> + gen_server:reply(From, ok), cast(ProfileName, {request_canceled, RequestId}). @@ -355,44 +357,32 @@ do_init(ProfileName, CookiesDir) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call({request, Request}, _From, State) -> - ?hcrv("request", [{request, Request}]), +handle_call({request, Request}, _, State) -> + ?hcri("request", [{request, Request}]), case (catch handle_request(Request, State)) of - {ok, ReqId, NewState} -> - {reply, {ok, ReqId}, NewState}; - + {reply, Msg, NewState} -> + {reply, Msg, NewState}; Error -> - NewError = {error, {failed_process_request, Error}}, - {reply, NewError, State} + {stop, Error, httpc_response:error(Request, Error), State} end; - -handle_call({cancel_request, RequestId}, From, - #state{handler_db = HandlerDb} = State) -> - ?hcrv("cancel_request", [{request_id, RequestId}]), + +handle_call({cancel_request, RequestId}, From, State) -> + ?hcri("cancel_request", [{request_id, RequestId}]), case ets:lookup(State#state.handler_db, RequestId) of [] -> - ?hcrd("nothing to cancel", []), - Reply = ok, %% Nothing to cancel - {reply, Reply, State}; - - [#handler_info{handler = Pid}] when is_pid(Pid) -> - ?hcrd("found operational handler for this request", - [{handler, Pid}]), - httpc_handler:cancel(RequestId, Pid), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}}; - - [#handler_info{starter = Pid, state = HandlerState}] - when is_pid(Pid) -> - ?hcri("found *initiating* handler for this request", - [{starter, Pid}, {state, HandlerState}]), - ets:update_element(HandlerDb, RequestId, - {#handler_info.state, canceled}), + %% The request has allready compleated make sure + %% it is deliverd to the client process queue so + %% it can be thrown away by httpc:cancel_request + %% This delay is hopfully a temporary workaround. + %% Note that it will not not delay the manager, + %% only the client that called httpc:cancel_request + timer:apply_after(?DELAY, gen_server, reply, [From, ok]), + {noreply, State}; + [{_, Pid, _}] -> + httpc_handler:cancel(RequestId, Pid, From), {noreply, State#state{cancel = - [{RequestId, Pid, From} | + [{RequestId, Pid, From} | State#state.cancel]}} - end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -437,43 +427,16 @@ handle_call(Req, From, #state{profile_name = ProfileName} = State) -> %%-------------------------------------------------------------------- handle_cast({retry_or_redirect_request, {Time, Request}}, #state{profile_name = ProfileName} = State) -> - ?hcrv("retry or redirect request", [{time, Time}, {request, Request}]), - case timer:apply_after(Time, ?MODULE, retry_request, - [Request, ProfileName]) of - {ok, _} -> - {noreply, State}; - {error, Reason} -> - error_report(ProfileName, - "failed scheduling retry/redirect request" - "~n Time: ~p" - "~n Request: ~p" - "~n Reason: ~p", [Time, Request, Reason]), - {noreply, State} - end; + {ok, _} = timer:apply_after(Time, ?MODULE, retry_request, [Request, ProfileName]), + {noreply, State}; -handle_cast({retry_or_redirect_request, Request}, - #state{profile_name = Profile, - handler_db = HandlerDb} = State) -> - ?hcrv("retry or redirect request", [{request, Request}]), +handle_cast({retry_or_redirect_request, Request}, State) -> case (catch handle_request(Request, State)) of - {ok, _, NewState} -> + {reply, {ok, _}, NewState} -> {noreply, NewState}; - Error -> - ReqId = Request#request.id, - error_report(Profile, - "failed to retry or redirect request ~p" - "~n Error: ~p", [ReqId, Error]), - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{from = From}] -> - Error2 = httpc_response:error(Request, Error), - httpc_response:send(From, Error2), - ok; - - _ -> - ok - end, - {noreply, State} + httpc_response:error(Request, Error), + {stop, Error, State} end; handle_cast({request_canceled, RequestId}, State) -> @@ -482,7 +445,6 @@ handle_cast({request_canceled, RequestId}, State) -> case lists:keysearch(RequestId, 1, State#state.cancel) of {value, Entry = {RequestId, _, From}} -> ?hcrt("found in cancel", [{from, From}]), - gen_server:reply(From, ok), {noreply, State#state{cancel = lists:delete(Entry, State#state.cancel)}}; Else -> @@ -539,8 +501,6 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> "recived unknown message" "~n Msg: ~p", [Msg]), {noreply, State}. - - %%-------------------------------------------------------------------- %% Function: handle_info(Info, State) -> {noreply, State} | @@ -548,39 +508,17 @@ handle_cast(Msg, #state{profile_name = ProfileName} = State) -> %% {stop, Reason, State} (terminate/2 is called) %% Description: Handling all non call/cast messages %%--------------------------------------------------------- - -handle_info({started, StarterPid, ReqId, HandlerPid}, State) -> - handle_started(StarterPid, ReqId, HandlerPid, State), - {noreply, State}; - -handle_info({connect_and_send, StarterPid, ReqId, HandlerPid, Res}, State) -> - handle_connect_and_send(StarterPid, ReqId, HandlerPid, Res, State), - {noreply, State}; - -handle_info({failed_starting_handler, StarterPid, ReqId, Res}, State) -> - handle_failed_starting_handler(StarterPid, ReqId, Res, State), - {noreply, State}; - -handle_info({'EXIT', Pid, Reason}, #state{handler_db = HandlerDb} = State) -> - maybe_handle_terminating_starter(Pid, Reason, HandlerDb), +handle_info({'EXIT', _, _}, State) -> + %% Handled in DOWN {noreply, State}; - handle_info({'DOWN', _, _, Pid, _}, State) -> - - %% - %% Normally this should have been cleaned up already - %% (when receiving {request_done, PequestId}), but - %% just in case there is a glitch, cleanup anyway. - %% - - Pattern = #handler_info{handler = Pid, _ = '_'}, - ets:match_delete(State#state.handler_db, Pattern), + ets:match_delete(State#state.handler_db, {'_', Pid, '_'}), %% If there where any canceled request, handled by the %% the process that now has terminated, the %% cancelation can be viewed as sucessfull! - NewCanceledList = - lists:foldl(fun({_, HandlerPid, From} = Entry, Acc) -> + NewCanceldList = + lists:foldl(fun(Entry = {_, HandlerPid, From}, Acc) -> case HandlerPid of Pid -> gen_server:reply(From, ok), @@ -589,15 +527,13 @@ handle_info({'DOWN', _, _, Pid, _}, State) -> Acc end end, State#state.cancel, State#state.cancel), - {noreply, State#state{cancel = NewCanceledList}}; - -handle_info(Info, #state{profile_name = ProfileName} = State) -> - error_report(ProfileName, - "received unknown info" - "~n Info: ~p", [Info]), + {noreply, State#state{cancel = NewCanceldList}}; +handle_info(Info, State) -> + Report = io_lib:format("Unknown message in " + "httpc_manager:handle_info ~p~n", [Info]), + error_logger:error_report(Report), {noreply, State}. - %%-------------------------------------------------------------------- %% Function: terminate(Reason, State) -> _ (ignored by gen_server) %% Description: Shutdown the httpc_handler @@ -655,224 +591,79 @@ get_handler_info(Tab) -> {Pid, State} <- Handlers2], Handlers3. - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When the handler has sucessfully been started, -%% this message (started) is sent. -%% - -handle_started(StarterPid, ReqId, HandlerPid, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = initiating} = HandlerInfo] -> - ?hcri("received started ack for initiating handler", []), - %% As a last resort, make sure we know when it exits, - %% in case it forgets to notify us. - %% We dont need to know the ref id? - erlang:monitor(process, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{handler = HandlerPid, - state = started}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] -> - error_report(Profile, - "unexpected (started) message for handler (~p) in state " - "~p regarding request ~p - ignoring", [HandlerPid, State, ReqId]), - ?hcri("received unexpected started message", [{state, State}]), - ok; - - [] -> - error_report(Profile, - "unknown handler ~p (~p) started for request ~w - canceling", - [HandlerPid, StarterPid, ReqId]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -%% -%% The request handler process is started asynchronously by a -%% "starter process". When that process terminates it sends -%% one of two messages. These ara handled by the two functions -%% below. -%% - -handle_connect_and_send(_StarterPid, ReqId, HandlerPid, Result, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = started} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for started handler", []), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid, - state = operational}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = canceled} = HandlerInfo] when Result =:= ok -> - ?hcri("received connect-and-send ack for canceled handler", []), - httpc_handler:cancel(ReqId, HandlerPid), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined, - handler = HandlerPid}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - [#handler_info{state = State}] when Result =/= ok -> - error_report(Profile, - "handler (~p, ~w) failed to connect and/or " - "send request ~p" - "~n Result: ~p", - [HandlerPid, State, ReqId, Result]), - ?hcri("received connect-and-send error", - [{result, Result}, {state, State}]), - %% We don't need to send a response to the original caller - %% because the handler already sent one in its terminate - %% function. - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - ?hcri("handler successfully started " - "for unknown request => canceling", - [{profile, Profile}, - {handler, HandlerPid}, - {request, ReqId}]), - httpc_handler:cancel(ReqId, HandlerPid) - end. - - -handle_failed_starting_handler(_StarterPid, ReqId, Error, - #state{profile_name = Profile, - handler_db = HandlerDb}) -> - case ets:lookup(HandlerDb, ReqId) of - [#handler_info{state = canceled}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - request_canceled(Profile, ReqId), % Fake signal from handler - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{from = From}] -> - error_report(Profile, - "failed starting handler for request ~p" - "~n Error: ~p", [ReqId, Error]), - Reason2 = - case Error of - {error, Reason} -> - {failed_connecting, Reason}; - _ -> - {failed_connecting, Error} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [] -> - error_report(Profile, - "failed starting handler for unknown request ~p" - "~n Error: ~p", [ReqId, Error]), - ok - end. - - -maybe_handle_terminating_starter(MeybeStarterPid, Reason, HandlerDb) -> - Pattern = #handler_info{starter = MeybeStarterPid, _ = '_'}, - case ets:match_object(HandlerDb, Pattern) of - [#handler_info{id = ReqId, from = From, state = initiating}] -> - %% The starter process crashed before it could start the - %% the handler process, therefor we need to answer the - %% original caller. - ?hcri("starter process crashed bfore starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}]), - Reason2 = - case Reason of - {error, Error} -> - {failed_connecting, Error}; - _ -> - {failed_connecting, Reason} - end, - DummyReq = #request{id = ReqId}, - httpc_response:send(From, httpc_response:error(DummyReq, Reason2)), - ets:delete(HandlerDb, ReqId), - ok; - - [#handler_info{state = State} = HandlerInfo] -> - %% The starter process crashed after the handler was started. - %% The handler will answer to the original caller. - ?hcri("starter process crashed after starting handler", - [{starter, MeybeStarterPid}, {reason, Reason}, {state, State}]), - HandlerInfo2 = HandlerInfo#handler_info{starter = undefined}, - ets:insert(HandlerDb, HandlerInfo2), - ok; - - _ -> - ok - end. - - -%% ----- -%% Act as an HTTP/0.9 client that does not know anything -%% about persistent connections handle_request(#request{settings = - #http_options{version = "HTTP/0.9"}} = Request0, + #http_options{version = "HTTP/0.9"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = undefined}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - -%% ----- -%% Act as an HTTP/1.0 client that does not -%% use persistent connections + %% Act as an HTTP/0.9 client that does not know anything + %% about persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = undefined}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + handle_request(#request{settings = - #http_options{version = "HTTP/1.0"}} = Request0, + #http_options{version = "HTTP/1.0"}} = Request, State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State), - {ok, Request2#request.id, State}; - - -%% ----- -handle_request(#request{method = Method, - address = Address, - scheme = Scheme} = Request0, - #state{options = Opts} = State) -> - Request1 = handle_cookies(generate_request_id(Request0), State), - SessionType = session_type(Opts), - case select_session(Method, Address, Scheme, SessionType, State) of + %% Act as an HTTP/1.0 client that does not + %% use persistent connections + + NewRequest = handle_cookies(generate_request_id(Request), State), + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State), + {reply, {ok, NewRequest#request.id}, State}; + +handle_request(Request, State = #state{options = Options}) -> + + NewRequest = handle_cookies(generate_request_id(Request), State), + SessionType = session_type(Options), + case select_session(Request#request.method, + Request#request.address, + Request#request.scheme, SessionType, State) of {ok, HandlerPid} -> - pipeline_or_keep_alive(Request1, HandlerPid, State); + pipeline_or_keep_alive(NewRequest, HandlerPid, State); no_connection -> - create_handler_starter(Request1, State); - {no_session, OpenSessions} - when OpenSessions < Opts#options.max_sessions -> - create_handler_starter(Request1, State); + start_handler(NewRequest, State); + {no_session, OpenSessions} when OpenSessions + < Options#options.max_sessions -> + start_handler(NewRequest, State); {no_session, _} -> %% Do not start any more persistent connections %% towards this server. - Hdrs0 = Request1#request.headers, - Hdrs1 = Hdrs0#http_request_h{connection = "close"}, - Request2 = Request1#request{headers = Hdrs1}, - create_handler_starter(Request2, State) + NewHeaders = + (NewRequest#request.headers)#http_request_h{connection + = "close"}, + start_handler(NewRequest#request{headers = NewHeaders}, State) end, - {ok, Request1#request.id, State}. + {reply, {ok, NewRequest#request.id}, State}. + + +start_handler(Request, State) -> + {ok, Pid} = + case is_inets_manager() of + true -> + httpc_handler_sup:start_child([whereis(httpc_handler_sup), + Request, State#state.options, + State#state.profile_name]); + false -> + httpc_handler:start_link(self(), Request, State#state.options, + State#state.profile_name) + end, + ets:insert(State#state.handler_db, {Request#request.id, + Pid, Request#request.from}), + erlang:monitor(process, Pid). select_session(Method, HostPort, Scheme, SessionType, #state{options = #options{max_pipeline_length = MaxPipe, max_keep_alive_length = MaxKeepAlive}, session_db = SessionDb}) -> - ?hcrd("select session", [{session_type, SessionType}, - {max_pipeline_length, MaxPipe}, + ?hcrd("select session", [{session_type, SessionType}, + {max_pipeline_length, MaxPipe}, {max_keep_alive_length, MaxKeepAlive}]), case httpc_request:is_idempotent(Method) orelse (SessionType =:= keep_alive) of @@ -918,92 +709,17 @@ select_session(Candidates, Max) -> ?hcrd("select session - found one", [{handler, HandlerPid}]), {ok, HandlerPid} end. - -pipeline_or_keep_alive(#request{id = Id} = Request, HandlerPid, State) -> - ?hcrd("pipeline of keep-alive", [{id, Id}, {handler, HandlerPid}]), + +pipeline_or_keep_alive(Request, HandlerPid, State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ?hcrd("pipeline or keep-alive - successfully sent", []), - Entry = #handler_info{id = Id, - handler = HandlerPid, - state = operational}, - ets:insert(State#state.handler_db, Entry); - - _ -> %% timeout pipelining failed - ?hcrd("pipeline or keep-alive - failed sending -> " - "start a new handler", []), - create_handler_starter(Request, State) + ets:insert(State#state.handler_db, {Request#request.id, + HandlerPid, + Request#request.from}); + _ -> %timeout pipelining failed + start_handler(Request, State) end. - -create_handler_starter(#request{socket_opts = SocketOpts} = Request, - #state{options = Options} = State) - when is_list(SocketOpts) -> - %% The user provided us with (override) socket options - ?hcrt("create handler starter", [{socket_opts, SocketOpts}, {options, Options}]), - Options2 = Options#options{socket_opts = SocketOpts}, - create_handler_starter(Request#request{socket_opts = undefined}, - State#state{options = Options2}); - -create_handler_starter(#request{id = Id, - from = From} = Request, - #state{profile_name = ProfileName, - options = Options, - handler_db = HandlerDb} = _State) -> - ?hcrv("create handler starter", [{id, Id}, {profile, ProfileName}]), - IsInetsManager = is_inets_manager(), - ManagerPid = self(), - StarterFun = - fun() -> - ?hcrd("handler starter - start", - [{id, Id}, - {profile, ProfileName}, - {inets_manager, IsInetsManager}]), - Result1 = - case IsInetsManager of - true -> - httpc_handler_sup:start_child(Options, - ProfileName); - false -> - httpc_handler:start_link(Options, - ProfileName) - end, - ?hcrd("handler starter - maybe connect and send", - [{id, Id}, {profile, ProfileName}, {result, Result1}]), - case Result1 of - {ok, HandlerPid} -> - StartedMessage = - {started, self(), Id, HandlerPid}, - ManagerPid ! StartedMessage, - Result2 = httpc_handler:connect_and_send(Request, - HandlerPid), - ?hcrd("handler starter - connected and sent", - [{id, Id}, {profile, ProfileName}, - {handler, HandlerPid}, {result, Result2}]), - ConnAndSendMessage = - {connect_and_send, - self(), Id, HandlerPid, Result2}, - ManagerPid ! ConnAndSendMessage; - {error, Reason} -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Reason}, - ManagerPid ! StartFailureMessage; - _ -> - StartFailureMessage = - {failed_starting_handler, self(), Id, Result1}, - ManagerPid ! StartFailureMessage - end - end, - Starter = erlang:spawn_link(StarterFun), - ?hcrd("create handler starter - started", [{id, Id}, {starter, Starter}]), - Entry = #handler_info{id = Id, - starter = Starter, - from = From, - state = initiating}, - ets:insert(HandlerDb, Entry), - ok. - - is_inets_manager() -> case get('$ancestors') of [httpc_profile_sup | _] -> @@ -1045,8 +761,6 @@ do_store_cookies([Cookie | Cookies], #state{cookie_db = CookieDb} = State) -> ok = httpc_cookie:insert(CookieDb, Cookie), do_store_cookies(Cookies, State). - - session_db_name(ProfileName) -> make_db_name(ProfileName, "__session_db"). @@ -1074,7 +788,6 @@ cast(ProfileName, Msg) -> gen_server:cast(ProfileName, Msg). - get_proxy(Opts, #options{proxy = Default}) -> proplists:get_value(proxy, Opts, Default). @@ -1133,20 +846,6 @@ handle_verbose(trace) -> handle_verbose(_) -> ok. - error_report(Profile, F, A) -> Report = io_lib:format("HTTPC-MANAGER<~p> " ++ F ++ "~n", [Profile | A]), error_logger:error_report(Report). - - -%% d(F) -> -%% d(F, []). - -%% d(F, A) -> -%% d(get(dbg), F, A). - -%% d(true, F, A) -> -%% io:format(user, "~w:~w:" ++ F ++ "~n", [self(), ?MODULE | A]); -%% d(_, _, _) -> -%% ok. - diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index 0d602adb11..879053f0f2 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -120,7 +120,7 @@ do_send_body(SocketType, Socket, Method, Uri, Version, Headers, Body) -> version(Version), ?CRLF, headers(Headers, Version), ?CRLF, Body], ?hcrd("send", [{message, Message}]), - http_transport:send(SocketType, Socket, lists:append(Message)). + http_transport:send(SocketType, Socket, Message). do_send_body(SocketType, Socket, ProcessBody, Acc) -> @@ -128,9 +128,7 @@ do_send_body(SocketType, Socket, ProcessBody, Acc) -> eof -> ok; {ok, Data, NewAcc} -> - DataBin = iolist_to_binary(Data), - ?hcrd("send", [{data, DataBin}]), - case http_transport:send(SocketType, Socket, DataBin) of + case http_transport:send(SocketType, Socket, Data) of ok -> do_send_body(SocketType, Socket, ProcessBody, NewAcc); Error -> diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 0a590c9c36..91fb064eec 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -20,38 +20,12 @@ [ {"5.5.2", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + {restart_application, inets} ] }, {"5.5.1", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_chunk, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, - [httpc_request, http_chunk]} + {restart_application, inets} ] }, {"5.5", @@ -68,38 +42,12 @@ [ {"5.5.2", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_request]} + {restart_application, inets} ] }, {"5.5.1", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {load_module, http_chunk, soft_purge, soft_purge, []}, - {load_module, http_util, soft_purge, soft_purge, []}, - {load_module, http_transport, soft_purge, soft_purge, []}, - {load_module, httpd_util, soft_purge, soft_purge, [http_util]}, - {load_module, httpd_conf, soft_purge, soft_purge, []}, - {load_module, httpd_file, soft_purge, soft_purge, []}, - {load_module, httpd_log, soft_purge, soft_purge, []}, - {load_module, mod_esi, soft_purge, soft_purge, []}, - {load_module, httpc, soft_purge, soft_purge, [httpc_handler]}, - {load_module, httpc_request, soft_purge, soft_purge, []}, - {update, httpd_sup, soft, soft_purge, soft_purge, [http_transport]}, - {update, httpd_request_handler, soft, soft_purge, soft_purge, []}, - {update, httpc_handler, soft, soft_purge, soft_purge, - [httpc_request, http_chunk]} + {restart_application, inets} ] }, {"5.5", diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 81e9c2b230..7607bc9eb6 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -115,7 +115,8 @@ all() -> options, ipv6, headers_as_is, - {group, tickets} + {group, tickets}, + initial_server_connect ]. groups() -> @@ -140,6 +141,7 @@ groups() -> otp_8106_fun, otp_8106_mfa]}]. + init_per_group(_GroupName, Config) -> Config. @@ -199,7 +201,6 @@ end_per_suite(Config) -> application:stop(ssl), ok. - %%-------------------------------------------------------------------- %% Function: init_per_testcase(Case, Config) -> Config %% Case - atom() @@ -214,6 +215,15 @@ end_per_suite(Config) -> %%-------------------------------------------------------------------- init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); + +init_per_testcase(initial_server_connect, Config) -> + inets:start(), + application:start(crypto), + application:start(public_key), + application:start(ssl), + application:start(inets), + Config; + init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). @@ -601,34 +611,35 @@ http_inets_pipe(Config) when is_list(Config) -> {skip, "Failed to start local http-server"} end. + test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1"), + {ok, RequestId1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), + test_server:format("RequestId1: ~p~n", [RequestId1]), + p("test_pipeline -> RequestId1: ~p", [RequestId1]), - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = + p("test_pipeline -> issue (async) request 2"), + {ok, RequestId2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), + tsp("RequestId2: ~p", [RequestId2]), + p("test_pipeline -> RequestId2: ~p", [RequestId2]), - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = httpc:request(get, {URL, []}, [], []), p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive + receive {http, {RequestId1, {{_, 200, _}, _, _}}} -> p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), receive @@ -646,46 +657,46 @@ test_pipeline(URL) -> ok; {http, Msg2} -> test_server:fail(Msg2) - end; + end; {http, Msg3} -> test_server:fail(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - test_server:fail({error, {timeout, Any1}}) - end + after 60000 -> + receive Any1 -> + tsp("received crap after timeout: ~n ~p", [Any1]), + test_server:fail({error, {timeout, Any1}}) + end end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = - httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), + p("test_pipeline -> sleep some"), + test_server:sleep(4000), - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = + p("test_pipeline -> issue (async) request 4"), + {ok, RequestId3} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = httpc:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - test_server:fail(http_cancel_request_failed) - after 3000 -> - ok - end, + tsp("RequestId3: ~p", [RequestId3]), + p("test_pipeline -> RequestId3: ~p", [RequestId3]), - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> + p("test_pipeline -> issue (async) request 5"), + {ok, RequestId4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestId4: ~p~n", [RequestId4]), + p("test_pipeline -> RequestId4: ~p", [RequestId4]), + + p("test_pipeline -> cancel (async) request 4"), + ok = httpc:cancel_request(RequestId3), + + p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), + receive + {http, {RequestId3, _}} -> + test_server:fail(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request 4"), + Body = + receive + {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> p("test_pipeline -> received reply for (async) request 5"), tsp("Receive : ~p", [Res]), BinBody4; @@ -700,9 +711,9 @@ test_pipeline(URL) -> p("test_pipeline -> check reply for (async) request 5"), inets_test_lib:check_body(binary_to_list(Body)), - + p("test_pipeline -> ensure no unexpected incomming"), - receive + receive {http, Any} -> test_server:fail({unexpected_message, Any}) after 500 -> @@ -712,8 +723,6 @@ test_pipeline(URL) -> p("test_pipeline -> done"), ok. - - %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -1675,25 +1684,11 @@ proxy_https_not_supported(suite) -> proxy_https_not_supported(Config) when is_list(Config) -> Result = httpc:request(get, {"https://login.yahoo.com", []}, [], []), case Result of - {error, Reason} -> - %% ok so far - case Reason of - {failed_connecting, Why} -> - %% ok, now check why - case Why of - https_through_proxy_is_not_currently_supported -> - ok; - _ -> - tsf({unexpected_why, Why}) - end; - _ -> - tsf({unexpected_reason, Reason}) - end; + {error, https_through_proxy_is_not_currently_supported} -> + ok; _ -> - tsf({unexpected_result, Result}) - end, - ok. - + tsf({unexpected_reason, Result}) + end. %%------------------------------------------------------------------------- @@ -2446,7 +2441,7 @@ otp_8106_fun(Config) when is_list(Config) -> ok; _ -> {skip, "Failed to start local http-server"} - end. + end. otp_8106_mfa(doc) -> @@ -2672,7 +2667,7 @@ otp_8739(Config) when is_list(Config) -> Request = {URL, []}, HttpOptions = [{connect_timeout, 500}, {timeout, 1}], Options = [{sync, true}], - case http:request(Method, Request, HttpOptions, Options) of + case httpc:request(Method, Request, HttpOptions, Options) of {error, timeout} -> %% And now we check the size of the handler db Info = httpc:info(), @@ -2729,7 +2724,31 @@ otp_8739_dummy_server_main(_Parent, ListenSocket) -> exit(Error) end. - +%%------------------------------------------------------------------------- + +initial_server_connect(doc) -> + ["If this test cases times out the init of httpc_handler process is" + "blocking the manager/client process (implementation dependent which) but nither" + "should be blocked."]; +initial_server_connect(suite) -> + []; +initial_server_connect(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + ok = httpc:set_options([{ipfamily, inet}]), + + CertFile = filename:join(DataDir, "ssl_server_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + + {DummyServerPid, Port} = dummy_ssl_server_hang(self(), ipv4, SSLOptions), + + URL = ?SSL_URL_START ++ integer_to_list(Port) ++ "/index.html", + + httpc:request(get, {URL, []}, [{ssl,{essl,[]}}], [{sync, false}]), + + [{session_cookies,[]}] = httpc:which_cookies(), + + DummyServerPid ! stop, + ok = httpc:set_options([{ipfamily, inet6fb4}]). %%-------------------------------------------------------------------- %% Internal functions @@ -3242,11 +3261,9 @@ pick_header(Headers, Name) -> Val end. - not_implemented_yet() -> exit(not_implemented_yet). - p(F) -> p(F, []). @@ -3260,3 +3277,37 @@ tsp(F, A) -> tsf(Reason) -> test_server:fail(Reason). + + +dummy_ssl_server_hang(Caller, IpV, SslOpt) -> + Pid = spawn(httpc_SUITE, dummy_ssl_server_hang_init, [Caller, IpV, SslOpt]), + receive + {port, Port} -> + {Pid, Port} + end. + +dummy_ssl_server_hang_init(Caller, IpV, SslOpt) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + ssl:listen(0, [binary, inet, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt); + ipv6 -> + ssl:listen(0, [binary, inet6, {packet, 0}, + {reuseaddr,true}, + {active, false}] ++ SslOpt) + end, + {ok, {_,Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_hang_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + {ok, AcceptSocket} = ssl:transport_accept(ListenSocket), + dummy_ssl_server_hang_loop(AcceptSocket). + +dummy_ssl_server_hang_loop(_) -> + %% Do not do ssl:ssl_accept as we + %% want to time out the underlying gen_tcp:connect + receive + stop -> + ok + end. -- cgit v1.2.3 From 39520f55033d73f9754312f8ba13ea3e36403114 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Tue, 29 Mar 2011 11:35:32 +0200 Subject: lock checking fix in hipe_bif2.c --- erts/emulator/hipe/hipe_bif2.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/erts/emulator/hipe/hipe_bif2.c b/erts/emulator/hipe/hipe_bif2.c index 19cd32c68f..2660f74a82 100644 --- a/erts/emulator/hipe/hipe_bif2.c +++ b/erts/emulator/hipe/hipe_bif2.c @@ -1,9 +1,8 @@ /* * %CopyrightBegin% - - * + * * Copyright Ericsson AB 2001-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 @@ -37,14 +36,25 @@ #include "hipe_arch.h" #include "hipe_stack.h" -BIF_RETTYPE hipe_bifs_show_estack_1(BIF_ALIST_1) +static void proc_unlock(Process* c_p, Process* rp) { + ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL; + if (rp == c_p) { + locks &= ~ERTS_PROC_LOCK_MAIN; + } + if (rp && locks) { + erts_smp_proc_unlock(rp, locks); + } +} + +BIF_RETTYPE hipe_bifs_show_estack_1(BIF_ALIST_1) +{ Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_ARG_1, ERTS_PROC_LOCKS_ALL); if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_estack(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } @@ -55,7 +65,7 @@ BIF_RETTYPE hipe_bifs_show_heap_1(BIF_ALIST_1) if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_heap(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } @@ -66,7 +76,7 @@ BIF_RETTYPE hipe_bifs_show_nstack_1(BIF_ALIST_1) if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_nstack(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } @@ -82,7 +92,7 @@ BIF_RETTYPE hipe_bifs_show_pcb_1(BIF_ALIST_1) if (!rp) BIF_ERROR(BIF_P, BADARG); hipe_print_pcb(rp); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); + proc_unlock(BIF_P, rp); BIF_RET(am_true); } -- cgit v1.2.3 From 61c6a52561fd2be45adf3fbf7c65789a161aad38 Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 1 Apr 2011 20:00:20 +0200 Subject: Fix code:is_module_native segv on deleted module --- erts/emulator/beam/beam_bif_load.c | 4 ++-- erts/emulator/test/code_SUITE.erl | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 1ca405961f..5132cd7c8e 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -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 @@ -142,7 +142,7 @@ BIF_RETTYPE code_is_module_native_1(BIF_ALIST_1) if ((modp = erts_get_module(BIF_ARG_1)) == NULL) { return am_undefined; } - return (is_native(modp->code) || + return ((modp->code && is_native(modp->code)) || (modp->old_code != 0 && is_native(modp->old_code))) ? am_true : am_false; } diff --git a/erts/emulator/test/code_SUITE.erl b/erts/emulator/test/code_SUITE.erl index c1a048be75..a062cea117 100644 --- a/erts/emulator/test/code_SUITE.erl +++ b/erts/emulator/test/code_SUITE.erl @@ -498,7 +498,9 @@ do_false_dependency(Init, Code) -> ?line unlink(Pid), exit(Pid, kill), ?line true = erlang:purge_module(cpbugx), ?line true = erlang:delete_module(cpbugx), + ?line code:is_module_native(cpbugx), % test is_module_native on deleted code ?line true = erlang:purge_module(cpbugx), + ?line code:is_module_native(cpbugx), % test is_module_native on purged code ok. false_dependency_loop(Parent, Init, SendInitAck) -> -- cgit v1.2.3 From e240140365fe61f747d788cd4f96f618f76cc63d Mon Sep 17 00:00:00 2001 From: Sverker Eriksson Date: Fri, 6 May 2011 19:58:27 +0200 Subject: hipe_mkliterals print argv[0] in generated files --- erts/emulator/hipe/hipe_mkliterals.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 650861b54b..bced90785d 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -587,9 +587,9 @@ static void print_params(FILE *fp, void (*print_param)(FILE*,const struct rts_pa (*print_param)(fp, &rts_params[i]); } -static int do_c(FILE *fp) +static int do_c(FILE *fp, const char* this_exe) { - fprintf(fp, "/* File: hipe_literals.h, generated by hipe_mkliterals */\n"); + fprintf(fp, "/* File: hipe_literals.h, generated by %s */\n", this_exe); fprintf(fp, "#ifndef __HIPE_LITERALS_H__\n"); fprintf(fp, "#define __HIPE_LITERALS_H__\n\n"); print_literals(fp, c_define_literal); @@ -603,9 +603,9 @@ static int do_c(FILE *fp) return 0; } -static int do_e(FILE *fp) +static int do_e(FILE *fp, const char* this_exe) { - fprintf(fp, "%%%% File: hipe_literals.hrl, generated by hipe_mkliterals"); + fprintf(fp, "%%%% File: hipe_literals.hrl, generated by %s", this_exe); fprintf(fp, "\n\n"); print_literals(fp, e_define_literal); fprintf(fp, "\n"); @@ -622,9 +622,9 @@ int main(int argc, const char **argv) compute_crc(); if (argc == 2) { if (strcmp(argv[1], "-c") == 0) - return do_c(stdout); + return do_c(stdout, argv[0]); if (strcmp(argv[1], "-e") == 0) - return do_e(stdout); + return do_e(stdout, argv[0]); } fprintf(stderr, "usage: %s [-c | -e] > output-file\n", argv[0]); return 1; -- cgit v1.2.3 From 3c0fb92e8eb30fec5def79c80a4e37ee510b87c1 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Wed, 11 May 2011 16:44:09 +0200 Subject: git ignore lib/xmerl/test/xmerl_test.erl --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b35a6c1c31..592ac6668b 100644 --- a/.gitignore +++ b/.gitignore @@ -366,5 +366,6 @@ make/win32/ /lib/xmerl/src/xmerl_sax_parser_*.erl /lib/xmerl/src/xmerl_b64Bin.erl /lib/xmerl/src/xmerl_xpath_parse.erl +/lib/xmerl/test/xmerl_test.erl /lib/erl_interface/config.h.in /lib/configure.in -- cgit v1.2.3 From 416668b76a84d39855b8d78b84838b8a80f40db9 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Wed, 11 May 2011 16:55:21 +0200 Subject: Also updated release notes. --- lib/inets/doc/src/notes.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index b885bcbcdb..edb994a91b 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -104,6 +104,18 @@

*** POTENTIAL INCOMPATIBILITY ***

+ +

[httpc] httpc manager crashes. + When a request results in a retry, the request id will be "reused" + in the previous implementation a race condition could occur causing + the manager to crash.

+

This is now avoided by using proc_lib:init_ack and + gen_server:enter_loop to allow mor advanced initialization of + httpc_handlers without blocking the httpc_manger and eliminating + extra processes that can cause race conditions.

+

Own Id: OTP-9246

+
+
-- cgit v1.2.3 From 229d0d8ca88bc344bed89e46541b325c1d267996 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 6 May 2011 15:58:09 +0200 Subject: r Use Erlang specs and types for documentation --- lib/compiler/doc/src/compile.xml | 2 +- lib/stdlib/doc/specs/.gitignore | 1 + lib/stdlib/doc/src/Makefile | 15 +- lib/stdlib/doc/src/array.xml | 177 ++--- lib/stdlib/doc/src/base64.xml | 35 +- lib/stdlib/doc/src/beam_lib.xml | 271 +++----- lib/stdlib/doc/src/binary.xml | 150 ++--- lib/stdlib/doc/src/c.xml | 127 ++-- lib/stdlib/doc/src/calendar.xml | 226 +++---- lib/stdlib/doc/src/dets.xml | 705 +++++++++----------- lib/stdlib/doc/src/dict.xml | 212 ++---- lib/stdlib/doc/src/digraph.xml | 395 +++++------ lib/stdlib/doc/src/digraph_utils.xml | 170 ++--- lib/stdlib/doc/src/epp.xml | 62 +- lib/stdlib/doc/src/erl_eval.xml | 153 +++-- lib/stdlib/doc/src/erl_expand_records.xml | 6 +- lib/stdlib/doc/src/erl_id_trans.xml | 6 +- lib/stdlib/doc/src/erl_internal.xml | 73 +-- lib/stdlib/doc/src/erl_lint.xml | 50 +- lib/stdlib/doc/src/erl_parse.xml | 122 ++-- lib/stdlib/doc/src/erl_pp.xml | 123 ++-- lib/stdlib/doc/src/erl_scan.xml | 362 +++++----- lib/stdlib/doc/src/ets.xml | 255 +++---- lib/stdlib/doc/src/file_sorter.xml | 266 ++++---- lib/stdlib/doc/src/filelib.xml | 89 +-- lib/stdlib/doc/src/filename.xml | 156 ++--- lib/stdlib/doc/src/gb_sets.xml | 288 +++----- lib/stdlib/doc/src/gb_trees.xml | 216 ++---- lib/stdlib/doc/src/gen_event.xml | 16 +- lib/stdlib/doc/src/io.xml | 354 ++++------ lib/stdlib/doc/src/io_lib.xml | 179 ++--- lib/stdlib/doc/src/lib.xml | 37 +- lib/stdlib/doc/src/lists.xml | 764 +++++++-------------- lib/stdlib/doc/src/log_mf_h.xml | 31 +- lib/stdlib/doc/src/math.xml | 2 +- lib/stdlib/doc/src/ms_transform.xml | 25 +- lib/stdlib/doc/src/orddict.xml | 213 ++---- lib/stdlib/doc/src/ordsets.xml | 153 ++--- lib/stdlib/doc/src/pg.xml | 55 +- lib/stdlib/doc/src/pool.xml | 46 +- lib/stdlib/doc/src/proc_lib.xml | 163 ++--- lib/stdlib/doc/src/proplists.xml | 210 ++---- lib/stdlib/doc/src/qlc.xml | 646 ++++++++---------- lib/stdlib/doc/src/queue.xml | 286 +++----- lib/stdlib/doc/src/random.xml | 50 +- lib/stdlib/doc/src/re.xml | 131 ++-- lib/stdlib/doc/src/regexp.xml | 152 ++--- lib/stdlib/doc/src/sets.xml | 153 ++--- lib/stdlib/doc/src/shell.xml | 37 +- lib/stdlib/doc/src/slave.xml | 60 +- lib/stdlib/doc/src/sofs.xml | 1021 ++++++++++++----------------- lib/stdlib/doc/src/specs.xml | 63 ++ lib/stdlib/doc/src/string.xml | 230 +++---- lib/stdlib/doc/src/supervisor.xml | 218 +++--- lib/stdlib/doc/src/supervisor_bridge.xml | 47 +- lib/stdlib/doc/src/sys.xml | 212 +++--- lib/stdlib/doc/src/timer.xml | 173 +++-- lib/stdlib/doc/src/unicode.xml | 174 ++--- lib/stdlib/doc/src/win32reg.xml | 98 +-- lib/stdlib/doc/src/zip.xml | 281 +++----- 60 files changed, 4347 insertions(+), 6646 deletions(-) create mode 100644 lib/stdlib/doc/specs/.gitignore create mode 100644 lib/stdlib/doc/src/specs.xml diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index f2af932aef..830c89ae84 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -68,7 +68,7 @@ to be an error if the module name in the source code is not the same as the basename of the output file.

-

Here follows first all elements of Options that in +

Here follows first all elements of Options that in some way control the behavior of the compiler.

basic_validation diff --git a/lib/stdlib/doc/specs/.gitignore b/lib/stdlib/doc/specs/.gitignore new file mode 100644 index 0000000000..322eebcb06 --- /dev/null +++ b/lib/stdlib/doc/specs/.gitignore @@ -0,0 +1 @@ +specs_*.xml diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index b558697d63..16e0a86e3b 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/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 @@ -125,18 +125,24 @@ HTML_REF_MAN_FILE = $(HTMLDIR)/index.html TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf +SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) + +TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- XML_FLAGS += +SPECS_FLAGS = -I../../include -I../../../kernel/include + # ---------------------------------------------------- # Targets # ---------------------------------------------------- $(HTMLDIR)/%.gif: %.gif $(INSTALL_DATA) $< $@ -docs: pdf html man +docs: man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -155,8 +161,13 @@ clean clean_docs: rm -f $(MAN3DIR)/* rm -f $(MAN6DIR)/* rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* rm -f errs core *~ +$(SPECDIR)/specs_erl_id_trans.xml: + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ + -o$(dir $@) -module erl_id_trans + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/stdlib/doc/src/array.xml b/lib/stdlib/doc/src/array.xml index 5c3ac6a2a9..a79fcd487e 100644 --- a/lib/stdlib/doc/src/array.xml +++ b/lib/stdlib/doc/src/array.xml @@ -3,7 +3,7 @@
- 20072009 + 20072011 Ericsson AB. All Rights Reserved. @@ -82,19 +82,35 @@ the default value cannot be confused with the values of set entries.

%% allow accesses beyond the last set entry {'EXIT',{badarg,_}} = (catch array:set(18, true, A3)). {'EXIT',{badarg,_}} = (catch array:get(18, A3)). -
DATA TYPES - - -array() - -

A functional, extendible array. The representation is - not documented and is subject to change without notice. Note that - arrays cannot be directly compared for equality.

-
-
+ + + array() + +

A functional, extendible array. The representation is + not documented and is subject to change without notice. Note that + arrays cannot be directly compared for equality.

+
+
+ + + + + + + + + + + + + + + +
+ -default(Array::array()) -> term() + Get the value used for uninitialized entries. @@ -104,7 +120,7 @@ the default value cannot be confused with the values of set entries.

See also: new/2.

-fix(Array::array()) -> array() + Fix the size of the array. @@ -114,105 +130,100 @@ the default value cannot be confused with the values of set entries.

See also: relax/1.

-foldl(Function, InitialAcc::term(), Array::array()) -> term() + Fold the elements of the array using the given function and initial accumulator value. - -Function = (Index::integer(), Value::term(), Acc::term()) -> term() -

Fold the elements of the array using the given function and initial accumulator value. The elements are visited in order from the - lowest index to the highest. If Function is not a function, the + lowest index to the highest. If Function is not a function, the call fails with reason badarg.

See also: foldr/3, map/2, sparse_foldl/3.

-foldr(Function, InitialAcc::term(), Array::array()) -> term() + Fold the elements of the array right-to-left using the given function and initial accumulator value. - -Function = (Index::integer(), Value::term(), Acc::term()) -> term()

Fold the elements of the array right-to-left using the given function and initial accumulator value. The elements are visited in - order from the highest index to the lowest. If Function is not a + order from the highest index to the lowest. If Function is not a function, the call fails with reason badarg.

See also: foldl/3, map/2.

-from_list(List::list()) -> array() + Equivalent to from_list(List, undefined). -

Equivalent to from_list(List, undefined).

+

Equivalent to from_list(List, undefined).

-from_list(List::list(), Default::term()) -> array() + Convert a list to an extendible array. -

Convert a list to an extendible array. Default is used as the value - for uninitialized entries of the array. If List is not a proper list, +

Convert a list to an extendible array. Default is used as the value + for uninitialized entries of the array. If List is not a proper list, the call fails with reason badarg.

See also: new/2, to_list/1.

-from_orddict(Orddict::list()) -> array() + Equivalent to from_orddict(Orddict, undefined). -

Equivalent to from_orddict(Orddict, undefined).

+

Equivalent to from_orddict(Orddict, undefined).

-from_orddict(List::list(), Default::term()) -> array() + Convert an ordered list of pairs {Index, Value} to a corresponding extendible array.

Convert an ordered list of pairs {Index, Value} to a - corresponding extendible array. Default is used as the value for - uninitialized entries of the array. If List is not a proper, + corresponding extendible array. Default is used as the value for + uninitialized entries of the array. If Orddict is not a proper, ordered list of pairs whose first elements are nonnegative integers, the call fails with reason badarg.

See also: new/2, to_orddict/1.

-get(I::integer(), Array::array()) -> term() + Get the value of entry I. -

Get the value of entry I. If I is not a nonnegative - integer, or if the array has fixed size and I is larger than the +

Get the value of entry I. If I is not a nonnegative + integer, or if the array has fixed size and I is larger than the maximum index, the call fails with reason badarg.

If the array does not have fixed size, this function will return the - default value for any index I greater than size(Array)-1.

+ default value for any index I greater than size(Array)-1.

See also: set/3.

-is_array(X::term()) -> bool() + Returns true if X appears to be an array, otherwise false. -

Returns true if X appears to be an array, otherwise false. - Note that the check is only shallow; there is no guarantee that X +

Returns true if X appears to be an array, otherwise false. + Note that the check is only shallow; there is no guarantee that X is a well-formed array representation even if this function returns true.

-is_fix(Array::array()) -> bool() + Check if the array has fixed size. @@ -222,20 +233,18 @@ the default value cannot be confused with the values of set entries.

See also: fix/1.

-map(Function, Array::array()) -> array() + Map the given function onto each element of the array. - -Function = (Index::integer(), Value::term()) -> term()

Map the given function onto each element of the array. The elements are visited in order from the lowest index to the highest. - If Function is not a function, the call fails with reason badarg. + If Function is not a function, the call fails with reason badarg.

See also: foldl/3, foldr/3, sparse_map/2.

-new() -> array() + Create a new, extendible array with initial size zero. @@ -244,7 +253,7 @@ the default value cannot be confused with the values of set entries.

See also: new/1, new/2.

-new(Options::term()) -> array() + Create a new array according to the given options. @@ -253,10 +262,10 @@ the default value cannot be confused with the values of set entries.

the array is extendible and has initial size zero. Array indices start at 0.

-

Options is a single term or a list of terms, selected from the +

Options is a single term or a list of terms, selected from the following:

- N::integer() or {size, N::integer()} + N::integer() >= 0 or {size, N::integer() >= 0}

Specifies the initial size of the array; this also implies {fixed, true}. If N is not a nonnegative integer, the call fails with reason badarg.

@@ -283,19 +292,19 @@ cannot be changed once the array has been created.

See also: fix/1, from_list/2, get/2, new/0, new/2, set/3.

-new(Size::integer(), Options::term()) -> array() + Create a new array according to the given size and options.

Create a new array according to the given size and options. If - Size is not a nonnegative integer, the call fails with reason + Size is not a nonnegative integer, the call fails with reason badarg. By default, the array has fixed size. Note that any size - specifications in Options will override the Size parameter.

+ specifications in Options will override the Size parameter.

-

If Options is a list, this is simply equivalent to new([{size, - Size} | Options], otherwise it is equivalent to new([{size, Size} | - [Options]]. However, using this function directly is more efficient.

+

If Options is a list, this is simply equivalent to new([{size, + Size} | Options], otherwise it is equivalent to new([{size, Size} | + [Options]]. However, using this function directly is more efficient.

Example:

     array:new(100, {default,0})

creates a fixed-size array of size @@ -304,7 +313,7 @@ cannot be changed once the array has been created.

See also: new/1.

-relax(Array::array()) -> array() + Make the array resizable. @@ -313,24 +322,24 @@ cannot be changed once the array has been created.

See also: fix/1.

-reset(I::integer(), Array::array()) -> array() + Reset entry I to the default value for the array. -

Reset entry I to the default value for the array. - If the value of entry I is the default value the array will be +

Reset entry I to the default value for the array. + If the value of entry I is the default value the array will be returned unchanged. Reset will never change size of the array. Shrinking can be done explicitly by calling resize/2.

-

If I is not a nonnegative integer, or if the array has fixed size - and I is larger than the maximum index, the call fails with reason +

If I is not a nonnegative integer, or if the array has fixed size + and I is larger than the maximum index, the call fails with reason badarg; cf. set/3

See also: new/2, set/3.

-resize(Array::array()) -> array() + Change the size of the array to that reported by sparse_size/1. @@ -340,90 +349,84 @@ cannot be changed once the array has been created.

See also: resize/2, sparse_size/1.

-resize(Size::integer(), Array::array()) -> array() + Change the size of the array. -

Change the size of the array. If Size is not a nonnegative +

Change the size of the array. If Size is not a nonnegative integer, the call fails with reason badarg. If the given array has fixed size, the resulting array will also have fixed size.

-set(I::integer(), Value::term(), Array::array()) -> array() + Set entry I of the array to Value. -

Set entry I of the array to Value. If I is not a - nonnegative integer, or if the array has fixed size and I is larger +

Set entry I of the array to Value. If I is not a + nonnegative integer, or if the array has fixed size and I is larger than the maximum index, the call fails with reason badarg.

-

If the array does not have fixed size, and I is greater than - size(Array)-1, the array will grow to size I+1. +

If the array does not have fixed size, and I is greater than + size(Array)-1, the array will grow to size I+1.

See also: get/2, reset/2.

-size(Array::array()) -> integer() + Get the number of entries in the array.

Get the number of entries in the array. Entries are numbered - from 0 to size(Array)-1; hence, this is also the index of the first + from 0 to size(Array)-1; hence, this is also the index of the first entry that is guaranteed to not have been previously set.

See also: set/3, sparse_size/1.

-sparse_foldl(Function, InitialAcc::term(), Array::array()) -> term() + Fold the elements of the array using the given function and initial accumulator value, skipping default-valued entries. - -Function = (Index::integer(), Value::term(), Acc::term()) -> term()

Fold the elements of the array using the given function and initial accumulator value, skipping default-valued entries. The elements are visited in order from the lowest index to the highest. - If Function is not a function, the call fails with reason badarg. + If Function is not a function, the call fails with reason badarg.

See also: foldl/3, sparse_foldr/3.

-sparse_foldr(Function, InitialAcc::term(), Array::array()) -> term() + Fold the elements of the array right-to-left using the given function and initial accumulator value, skipping default-valued entries. - -Function = (Index::integer(), Value::term(), Acc::term()) -> term()

Fold the elements of the array right-to-left using the given function and initial accumulator value, skipping default-valued entries. The elements are visited in order from the highest index to - the lowest. If Function is not a function, the call fails with + the lowest. If Function is not a function, the call fails with reason badarg.

See also: foldr/3, sparse_foldl/3.

-sparse_map(Function, Array::array()) -> array() + Map the given function onto each element of the array, skipping default-valued entries. - -Function = (Index::integer(), Value::term()) -> term()

Map the given function onto each element of the array, skipping default-valued entries. The elements are visited in order from the - lowest index to the highest. If Function is not a function, the + lowest index to the highest. If Function is not a function, the call fails with reason badarg.

See also: map/2.

-sparse_size(A::array()) -> integer() + Get the number of entries in the array up until the last non-default valued entry. @@ -436,7 +439,7 @@ cannot be changed once the array has been created.

See also: resize/1, size/1.

-sparse_to_list(Array::array()) -> list() + Converts the array to a list, skipping default-valued entries. @@ -446,7 +449,7 @@ cannot be changed once the array has been created.

See also: to_list/1.

-sparse_to_orddict(Array::array()) -> [{Index::integer(), Value::term()}] + Convert the array to an ordered list of pairs {Index, Value}, skipping default-valued entries. @@ -458,7 +461,7 @@ cannot be changed once the array has been created.

See also: to_orddict/1.

-to_list(Array::array()) -> list() + Converts the array to a list. @@ -468,7 +471,7 @@ cannot be changed once the array has been created.

See also: from_list/2, sparse_to_list/1.

-to_orddict(Array::array()) -> [{Index::integer(), Value::term()}] + Convert the array to an ordered list of pairs {Index, Value}. diff --git a/lib/stdlib/doc/src/base64.xml b/lib/stdlib/doc/src/base64.xml index d3fd7a843b..bfe8494a73 100644 --- a/lib/stdlib/doc/src/base64.xml +++ b/lib/stdlib/doc/src/base64.xml @@ -4,7 +4,7 @@
- 20072009 + 20072011 Ericsson AB. All Rights Reserved. @@ -33,32 +33,33 @@

Implements base 64 encode and decode, see RFC2045.

+ + + + + - encode(Data) -> Base64 - encode_to_string(Data) -> Base64String + + Encodes data into base64. - - Data = string() | binary() - Base64 = binary() - Base64String = string() - + + +

Encodes a plain ASCII string into base64. The result will be 33% larger than the data.

- decode(Base64) -> Data - decode_to_string(Base64) -> DataString - mime_decode(Base64) -> Data - mime_decode_to_string(Base64) -> DataString + + + + Decodes a base64 encoded string to data. - - Base64 = string() | binary() - Data = binary() - DataString = string() - + + +

Decodes a base64 encoded string to plain ASCII. See RFC4648. mime_decode/1 and mime_decode_to_string/1 diff --git a/lib/stdlib/doc/src/beam_lib.xml b/lib/stdlib/doc/src/beam_lib.xml index adc411e272..013e94c393 100644 --- a/lib/stdlib/doc/src/beam_lib.xml +++ b/lib/stdlib/doc/src/beam_lib.xml @@ -4,7 +4,7 @@

- 20002010 + 20002011 Ericsson AB. All Rights Reserved. @@ -154,70 +154,78 @@
-
- DATA TYPES - -beam() -> Module | Filename | binary() - Module = atom() - Filename = string() | atom() -

Each of the functions described below accept either the module - name, the filename, or a binary containing the beam module.

- -chunkdata() = {ChunkId, DataB} | {ChunkName, DataT} - ChunkId = chunkid() - DataB = binary() - {ChunkName, DataT} = - {abstract_code, AbstractCode} - | {attributes, [{Attribute, [AttributeValue]}]} - | {compile_info, [{InfoKey, [InfoValue]}]} - | {exports, [{Function, Arity}]} - | {labeled_exports, [{Function, Arity, Label}]} - | {imports, [{Module, Function, Arity}]} - | {indexed_imports, [{Index, Module, Function, Arity}]} - | {locals, [{Function, Arity}]}]} - | {labeled_locals, [{Function, Arity, Label}]}]} - | {atoms, [{integer(), atom()}]} - AbstractCode = {AbstVersion, Forms} | no_abstract_code - AbstVersion = atom() - Attribute = atom() - AttributeValue = term() - Module = Function = atom() - Arity = int() - Label = int() -

It is not checked that the forms conform to the abstract format - indicated by AbstVersion. no_abstract_code means - that the "Abst" chunk is present, but empty.

-

The list of attributes is sorted on Attribute, and each - attribute name occurs once in the list. The attribute values - occur in the same order as in the file. The lists of functions - are also sorted.

- -chunkid() = "Abst" | "Attr" | "CInf" - | "ExpT" | "ImpT" | "LocT" - | "Atom" + + + + +

Each of the functions described below accept either the + module name, the filename, or a binary containing the beam + module.

+
+
+ + + +

The list of attributes is sorted on Attribute + (in attrib_entry()), and each + attribute name occurs once in the list. The attribute values + occur in the same order as in the file. The lists of functions + are also sorted.

+
+
+ + + +

"Abst" | "Attr" | "CInf" | "ExpT" | "ImpT" | "LocT" | "Atom"

+
+
+ + + + + + +

It is not checked that the forms conform to the abstract format + indicated by AbstVersion. no_abstract_code means + that the "Abst" chunk is present, but empty.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-chunkname() = abstract_code | attributes | compile_info - | exports | labeled_exports - | imports | indexed_imports - | locals | labeled_locals - | atoms - -chunkref() = chunkname() | chunkid()
-
- chunks(Beam, [ChunkRef]) -> {ok, {Module, [ChunkData]}} | {error, beam_lib, Reason} + Read selected chunks from a BEAM file or binary - - Beam = beam() - ChunkRef = chunkref() - Module = atom() - ChunkData = chunkdata() - Reason = {unknown_chunk, Filename, atom()} -   | {key_missing_or_invalid, Filename, abstract_code} -   | Reason1 -- see info/1 -  Filename = string() -

Reads chunk data for selected chunks refs. The order of the returned list of chunk data is determined by the order @@ -225,43 +233,26 @@ chunkref() = chunkname() | chunkid() - chunks(Beam, [ChunkRef], [Option]) -> {ok, {Module, [ChunkResult]}} | {error, beam_lib, Reason} + Read selected chunks from a BEAM file or binary - - Beam = beam() - ChunkRef = chunkref() - Module = atom() - Option = allow_missing_chunks - ChunkResult = {chunkref(), ChunkContents} | {chunkref(), missing_chunk} - Reason = {missing_chunk, Filename, atom()} -   | {key_missing_or_invalid, Filename, abstract_code} -   | Reason1 -- see info/1 -  Filename = string() -

Reads chunk data for selected chunks refs. The order of the returned list of chunk data is determined by the order of the list of chunks references.

-

By default, if any requested chunk is missing in Beam, +

By default, if any requested chunk is missing in Beam, an error tuple is returned. However, if the option allow_missing_chunks has been given, a result will be returned even if chunks are missing. In the result list, any missing chunks will be represented as - {ChunkRef,missing_chunk}. + {ChunkRef,missing_chunk}. Note, however, that if the "Atom" chunk if missing, that is considered a fatal error and the return value will be an error tuple.

- version(Beam) -> {ok, {Module, [Version]}} | {error, beam_lib, Reason} + Read the BEAM file's module version - - Beam = beam() - Module = atom() - Version = term() - Reason -- see chunks/2 -

Returns the module version(s). A version is defined by the module attribute -vsn(Vsn). If this attribute is @@ -282,51 +273,30 @@ chunkref() = chunkname() | chunkid() - md5(Beam) -> {ok, {Module, MD5}} | {error, beam_lib, Reason} + Read the BEAM file's module version - - Beam = beam() - Module = atom() - MD5 = binary() - Reason -- see chunks/2 -

Calculates an MD5 redundancy check for the code of the module (compilation date and other attributes are not included).

- info(Beam) -> [{Item, Info}] | {error, beam_lib, Reason1} + Information about a BEAM file - - Beam = beam() - Item, Info -- see below - Reason1 = {chunk_too_big, Filename, ChunkId, ChunkSize, FileSize} -   | {invalid_beam_file, Filename, Pos} -   | {invalid_chunk, Filename, ChunkId} -   | {missing_chunk, Filename, ChunkId} -   | {not_a_beam_file, Filename} -   | {file_error, Filename, Posix} -  Filename = string() -  ChunkId = chunkid() -  ChunkSize = FileSize = int() -  Pos = int() -  Posix = posix() -- see file(3) -

Returns a list containing some information about a BEAM file as tuples {Item, Info}:

- {file, Filename} | {binary, Binary} + {file, Filename} | {binary, Binary}

The name (string) of the BEAM file, or the binary from which the information was extracted.

- {module, Module} + {module, Module}

The name (atom) of the module.

- {chunks, [{ChunkId, Pos, Size}]} + {chunks, [{ChunkId, Pos, Size}]}

For each chunk, the identifier (string) and the position and size of the chunk data, in bytes.

@@ -335,17 +305,9 @@ chunkref() = chunkname() | chunkid()
- cmp(Beam1, Beam2) -> ok | {error, beam_lib, Reason} + Compare two BEAM files - - Beam1 = Beam2 = beam() - Reason = {modules_different, Module1, Module2} -   | {chunks_different, ChunkId} -   | different_chunks -   | Reason1 -- see info/1 -  Module1 = Module2 = atom() -  ChunkId = chunkid() - +

Compares the contents of two BEAM files. If the module names are the same, and all chunks except for the "CInf" chunk @@ -356,34 +318,23 @@ chunkref() = chunkname() | chunkid() - cmp_dirs(Dir1, Dir2) -> {Only1, Only2, Different} | {error, beam_lib, Reason1} + Compare the BEAM files in two directories - - Dir1 = Dir2 = string() | atom() - Different = [{Filename1, Filename2}] - Only1 = Only2 = [Filename] - Filename = Filename1 = Filename2 = string() - Reason1 = {not_a_directory, term()} | -- see info/1 -

The cmp_dirs/2 function compares the BEAM files in two directories. Only files with extension ".beam" are - compared. BEAM files that exist in directory Dir1 - (Dir2) only are returned in Only1 - (Only2). BEAM files that exist on both directories but + compared. BEAM files that exist in directory Dir1 + (Dir2) only are returned in Only1 + (Only2). BEAM files that exist on both directories but are considered different by cmp/2 are returned as - pairs {Filename1, Filename2} where - Filename1 (Filename2) exists in directory - Dir1 (Dir2).

+ pairs {Filename1, Filename2} where + Filename1 (Filename2) exists in directory + Dir1 (Dir2).

- diff_dirs(Dir1, Dir2) -> ok | {error, beam_lib, Reason1} + Compare the BEAM files in two directories - - Dir1 = Dir2 = string() | atom() - Reason1 = {not_a_directory, term()} | -- see info/1 -

The diff_dirs/2 function compares the BEAM files in two directories the way cmp_dirs/2 does, but names of @@ -392,13 +343,8 @@ chunkref() = chunkname() | chunkid() - strip(Beam1) -> {ok, {Module, Beam2}} | {error, beam_lib, Reason1} + Removes chunks not needed by the loader from a BEAM file - - Beam1 = Beam2 = beam() - Module = atom() - Reason1 -- see info/1 -

The strip/1 function removes all chunks from a BEAM file except those needed by the loader. In particular, @@ -406,15 +352,8 @@ chunkref() = chunkname() | chunkid() - strip_files(Files) -> {ok, [{Module, Beam2}]} | {error, beam_lib, Reason1} + Removes chunks not needed by the loader from BEAM files - - Files = [Beam1] -  Beam1 = beam() - Module = atom() - Beam2 = beam() - Reason1 -- see info/1 -

The strip_files/1 function removes all chunks except those needed by the loader from BEAM files. In particular, @@ -424,30 +363,20 @@ chunkref() = chunkname() | chunkid() - strip_release(Dir) -> {ok, [{Module, Filename]}} | {error, beam_lib, Reason1} + Removes chunks not needed by the loader from all BEAM files of a release - - Dir = string() | atom() - Module = atom() - Filename = string() - Reason1 = {not_a_directory, term()} | -- see info/1 -

The strip_release/1 function removes all chunks except those needed by the loader from the BEAM files of a - release. Dir should be the installation root + release. Dir should be the installation root directory. For example, the current OTP release can be stripped with the call beam_lib:strip_release(code:root_dir()).

- format_error(Reason) -> Chars + Return an English description of a BEAM read error reply - - Reason -- see other functions - Chars = [char() | Chars] -

Given the error returned by any function in this module, the function format_error returns a descriptive string @@ -456,12 +385,11 @@ chunkref() = chunkname() | chunkid() - crypto_key_fun(CryptoKeyFun) -> ok | {error, Reason} + Register a fun that provides a crypto key - - CryptoKeyFun = fun() -- see below - Reason = badfun | exists | term() - + + +

The crypto_key_fun/1 function registers a unary fun that will be called if beam_lib needs to read an @@ -495,11 +423,8 @@ chunkref() = chunkname() | chunkid() - clear_crypto_key_fun() -> {ok, Result} + Unregister the current crypto key fun - - Result = undefined | term() -

Unregisters the crypto key fun and terminates the process holding it, started by crypto_key_fun/1.

diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index c81023862e..88ce77e0d0 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -5,7 +5,7 @@
2009 - 2010 + 2011 Ericsson AB, All Rights Reserved @@ -53,37 +53,35 @@ module.

- - -
- DATA TYPES - - cp() - - Opaque data-type representing a compiled search-pattern. Guaranteed to be a tuple() - to allow programs to distinguish it from non precompiled search patterns. - - - part() = {Start,Length} - Start = int() - Length = int() - - A representaion of a part (or range) in a binary. Start is a + + + +

Opaque data-type representing a compiled + search-pattern. Guaranteed to be a tuple() to allow programs to + distinguish it from non precompiled search patterns.

+
+
+ + +

A representaion of a part (or range) in a binary. Start is a zero-based offset into a binary() and Length is the length of that part. As input to functions in this module, a reverse part specification is allowed, constructed with a negative Length, so that the part of the binary begins at Start + Length and is -Length long. This is useful for referencing the last N bytes of a binary as {size(Binary), -N}. The functions - in this module always return part()'s with positive Length. - -

+ in this module always return part()'s with positive Length.

+ + + - at(Subject, Pos) -> int() + at(Subject, Pos) -> byte() Returns the byte at a specific position in a binary Subject = binary() - Pos = int() >= 0 + Pos = integer() >= 0 @@ -95,7 +93,7 @@ - bin_to_list(Subject) -> list() + bin_to_list(Subject) -> [byte()] Convert a binary to a list of integers Subject = binary() @@ -105,7 +103,7 @@ - bin_to_list(Subject, PosLen) -> list() + bin_to_list(Subject, PosLen) -> [byte()] Convert a binary to a list of integers Subject = binary() @@ -113,7 +111,7 @@ -

Converts Subject to a list of int()s, each representing +

Converts Subject to a list of byte()s, each representing the value of one byte. The part() denotes which part of the binary() to convert. Example:

@@ -126,12 +124,12 @@
- bin_to_list(Subject, Pos, Len) -> list() + bin_to_list(Subject, Pos, Len) -> [byte()] Convert a binary to a list of integers Subject = binary() - Pos = int() - Len = int() + Pos = integer() >= 0 + Len = integer() >= 0

The same as bin_to_list(Subject,{Pos,Len}).

@@ -185,7 +183,7 @@ Duplicates a binary N times and creates a new Subject = binary() - N = int() >= 0 + N = integer() >= 0

Creates a binary with the content of Subject duplicated N times.

@@ -211,7 +209,7 @@ Decode a whole binary into an integer of arbitrary size Subject = binary() - Unsigned = int() >= 0 + Unsigned = integer() >= 0

The same as decode_unsigned(Subject,big).

@@ -223,12 +221,12 @@ Subject = binary() Endianess = big | little - Unsigned = int() >= 0 + Unsigned = integer() >= 0

Converts the binary digit representation, in big or little - endian, of a positive integer in Subject to an Erlang int().

+ endian, of a positive integer in Subject to an Erlang integer().

Example:

@@ -242,7 +240,7 @@ encode_unsigned(Unsigned) -> binary() Encodes an unsigned integer into the minimal binary - Unsigned = int() >= 0 + Unsigned = integer() >= 0

The same as encode_unsigned(Unsigned,big).

@@ -252,7 +250,7 @@ encode_unsigned(Unsigned,Endianess) -> binary() Encodes an unsigned integer into the minimal binary - Unsigned = int() >= 0 + Unsigned = integer() >= 0 Endianess = big | little @@ -270,7 +268,7 @@
- first(Subject) -> int() + first(Subject) -> byte() Returns the first byte of a binary Subject = binary() @@ -283,7 +281,7 @@ - last(Subject) -> int() + last(Subject) -> byte() Returns the last byte of a binary Subject = binary() @@ -306,7 +304,7 @@ - longest_common_prefix(Binaries) -> int() + longest_common_prefix(Binaries) -> integer() >= 0 Returns length of longest common prefix for a set of binaries Binaries = [ binary() ] @@ -327,7 +325,7 @@ - longest_common_suffix(Binaries) -> int() + longest_common_suffix(Binaries) -> integer() >= 0 Returns length of longest common suffix for a set of binaries Binaries = [ binary() ] @@ -450,7 +448,7 @@ [{1,4}] -

The result shows that <<bcde">> is selected instead of the +

The result shows that <<"bcde">> is selected instead of the shorter match <<"bc">> (which would have given raise to one more match,<<"de">>). This corresponds to the behavior of posix regular expressions (and programs like awk), but is not @@ -506,15 +504,15 @@ Extracts a part of a binary Subject = binary() - Pos = int() - Len = int() + Pos = integer() >= 0 + Len = integer() >= 0

The same as part(Subject, {Pos, Len}).

- referenced_byte_size(binary()) -> int() + referenced_byte_size(binary()) -> integer() >= 0 Determines the size of the actual binary pointed out by a sub-binary @@ -581,42 +579,28 @@ store(Binary, GBSet) -> - replace(Subject,Pattern,Replacement) -> Result + Replaces bytes in a binary according to a pattern - - Subject = binary() - Pattern = binary() | [ binary() ] | cp() - Replacement = binary() - Result = binary() - -

The same as replace(Subject,Pattern,Replacement,[]).

+

The same as replace(Subject,Pattern,Replacement,[]).

- replace(Subject,Pattern,Replacement,Options) -> Result + Replaces bytes in a binary according to a pattern - - Subject = binary() - Pattern = binary() | [ binary() ] | cp() - Replacement = binary() - Result = binary() - Options = [ Option ] - Option = global | {scope, part()} | {insert_replaced, InsPos} - InsPos = OnePos | [ OnePos ] - OnePos = int() =< byte_size(Replacement) - + An integer() =< byte_size(Replacement) +

Constructs a new binary by replacing the parts in - Subject matching Pattern with the content of - Replacement.

+ Subject matching Pattern with the content of + Replacement.

-

If the matching sub-part of Subject giving raise to the +

If the matching sub-part of Subject giving raise to the replacement is to be inserted in the result, the option - {insert_replaced, InsPos} will insert the matching part into - Replacement at the given position (or positions) before actually - inserting Replacement into the Subject. Example:

+ {insert_replaced, InsPos} will insert the matching part into + Replacement at the given position (or positions) before actually + inserting Replacement into the Subject. Example:

1> binary:replace(<<"abcde">>,<<"b">>,<<"[]">>,[{insert_replaced,1}]). @@ -632,42 +616,30 @@ store(Binary, GBSet) -> <<"a[b-b]c[d-d]e">> -

If any position given in InsPos is greater than the size of the replacement binary, a badarg exception is raised.

+

If any position given in InsPos is greater than the size of the replacement binary, a badarg exception is raised.

The options global and {scope, part()} work as for split/3. The return type is always a binary().

-

For a description of Pattern, see compile_pattern/1.

+

For a description of Pattern, see compile_pattern/1.

- split(Subject,Pattern) -> Parts + Splits a binary according to a pattern - - Subject = binary() - Pattern = binary() | [ binary() ] | cp() - Parts = [ binary() ] - -

The same as split(Subject, Pattern, []).

+

The same as split(Subject, Pattern, []).

- split(Subject,Pattern,Options) -> Parts + Splits a binary according to a pattern - - Subject = binary() - Pattern = binary() | [ binary() ] | cp() - Parts = [ binary() ] - Options = [ Option ] - Option = {scope, part()} | trim | global - -

Splits Binary into a list of binaries based on Pattern. If +

Splits Subject into a list of binaries based on Pattern. If the option global is not given, only the first occurrence of - Pattern in Subject will give rise to a split.

+ Pattern in Subject will give rise to a split.

-

The parts of Pattern actually found in Subject are not included in the result.

+

The parts of Pattern actually found in Subject are not included in the result.

Example:

@@ -696,7 +668,7 @@ store(Binary, GBSet) -> global -

Repeats the split until the Subject is +

Repeats the split until the Subject is exhausted. Conceptually the global option makes split work on the positions returned by matches/3, while it normally @@ -716,12 +688,12 @@ store(Binary, GBSet) ->

The return type is always a list of binaries that are all - referencing Subject. This means that the data in Subject is not - actually copied to new binaries and that Subject cannot be + referencing Subject. This means that the data in Subject is not + actually copied to new binaries and that Subject cannot be garbage collected until the results of the split are no longer referenced.

-

For a description of Pattern, see compile_pattern/1.

+

For a description of Pattern, see compile_pattern/1.

diff --git a/lib/stdlib/doc/src/c.xml b/lib/stdlib/doc/src/c.xml index 19e3ac1f08..ddae388a1b 100644 --- a/lib/stdlib/doc/src/c.xml +++ b/lib/stdlib/doc/src/c.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -39,43 +39,33 @@ - bt(Pid) -> void() + Stack backtrace for a process - - Pid = pid() -

Stack backtrace for a process. Equivalent to - erlang:process_display(Pid, backtrace).

+ erlang:process_display(Pid, backtrace).

- c(File) -> {ok, Module} | error - c(File, Options) -> {ok, Module} | error + + Compile and load code in a file - - File = name() -- see filename(3) - Options = [Opt] -- see compile:file/2 -

c/1,2 compiles and then purges and loads the code for - a file. Options defaults to []. Compilation is + a file. Options defaults to []. Compilation is equivalent to:

-compile:file(File, Options ++ [report_errors, report_warnings]) +compile:file(File, Options ++ [report_errors, report_warnings])

Note that purging the code means that any processes lingering in old code for the module are killed without warning. See code/3 for more information.

- cd(Dir) -> void() + Change working directory - - Dir = name() -- see filename(3) - -

Changes working directory to Dir, which may be a +

Changes working directory to Dir, which may be a relative name, and then prints the name of the new working directory.

@@ -84,14 +74,14 @@ compile:file(File, Options ++ [report_errors, report_warnings])
       
     
     
-      flush() -> void()
+      
       Flush any messages sent to the shell
       
         

Flushes any messages sent to the shell.

- help() -> void() + Help information

Displays help information: all valid shell internal commands, @@ -99,8 +89,8 @@ compile:file(File, Options ++ [report_errors, report_warnings]) - i() -> void() - ni() -> void() + + Information about the system

i/0 displays information about the system, listing @@ -109,26 +99,20 @@ compile:file(File, Options ++ [report_errors, report_warnings]) - i(X, Y, Z) -> void() + Information about pid <X.Y.Z> - - X = Y = Z = int() -

Displays information about a process, Equivalent to - process_info(pid(X, Y, Z)), but location transparent.

+ process_info(pid(X, Y, Z)), but location transparent.

- l(Module) -> void() + Load or reload module - - Module = atom() -

Purges and loads, or reloads, a module by calling - code:purge(Module) followed by - code:load_file(Module).

+ code:purge(Module) followed by + code:load_file(Module).

Note that purging the code means that any processes lingering in old code for the module are killed without warning. See code/3 for more information.

@@ -136,35 +120,33 @@ compile:file(File, Options ++ [report_errors, report_warnings])
lc(Files) -> ok - Compile a list of files Files = [File] -  File = name() -- see filename(3) + File = file:filename() + + Compile a list of files

Compiles a list of files by calling compile:file(File, [report_errors, report_warnings]) for each File in Files.

- ls() -> void() + List files in the current directory

Lists files in the current directory.

- ls(Dir) -> void() + List files in a directory - - Dir = name() -- see filename(3) - -

Lists files in directory Dir.

+

Lists files in directory Dir.

- m() -> void() + Which modules are loaded

Displays information about the loaded modules, including @@ -172,84 +154,67 @@ compile:file(File, Options ++ [report_errors, report_warnings]) - m(Module) -> void() + Information about a module - - Module = atom() - -

Displays information about Module.

+

Displays information about Module.

- memory() -> [{Type, Size}] + Memory allocation information - - Type, Size -- see erlang:memory/0 -

Memory allocation information. Equivalent to - erlang:memory/0.

+ erlang:memory/0 + .

- memory(Type) -> Size - memory([Type]) -> [{Type, Size}] + + Memory allocation information - - Type, Size -- see erlang:memory/0 -

Memory allocation information. Equivalent to - erlang:memory/1.

+ erlang:memory/1 + .

- nc(File) -> {ok, Module} | error - nc(File, Options) -> {ok, Module} | error + + Compile and load code in a file on all nodes - - File = name() -- see filename(3) - Options = [Opt] -- see compile:file/2 -

Compiles and then loads the code for a file on all nodes. - Options defaults to []. Compilation is equivalent to:

+ Options defaults to []. Compilation is equivalent to:

-compile:file(File, Opts ++ [report_errors, report_warnings]) +compile:file(File, Options ++ [report_errors, report_warnings])
- nl(Module) -> void() + Load module on all nodes - - Module = atom() - -

Loads Module on all nodes.

+

Loads Module on all nodes.

- pid(X, Y, Z) -> pid() + Convert X,Y,Z to a pid - - X = Y = Z = int() - -

Converts X, Y, Z to the pid +

Converts X, Y, Z to the pid ]]>. This function should only be used when debugging.

- pwd() -> void() + Print working directory

Prints the name of the working directory.

- q() -> void() + Quit - shorthand for init:stop()

This function is shorthand for init:stop(), that is, @@ -257,8 +222,8 @@ compile:file(File, Opts ++ [report_errors, report_warnings]) - regs() -> void() - nregs() -> void() + + Information about registered processes

regs/0 displays information about all registered diff --git a/lib/stdlib/doc/src/calendar.xml b/lib/stdlib/doc/src/calendar.xml index 075c7f9c78..4876b37127 100644 --- a/lib/stdlib/doc/src/calendar.xml +++ b/lib/stdlib/doc/src/calendar.xml @@ -73,100 +73,125 @@ week number.

-
- DATA TYPES - -date() = {Year, Month, Day} - Year = int() - Month = 1..12 - Day = 1..31 -Year cannot be abbreviated. Example: 93 denotes year 93, not 1993. -Valid range depends on the underlying OS. -The date tuple must denote a valid date. + + + + + + + + + + + + +

Year cannot be abbreviated. Example: 93 denotes year + 93, not 1993. Valid range depends on the underlying OS. The + date tuple must denote a valid date.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

See erlang:now/0.

+
+
+ + + + + + +
-time() = {Hour, Minute, Second} - Hour = 0..23 - Minute = Second = 0..59
-
- date_to_gregorian_days(Date) -> Days - date_to_gregorian_days(Year, Month, Day) -> Days + + + + + + Compute the number of days from year 0 up to the given date - - Date = date() - Days = int() -

This function computes the number of gregorian days starting with year 0 and ending at the given date.

- datetime_to_gregorian_seconds({Date, Time}) -> Seconds + Compute the number of seconds from year 0 up to the given date and time - - Date = date() - Time = time() - Seconds = int() -

This function computes the number of gregorian seconds starting with year 0 and ending at the given date and time.

- day_of_the_week(Date) -> DayNumber - day_of_the_week(Year, Month, Day) -> DayNumber + + Compute the day of the week - - Date = date() - DayNumber = 1..7 - + + + + -

This function computes the day of the week given Year, - Month and Day. The return value denotes the day +

This function computes the day of the week given Year, + Month and Day. The return value denotes the day of the week as 1: Monday, 2: Tuesday, and so on.

- gregorian_days_to_date(Days) -> Date + Compute the date given the number of gregorian days - - Days = int() - Date = date() -

This function computes the date given the number of gregorian days.

- gregorian_seconds_to_datetime(Seconds) -> {Date, Time} + Compute the date given the number of gregorian days - - Seconds = int() - Date = date() - Time = time() -

This function computes the date and time from the given number of gregorian seconds.

- is_leap_year(Year) -> bool() + Check if a year is a leap year

This function checks if a year is a leap year.

- iso_week_number() -> IsoWeekNumber + Compute the iso week number for the actual date - - IsoWeekNumber = {int(), int()} -

This function returns the tuple {Year, WeekNum} representing the iso week number for the actual date. For determining the @@ -174,42 +199,34 @@ time() = {Hour, Minute, Second} - iso_week_number(Date) -> IsoWeekNumber + Compute the iso week number for the given date - - Date = date() - IsoWeekNumber = {int(), int()} -

This function returns the tuple {Year, WeekNum} representing the iso week number for the given date.

- last_day_of_the_month(Year, Month) -> int() + Compute the number of days in a month

This function computes the number of days in a month.

- local_time() -> {Date, Time} + Compute local time - - Date = date() - Time = time() -

This function returns the local time reported by the underlying operating system.

- local_time_to_universal_time({Date1, Time1}) -> {Date2, Time2} + Convert from local time to universal time (deprecated)

This function converts from local time to Universal - Coordinated Time (UTC). Date1 must refer to a local + Coordinated Time (UTC). DateTime1 must refer to a local date after Jan 1, 1970.

This function is deprecated. Use @@ -222,15 +239,11 @@ time() = {Hour, Minute, Second} - local_time_to_universal_time_dst({Date1, Time1}) -> [{Date, Time}] + Convert from local time to universal time(s) - - Date1 = Date = date() - Time1 = Time = time() -

This function converts from local time to Universal - Coordinated Time (UTC). Date1 must refer to a local + Coordinated Time (UTC). DateTime1 must refer to a local date after Jan 1, 1970.

The return value is a list of 0, 1 or 2 possible UTC times:

@@ -258,65 +271,48 @@ time() = {Hour, Minute, Second}
- now_to_local_time(Now) -> {Date, Time} + Convert now to local date and time - - Now -- see erlang:now/0 - Date = date() - Time = time() -

This function returns local date and time converted from the return value from erlang:now().

- now_to_universal_time(Now) -> {Date, Time} - now_to_datetime(Now) -> {Date, Time} + + Convert now to date and time - - Now -- see erlang:now/0 - Date = date() - Time = time() -

This function returns Universal Coordinated Time (UTC) converted from the return value from erlang:now().

- seconds_to_daystime(Seconds) -> {Days, Time} + Compute days and time from seconds - - Seconds = Days = int() - Time = time() -

This function transforms a given number of seconds into days, - hours, minutes, and seconds. The Time part is always - non-negative, but Days is negative if the argument - Seconds is.

+ hours, minutes, and seconds. The Time part is always + non-negative, but Days is negative if the argument + Seconds is.

- seconds_to_time(Seconds) -> Time + Compute time from seconds - - Seconds = int() < 86400 - Time = time() - +

This function computes the time from the given number of - seconds. Seconds must be less than the number of + seconds. Seconds must be less than the number of seconds per day (86400).

- time_difference(T1, T2) -> {Days, Time} + Compute the difference between two times (deprecated) -

This function returns the difference between two {Date, Time} tuples. T2 should refer to an epoch later - than T1.

+

This function returns the difference between two {Date, Time} tuples. T2 should refer to an epoch later + than T1.

This function is obsolete. Use the conversion functions for gregorian days and seconds instead.

@@ -324,24 +320,17 @@ time() = {Hour, Minute, Second}
- time_to_seconds(Time) -> Seconds + Compute the number of seconds since midnight up to the given time - - Time = time() - Seconds = int() - +

This function computes the number of seconds since midnight up to the specified time.

- universal_time() -> {Date, Time} + Compute universal time - - Date = date() - Time = time() -

This function returns the Universal Coordinated Time (UTC) reported by the underlying operating system. Local time is @@ -349,25 +338,22 @@ time() = {Hour, Minute, Second} - universal_time_to_local_time({Date1, Time1}) -> {Date2, Time2} + Convert from universal time to local time - - Date1 = Date2 = date() - Time1 = Time2 = time() -

This function converts from Universal Coordinated Time (UTC) - to local time. Date1 must refer to a date after Jan 1, + to local time. DateTime must refer to a date after Jan 1, 1970.

- valid_date(Date) -> bool() - valid_date(Year, Month, Day) -> bool() + + + + + + Check if a date is valid - - Date = date() -

This function checks if a date is a valid.

diff --git a/lib/stdlib/doc/src/dets.xml b/lib/stdlib/doc/src/dets.xml index b002af6616..2512c84e18 100644 --- a/lib/stdlib/doc/src/dets.xml +++ b/lib/stdlib/doc/src/dets.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -100,50 +100,99 @@ the process with the error tuple). If given badly formed arguments, all functions exit the process with a badarg message.

-

Types

-
-access() = read | read_write
-auto_save() = infinity | int()
-bindings_cont() = tuple()
-bool() = true | false
-file() = string()
-int() = integer() >= 0
-keypos() = integer() >= 1
-name() = atom() | reference()
-no_slots() = integer() >= 0 | default
-object() = tuple()
-object_cont() = tuple()
-select_cont() = tuple()
-type() = bag | duplicate_bag | set
-version() = 8 | 9 | default    
+ + + + + + + + + + +

Opaque continuation used by + match/1 and + match/3.

+
+
+ + + +

Opaque continuation used by + bchunk/2.

+
+
+ + + + + + +

Match specifications, see the match specification + documentation in the ERTS User's Guide and ms_transform(3).

+
+
+ + + + + + + + + +

Opaque continuation used by + match_object/1 and + match_object/3.

+
+
+ + + +

See ets:match/2 for a + description of patterns.

+
+
+ + + +

Opaque continuation used by + select/1 and + select/3.

+
+
+ + + + + + + + + +
- all() -> [Name] + Return a list of the names of all open Dets tables on this node. - - Name = name() -

Returns a list of the names of all open tables on this node.

- bchunk(Name, Continuation) -> {Continuation2, Data} | '$end_of_table' | {error, Reason} + Return a chunk of objects stored in a Dets table. - - Name = name() - Continuation = start | cont() - Continuation2 = cont() - Data = binary() | tuple() -

Returns a list of objects stored in a table. The exact representation of the returned objects is not public. The lists of data can be used for initializing a table by giving the value bchunk to the format option of the - init_table/3 function. The Mnesia application uses this + init_table/3 + function. The Mnesia application uses this function for copying open tables.

Unless the table is protected using safe_fixtable/2, calls to bchunk/2 may not work as expected if @@ -151,24 +200,23 @@ version() = 8 | 9 | default

The first time bchunk/2 is called, an initial continuation, the atom start, must be provided.

The bchunk/2 function returns a tuple - {Continuation2, Data}, where Data is a list of - objects. Continuation2 is another continuation which is + {Continuation2, Data}, + where Data is a list of + objects. Continuation2 is another continuation + which is to be passed on to a subsequent call to bchunk/2. With a series of calls to bchunk/2 it is possible to extract all objects of the table.

bchunk/2 returns '$end_of_table' when all - objects have been returned, or {error, Reason} if an - error occurs. + objects have been returned, or {error, Reason} + if an error occurs.

- close(Name) -> ok | {error, Reason} + Close a Dets table. - - Name = name() -

Closes a table. Only processes that have opened a table are allowed to close it. @@ -180,22 +228,16 @@ version() = 8 | 9 | default - delete(Name, Key) -> ok | {error, Reason} + Delete all objects with a given key from a Dets table. - - Name = name() - -

Deletes all objects with the key Key from the table - Name.

+

Deletes all objects with the key Key from + the table Name.

- delete_all_objects(Name) -> ok | {error, Reason} + Delete all objects from a Dets table. - - Name = name() -

Deletes all objects from a table in almost constant time. However, if the table if fixed, delete_all_objects(T) @@ -203,12 +245,8 @@ version() = 8 | 9 | default - delete_object(Name, Object) -> ok | {error, Reason} + Delete a given object from a Dets table. - - Name = name() - Object = object() -

Deletes all instances of a given object from a table. If a table is of type bag or duplicate_bag, the @@ -218,18 +256,15 @@ version() = 8 | 9 | default - first(Name) -> Key | '$end_of_table' + Return the first key stored in a Dets table. - - Key = term() - Name = name() - -

Returns the first key stored in the table Name +

Returns the first key stored in the table Name according to the table's internal order, or '$end_of_table' if the table is empty.

Unless the table is protected using safe_fixtable/2, - subsequent calls to next/2 may not work as expected if + subsequent calls to next/2 + may not work as expected if concurrent updates are made to the table.

Should an error occur, the process is exited with an error tuple {error, Reason}. The reason for not returning the @@ -243,107 +278,78 @@ version() = 8 | 9 | default - foldl(Function, Acc0, Name) -> Acc1 | {error, Reason} - Fold a function over a Dets table. - - Function = fun(Object, AccIn) -> AccOut - Acc0 = Acc1 = AccIn = AccOut = term() - Name = name() - Object = object() - - -

Calls Function on successive elements of the table - Name together with an extra argument AccIn. The - order in which the elements of the table are traversed is - unspecified. Function must return a new accumulator - which is passed to the next call. Acc0 is returned if - the table is empty.

-
-
- - foldr(Function, Acc0, Name) -> Acc1 | {error, Reason} + + Fold a function over a Dets table. - - Function = fun(Object, AccIn) -> AccOut - Acc0 = Acc1 = AccIn = AccOut = term() - Name = name() - Object = object() - -

Calls Function on successive elements of the table - Name together with an extra argument AccIn. The +

Calls Function on successive elements of + the table Name together with an extra argument + AccIn. The order in which the elements of the table are traversed is - unspecified. Function must return a new accumulator - which is passed to the next call. Acc0 is returned if + unspecified. Function must return a new + accumulator which is passed to the next call. + Acc0 is returned if the table is empty.

- from_ets(Name, EtsTab) -> ok | {error, Reason} + Replace the objects of a Dets table with the objects of an Ets table. - - Name = name() - EtsTab = - see ets(3) - - -

Deletes all objects of the table Name and then - inserts all the objects of the Ets table EtsTab. The - order in which the objects are inserted is not specified. +

Deletes all objects of the table Name and then + inserts all the objects of the Ets table EtsTab. + The order in which the objects are inserted is not specified. Since ets:safe_fixtable/2 is called the Ets table must be public or owned by the calling process.

- info(Name) -> InfoList | undefined + Return information about a Dets table. - - Name = name() - InfoList = [{Item, Value}] - -

Returns information about the table Name as a list of - {Item, Value} tuples:

+

Returns information about the table Name + as a list of tuples:

-

{file_size, int()}, the size of the file in +

{file_size, integer() >= 0}, the size of the file in bytes.

-

{filename, file()}, the name of the file - where objects are stored.

+

{filename, file:name()}, + the name of the file where objects are stored.

-

{keypos, keypos()}, the position of the - key.

+

{keypos, keypos()} + , the position of the key.

-

{size, int()}, the number of objects stored +

{size, integer() >= 0}, the number of objects stored in the table.

-

{type, type()}, the type of the table.

+

{type, type()}, + the type of the table.

- info(Name, Item) -> Value | undefined + Return the information associated with a given item for a Dets table. - - Name = name() - -

Returns the information associated with Item for the - table Name. In addition to the {Item, Value} +

Returns the information associated with Item + for the table Name. + In addition to the {Item, Value} pairs defined for info/1, the following items are allowed:

-

{access, access()}, the access mode.

+

{access, access()} + , the access mode.

-

{auto_save, auto_save()}, the auto save - interval.

+

{auto_save, + auto_save()}, the auto save interval.

{bchunk_format, binary()}, an opaque binary @@ -362,21 +368,22 @@ version() = 8 | 9 | default erlang:phash2/1 BIF is used.

-

{memory, int()}, the size of the file in +

{memory, integer() >= 0}, the size of the file in bytes. The same value is associated with the item file_size.

-

{no_keys, int()}, the number of different +

{no_keys, integer >= 0()}, the number of different keys stored in the table. Only available for version 9 tables.

-

{no_objects, int()}, the number of objects +

{no_objects, integer >= 0()}, the number of objects stored in the table.

-

{no_slots, {Min, Used, Max}}, the number of +

{no_slots, {Min, Used, Max}}, + the number of slots of the table. Min is the minimum number of slots, Used is the number of currently used slots, and Max is the maximum number of slots. Only @@ -387,7 +394,7 @@ version() = 8 | 9 | default handles requests to the Dets table.

-

{ram_file, bool()}, whether the table is +

{ram_file, boolean()}, whether the table is kept in RAM.

@@ -399,32 +406,28 @@ version() = 8 | 9 | default table is not fixed, SafeFixed is the atom false.

-

{version, int()}, the version of the format - of the table.

+

{version, integer(), the version of the format of + the table.

- init_table(Name, InitFun [, Options]) -> ok | {error, Reason} + + Replace all objects of a Dets table. - - Name = atom() - InitFun = fun(Arg) -> Res - Arg = read | close - Res = end_of_input | {[object()], InitFun} | {Data, InitFun} | term() - Data = binary() | tuple() - -

Replaces the existing objects of the table Name with - objects created by calling the input function InitFun, +

Replaces the existing objects of the table Name + with objects created by calling the input function + InitFun, see below. The reason for using this function rather than calling insert/2 is that of efficiency. It should be noted that the input functions are called by the process that handles requests to the Dets table, not by the calling process.

When called with the argument read the function - InitFun is assumed to return end_of_input when + InitFun is assumed to return + end_of_input when there is no more input, or {Objects, Fun}, where Objects is a list of objects and Fun is a new input function. Any other value Value is returned as an error @@ -448,7 +451,8 @@ version() = 8 | 9 | default item no_slots. See also the min_no_slots option below.

-

The Options argument is a list of {Key, Val} +

The Options argument is a list of + {Key, Val} tuples where the following values are allowed:

@@ -461,87 +465,68 @@ version() = 8 | 9 | default

{format, Format}. Specifies the format of the - objects returned by the function InitFun. If + objects returned by the function InitFun. If Format is term (the default), - InitFun is assumed to return a list of tuples. If - Format is bchunk, InitFun is - assumed to return Data as returned by - bchunk/2. This option overrides the + InitFun is assumed to return a list of tuples. If + Format is bchunk, InitFun is + assumed to return Data as returned by + bchunk/2. + This option overrides the min_no_slots option.

- insert(Name, Objects) -> ok | {error, Reason} + Insert one or more objects into a Dets table. - - Name = name() - Objects = object() | [object()] - -

Inserts one or more objects into the table Name. If - there already exists an object with a key matching the key of +

Inserts one or more objects into the table Name. + If there already exists an object with a key matching the key of some of the given objects and the table type is set, the old object will be replaced.

- insert_new(Name, Objects) -> Bool + Insert one or more objects into a Dets table. - - Name = name() - Objects = object() | [object()] - Bool = bool() - -

Inserts one or more objects into the table Name. If - there already exists some object with a key matching the key +

Inserts one or more objects into the table Name. + If there already exists some object with a key matching the key of any of the given objects the table is not updated and false is returned, otherwise the objects are inserted and true returned.

- is_compatible_bchunk_format(Name, BchunkFormat) -> Bool + Test compatibility of a table's chunk data. - - Name = name() - BchunkFormat = binary() - Bool = bool() -

Returns true if it would be possible to initialize - the table Name, using init_table/3 with the + the table Name, using + init_table/3 + with the option {format, bchunk}, with objects read with - bchunk/2 from some table T such that calling + bchunk/2 from some + table T such that calling info(T, bchunk_format) returns BchunkFormat.

- is_dets_file(FileName) -> Bool | {error, Reason} + Test for a Dets table. - - FileName = file() - Bool = bool() - -

Returns true if the file FileName is a Dets - table, false otherwise.

+

Returns true if the file Filename + is a Dets table, false otherwise.

- lookup(Name, Key) -> [Object] | {error, Reason} + Return all objects with a given key stored in a Dets table. - - Key = term() - Name = name() - Object = object() - -

Returns a list of all objects with the key Key - stored in the table Name. For example:

+

Returns a list of all objects with the key Key + stored in the table Name. For example:

 2> dets:open_file(abc, [{type, bag}]).
 {ok,abc}
@@ -562,71 +547,57 @@ ok
       
     
     
-      match(Continuation) -> {[Match], Continuation2} | '$end_of_table' | {error, Reason}
+      
       Match a chunk of objects stored in a Dets table and return a list of variable bindings.
-      
-        Continuation = Continuation2 = bindings_cont()
-        Match = [term()]
-      
       
         

Matches some objects stored in a table and returns a non-empty list of the bindings that match a given pattern in some unspecified order. The table, the pattern, and the number of objects that are matched are all defined by - Continuation, which has been returned by a prior call - to match/1 or match/3.

+ Continuation, which has been returned by a prior + call to match/1 or match/3.

When all objects of the table have been matched, '$end_of_table' is returned.

- match(Name, Pattern) -> [Match] | {error, Reason} + Match the objects stored in a Dets table and return a list of variable bindings. - - Name = name() - Pattern = tuple() - Match = [term()] - -

Returns for each object of the table Name that - matches Pattern a list of bindings in some unspecified - order. See ets(3) for a +

Returns for each object of the table Name that + matches Pattern a list of bindings in some unspecified + order. See ets:match/2 for a description of patterns. If the keypos'th element of - Pattern is unbound, all objects of the table are + Pattern is unbound, all objects of the table are matched. If the keypos'th element is bound, only the objects with the right key are matched.

- match(Name, Pattern, N) -> {[Match], Continuation} | '$end_of_table' | {error, Reason} + Match the first chunk of objects stored in a Dets table and return a list of variable bindings. - - Name = name() - Pattern = tuple() - N = default | int() - Match = [term()] - Continuation = bindings_cont() - -

Matches some or all objects of the table Name and +

Matches some or all objects of the table Name and returns a non-empty list of the bindings that match - Pattern in some unspecified order. See ets(3) for a description of - patterns.

+ Pattern in some unspecified order. + See ets:match/2 for a + description of patterns.

A tuple of the bindings and a continuation is returned, unless the table is empty, in which case '$end_of_table' is returned. The continuation is to be used when matching further objects by calling - match/1.

-

If the keypos'th element of Pattern is bound, all - objects of the table are matched. If the keypos'th element is - unbound, all objects of the table are matched, N + match/1.

+

If the keypos'th element of Pattern is bound, + all objects of the table are matched. If the keypos'th element is + unbound, all objects of the table are matched, N objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by - giving N the value default, is to let the number + giving N the value default, + is to let the number of objects vary depending on the sizes of the objects. If - Name is a version 9 table, all objects with the same + Name is a version 9 table, all objects with the same key are always matched at the same time which implies that - more than N objects may sometimes be matched. + more than N objects may sometimes be matched.

The table should always be protected using safe_fixtable/2 before calling match/3, or @@ -634,15 +605,11 @@ ok - match_delete(Name, Pattern) -> ok | {error, Reason} + Delete all objects that match a given pattern from a Dets table. - - Name = name() - Pattern = tuple() - -

Deletes all objects that match Pattern from the - table Name. +

Deletes all objects that match Pattern from the + table Name. See ets:match/2 for a description of patterns.

If the keypos'th element of Pattern is bound, @@ -650,17 +617,13 @@ ok - match_object(Continuation) -> {[Object], Continuation2} | '$end_of_table' | {error, Reason} + Match a chunk of objects stored in a Dets table and return a list of objects. - - Continuation = Continuation2 = object_cont() - Object = object() -

Returns a non-empty list of some objects stored in a table that match a given pattern in some unspecified order. The table, the pattern, and the number of objects that are matched - are all defined by Continuation, which has been + are all defined by Continuation, which has been returned by a prior call to match_object/1 or match_object/3.

When all objects of the table have been matched, @@ -668,20 +631,17 @@ ok - match_object(Name, Pattern) -> [Object] | {error, Reason} + Match the objects stored in a Dets table and return a list of objects. - - Name = name() - Pattern = tuple() - Object = object() - -

Returns a list of all objects of the table Name that - match Pattern in some unspecified order. See ets(3) for a description of patterns. +

Returns a list of all objects of the table Name that + match Pattern in some unspecified order. + See ets:match/2 for a + description of patterns.

-

If the keypos'th element of Pattern is +

If the keypos'th element of Pattern is unbound, all objects of the table are matched. If the - keypos'th element of Pattern is bound, only the + keypos'th element of Pattern is bound, only the objects with the right key are matched.

Using the match_object functions for traversing all objects of a table is more efficient than calling @@ -689,34 +649,28 @@ ok - match_object(Name, Pattern, N) -> {[Object], Continuation} | '$end_of_table' | {error, Reason} + Match the first chunk of objects stored in a Dets table and return a list of objects. - - Name = name() - Pattern = tuple() - N = default | int() - Object = object() - Continuation = object_cont() - -

Matches some or all objects stored in the table Name +

Matches some or all objects stored in the table Name and returns a non-empty list of the objects that match - Pattern in some unspecified order. See ets(3) for a description of - patterns.

+ Pattern in some unspecified order. + See ets:match/2 for a + description of patterns.

A list of objects and a continuation is returned, unless the table is empty, in which case '$end_of_table' is returned. The continuation is to be used when matching further objects by calling match_object/1.

-

If the keypos'th element of Pattern is bound, all +

If the keypos'th element of Pattern is bound, all objects of the table are matched. If the keypos'th element is - unbound, all objects of the table are matched, N + unbound, all objects of the table are matched, N objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by - giving N the value default, is to let the number + giving N the value default, is to let the number of objects vary depending on the sizes of the objects. If - Name is a version 9 table, all matching objects with + Name is a version 9 table, all matching objects with the same key are always returned in the same reply which - implies that more than N objects may sometimes be returned. + implies that more than N objects may sometimes be returned.

The table should always be protected using safe_fixtable/2 before calling match_object/3, @@ -724,43 +678,31 @@ ok - member(Name, Key) -> Bool | {error, Reason} + Test for occurrence of a key in a Dets table. - - Name = name() - Key = term() - Bool = bool() -

Works like lookup/2, but does not return the objects. The function returns true if one or more - elements of the table has the key Key, false + elements of the table has the key Key, false otherwise.

- next(Name, Key1) -> Key2 | '$end_of_table' + Return the next key in a Dets table. - - Name = name() - Key1 = Key2 = term() - -

Returns the key following Key1 in the table - Name according to the table's internal order, or +

Returns the key following Key1 in the table + Name according to the table's internal order, or '$end_of_table' if there is no next key.

Should an error occur, the process is exited with an error tuple {error, Reason}.

-

Use first/1 to find the first key in the table.

+

Use first/1 to find + the first key in the table.

- open_file(Filename) -> {ok, Reference} | {error, Reason} + Open an existing Dets table. - - FileName = file() - Reference = reference() -

Opens an existing table. If the table has not been properly closed, it will be repaired. The returned reference is to be @@ -769,15 +711,12 @@ ok - open_file(Name, Args) -> {ok, Name} | {error, Reason} + Open a Dets table. - - Name = atom() -

Opens a table. An empty Dets table is created if no file exists.

-

The atom Name is the name of the table. The table +

The atom Name is the name of the table. The table name must be provided in all subsequent operations on the table. The name can be used by other processes as well, and several process can share one table. @@ -786,18 +725,20 @@ ok name and arguments, then the table will have two users. If one user closes the table, it still remains open until the second user closes the table.

-

The Args argument is a list of {Key, Val} +

The Args argument is a list of {Key, Val} tuples where the following values are allowed:

-

{access, access()}. It is possible to open +

{access, + access()}. It is possible to open existing tables in read-only mode. A table which is opened in read-only mode is not subjected to the automatic file reparation algorithm if it is later opened after a crash. The default value is read_write.

-

{auto_save, auto_save()}, the auto save +

{auto_save, + auto_save()}, the auto save interval. If the interval is an integer Time, the table is flushed to disk whenever it is not accessed for Time milliseconds. A table that has been flushed @@ -807,15 +748,18 @@ ok is 180000 (3 minutes).

-

{estimated_no_objects, int()}. Equivalent to the +

{estimated_no_objects, + no_slots()}. Equivalent to the min_no_slots option.

-

{file, file()}, the name of the file to be +

{file, + file:name()}, the name of the file to be opened. The default value is the name of the table.

-

{max_no_slots, no_slots()}, the maximum number +

{max_no_slots, + no_slots()}, the maximum number of slots that will be used. The default value as well as the maximal value is 32 M. Note that a higher value may increase the fragmentation of the table, and conversely, @@ -824,14 +768,16 @@ ok 9 tables.

-

{min_no_slots, no_slots()}. Application +

{min_no_slots, + no_slots()}. Application performance can be enhanced with this flag by specifying, when the table is created, the estimated number of different keys that will be stored in the table. The default value as well as the minimum value is 256.

-

{keypos, keypos()}, the position of the +

{keypos, + keypos()}, the position of the element of each object to be used as key. The default value is 1. The ability to explicitly state the key position is most convenient when we want to store Erlang @@ -839,7 +785,7 @@ ok name of the record type.

-

{ram_file, bool()}, whether the table is to +

{ram_file, boolean()}, whether the table is to be kept in RAM. Keeping the table in RAM may sound like an anomaly, but can enhance the performance of applications which open a table, insert a set of objects, and then @@ -849,7 +795,7 @@ ok

{repair, Value}. Value can be either - a bool() or the atom force. The flag + a boolean() or the atom force. The flag specifies whether the Dets server should invoke the automatic file reparation algorithm. The default is true. If false is specified, there is no @@ -868,11 +814,12 @@ ok already open.

-

{type, type()}, the type of the table. The - default value is set.

+

{type, type()}, + the type of the table. The default value is set.

-

{version, version()}, the version of the format +

{version, + version()}, the version of the format used for the table. The default value is 9. Tables on the format used before OTP R8 can be created by giving the value 8. A version 8 table can be converted to @@ -883,12 +830,8 @@ ok - pid2name(Pid) -> {ok, Name} | undefined + Return the name of the Dets table handled by a pid. - - Name = name() - Pid = pid() -

Returns the name of the table given the pid of a process that handles requests to a table, or undefined if @@ -897,12 +840,8 @@ ok - repair_continuation(Continuation, MatchSpec) -> Continuation2 + Repair a continuation from select/1 or select/3. - - Continuation = Continuation2 = select_cont() - MatchSpec = match_spec() -

This function can be used to restore an opaque continuation returned by select/3 or select/1 if the @@ -932,14 +871,11 @@ ok - safe_fixtable(Name, Fix) + Fix a Dets table for safe traversal. - - Name = name() - Fix = bool() - -

If Fix is true, the table Name is +

If Fix is true, the table + Name is fixed (once more) by the calling process, otherwise the table is released. The table is also released when a fixing process terminates. @@ -961,17 +897,13 @@ ok - select(Continuation) -> {Selection, Continuation2} | '$end_of_table' | {error, Reason} + Apply a match specification to some objects stored in a Dets table. - - Continuation = Continuation2 = select_cont() - Selection = [term()] -

Applies a match specification to some objects stored in a table and returns a non-empty list of the results. The table, the match specification, and the number of objects - that are matched are all defined by Continuation, + that are matched are all defined by Continuation, which has been returned by a prior call to select/1 or select/3.

When all objects of the table have been matched, @@ -979,20 +911,15 @@ ok - select(Name, MatchSpec) -> Selection | {error, Reason} + Apply a match specification to all objects stored in a Dets table. - - Name = name() - MatchSpec = match_spec() - Selection = [term()] -

Returns the results of applying the match specification - MatchSpec to all or some objects stored in the table - Name. The order of the objects is not specified. See + MatchSpec to all or some objects stored in the table + Name. The order of the objects is not specified. See the ERTS User's Guide for a description of match specifications.

-

If the keypos'th element of MatchSpec is +

If the keypos'th element of MatchSpec is unbound, the match specification is applied to all objects of the table. If the keypos'th element is bound, the match specification is applied to the objects with the right key(s) @@ -1004,19 +931,12 @@ ok - select(Name, MatchSpec, N) -> {Selection, Continuation} | '$end_of_table' | {error, Reason} + Apply a match specification to the first chunk of objects stored in a Dets table. - - Name = name() - MatchSpec = match_spec() - N = default | int() - Selection = [term()] - Continuation = select_cont() -

Returns the results of applying the match specification - MatchSpec to some or all objects stored in the table - Name. The order of the objects is not specified. See + MatchSpec to some or all objects stored in the table + Name. The order of the objects is not specified. See the ERTS User's Guide for a description of match specifications.

A tuple of the results of applying the match specification @@ -1024,18 +944,18 @@ ok in which case '$end_of_table' is returned. The continuation is to be used when matching further objects by calling select/1.

-

If the keypos'th element of MatchSpec is bound, the +

If the keypos'th element of MatchSpec is bound, the match specification is applied to all objects of the table with the right key(s). If the keypos'th element of - MatchSpec is unbound, the match specification is - applied to all objects of the table, N objects at a + MatchSpec is unbound, the match specification is + applied to all objects of the table, N objects at a time, until at least one object matches or the end of the table has been reached. The default, indicated by giving - N the value default, is to let the number of + N the value default, is to let the number of objects vary depending on the sizes of the objects. If - Name is a version 9 table, all objects with the same + Name is a version 9 table, all objects with the same key are always handled at the same time which implies that the - match specification may be applied to more than N objects. + match specification may be applied to more than N objects.

The table should always be protected using safe_fixtable/2 before calling select/3, or @@ -1043,48 +963,35 @@ ok - select_delete(Name, MatchSpec) -> N | {error, Reason} + Delete all objects that match a given pattern from a Dets table. - - Name = name() - MatchSpec = match_spec() - N = int() - -

Deletes each object from the table Name such that - applying the match specification MatchSpec to the +

Deletes each object from the table Name such that + applying the match specification MatchSpec to the object returns the value true. See the ERTS User's Guide for a description of match specifications. Returns the number of deleted objects.

-

If the keypos'th element of MatchSpec is +

If the keypos'th element of MatchSpec is bound, the match specification is applied to the objects with the right key(s) only.

- slot(Name, I) -> '$end_of_table' | [Object] | {error, Reason} + Return the list of objects associated with a slot of a Dets table. - - Name = name() - I = int() - Object = object() -

The objects of a table are distributed among slots, starting with slot 0 and ending with slot n. This function returns the list of objects associated with slot - I. If I is greater than n '$end_of_table' - is returned.

+ I. If I is greater than n + '$end_of_table' is returned.

- sync(Name) -> ok | {error, Reason} + Ensure that all updates made to a Dets table are written to disk. - - Name = name() - -

Ensures that all updates made to the table Name are +

Ensures that all updates made to the table Name are written to disk. This also applies to tables which have been opened with the ram_file flag set to true. In this case, the contents of the RAM file are flushed to @@ -1095,25 +1002,17 @@ ok - table(Name [, Options]) -> QueryHandle + + Return a QLC query handle. - - Name = name() - QueryHandle = - a query handle, see qlc(3) - - Options = [Option] | Option - Option = {n_objects, Limit} | {traverse, TraverseMethod} - Limit = default | integer() >= 1 - TraverseMethod = first_next | select | {select, MatchSpec} - MatchSpec = match_spec() - -

-Returns a QLC (Query List +

+ Returns a QLC (Query List Comprehension) query handle. The module qlc implements a query language aimed mainly at Mnesia but Ets tables, Dets tables, and lists are also recognized by qlc as sources of data. Calling dets:table/1,2 is the - means to make the Dets table Name usable to qlc.

+ means to make the Dets table Name usable to qlc.

When there are only simple restrictions on the key position qlc uses dets:lookup/2 to look up the keys, but when that is not possible the whole table is traversed. The @@ -1137,7 +1036,8 @@ Returns a QLC (Query List specification that matches all objects.

-

{select, MatchSpec}. As for select +

{select, + match_spec()}. As for select the table is traversed by calling dets:select/3 and dets:select/1. The difference is that the match specification is explicitly given. This is how to @@ -1166,35 +1066,23 @@ true

- to_ets(Name, EtsTab) -> EtsTab | {error, Reason} + Insert all objects of a Dets table into an Ets table. - - Name = name() - EtsTab = - see ets(3) - - -

Inserts the objects of the Dets table Name into the - Ets table EtsTab. The order in which the objects are +

Inserts the objects of the Dets table Name into the + Ets table EtsTab. The order in which the objects are inserted is not specified. The existing objects of the Ets table are kept unless overwritten.

- traverse(Name, Fun) -> Return | {error, Reason} + Apply a function to all or some objects stored in a Dets table. - - Fun = fun(Object) -> FunReturn - FunReturn = continue | {continue, Val} | {done, Value} - Val = Value = term() - Name = name() - Object = object() - Return = [term()] - -

Applies Fun to each object stored in the table - Name in some unspecified order. Different actions are - taken depending on the return value of Fun. The - following Fun return values are allowed:

+

Applies Fun to each object stored in the table + Name in some unspecified order. Different actions are + taken depending on the return value of Fun. The + following Fun return values are allowed:

continue @@ -1206,35 +1094,31 @@ fun(X) -> io:format("~p~n", [X]), continue end. {continue, Val} -

Continue the traversal and accumulate Val. The +

Continue the traversal and accumulate Val. The following function is supplied in order to collect all objects of a table in a list:

 fun(X) -> {continue, X} end.            
- {done, Value} + {done, Value} -

Terminate the traversal and return [Value | Acc].

+

Terminate the traversal and return [Value | Acc].

-

Any other value returned by Fun terminates the +

Any other value returned by Fun terminates the traversal and is immediately returned.

- update_counter(Name, Key, Increment) -> Result + Update a counter object stored in a Dets table. - - Name = name() - Key = term() - Increment = {Pos, Incr} | Incr - Pos = Incr = Result = integer() - -

Updates the object with key Key stored in the table - Name of type set by adding Incr to the - element at the Pos:th position. The new counter value +

Updates the object with key Key stored in the + table Name of type set by adding + Incr to the + element at the Pos:th position. + The new counter value is returned. If no position is specified, the element directly following the key is updated.

This functions provides a way of updating a counter, @@ -1252,4 +1136,3 @@ fun(X) -> {continue, X} end. qlc(3)

- diff --git a/lib/stdlib/doc/src/dict.xml b/lib/stdlib/doc/src/dict.xml index 0cc76e0c78..b01acd02bf 100644 --- a/lib/stdlib/doc/src/dict.xml +++ b/lib/stdlib/doc/src/dict.xml @@ -39,174 +39,120 @@ they do not compare equal (==).

-
- DATA TYPES - -dictionary() - as returned by new/0 -
+ + + dict() +

Dictionary as returned by new/0.

+
+
- append(Key, Value, Dict1) -> Dict2 + Append a value to keys in a dictionary - - Key = Value = term() - Dict1 = Dict2 = dictionary() - -

This function appends a new Value to the current list - of values associated with Key.

-
+

This function appends a new Value to the current list + of values associated with Key.

+
- append_list(Key, ValList, Dict1) -> Dict2 + Append new values to keys in a dictionary - - ValList = [Value] - Key = Value = term() - Dict1 = Dict2 = dictionary() - -

This function appends a list of values ValList to - the current list of values associated with Key. An +

This function appends a list of values ValList to + the current list of values associated with Key. An exception is generated if the initial value associated with - Key is not a list of values.

+ Key is not a list of values.

- erase(Key, Dict1) -> Dict2 + Erase a key from a dictionary - - Key = term() - Dict1 = Dict2 = dictionary() -

This function erases all items with a given key from a dictionary.

- fetch(Key, Dict) -> Value + Look-up values in a dictionary - - Key = Value = term() - Dict = dictionary() - -

This function returns the value associated with Key - in the dictionary Dict. fetch assumes that - the Key is present in the dictionary and an exception - is generated if Key is not in the dictionary.

+

This function returns the value associated with Key + in the dictionary Dict. fetch assumes that + the Key is present in the dictionary and an exception + is generated if Key is not in the dictionary.

- fetch_keys(Dict) -> Keys + Return all keys in a dictionary - - Dict = dictionary() - Keys = [term()] -

This function returns a list of all keys in the dictionary.

- filter(Pred, Dict1) -> Dict2 + Choose elements which satisfy a predicate - - Pred = fun(Key, Value) -> bool() -  Key = Value = term() - Dict1 = Dict2 = dictionary() - -

Dict2 is a dictionary of all keys and values in - Dict1 for which Pred(Key, Value) is true.

+

Dict2 is a dictionary of all keys and values in + Dict1 for which Pred(Key, Value) is true.

- find(Key, Dict) -> {ok, Value} | error + Search for a key in a dictionary - - Key = Value = term() - Dict = dictionary() -

This function searches for a key in a dictionary. Returns - {ok, Value} where Value is the value associated - with Key, or error if the key is not present in + {ok, Value} where Value is the value associated + with Key, or error if the key is not present in the dictionary.

- fold(Fun, Acc0, Dict) -> Acc1 + Fold a function over a dictionary - - Fun = fun(Key, Value, AccIn) -> AccOut - Key = Value = term() - Acc0 = Acc1 = AccIn = AccOut = term() - Dict = dictionary() - -

Calls Fun on successive keys and values of - Dict together with an extra argument Acc - (short for accumulator). Fun must return a new - accumulator which is passed to the next call. Acc0 is +

Calls Fun on successive keys and values of + Dict together with an extra argument Acc + (short for accumulator). Fun must return a new + accumulator which is passed to the next call. Acc0 is returned if the list is empty. The evaluation order is undefined.

- from_list(List) -> Dict + Convert a list of pairs to a dictionary - - List = [{Key, Value}] - Dict = dictionary() - -

This function converts the Key - Value list - List to a dictionary.

+

This function converts the Key - Value list + List to a dictionary.

- is_key(Key, Dict) -> bool() + Test if a key is in a dictionary - - Key = term() - Dict = dictionary() - -

This function tests if Key is contained in - the dictionary Dict.

+

This function tests if Key is contained in + the dictionary Dict.

- map(Fun, Dict1) -> Dict2 + Map a function over a dictionary - - Fun = fun(Key, Value1) -> Value2 -  Key = Value1 = Value2 = term() - Dict1 = Dict2 = dictionary() - -

map calls Func on successive keys and values - of Dict to return a new value for each key. +

map calls Fun on successive keys and values + of Dict1 to return a new value for each key. The evaluation order is undefined.

- merge(Fun, Dict1, Dict2) -> Dict3 + Merge two dictionaries - - Fun = fun(Key, Value1, Value2) -> Value -  Key = Value1 = Value2 = Value3 = term() - Dict1 = Dict2 = Dict3 = dictionary() - -

merge merges two dictionaries, Dict1 and - Dict2, to create a new dictionary. All the Key - - Value pairs from both dictionaries are included in +

merge merges two dictionaries, Dict1 and + Dict2, to create a new dictionary. All the Key + - Value pairs from both dictionaries are included in the new dictionary. If a key occurs in both dictionaries then - Fun is called with the key and both values to return a + Fun is called with the key and both values to return a new value. merge could be defined as:

merge(Fun, D1, D2) -> @@ -217,75 +163,52 @@ merge(Fun, D1, D2) ->
- new() -> dictionary() + Create a dictionary

This function creates a new dictionary.

- size(Dict) -> int() + Return the number of elements in a dictionary - - Dict = dictionary() - -

Returns the number of elements in a Dict.

+

Returns the number of elements in a Dict.

- store(Key, Value, Dict1) -> Dict2 + Store a value in a dictionary - - Key = Value = term() - Dict1 = Dict2 = dictionary() - -

This function stores a Key - Value pair in a - dictionary. If the Key already exists in Dict1, - the associated value is replaced by Value.

+

This function stores a Key - Value pair in a + dictionary. If the Key already exists in Dict1, + the associated value is replaced by Value.

- to_list(Dict) -> List + Convert a dictionary to a list of pairs - - Dict = dictionary() - List = [{Key, Value}] -

This function converts the dictionary to a list representation.

- update(Key, Fun, Dict1) -> Dict2 + Update a value in a dictionary - - Key = term() - Fun = fun(Value1) -> Value2 -  Value1 = Value2 = term() - Dict1 = Dict2 = dictionary() - -

Update a value in a dictionary by calling Fun on +

Update a value in a dictionary by calling Fun on the value to get a new value. An exception is generated if - Key is not present in the dictionary.

+ Key is not present in the dictionary.

- update(Key, Fun, Initial, Dict1) -> Dict2 + Update a value in a dictionary - - Key = Initial = term() - Fun = fun(Value1) -> Value2 -  Value1 = Value2 = term() - Dict1 = Dict2 = dictionary() - -

Update a value in a dictionary by calling Fun on - the value to get a new value. If Key is not present - in the dictionary then Initial will be stored as +

Update a value in a dictionary by calling Fun on + the value to get a new value. If Key is not present + in the dictionary then Initial will be stored as the first value. For example append/3 could be defined as:

@@ -294,17 +217,12 @@ append(Key, Val, D) ->
- update_counter(Key, Increment, Dict1) -> Dict2 + Increment a value in a dictionary - - Key = term() - Increment = number() - Dict1 = Dict2 = dictionary() - -

Add Increment to the value associated with Key - and store this value. If Key is not present in - the dictionary then Increment will be stored as +

Add Increment to the value associated with Key + and store this value. If Key is not present in + the dictionary then Increment will be stored as the first value.

This could be defined as:

diff --git a/lib/stdlib/doc/src/digraph.xml b/lib/stdlib/doc/src/digraph.xml index ad256e671f..0afc70ebe0 100644 --- a/lib/stdlib/doc/src/digraph.xml +++ b/lib/stdlib/doc/src/digraph.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -87,67 +87,79 @@ is a digraph that has no cycles.

+ + + + + + + + + + + + digraph() +

A digraph as returned by new/0,1.

+
+ + edge() + + + + + + vertex() + +
- add_edge(G, E, V1, V2, Label) -> edge() | {error, Reason} - add_edge(G, V1, V2, Label) -> edge() | {error, Reason} - add_edge(G, V1, V2) -> edge() | {error, Reason} + + + Add an edge to a digraph. - - G = digraph() - E = edge() - V1 = V2 = vertex() - Label = label() - Reason = {bad_edge, Path} | {bad_vertex, V} - Path = [vertex()] - + -

add_edge/5 creates (or modifies) the edge E - of the digraph G, using Label as the (new) +

add_edge/5 creates (or modifies) the edge E + of the digraph G, using Label as the (new) label of the edge. The edge is emanating from - V1 and incident - on V2. Returns E. + V1 and incident + on V2. Returns E.

-

add_edge(G, V1, V2, Label) is +

add_edge(GV1V2Label) is equivalent to - add_edge(G, E, V1, V2, Label), - where E is a created edge. The created edge is + add_edge(GEV1V2Label), + where E is a created edge. The created edge is represented by the term ['$e' | N], where N is an integer >= 0.

-

add_edge(G, V1, V2) is equivalent to - add_edge(G, V1, V2, []). +

add_edge(GV1V2) is equivalent to + add_edge(GV1V2, []).

If the edge would create a cycle in an acyclic digraph, - then {error, {bad_edge, Path}} is returned. If - either of V1 or V2 is not a vertex of the - digraph G, then - {error, {bad_vertex, V}} is - returned, V = V1 or - V = V2. + then {error, {bad_edge, Path}} is returned. If + either of V1 or V2 is not a vertex of the + digraph G, then + {error, {bad_vertex, V}} is + returned, V = V1 or + V = V2.

- add_vertex(G, V, Label) -> vertex() - add_vertex(G, V) -> vertex() - add_vertex(G) -> vertex() + + + Add or modify a vertex of a digraph. - - G = digraph() - V = vertex() - Label = label() - -

add_vertex/3 creates (or modifies) the vertex V - of the digraph G, using Label as the (new) +

add_vertex/3 creates (or modifies) the vertex V + of the digraph G, using Label as the (new) label of the - vertex. Returns V. + vertex. Returns V.

-

add_vertex(G, V) is equivalent to - add_vertex(G, V, []). +

add_vertex(GV) is equivalent to + add_vertex(GV, []).

add_vertex/1 creates a vertex using the empty list as label, and returns the created vertex. The created vertex @@ -157,304 +169,227 @@ - del_edge(G, E) -> true + Delete an edge from a digraph. - - G = digraph() - E = edge() - -

Deletes the edge E from the digraph G. +

Deletes the edge E from the digraph G.

- del_edges(G, Edges) -> true + Delete edges from a digraph. - - G = digraph() - Edges = [edge()] - -

Deletes the edges in the list Edges from the digraph - G. +

Deletes the edges in the list Edges from the digraph + G.

- del_path(G, V1, V2) -> true + Delete paths from a digraph. - - G = digraph() - V1 = V2 = vertex() - -

Deletes edges from the digraph G until there are no +

Deletes edges from the digraph G until there are no paths from the vertex - V1 to the vertex V2. + V1 to the vertex V2.

A sketch of the procedure employed: Find an arbitrary simple path - v[1], v[2], ..., v[k] from V1 to - V2 in G. Remove all edges of - G emanating from v[i] + v[1], v[2], ..., v[k] from V1 to + V2 in G. Remove all edges of + G emanating from v[i] and incident to v[i+1] for 1 <= i < k (including multiple - edges). Repeat until there is no path between V1 and - V2. + edges). Repeat until there is no path between V1 and + V2.

- del_vertex(G, V) -> true + Delete a vertex from a digraph. - - G = digraph() - V = vertex() - -

Deletes the vertex V from the digraph G. Any +

Deletes the vertex V from the digraph G. Any edges emanating from - V or incident - on V are also deleted. + V or incident + on V are also deleted.

- del_vertices(G, Vertices) -> true + Delete vertices from a digraph. - - G = digraph() - Vertices = [vertex()] - -

Deletes the vertices in the list Vertices from the - digraph G. +

Deletes the vertices in the list Vertices from the + digraph G.

- delete(G) -> true + Delete a digraph. - - G = digraph() - -

Deletes the digraph G. This call is important - because digraphs are implemented with Ets. There is - no garbage collection of Ets tables. The digraph +

Deletes the digraph G. This call is important + because digraphs are implemented with ETS. There is + no garbage collection of ETS tables. The digraph will, however, be deleted if the process that created the digraph terminates.

- edge(G, E) -> {E, V1, V2, Label} | false + Return the vertices and the label of an edge of a digraph. - - G = digraph() - E = edge() - V1 = V2 = vertex() - Label = label() - -

Returns {E, V1, V2, Label} where - Label is the label +

Returns {EV1V2Label} where + Label is the label of the edge - E emanating from - V1 and incident on - V2 of the digraph G. - If there is no edge E of the - digraph G, then false is returned. + E emanating from + V1 and incident on + V2 of the digraph G. + If there is no edge E of the + digraph G, then false is returned.

- edges(G) -> Edges + Return all edges of a digraph. - - G = digraph() - Edges = [edge()] - -

Returns a list of all edges of the digraph G, in +

Returns a list of all edges of the digraph G, in some unspecified order.

- edges(G, V) -> Edges + Return the edges emanating from or incident on a vertex of a digraph. - - G = digraph() - V = vertex() - Edges = [edge()] -

Returns a list of all edges emanating from - or incident on V - of the digraph G, in some unspecified order.

+ or incident on V + of the digraph G, in some unspecified order.

- get_cycle(G, V) -> Vertices | false + Find one cycle in a digraph. - - G = digraph() - V1 = V2 = vertex() - Vertices = [vertex()] -

If there is a simple cycle of length two or more through the vertex - V, then the cycle is returned as a list - [V, ..., V] of vertices, otherwise if there + V, then the cycle is returned as a list + [V, ..., V] of vertices, otherwise if there is a loop through - V, then the loop is returned as a list [V]. If - there are no cycles through V, then false is + V, then the loop is returned as a list [V]. If + there are no cycles through V, then false is returned.

get_path/3 is used for finding a simple cycle - through V. + through V.

- get_path(G, V1, V2) -> Vertices | false + Find one path in a digraph. - - G = digraph() - V1 = V2 = vertex() - Vertices = [vertex()] -

Tries to find a simple path from - the vertex V1 to the vertex - V2 of the digraph G. Returns the path as a - list [V1, ..., V2] of vertices, or - false if no simple path from V1 to V2 + the vertex V1 to the vertex + V2 of the digraph G. Returns the path as a + list [V1, ..., V2] of vertices, or + false if no simple path from V1 to V2 of length one or more exists.

-

The digraph G is traversed in a depth-first manner, +

The digraph G is traversed in a depth-first manner, and the first path found is returned.

- get_short_cycle(G, V) -> Vertices | false + Find one short cycle in a digraph. - - G = digraph() - V1 = V2 = vertex() - Vertices = [vertex()] -

Tries to find an as short as possible simple cycle through - the vertex V of the digraph G. Returns the cycle - as a list [V, ..., V] of vertices, or - false if no simple cycle through V exists. + the vertex V of the digraph G. Returns the cycle + as a list [V, ..., V] of vertices, or + false if no simple cycle through V exists. Note that a loop through - V is returned as the list [V, V]. + V is returned as the list [VV].

get_short_path/3 is used for finding a simple cycle - through V. + through V.

- get_short_path(G, V1, V2) -> Vertices | false + Find one short path in a digraph. - - G = digraph() - V1 = V2 = vertex() - Vertices = [vertex()] -

Tries to find an as short as possible simple path from - the vertex V1 to the vertex V2 of the digraph G. - Returns the path as a list [V1, ..., V2] of - vertices, or false if no simple path from V1 - to V2 of length one or more exists. + the vertex V1 to the vertex V2 of the digraph G. + Returns the path as a list [V1, ..., V2] of + vertices, or false if no simple path from V1 + to V2 of length one or more exists.

-

The digraph G is traversed in a breadth-first +

The digraph G is traversed in a breadth-first manner, and the first path found is returned.

- in_degree(G, V) -> integer() + Return the in-degree of a vertex of a digraph. - - G= digraph() - V = vertex() -

Returns the in-degree of the vertex - V of the digraph G. + V of the digraph G.

- in_edges(G, V) -> Edges + Return all edges incident on a vertex of a digraph. - - G = digraph() - V = vertex() - Edges = [edge()] -

Returns a list of all edges incident on - V of the digraph G, in some unspecified order. + V of the digraph G, in some unspecified order.

- in_neighbours(G, V) -> Vertices + Return all in-neighbours of a vertex of a digraph. - - G = digraph() - V = vertex() - Vertices = [vertex()] -

Returns a list of all in-neighbours of - V of the digraph G, in some unspecified order. + V of the digraph G, in some unspecified order.

- info(G) -> InfoList + Return information about a digraph. - - G = digraph() - InfoList = [{cyclicity, Cyclicity}, {memory, NoWords}, {protection, Protection}] - Cyclicity = cyclic | acyclic - Protection = protected | private - NoWords = integer() >= 0 - + +

Returns a list of {Tag, Value} pairs describing the - digraph G. The following pairs are returned: + digraph G. The following pairs are returned:

-

{cyclicity, Cyclicity}, where Cyclicity +

{cyclicity, Cyclicity}, where Cyclicity is cyclic or acyclic, according to the options given to new.

-

{memory, NoWords}, where NoWords is - the number of words allocated to the ets tables.

+

{memory, NoWords}, where NoWords is + the number of words allocated to the ETS tables.

-

{protection, Protection}, where Protection +

{protection, Protection}, where Protection is protected or private, according to the options given to new.

@@ -462,7 +397,7 @@
- new() -> digraph() + Return a protected empty digraph, where cycles are allowed.

Equivalent to new([]). @@ -470,15 +405,16 @@ - new(Type) -> digraph() + Create a new empty digraph. - - Type = [cyclic | acyclic | private | protected] - + + + +

Returns an empty digraph with - properties according to the options in Type:

+ properties according to the options in Type:

cyclic Allow cycles in the @@ -491,101 +427,72 @@ The digraph can be read and modified by the creating process only. -

If an unrecognized type option T is given or Type +

If an unrecognized type option T is given or Type is not a proper list, there will be a badarg exception.

- no_edges(G) -> integer() >= 0 + Return the number of edges of the a digraph. - - G = digraph() - -

Returns the number of edges of the digraph G. +

Returns the number of edges of the digraph G.

- no_vertices(G) -> integer() >= 0 + Return the number of vertices of a digraph. - - G = digraph() - -

Returns the number of vertices of the digraph G. +

Returns the number of vertices of the digraph G.

- out_degree(G, V) -> integer() + Return the out-degree of a vertex of a digraph. - - G = digraph() - V = vertex() -

Returns the out-degree of the vertex - V of the digraph G. + V of the digraph G.

- out_edges(G, V) -> Edges + Return all edges emanating from a vertex of a digraph. - - G = digraph() - V = vertex() - Edges = [edge()] -

Returns a list of all edges emanating from - V of the digraph G, in some unspecified order. + V of the digraph G, in some unspecified order.

- out_neighbours(G, V) -> Vertices + Return all out-neighbours of a vertex of a digraph. - - G = digraph() - V = vertex() - Vertices = [vertex()] -

Returns a list of all out-neighbours of - V of the digraph G, in some unspecified order. + V of the digraph G, in some unspecified order.

- vertex(G, V) -> {V, Label} | false + Return the label of a vertex of a digraph. - - G = digraph() - V = vertex() - Label = label() - -

Returns {V, Label} where Label is the +

Returns {VLabel} where Label is the label of the vertex - V of the digraph G, or false if there - is no vertex V of the digraph G. + V of the digraph G, or false if there + is no vertex V of the digraph G.

- vertices(G) -> Vertices + Return all vertices of a digraph. - - G = digraph() - Vertices = [vertex()] - -

Returns a list of all vertices of the digraph G, in +

Returns a list of all vertices of the digraph G, in some unspecified order.

diff --git a/lib/stdlib/doc/src/digraph_utils.xml b/lib/stdlib/doc/src/digraph_utils.xml index 4b137456b3..e44632bfd2 100644 --- a/lib/stdlib/doc/src/digraph_utils.xml +++ b/lib/stdlib/doc/src/digraph_utils.xml @@ -4,7 +4,7 @@
- 20002009 + 20002011 Ericsson AB. All Rights Reserved. @@ -119,49 +119,43 @@ considering all edges undirected.

+ + + digraph() +

A digraph as returned by digraph:new/0,1.

+
+
- arborescence_root(Digraph) -> no | {yes, Root} + Check if a digraph is an arborescence. - - Digraph = digraph() - Root = vertex() - - -

Returns {yes, Root} if Root is +

Returns {yes, Root} if Root is the root of the arborescence - Digraph, no otherwise. + Digraph, no otherwise.

- components(Digraph) -> [Component] + Return the components of a digraph. - - Digraph = digraph() - Component = [vertex()] -

Returns a list of connected components. Each component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of the digraph - Digraph occurs in exactly one component. + Digraph occurs in exactly one component.

- condensation(Digraph) -> CondensedDigraph + Return a condensed graph of a digraph. - - Digraph = CondensedDigraph = digraph() -

Creates a digraph where the vertices are the strongly connected - components of Digraph as returned by + components of Digraph as returned by strong_components/1. If X and Y are strongly connected components, and there exist vertices x and y in X and Y respectively such that there is an @@ -169,7 +163,7 @@ and incident on y, then an edge emanating from X and incident on Y is created.

-

The created digraph has the same type as Digraph. +

The created digraph has the same type as Digraph. All vertices and edges have the default label [].

@@ -181,12 +175,8 @@
- cyclic_strong_components(Digraph) -> [StrongComponent] + Return the cyclic strong components of a digraph. - - Digraph = digraph() - StrongComponent = [vertex()] -

Returns a list of strongly connected components. @@ -194,67 +184,50 @@ by its vertices. The order of the vertices and the order of the components are arbitrary. Only vertices that are included in some cycle in - Digraph are returned, otherwise the returned list is + Digraph are returned, otherwise the returned list is equal to that returned by strong_components/1.

- is_acyclic(Digraph) -> bool() + Check if a digraph is acyclic. - - Digraph = digraph() -

Returns true if and only if the digraph - Digraph is acyclic.

+ Digraph is acyclic.

- is_arborescence(Digraph) -> bool() + Check if a digraph is an arborescence. - - Digraph = digraph() -

Returns true if and only if the digraph - Digraph is + Digraph is an arborescence.

- is_tree(Digraph) -> bool() + Check if a digraph is a tree. - - Digraph = digraph() -

Returns true if and only if the digraph - Digraph is + Digraph is a tree.

- loop_vertices(Digraph) -> Vertices + Return the vertices of a digraph included in some loop. - - Digraph = digraph() - Vertices = [vertex()] - -

Returns a list of all vertices of Digraph that are +

Returns a list of all vertices of Digraph that are included in some loop.

- postorder(Digraph) -> Vertices + Return the vertices of a digraph in post-order. - - Digraph = digraph() - Vertices = [vertex()] - -

Returns all vertices of the digraph Digraph. The +

Returns all vertices of the digraph Digraph. The order is given by a depth-first traversal of the digraph, collecting visited @@ -266,14 +239,10 @@ - preorder(Digraph) -> Vertices + Return the vertices of a digraph in pre-order. - - Digraph = digraph() - Vertices = [vertex()] - -

Returns all vertices of the digraph Digraph. The +

Returns all vertices of the digraph Digraph. The order is given by a depth-first traversal of the digraph, collecting visited @@ -281,119 +250,94 @@ - reachable(Vertices, Digraph) -> Vertices + Return the vertices reachable from some vertices of a digraph. - - Digraph = digraph() - Vertices = [vertex()] -

Returns an unsorted list of digraph vertices such that for each vertex in the list, there is - a path in Digraph from some - vertex of Vertices to the vertex. In particular, + a path in Digraph from some + vertex of Vertices to the vertex. In particular, since paths may have length zero, the vertices of - Vertices are included in the returned list. + Vertices are included in the returned list.

- reachable_neighbours(Vertices, Digraph) -> Vertices + Return the neighbours reachable from some vertices of a digraph. - - Digraph = digraph() - Vertices = [vertex()] -

Returns an unsorted list of digraph vertices such that for each vertex in the list, there is - a path in Digraph of length - one or more from some vertex of Vertices to the + a path in Digraph of length + one or more from some vertex of Vertices to the vertex. As a consequence, only those vertices - of Vertices that are included in + of Vertices that are included in some cycle are returned.

- reaching(Vertices, Digraph) -> Vertices + Return the vertices that reach some vertices of a digraph. - - Digraph = digraph() - Vertices = [vertex()] -

Returns an unsorted list of digraph vertices such that for each vertex in the list, there is a path from the vertex to some - vertex of Vertices. In particular, since paths may have - length zero, the vertices of Vertices are included in + vertex of Vertices. In particular, since paths may have + length zero, the vertices of Vertices are included in the returned list.

- reaching_neighbours(Vertices, Digraph) -> Vertices + Return the neighbours that reach some vertices of a digraph. - - Digraph = digraph() - Vertices = [vertex()] -

Returns an unsorted list of digraph vertices such that for each vertex in the list, there is a path of length one or more - from the vertex to some vertex of Vertices. As a consequence, - only those vertices of Vertices that are included in + from the vertex to some vertex of Vertices. As a consequence, + only those vertices of Vertices that are included in some cycle are returned.

- strong_components(Digraph) -> [StrongComponent] + Return the strong components of a digraph. - - Digraph = digraph() - StrongComponent = [vertex()] -

Returns a list of strongly connected components. Each strongly component is represented by its vertices. The order of the vertices and the order of the components are arbitrary. Each vertex of the digraph - Digraph occurs in exactly one strong component. + Digraph occurs in exactly one strong component.

- subgraph(Digraph, Vertices [, Options]) -> Subgraph + + Return a subgraph of a digraph. - - Digraph = Subgraph = digraph() - Options = [{type, SubgraphType}, {keep_labels, bool()}] - SubgraphType = inherit | type() - Vertices = [vertex()] -

Creates a maximal subgraph of Digraph having - as vertices those vertices of Digraph that are - mentioned in Vertices. + as vertices those vertices of Digraph that are + mentioned in Vertices.

If the value of the option type is inherit, - which is the default, then the type of Digraph is used + which is the default, then the type of Digraph is used for the subgraph as well. Otherwise the option value of type is used as argument to digraph:new/1.

If the value of the option keep_labels is true, which is the default, then the labels of vertices and edges - of Digraph are used for the subgraph as well. If the value + of Digraph are used for the subgraph as well. If the value is false, then the default label, [], is used for the subgraph's vertices and edges.

-

subgraph(Digraph, Vertices) is equivalent to - subgraph(Digraph, Vertices, []). +

subgraph(Digraph, Vertices) is equivalent to + subgraph(Digraph, Vertices, []).

There will be a badarg exception if any of the arguments are invalid. @@ -401,16 +345,12 @@ - topsort(Digraph) -> Vertices | false + Return a topological sorting of the vertices of a digraph. - - Digraph = digraph() - Vertices = [vertex()] -

Returns a topological ordering of the vertices of the digraph - Digraph if such an ordering exists, false + Digraph if such an ordering exists, false otherwise. For each vertex in the returned list, there are no out-neighbours that occur earlier in the list.

diff --git a/lib/stdlib/doc/src/epp.xml b/lib/stdlib/doc/src/epp.xml index e6b48b270a..488499581f 100644 --- a/lib/stdlib/doc/src/epp.xml +++ b/lib/stdlib/doc/src/epp.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -38,65 +38,61 @@ by compile to preprocess macros and include files before the actual parsing takes place.

+ + + + + + +

Handle to the epp server.

+
+
- open(FileName, IncludePath) -> {ok,Epp} | {error, ErrorDescriptor} - open(FileName, IncludePath, PredefMacros) -> {ok,Epp} | {error, ErrorDescriptor} + + Open a file for preprocessing - - FileName = atom() | string() - IncludePath = [DirectoryName] - DirectoryName = atom() | string() - PredefMacros = [{atom(),term()}] - Epp = pid() -- handle to the epp server - ErrorDescriptor = term() -

Opens a file for preprocessing.

- close(Epp) -> ok + Close the preprocessing of the file associated with Epp - - Epp = pid() -- handle to the epp server -

Closes the preprocessing of a file.

- parse_erl_form(Epp) -> {ok, AbsForm} | {eof, Line} | {error, ErrorInfo} + Return the next Erlang form from the opened Erlang source file - - Epp = pid() - AbsForm = term() - Line = integer() - ErrorInfo = see separate description below. -

Returns the next Erlang form from the opened Erlang source file. - The tuple {eof, Line} is returned at end-of-file. The first + The tuple {eof, Line} is returned at end-of-file. The first form corresponds to an implicit attribute -file(File,1)., where File is the name of the file.

- parse_file(FileName,IncludePath,PredefMacro) -> {ok,[Form]} | {error,OpenError} - Preprocesse and parse an Erlang source file - - FileName = atom() | string() - IncludePath = [DirectoryName] - DirectoryName = atom() | string() - PredefMacros = [{atom(),term()}] - Form = term() -- same as returned by erl_parse:parse_form - + + Preprocess and parse an Erlang source file

Preprocesses and parses an Erlang source file. - Note that the tuple {eof, Line} returned at end-of-file is + Note that the tuple {eof, Line} returned at end-of-file is included as a "form".

+ + + Format an error descriptor + +

Takes an ErrorDescriptor and returns + a string which + describes the error or warning. This function is usually + called implicitly when processing an ErrorInfo + structure (see below).

+
+
diff --git a/lib/stdlib/doc/src/erl_eval.xml b/lib/stdlib/doc/src/erl_eval.xml index 62af23c5eb..c95494bf25 100644 --- a/lib/stdlib/doc/src/erl_eval.xml +++ b/lib/stdlib/doc/src/erl_eval.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -36,63 +36,108 @@

This module provides an interpreter for Erlang expressions. The expressions are in the abstract syntax as returned by - erl_parse, the Erlang parser, or a call to - io:parse_erl_exprs/2.

+ erl_parse, + the Erlang parser, or + io.

+ + + + + + + A binding structure. + + + + + + +

As returned by + erl_parse:parse_exprs/1 or + + io:parse_erl_exprs/2.

+
+ + + + + + + + + + + + + + +

Further described + below.

+
+
+ + + + + + + + +

Further described + below.

+
+
+ + + +
- exprs(Expressions, Bindings) -> {value, Value, NewBindings} - exprs(Expressions, Bindings, LocalFunctionHandler) -> {value, Value, NewBindings} - exprs(Expressions, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> {value, Value, NewBindings} + + + Evaluate expressions - - Expressions = as returned by erl_parse or io:parse_erl_exprs/2 - Bindings = as returned by bindings/1 - LocalFunctionHandler = {value, Func} | {eval, Func} | none - NonlocalFunctionHandler = {value, Func} | none - -

Evaluates Expressions with the set of bindings - Bindings, where Expressions is a sequence of +

Evaluates Expressions with the set of bindings + Bindings, where Expressions + is a sequence of expressions (in abstract syntax) of a type which may be - returned by io:parse_erl_exprs/2. See below for an + returned by + io:parse_erl_exprs/2. See below for an explanation of how and when to use the arguments - LocalFunctionHandler and NonlocalFunctionHandler. + LocalFunctionHandler and + NonLocalFunctionHandler.

-

Returns {value, Value, NewBindings}

+

Returns {value, Value, NewBindings} +

- expr(Expression, Bindings) -> { value, Value, NewBindings } - expr(Expression, Bindings, LocalFunctionHandler) -> { value, Value, NewBindings } - expr(Expression, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> { value, Value, NewBindings } - expr(Expression, Bindings, LocalFunctionHandler, NonlocalFunctionHandler, ReturnFormat) -> { value, Value, NewBindings } | Value + + + + Evaluate expression - - Expression = as returned by io:parse_erl_form/2, for example - Bindings = as returned by bindings/1 - LocalFunctionHandler = {value, Func} | {eval, Func} | none - NonlocalFunctionHandler = {value, Func} | none - ReturnFormat = value | none - -

Evaluates Expression with the set of bindings - Bindings. Expression is an expression (in - abstract syntax) of a type which may be returned by - io:parse_erl_form/2. See below for an explanation of +

Evaluates Expression with the set of bindings + Bindings. Expression + is an expression in + abstract syntax. See below for an explanation of how and when to use the arguments - LocalFunctionHandler and - NonlocalFunctionHandler. + LocalFunctionHandler and + NonLocalFunctionHandler.

-

Returns {value, Value, NewBindings} by default. But - if the ReturnFormat is value only the Value - is returned.

+

Returns {value, Value, + NewBindings} by default. But if the + ReturnFormat is value only + the Value is returned.

- expr_list(ExpressionList, Bindings) -> {ValueList, NewBindings} - expr_list(ExpressionList, Bindings, LocalFunctionHandler) -> {ValueList, NewBindings} - expr_list(ExpressionList, Bindings, LocalFunctionHandler, NonlocalFunctionHandler) -> {ValueList, NewBindings} + + + Evaluate a list of expressions

Evaluates a list of expressions in parallel, using the same @@ -100,18 +145,19 @@ merge the bindings returned from each evaluation. This function is useful in the LocalFunctionHandler. See below.

-

Returns {ValueList, NewBindings}.

+

Returns {ValueList, NewBindings}. +

- new_bindings() -> BindingStruct + Return a bindings structure

Returns an empty binding structure.

- bindings(BindingStruct) -> Bindings + Return bindings

Returns the list of bindings contained in the binding @@ -119,25 +165,28 @@ - binding(Name, BindingStruct) -> Binding + Return bindings -

Returns the binding of Name in BindingStruct.

+

Returns the binding of Name + in BindingStruct.

- add_binding(Name, Value, Bindings) -> BindingStruct + Add a binding -

Adds the binding Name = Value to Bindings. +

Adds the binding Name = Value + to BindingStruct. Returns an updated binding structure.

- del_binding(Name, Bindings) -> BindingStruct + Delete a binding -

Removes the binding of Name in Bindings. +

Removes the binding of Name + in BindingStruct. Returns an updated binding structure.

@@ -145,7 +194,8 @@
Local Function Handler -

During evaluation of a function, no calls can be made to local +

+ During evaluation of a function, no calls can be made to local functions. An undefined function error would be generated. However, the optional argument LocalFunctionHandler may be used to define a function @@ -191,7 +241,8 @@ Func(Name, Arguments, Bindings)

Non-local Function Handler -

The optional argument NonlocalFunctionHandler may be +

+ The optional argument NonlocalFunctionHandler may be used to define a function which is called in the following cases: a functional object (fun) is called; a built-in function is called; a function is called using the M:F syntax, where M diff --git a/lib/stdlib/doc/src/erl_expand_records.xml b/lib/stdlib/doc/src/erl_expand_records.xml index c93248493f..8ead438b31 100644 --- a/lib/stdlib/doc/src/erl_expand_records.xml +++ b/lib/stdlib/doc/src/erl_expand_records.xml @@ -39,12 +39,8 @@ - module(AbsForms, CompileOptions) -> AbsForms + Expand all records in a module - - AbsForms = [term()] - CompileOptions = [term()] -

Expands all records in a module. The returned module has no references to records, neither attributes nor code.

diff --git a/lib/stdlib/doc/src/erl_id_trans.xml b/lib/stdlib/doc/src/erl_id_trans.xml index cfb18ec131..18cc2460f9 100644 --- a/lib/stdlib/doc/src/erl_id_trans.xml +++ b/lib/stdlib/doc/src/erl_id_trans.xml @@ -5,7 +5,7 @@
1996 - 2010 + 2011 Ericsson AB, All Rights Reserved @@ -48,8 +48,8 @@ parse_transform(Forms, Options) -> Forms Transform Erlang forms - Forms = [erlang_form()] - Options = [compiler_options()] + Forms = [erl_parse:abstract_form()] + Options = [compile:option()]

Performs an identity transformation on Erlang forms, as an example.

diff --git a/lib/stdlib/doc/src/erl_internal.xml b/lib/stdlib/doc/src/erl_internal.xml index 732d77c3ae..b8d5ad73b3 100644 --- a/lib/stdlib/doc/src/erl_internal.xml +++ b/lib/stdlib/doc/src/erl_internal.xml @@ -42,112 +42,75 @@ - bif(Name, Arity) -> bool() + Test for an Erlang BIF - - Name = atom() - Arity = integer() - -

Returns true if Name/Arity is an Erlang BIF +

Returns true if Name/Arity is an Erlang BIF which is automatically recognized by the compiler, otherwise false.

- guard_bif(Name, Arity) -> bool() + Test for an Erlang BIF allowed in guards - - Name = atom() - Arity = integer() - -

Returns true if Name/Arity is an Erlang BIF +

Returns true if Name/Arity is an Erlang BIF which is allowed in guards, otherwise false.

- type_test(Name, Arity) -> bool() + Test for a valid type test - - Name = atom() - Arity = integer() - -

Returns true if Name/Arity is a valid Erlang +

Returns true if Name/Arity is a valid Erlang type test, otherwise false.

- arith_op(OpName, Arity) -> bool() + Test for an arithmetic operator - - OpName = atom() - Arity = integer() - -

Returns true if OpName/Arity is an arithmetic +

Returns true if OpName/Arity is an arithmetic operator, otherwise false.

- bool_op(OpName, Arity) -> bool() + Test for a Boolean operator - - OpName = atom() - Arity = integer() - -

Returns true if OpName/Arity is a Boolean +

Returns true if OpName/Arity is a Boolean operator, otherwise false.

- comp_op(OpName, Arity) -> bool() + Test for a comparison operator - - OpName = atom() - Arity = integer() - -

Returns true if OpName/Arity is a comparison +

Returns true if OpName/Arity is a comparison operator, otherwise false.

- list_op(OpName, Arity) -> bool() + Test for a list operator - - OpName = atom() - Arity = integer() - -

Returns true if OpName/Arity is a list +

Returns true if OpName/Arity is a list operator, otherwise false.

- send_op(OpName, Arity) -> bool() + Test for a send operator - - OpName = atom() - Arity = integer() - -

Returns true if OpName/Arity is a send +

Returns true if OpName/Arity is a send operator, otherwise false.

- op_type(OpName, Arity) -> Type + Return operator type - - OpName = atom() - Arity = integer() - Type = arith | bool | comp | list | send - -

Returns the Type of operator that OpName/Arity +

Returns the Type of operator that OpName/Arity belongs to, or generates a function_clause error if it is not an operator at all.

diff --git a/lib/stdlib/doc/src/erl_lint.xml b/lib/stdlib/doc/src/erl_lint.xml index 8639d678fa..b7fbdd8799 100644 --- a/lib/stdlib/doc/src/erl_lint.xml +++ b/lib/stdlib/doc/src/erl_lint.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -60,29 +60,30 @@ functions separately unless you have written your own Erlang compiler.

+ + + + + + + + - module(AbsForms) -> {ok,Warnings} | {error,Errors,Warnings} - module(AbsForms, FileName) -> {ok,Warnings} | {error,Errors,Warnings} - module(AbsForms, FileName, CompileOptions) -> {ok,Warnings} | {error,Errors,Warnings} + + + Check a module for errors - - AbsForms = [term()] - FileName = FileName2 = atom() | string() - Warnings = Errors = [{Filename2,[ErrorInfo]}] - ErrorInfo = see separate description below. - CompileOptions = [term()] -

This function checks all the forms in a module for errors. It returns:

- {ok,Warnings} + {ok,Warnings}

There were no errors in the module.

- {error,Errors,Warnings} + {error,Errors,Warnings}

There were errors in the module.

@@ -93,14 +94,14 @@ elements of Options that control the warnings are only described in compile(3).

-

The AbsForms of a module which comes from a file +

The AbsForms of a module which comes from a file that is read through epp, the Erlang pre-processor, can come from many files. This means that any references to errors must include the file name (see epp(3), or parser erl_parse(3)). The warnings and errors returned have the following format:

- [{FileName2,[ErrorInfo]}] + [{FileName2,[ErrorInfo]}]

The errors and warnings are listed in the order in which they are encountered in the forms. This means that the errors from one file may be split into different entries in @@ -108,27 +109,20 @@ - is_guard_test(Expr) -> bool() + Test for a guard test - - Expr = term() - -

This function tests if Expr is a legal guard test. - Expr is an Erlang term representing the abstract form +

This function tests if Expr is a legal guard test. + Expr is an Erlang term representing the abstract form for the expression. erl_parse:parse_exprs(Tokens) can - be used to generate a list of Expr.

+ be used to generate a list of Expr.

- format_error(ErrorDescriptor) -> Chars + Format an error descriptor - - ErrorDescriptor = errordesc() - Chars = [char() | Chars] - -

Takes an ErrorDescriptor and returns a string which +

Takes an ErrorDescriptor and returns a string which describes the error or warning. This function is usually called implicitly when processing an ErrorInfo structure (see below).

diff --git a/lib/stdlib/doc/src/erl_parse.xml b/lib/stdlib/doc/src/erl_parse.xml index 18b592deea..bafc2e0746 100644 --- a/lib/stdlib/doc/src/erl_parse.xml +++ b/lib/stdlib/doc/src/erl_parse.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -36,31 +36,51 @@

This module is the basic Erlang parser which converts tokens into the abstract form of either forms (i.e., top-level constructs), - expressions, or terms. The Abstract Format is described in the ERTS - User's Guide. + expressions, or terms. The Abstract Format is described in the + ERTS User's Guide. Note that a token list must end with the dot token in order to be acceptable to the parse functions (see erl_scan(3)).

+ + + +

Parse tree for Erlang clause.

+
+
+ + +

Parse tree for Erlang expression.

+
+
+ + +

Parse tree for Erlang form.

+
+
+ + + + + + + + + +
- parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} + Parse an Erlang form - - Tokens = [Token] - Token = {Tag,Line} | {Tag,Line,term()} - Tag = atom() - AbsForm = term() - ErrorInfo = see section Error Information below. - -

This function parses Tokens as if it were a form. It returns:

+

This function parses Tokens as if it were + a form. It returns:

- {ok, AbsForm} + {ok, AbsForm} -

The parsing was successful. AbsForm is the +

The parsing was successful. AbsForm is the abstract form of the parsed form.

- {error, ErrorInfo} + {error, ErrorInfo}

An error occurred.

@@ -68,25 +88,18 @@
- parse_exprs(Tokens) -> {ok, Expr_list} | {error, ErrorInfo} + Parse Erlang expressions - - Tokens = [Token] - Token = {Tag,Line} | {Tag,Line,term()} - Tag = atom() - Expr_list = [AbsExpr] - AbsExpr = term() - ErrorInfo = see section Error Information below. - -

This function parses Tokens as if it were a list of expressions. It returns:

+

This function parses Tokens as if it were + a list of expressions. It returns:

- {ok, Expr_list} + {ok, ExprList} -

The parsing was successful. Expr_list is a +

The parsing was successful. ExprList is a list of the abstract forms of the parsed expressions.

- {error, ErrorInfo} + {error, ErrorInfo}

An error occurred.

@@ -94,21 +107,15 @@
- parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} + Parse an Erlang term - - Tokens = [Token] - Token = {Tag,Line} | {Tag,Line,term()} - Tag = atom() - Term = term() - ErrorInfo = see section Error Information below. - -

This function parses Tokens as if it were a term. It returns:

+

This function parses Tokens as if it were + a term. It returns:

- {ok, Term} + {ok, Term} -

The parsing was successful. Term is +

The parsing was successful. Term is the Erlang term corresponding to the token list.

{error, ErrorInfo} @@ -122,7 +129,8 @@ format_error(ErrorDescriptor) -> Chars Format an error descriptor - ErrorDescriptor = errordesc() + ErrorDescriptor = error_description() Chars = [char() | Chars] @@ -133,44 +141,32 @@
- tokens(AbsTerm) -> Tokens - tokens(AbsTerm, MoreTokens) -> Tokens + + Generate a list of tokens for an expression - - Tokens = MoreTokens = [Token] - Token = {Tag,Line} | {Tag,Line,term()} - Tag = atom() - AbsTerm = term() - ErrorInfo = see section Error Information below. -

This function generates a list of tokens representing the abstract - form AbsTerm of an expression. Optionally, it appends - Moretokens.

+ form AbsTerm of an expression. Optionally, it + appends MoreTokens.

- normalise(AbsTerm) -> Data + Convert abstract form to an Erlang term - - AbsTerm = Data = term() - -

Converts the abstract form AbsTerm of a term into a +

Converts the abstract form AbsTerm of a + term into a conventional Erlang data structure (i.e., the term itself). This is the inverse of abstract/1.

- abstract(Data) -> AbsTerm + Convert an Erlang term into an abstract form - - Data = AbsTerm = term() - -

Converts the Erlang data structure Data into an - abstract form of type AbsTerm. This is the inverse of - normalise/1.

+

Converts the Erlang data structure Data into an + abstract form of type AbsTerm. + This is the inverse of normalise/1.

diff --git a/lib/stdlib/doc/src/erl_pp.xml b/lib/stdlib/doc/src/erl_pp.xml index 1fdda48893..57b5828bcd 100644 --- a/lib/stdlib/doc/src/erl_pp.xml +++ b/lib/stdlib/doc/src/erl_pp.xml @@ -43,125 +43,88 @@

All functions can have an optional argument which specifies a hook that is called if an attempt is made to print an unknown form.

+ + + + +

The optional argument + HookFunction, shown in the functions described below, + defines a function which is called when an unknown form occurs where there + should be a valid expression.

+ +

If HookFunction is equal to none there is no hook + function.

+ +

The called hook function should return a (possibly deep) list + of characters. expr/4 + is useful in a hook. +

+

If CurrentIndentation is negative, there will be no line + breaks and only a space is used as a separator.

+
+
+
- form(Form) -> DeepCharList - form(Form, HookFunction) -> DeepCharList + + Pretty print a form - - Form = term() - HookFunction = see separate description below. - DeepCharList = [char()|DeepCharList] -

Pretty prints a - Form which is an abstract form of a type which is - returned by erl_parse:parse_form.

+ Form which is an abstract form of a type which is + returned by + erl_parse:parse_form/1.

- attribute(Attribute) -> DeepCharList - attribute(Attribute, HookFunction) -> DeepCharList + + Pretty print an attribute - - Attribute = term() - HookFunction = see separate description below. - DeepCharList = [char()|DeepCharList] -

The same as form, but only for the attribute - Attribute.

+ Attribute.

- function(Function) -> DeepCharList - function(Function, HookFunction) -> DeepCharList + + Pretty print a function - - Function = term() - HookFunction = see separate description below. - DeepCharList = [char()|DeepCharList] -

The same as form, but only for the function - Function.

+ Function.

- guard(Guard) -> DeepCharList - guard(Guard, HookFunction) -> DeepCharList + + Pretty print a guard - - Form = term() - HookFunction = see separate description below. - DeepCharList = [char()|DeepCharList] -

The same as form, but only for the guard test - Guard.

+ Guard.

- exprs(Expressions) -> DeepCharList - exprs(Expressions, HookFunction) -> DeepCharList - exprs(Expressions, Indent, HookFunction) -> DeepCharList + + + Pretty print Expressions - - Expressions = term() - HookFunction = see separate description below. - Indent = integer() - DeepCharList = [char()|DeepCharList] -

The same as form, but only for the sequence of - expressions in Expressions.

+ expressions in Expressions.

- expr(Expression) -> DeepCharList - expr(Expression, HookFunction) -> DeepCharList - expr(Expression, Indent, HookFunction) -> DeepCharList - expr(Expression, Indent, Precedence, HookFunction) ->-> DeepCharList + + + + Pretty print one Expression - - Expression = term() - HookFunction = see separate description below. - Indent = integer() - Precedence = - DeepCharList = [char()|DeepCharList] -

This function prints one expression. It is useful for implementing hooks (see below).

-
- Unknown Expression Hooks -

The optional argument HookFunction, shown in the functions described above, defines a function which is called when an unknown form occurs where there - should be a valid expression. It can have the following formats:

- - Function - -

The hook function is called by:

- -Function(Expr, - CurrentIndentation, - CurrentPrecedence, - HookFunction) -
- none - -

There is no hook function

-
-
-

The called hook function should return a (possibly deep) list - of characters. expr/4 is useful in a hook. -

-

If CurrentIndentation is negative, there will be no line - breaks and only a space is used as a separator.

-
-
Bugs

It should be possible to have hook functions for unknown forms diff --git a/lib/stdlib/doc/src/erl_scan.xml b/lib/stdlib/doc/src/erl_scan.xml index 1199c34f0f..54240dea19 100644 --- a/lib/stdlib/doc/src/erl_scan.xml +++ b/lib/stdlib/doc/src/erl_scan.xml @@ -4,7 +4,7 @@

- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -37,56 +37,96 @@

This module contains functions for tokenizing characters into Erlang tokens.

-
- DATA TYPES - -category() = atom() -column() = integer() > 0 -line() = integer() -location() = line() | {line(), column()} -reserved_word_fun() -> fun(atom()) -> bool() -set_attribute_fun() -> fun(term()) -> term() -symbol() = atom() | float() | integer() | string() -token() = {category(), attributes()} | {category(), attributes(), symbol()} -attributes() = line() | list() | tuple() -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - string(String) -> Return - string(String, StartLocation) -> Return - string(String, StartLocation, Options) -> Return + + + Scan a string and return the Erlang tokens - - String = string() - Return = {ok, Tokens, EndLocation} | Error - Tokens = [token()] - Error = {error, ErrorInfo, EndLocation} - StartLocation = EndLocation = location() - Options = Option | [Option] - Option = {reserved_word_fun,reserved_word_fun()} - | return_comments | return_white_spaces | return - | text - -

Takes the list of characters String and tries to - scan (tokenize) them. Returns {ok, Tokens, EndLocation}, - where Tokens are the Erlang tokens from - String. EndLocation is the first location - after the last token.

-

{error, ErrorInfo, EndLocation} is returned if an - error occurs. EndLocation is the first location after +

Takes the list of characters String and tries to + scan (tokenize) them. Returns {ok, Tokens, + EndLocation}, + where Tokens are the Erlang tokens from + String. EndLocation + is the first location after the last token.

+

{error, ErrorInfo, ErrorLocation} + is returned if an error occurs. + ErrorLocation is the first location after the erroneous token.

-

string(String) is equivalent to - string(String, 1), and string(String, - StartLocation) is equivalent to string(String, - StartLocation, []).

-

StartLocation indicates the initial location when - scanning starts. If StartLocation is a line - attributes() as well as EndLocation and - ErrorLocation will be lines. If - StartLocation is a pair of a line and a column +

string(String) is equivalent to + string(String, 1), and + string(String, + StartLocation) is equivalent to + string(String, + StartLocation, []).

+

StartLocation indicates the initial location + when scanning starts. If StartLocation is a line + attributes() as well as EndLocation and + ErrorLocation will be lines. If + StartLocation is a pair of a line and a column attributes() takes the form of an opaque compound - data type, and EndLocation and ErrorLocation + data type, and EndLocation and + ErrorLocation will be pairs of a line and a column. The token attributes contain information about the column and the line where the token begins, as well as the text of the @@ -134,148 +174,130 @@ attributes() = line() | list() | tuple() - tokens(Continuation, CharSpec, StartLocation) -> Return - tokens(Continuation, CharSpec, StartLocation, Options) -> Return + + + + + An opaque continuation Re-entrant scanner - - Continuation = [] | Continuation1 - Return = {done, Result, LeftOverChars} | {more, Continuation1} - LeftOverChars = CharSpec - CharSpec = string() | eof - Continuation1 = tuple() - Result = {ok, Tokens, EndLocation} | {eof, EndLocation} | Error - Tokens = [token()] - Error = {error, ErrorInfo, EndLocation} - StartLocation = EndLocation = location() - Options = Option | [Option] - Option = {reserved_word_fun,reserved_word_fun()} - | return_comments | return_white_spaces | return - | text -

This is the re-entrant scanner which scans characters until a dot ('.' followed by a white space) or eof has been reached. It returns:

- {done, Result, LeftOverChars} + {done, Result, LeftOverChars} +

This return indicates that there is sufficient input - data to get a result. Result is:

+ data to get a result. Result is:

- {ok, Tokens, EndLocation} + {ok, Tokens, EndLocation} + -

The scanning was successful. Tokens is the - list of tokens including dot.

+

The scanning was successful. Tokens + is the list of tokens including dot.

{eof, EndLocation}

End of file was encountered before any more tokens.

- {error, ErrorInfo, EndLocation} + {error, ErrorInfo, EndLocation} + -

An error occurred. LeftOverChars is the remaining - characters of the input data, +

An error occurred. LeftOverChars + is the remaining characters of the input data, starting from EndLocation.

- {more, Continuation1} + {more, Continuation1}

More data is required for building a term. - Continuation1 must be passed in a new call to + Continuation1 must be passed in a new call to tokens/3,4 when more data is available.

-

The CharSpec eof signals end of file. - LeftOverChars will then take the value eof as - well.

-

tokens(Continuation, CharSpec, StartLocation) is - equivalent to tokens(Continuation, CharSpec, - StartLocation, []).

+

The CharSpec eof signals end of file. + LeftOverChars will then take the value eof + as well.

+

tokens(Continuation, CharSpec, + StartLocation) is equivalent to + tokens(Continuation, CharSpec, + StartLocation, []).

See string/3 for a description of the various options.

- reserved_word(Atom) -> bool() + Test for a reserved word - - Atom = atom() - -

Returns true if Atom is an Erlang reserved - word, otherwise false.

+

Returns true if Atom is an Erlang + reserved word, otherwise false.

- token_info(Token) -> TokenInfo + Return information about a token - - Token = token() - TokenInfo = [TokenInfoTuple] - TokenInfoTuple = {TokenItem, Info} - TokenItem = atom() - Info = term() -

Returns a list containing information about the token - Token. The order of the TokenInfoTuples is not - defined. The following TokenItems are returned: - category, column, length, - line, symbol, and text. See Token. The order of the + TokenInfoTuples is not + defined. See token_info/2 for information about specific - TokenInfoTuples.

+ TokenInfoTuples.

Note that if token_info(Token, TokenItem) returns - undefined for some TokenItem in the list above, the - item is not included in TokenInfo.

+ undefined for some TokenItem, the + item is not included in TokenInfo.

- token_info(Token, TokenItemSpec) -> TokenInfo + + + + Return information about a token - - Token = token() - TokenItemSpec = TokenItem | [TokenItem] - TokenInfo = TokenInfoTuple | undefined | [TokenInfoTuple] - TokenInfoTuple = {TokenItem, Info} - TokenItem = atom() - Info = term() -

Returns a list containing information about the token - Token. If TokenItemSpec is a single - TokenItem, the returned value is the corresponding + Token. If one single + TokenItem is given the returned value is + the corresponding TokenInfoTuple, or undefined if the - TokenItem has no value. If TokenItemSpec is a - list of - TokenItem, the result is a list of - TokenInfoTuple. The TokenInfoTuples will - appear with the corresponding - TokenItems in the same order as the TokenItems - appeared in the list of TokenItems. TokenItems - with no value are not included in the list of - TokenInfoTuple.

-

The following TokenInfoTuples with corresponding - TokenItems are valid:

+ TokenItem has no value. If a list of + TokenItems is given the result is a list of + TokenInfoTuple. The + TokenInfoTuples will + appear with the corresponding TokenItems in + the same order as the TokenItems + appear in the list of TokenItems. + TokenItems with no value are not included + in the list of TokenInfoTuple.

+

The following TokenInfoTuples with corresponding + TokenItems are valid:

- {category, category()} + {category, + category()}

The category of the token.

- {column, column()} + {column, + column()}

The column where the token begins.

{length, integer() > 0}

The length of the token's text.

- {line, line()} + {line, + line()}

The line where the token begins.

- {location, location()} + {location, + location()}

The line and column where the token begins, or just the line if the column unknown.

- {symbol, symbol()} + {symbol, + symbol()}

The token's symbol.

{text, string()} @@ -285,70 +307,59 @@ attributes() = line() | list() | tuple()
- attributes_info(Attributes) -> AttributesInfo + Return information about token attributes - - Attributes = attributes() - AttributesInfo = [AttributeInfoTuple] - AttributeInfoTuple = {AttributeItem, Info} - AttributeItem = atom() - Info = term() -

Returns a list containing information about the token - attributes Attributes. The order of the - AttributeInfoTuples is not defined. The following - AttributeItems are returned: - column, length, line, and text. + attributes Attributes. The order of the + AttributeInfoTuples is not defined. See attributes_info/2 for information about specific - AttributeInfoTuples.

+ AttributeInfoTuples.

Note that if attributes_info(Token, AttributeItem) returns undefined for some AttributeItem in the list above, the item is not included in - AttributesInfo.

+ AttributesInfo.

- attributes_info(Attributes, AttributeItemSpec) -> AttributesInfo + + Return information about a token attributes - - Attributes = attributes() - AttributeItemSpec = AttributeItem | [AttributeItem] - AttributesInfo = AttributeInfoTuple | undefined - | [AttributeInfoTuple] - AttributeInfoTuple = {AttributeItem, Info} - AttributeItem = atom() - Info = term() - +

Returns a list containing information about the token - attributes Attributes. If AttributeItemSpec is - a single AttributeItem, the returned value is the - corresponding AttributeInfoTuple, or undefined - if the AttributeItem has no value. If - AttributeItemSpec is a list of - AttributeItem, the result is a list of - AttributeInfoTuple. The AttributeInfoTuples - will appear with the corresponding AttributeItems in - the same order as the AttributeItems appeared in the - list of AttributeItems. AttributeItems with no + attributes Attributes. If one single + AttributeItem is given the returned value is the + corresponding AttributeInfoTuple, + or undefined if the AttributeItem + has no value. If a list of AttributeItem + is given the result is a list of + AttributeInfoTuple. + The AttributeInfoTuples + will appear with the corresponding AttributeItems + in the same order as the AttributeItems + appear in the list of AttributeItems. + AttributeItems with no value are not included in the list of - AttributeInfoTuple.

-

The following AttributeInfoTuples with corresponding - AttributeItems are valid:

+ AttributeInfoTuple.

+

The following AttributeInfoTuples with + corresponding AttributeItems are valid:

- {column, column()} + {column, + column()}

The column where the token begins.

{length, integer() > 0}

The length of the token's text.

- {line, line()} + {line, + line()}

The line where the token begins.

- {location, location()} + {location, + location()}

The line and column where the token begins, or just the line if the column unknown.

@@ -359,29 +370,22 @@ attributes() = line() | list() | tuple()
- set_attribute(AttributeItem, Attributes, SetAttributeFun) -> AttributesInfo + Set a token attribute value - - AttributeItem = line - Attributes = attributes() - SetAttributeFun = set_attribute_fun() -

Sets the value of the line attribute of the token - attributes Attributes.

-

The SetAttributeFun is called with the value of + attributes Attributes.

+

The SetAttributeFun is called with the value of the line attribute, and is to return the new value of the line attribute.

- format_error(ErrorDescriptor) -> string() + Format an error descriptor - - ErrorDescriptor = errordesc() - -

Takes an ErrorDescriptor and returns a string which +

Takes an ErrorDescriptor and returns + a string which describes the error or warning. This function is usually called implicitly when processing an ErrorInfo structure (see below).

diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 746f94d3f4..8c952708c5 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -126,15 +126,30 @@ ERTS User's Guide.

-
- DATA TYPES - -match_spec() - a match specification, see above - -tid() - a table identifier, as returned by new/2 -
+ + + continuation() + +

Opaque continuation used by + select/1 and + select/3.

+
+
+ + +

A match specification, see above.

+
+ + + + + + + + +

A table identifier, as returned by new/2.

+
+
all() -> [Tab] @@ -197,37 +212,25 @@ tid() - file2tab(Filename) -> {ok,Tab} | {error,Reason} + Read an ETS table from a file. - - Filename = string() | atom() - Tab = tid() | atom() - Reason = term() -

Reads a file produced by tab2file/2 or tab2file/3 and creates the - corresponding table Tab.

-

Equivalent to file2tab(Filename,[]).

+ corresponding table Tab.

+

Equivalent to file2tab(Filename, []).

- file2tab(Filename,Options) -> {ok,Tab} | {error,Reason} + Read an ETS table from a file. - - Filename = string() | atom() - Tab = tid() | atom() - Options = [Option] - Option = {verify, bool()} - Reason = term() -

Reads a file produced by tab2file/2 or tab2file/3 and creates the - corresponding table Tab.

-

The currently only supported option is {verify,bool()}. If + corresponding table Tab.

+

The currently only supported option is {verify,boolean()}. If verification is turned on (by means of specifying {verify,true}), the function utilizes whatever information is present in the file to assert that the @@ -271,70 +274,52 @@ tid() - foldl(Function, Acc0, Tab) -> Acc1 + Fold a function over an ETS table - - Function = fun(A, AccIn) -> AccOut - Tab = tid() | atom() - Acc0 = Acc1 = AccIn = AccOut = term() - -

Acc0 is returned if the table is empty. +

Acc0 is returned if the table is empty. This function is similar to lists:foldl/3. The order in which the elements of the table are traversed is unspecified, except for tables of type ordered_set, for which they are traversed first to last.

-

If Function inserts objects into the table, or another +

If Function inserts objects into the table, or another process inserts objects into the table, those objects may (depending on key ordering) be included in the traversal.

- foldr(Function, Acc0, Tab) -> Acc1 + Fold a function over an ETS table - - Function = fun(A, AccIn) -> AccOut - Tab = tid() | atom() - Acc0 = Acc1 = AccIn = AccOut = term() - -

Acc0 is returned if the table is empty. +

Acc0 is returned if the table is empty. This function is similar to lists:foldr/3. The order in which the elements of the table are traversed is unspecified, except for tables of type ordered_set, for which they are traversed last to first.

-

If Function inserts objects into the table, or another +

If Function inserts objects into the table, or another process inserts objects into the table, those objects may (depending on key ordering) be included in the traversal.

- from_dets(Tab, DetsTab) -> true + Fill an ETS table with objects from a Dets table. - - Tab = tid() | atom() - DetsTab = atom() -

Fills an already created ETS table with the objects in the - already opened Dets table named DetsTab. The existing + already opened Dets table named DetsTab. The existing objects of the ETS table are kept unless overwritten.

Throws a badarg error if any of the tables does not exist or the dets table is not open.

- fun2ms(LiteralFun) -> MatchSpec + Pseudo function that transforms fun syntax to a match_spec. - - LiteralFun -- see below - MatchSpec = match_spec() -

Pseudo function that by means of a parse_transform - translates LiteralFun typed as parameter in the + translates LiteralFun typed as parameter in the function call to a match_spec. With "literal" is meant that the fun needs to textually be written @@ -342,7 +327,7 @@ tid() variable which in turn is passed to the function).

The parse transform is implemented in the module ms_transform and the source must include the - file ms_transform.hrl in stdlib for this + file ms_transform.hrl in STDLIB for this pseudo function to work. Failing to include the hrl file in the source will result in a runtime error, not a compile time ditto. The include file is easiest included by adding @@ -422,20 +407,17 @@ Error: fun containing local Erlang function calls - i() -> ok + Display information about all ETS tables on tty.

Displays information about all ETS tables on tty.

- i(Tab) -> ok + Browse an ETS table on tty. - - Tab = tid() | atom() - -

Browses the table Tab on tty.

+

Browses the table Tab on tty.

@@ -454,7 +436,7 @@ Error: fun containing local Erlang function calls correct type, this function fails with reason badarg.

- Item=memory, Value=int()

+ Item=memory, Value=integer()

The number of words allocated to the table.
Item=owner, Value=pid()

@@ -466,7 +448,7 @@ Error: fun containing local Erlang function calls Item=name, Value=atom()

The name of the table.
- Item=size, Value=int()

+ Item=size, Value=integer()

The number of objects inserted in the table.
Item=node, Value=atom()

@@ -479,7 +461,7 @@ Error: fun containing local Erlang function calls Item=type, Value=set|ordered_set|bag|duplicate_bag

The table type.
- Item=keypos, Value=int()

+ Item=keypos, Value=integer()

The key position.
Item=protection, Value=public|protected|private

@@ -536,25 +518,19 @@ Error: fun containing local Erlang function calls
- init_table(Name, InitFun) -> true + Replace all objects of an ETS table. - - Name = atom() - InitFun = fun(Arg) -> Res - Arg = read | close - Res = end_of_input | {[object()], InitFun} | term() - -

Replaces the existing objects of the table Tab with - objects created by calling the input function InitFun, +

Replaces the existing objects of the table Tab with + objects created by calling the input function InitFun, see below. This function is provided for compatibility with the dets module, it is not more efficient than filling a table by using ets:insert/2.

When called with the argument read the function - InitFun is assumed to return end_of_input when - there is no more input, or {Objects, Fun}, where - Objects is a list of objects and Fun is a new + InitFun is assumed to return end_of_input when + there is no more input, or {Objects, Fun}, where + Objects is a list of objects and Fun is a new input function. Any other value Value is returned as an error {error, {init_fun, Value}}. Each input function will be called exactly once, and should an error occur, the last @@ -594,7 +570,7 @@ Error: fun containing local Erlang function calls - insert_new(Tab, ObjectOrObjects) -> bool() + insert_new(Tab, ObjectOrObjects) -> boolean() Insert an object into an ETS table if the key is not already present. Tab = tid() | atom() @@ -615,7 +591,7 @@ Error: fun containing local Erlang function calls - is_compiled_ms(Term) -> bool() + is_compiled_ms(Term) -> boolean() Checks if an Erlang term is the result of ets:match_spec_compile Term = term() @@ -708,7 +684,7 @@ ets:is_compiled_ms(Broken). Tab = tid() | atom() Key = term() - Pos = int() + Pos = integer() Elem = term() | [term()] @@ -802,15 +778,11 @@ ets:is_compiled_ms(Broken). - match_delete(Tab, Pattern) -> true + Delete all objects which match a given pattern from an ETS table. - - Tab = tid() | atom() - Pattern = tuple() - -

Deletes all objects which match the pattern Pattern - from the table Tab. See match/2 for a +

Deletes all objects which match the pattern Pattern + from the table Tab. See match/2 for a description of patterns.

@@ -957,8 +929,8 @@ ets:select(Table,MatchSpec),  Option = Type | Access | named_table | {keypos,Pos} | {heir,pid(),HeirData} | {heir,none} | Tweaks   Type = set | ordered_set | bag | duplicate_bag   Access = public | protected | private -   Tweaks = {write_concurrency,bool()} | {read_concurrency,bool()} | compressed -   Pos = int() +   Tweaks = {write_concurrency,boolean()} | {read_concurrency,boolean()} | compressed +   Pos = integer()   HeirData = term() @@ -1050,7 +1022,7 @@ ets:select(Table,MatchSpec), -

{write_concurrency,bool()} +

{write_concurrency,boolean()} Performance tuning. Default is false, in which case an operation that mutates (writes to) the table will obtain exclusive access, blocking any concurrent access of the same table until finished. @@ -1074,7 +1046,7 @@ ets:select(Table,MatchSpec), -

{read_concurrency,bool()} +

{read_concurrency,boolean()} Performance tuning. Default is false. When set to true, the table is optimized for concurrent read operations. When this option is enabled on a runtime system with @@ -1160,12 +1132,8 @@ ets:select(Table,MatchSpec), - repair_continuation(Continuation, MatchSpec) -> Continuation + Repair a continuation from ets:select/1 or ets:select/3 that has passed through external representation - - Continuation = term() - MatchSpec = match_spec() -

This function can be used to restore an opaque continuation returned by ets:select/3 or ets:select/1 if the @@ -1551,7 +1519,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]> Return all objects in a given slot of an ETS table. Tab = tid() | atom() - I = int() + I = integer() Object = tuple() @@ -1572,32 +1540,19 @@ is_integer(X), is_integer(Y), X + Y < 4711]]> - tab2file(Tab, Filename) -> ok | {error,Reason} + Dump an ETS table to a file. - - Tab = tid() | atom() - Filename = string() | atom() - Reason = term() - -

Dumps the table Tab to the file Filename.

-

Equivalent to tab2file(Tab, Filename,[])

+

Dumps the table Tab to the file Filename.

+

Equivalent to tab2file(Tab, Filename,[])

- tab2file(Tab, Filename, Options) -> ok | {error,Reason} + Dump an ETS table to a file. - - Tab = tid() | atom() - Filename = string() | atom() - Options = [Option] - Option = {extended_info, [ExtInfo]} - ExtInfo = object_count | md5sum - Reason = term() - -

Dumps the table Tab to the file Filename.

+

Dumps the table Tab to the file Filename.

When dumping the table, certain information about the table is dumped to a header at the beginning of the dump. This information contains data about the table type, @@ -1634,26 +1589,15 @@ is_integer(X), is_integer(Y), X + Y < 4711]]> - tab2list(Tab) -> [Object] + Return a list of all objects in an ETS table. - - Tab = tid() | atom() - Object = tuple() - -

Returns a list of all objects in the table Tab.

+

Returns a list of all objects in the table Tab.

- tabfile_info(Filename) -> {ok, TableInfo} | {error, Reason} + Return a list of all objects in an ETS table. - - Filename = string() | atom() - TableInfo = [InfoItem] - InfoItem = {InfoTag, term()} - InfoTag = name | type | protection | named_table | keypos | size | extended_info | version - Reason = term() -

Returns information about the table dumped to file by tab2file/2 or @@ -1699,7 +1643,7 @@ is_integer(X), is_integer(Y), X + Y < 4711]]> one or more of the atoms object_count and md5sum. version - A tuple {Major,Minor} containing the major and + A tuple {Major,Minor} containing the major and minor version of the file format for ets table dumps. This version field was added beginning with stdlib-1.5.1, files dumped with older versions will return {0,0} in this @@ -1712,20 +1656,11 @@ is_integer(X), is_integer(Y), X + Y < 4711]]> - table(Tab [, Options]) -> QueryHandle + + Return a QLC query handle. - - Tab = tid() | atom() - QueryHandle = - a query handle, see qlc(3) - - Options = [Option] | Option - Option = {n_objects, NObjects} | {traverse, TraverseMethod} - NObjects = default | integer() > 0 - TraverseMethod = first_next | last_prev | select | {select, MatchSpec} - MatchSpec = match_spec() - -

-Returns a QLC (Query List +

Returns a QLC (Query List Comprehension) query handle. The module qlc implements a query language aimed mainly at Mnesia but ETS tables, Dets tables, and lists are also recognized by QLC as sources of @@ -1760,7 +1695,7 @@ Returns a QLC (Query List that matches all objects.

-

{select, MatchSpec}. As for select +

{select, MatchSpec}. As for select the table is traversed by calling ets:select/3 and ets:select/1. The difference is that the match_spec is explicitly given. This is how to state @@ -1788,41 +1723,31 @@ true - test_ms(Tuple, MatchSpec) -> {ok, Result} | {error, Errors} + Test a match_spec for use in ets:select/2. - - Tuple = tuple() - MatchSpec = match_spec() - Result = term() - Errors = [{warning|error, string()}] -

This function is a utility to test a match_spec used in calls to ets:select/2. The function both tests - MatchSpec for "syntactic" correctness and runs the - match_spec against the object Tuple. If the match_spec - contains errors, the tuple {error, Errors} is returned - where Errors is a list of natural language + MatchSpec for "syntactic" correctness and runs the + match_spec against the object Tuple. If the match_spec + contains errors, the tuple {error, Errors} is returned + where Errors is a list of natural language descriptions of what was wrong with the match_spec. If the match_spec is syntactically OK, the function returns - {ok,Term} where Term is what would have been + {ok,Result} where Result is what would have been the result in a real ets:select/2 call or false - if the match_spec does not match the object Tuple.

+ if the match_spec does not match the object Tuple.

This is a useful debugging and test tool, especially when writing complicated ets:select/2 calls.

- to_dets(Tab, DetsTab) -> DetsTab + Fill a Dets table with objects from an ETS table. - - Tab = tid() | atom() - DetsTab = atom() -

Fills an already created/opened Dets table with the objects - in the already opened ETS table named Tab. The Dets + in the already opened ETS table named Tab. The Dets table is emptied before the objects are inserted.

@@ -1835,7 +1760,7 @@ true Tab = tid() | atom() Key = term() UpdateOp = {Pos,Incr} | {Pos,Incr,Threshold,SetValue} - Pos = Incr = Threshold = SetValue = Result = int() + Pos = Incr = Threshold = SetValue = Result = integer()

This function provides an efficient way to update one or more @@ -1897,7 +1822,7 @@ true Tab = tid() | atom() Key = Value = term() - Pos = int() + Pos = integer()

This function provides an efficient way to update one or more diff --git a/lib/stdlib/doc/src/file_sorter.xml b/lib/stdlib/doc/src/file_sorter.xml index ccb32659a0..a6b3633066 100644 --- a/lib/stdlib/doc/src/file_sorter.xml +++ b/lib/stdlib/doc/src/file_sorter.xml @@ -4,7 +4,7 @@

- 20012010 + 20012011 Ericsson AB. All Rights Reserved. @@ -89,7 +89,7 @@ considerably. The keysort, keymerge and keycheck functions do not accept ordering functions. - {unique, bool()}. When sorting or merging files, + {unique, boolean()}. When sorting or merging files, only the first of a sequence of terms that compare equal (==) is output if this option is set to true. The default value is false which implies that all terms that @@ -112,7 +112,7 @@ overwritten. Temporary files are deleted unless some uncaught EXIT signal occurs. - {compressed, bool()}. Temporary files and the + {compressed, boolean()}. Temporary files and the output file may be compressed. The default value false implies that written files are not compressed. Regardless of the value of the compressed @@ -128,39 +128,6 @@ merged at a time. This option should rarely be needed. -

To summarize, here is the syntax of the options:

- - -

Options = [Option] | Option

-
- -

Option = {header, HeaderLength} | {format, Format} | {order, Order} | {unique, bool()} | {tmpdir, TempDirectory} | {compressed, bool()} | {size, Size} | {no_files, NoFiles}

-
- -

HeaderLength = int() > 0

-
- -

Format = binary_term | term | binary | FormatFun

-
- -

FormatFun = fun(Binary) -> Term

-
- -

Order = ascending | descending | OrderFun

-
- -

OrderFun = fun(Term, Term) -> bool()

-
- -

TempDirectory = "" | file_name()

-
- -

Size = int() >= 0

-
- -

NoFiles = int() > 1

-
-

As an alternative to sorting files, a function of one argument can be given as input. When called with the argument read the function is assumed to return end_of_input or @@ -234,8 +201,8 @@ output(L) -> occurs are:

-

bad_object, {bad_object, FileName}. - Applying the format function failed for some binary, +

bad_object, {bad_object, FileName}. + Applying the format function failed for some binary, or the key(s) could not be extracted from some term.

@@ -243,148 +210,181 @@ output(L) -> to read some term.

-

{file_error, FileName, Reason2}. See - file(3) for an explanation of Reason2.

+

{file_error, FileName, file:posix()}. See + file(3) for an explanation of file:posix().

-

{premature_eof, FileName}. End-of-file was +

{premature_eof, FileName}. End-of-file was encountered inside some binary term.

-

Types

-
-Binary = binary()
-FileName = file_name()
-FileNames = [FileName]
-ICommand = read | close
-IReply = end_of_input | {end_of_input, Value} | {[Object], Infun} | InputReply
-Infun = fun(ICommand) -> IReply
-Input = FileNames | Infun
-InputReply = Term
-KeyPos = int() > 0 | [int() > 0]
-OCommand = {value, Value} | [Object] | close
-OReply = Outfun | OutputReply
-Object = Term | Binary
-Outfun = fun(OCommand) -> OReply
-Output = FileName | Outfun
-OutputReply = Term
-Term = term()
-Value = Term
+ + + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ - sort(FileName) -> Reply - sort(Input, Output) -> Reply - sort(Input, Output, Options) -> Reply + Sort terms on files. - - Reply = ok | {error, Reason} | InputReply | OutputReply - -

Sorts terms on files. -

-

sort(FileName) is equivalent to - sort([FileName], FileName). -

-

sort(Input, Output) is equivalent to - sort(Input, Output, []). -

-

+

Sorts terms on files. sort(FileName) is equivalent + to sort([FileName], FileName).

+
+
+ + + + Sort terms on files. + +

Sorts terms on files. sort(Input, Output) is + equivalent to sort(Input, Output, []).

+
+
+ + + Sort terms on files by key. + +

Sorts tuples on files. keysort(N, FileName) is + equivalent to keysort(N, [FileName], FileName).

- keysort(KeyPos, FileName) -> Reply - keysort(KeyPos, Input, Output) -> Reply - keysort(KeyPos, Input, Output, Options) -> Reply + + Sort terms on files by key. - - Reply = ok | {error, Reason} | InputReply | OutputReply -

Sorts tuples on files. The sort is performed on the - element(s) mentioned in KeyPos. If two tuples - compare equal (==) on one element, next element according to - KeyPos is compared. The sort is stable. -

-

keysort(N, FileName) is equivalent to - keysort(N, [FileName], FileName). -

+ element(s) mentioned in KeyPos. If two + tuples compare equal (==) on one element, next + element according to KeyPos + is compared. The sort is stable.

keysort(N, Input, Output) is equivalent to - keysort(N, Input, Output, []). -

-

+ keysort(N, Input, Output, []).

- merge(FileNames, Output) -> Reply - merge(FileNames, Output, Options) -> Reply + + Merge terms on files. - - Reply = ok | {error, Reason} | OutputReply -

Merges terms on files. Each input file is assumed to be - sorted. -

+ sorted.

merge(FileNames, Output) is equivalent to - merge(FileNames, Output, []). -

+ merge(FileNames, Output, []).

- keymerge(KeyPos, FileNames, Output) -> Reply - keymerge(KeyPos, FileNames, Output, Options) -> Reply + + Merge terms on files by key. - - Reply = ok | {error, Reason} | OutputReply -

Merges tuples on files. Each input file is assumed to be - sorted on key(s). -

+ sorted on key(s).

keymerge(KeyPos, FileNames, Output) is equivalent - to keymerge(KeyPos, FileNames, Output, []). -

-

+ to keymerge(KeyPos, FileNames, Output, []).

- check(FileName) -> Reply - check(FileNames, Options) -> Reply + + Check whether terms on files are sorted. - - Reply = {ok, [Result]} | {error, Reason} - Result = {FileName, TermPosition, Term} - TermPosition = int() > 1 -

Checks files for sortedness. If a file is not sorted, the first out-of-order element is returned. The first term on a - file has position 1. -

+ file has position 1.

check(FileName) is equivalent to - check([FileName], []). -

+ check([FileName], []).

- keycheck(KeyPos, FileName) -> CheckReply - keycheck(KeyPos, FileNames, Options) -> Reply + + Check whether terms on files are sorted by key. - - Reply = {ok, [Result]} | {error, Reason} - Result = {FileName, TermPosition, Term} - TermPosition = int() > 1 -

Checks files for sortedness. If a file is not sorted, the first out-of-order element is returned. The first term on a - file has position 1. -

+ file has position 1.

keycheck(KeyPos, FileName) is equivalent - to keycheck(KeyPos, [FileName], []). -

-

+ to keycheck(KeyPos, [FileName], []).

diff --git a/lib/stdlib/doc/src/filelib.xml b/lib/stdlib/doc/src/filelib.xml index fab68ae77c..f3079c7337 100644 --- a/lib/stdlib/doc/src/filelib.xml +++ b/lib/stdlib/doc/src/filelib.xml @@ -41,62 +41,46 @@

For more information about raw file names, see the file module.

-
- DATA TYPES - -filename() = string() | atom() | DeepList | RawFilename - DeepList = [char() | atom() | DeepList] - RawFilename = binary() - If VM is in unicode filename mode, string() and char() are allowed to be > 255. - RawFilename is a filename not subject to Unicode translation, meaning that it - can contain characters not conforming to the Unicode encoding expected from the - filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode - filename mode). -dirname() = filename() -
+ + + + + + + + - ensure_dir(Name) -> ok | {error, Reason} + Ensure that all parent directories for a file or directory exist. - - Name = filename() | dirname() - Reason = posix() -- see file(3) -

The ensure_dir/1 function ensures that all parent - directories for the given file or directory name Name + directories for the given file or directory name Name exist, trying to create them if necessary.

Returns ok if all parent directories already exist - or could be created, or {error, Reason} if some parent + or could be created, or {error, Reason} if some parent directory does not exist and could not be created for some reason.

- file_size(Filename) -> integer() + Return the size in bytes of the file.

The file_size function returns the size of the given file.

- fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut + Fold over all files matching a regular expression. - - Dir = dirname() - RegExp = regular_expression_string() - Recursive = true|false - Fun = fun(F, AccIn) -> AccOut - AccIn = AccOut = term() -

The fold_files/5 function folds the function - Fun over all (regular) files F in the - directory Dir that match the regular expression RegExp + Fun over all (regular) files F in the + directory Dir that match the regular expression RegExp (see the re module for a description of the allowed regular expressions). - If Recursive is true all sub-directories to Dir + If Recursive is true all sub-directories to Dir are processed. The regular expression matching is done on just the filename without the directory part.

@@ -114,44 +98,32 @@ dirname() = filename()
- is_dir(Name) -> true | false + Test whether Name refer to a directory or not - - Name = filename() | dirname() - -

The is_dir/1 function returns true if Name +

The is_dir/1 function returns true if Name refers to a directory, and false otherwise.

- is_file(Name) -> true | false + Test whether Name refer to a file or directory. - - Name = filename() | dirname() - -

The is_file/1 function returns true if Name +

The is_file/1 function returns true if Name refers to a file or a directory, and false otherwise.

- is_regular(Name) -> true | false + Test whether Name refer to a (regular) file. - - Name = filename() - -

The is_regular/1 function returns true if Name +

The is_regular/1 function returns true if Name refers to a file (regular file), and false otherwise.

- last_modified(Name) -> {{Year,Month,Day},{Hour,Min,Sec}} | 0 + Return the local date and time when a file was last modified. - - Name = filename() | dirname() -

The last_modified/1 function returns the date and time the given file or directory was last modified, or 0 if the file @@ -159,14 +131,11 @@ dirname() = filename() - wildcard(Wildcard) -> list() + Match filenames using Unix-style wildcards. - - Wildcard = filename() | dirname() -

The wildcard/1 function returns a list of all files - that match Unix-style wildcard-string Wildcard.

+ that match Unix-style wildcard-string Wildcard.

The wildcard string looks like an ordinary filename, except that certain "wildcard characters" are interpreted in a special way. The following characters are special: @@ -226,15 +195,11 @@ dirname() = filename() - wildcard(Wildcard, Cwd) -> list() + Match filenames using Unix-style wildcards starting at a specified directory. - - Wildcard = filename() | dirname() - Cwd = dirname() -

The wildcard/2 function works like wildcard/1, - except that instead of the actual working directory, Cwd + except that instead of the actual working directory, Cwd will be used.

diff --git a/lib/stdlib/doc/src/filename.xml b/lib/stdlib/doc/src/filename.xml index cdee6e4a81..bc3a616d39 100644 --- a/lib/stdlib/doc/src/filename.xml +++ b/lib/stdlib/doc/src/filename.xml @@ -4,7 +4,7 @@
- 19972010 + 19972011 Ericsson AB. All Rights Reserved. @@ -47,28 +47,12 @@ file:native_name_encoding/0, a raw file name will also be returned. For example filename:join/1 provided with a path component being a binary (and also not being possible to interpret under the current native file name encoding) will result in a raw file name being returned (the join operation will have been performed of course). For more information about raw file names, see the file module.

-
- DATA TYPES - -name() = string() | atom() | DeepList | RawFilename - DeepList = [char() | atom() | DeepList] - RawFilename = binary() - If VM is in unicode filename mode, string() and char() are allowed to be > 255. - RawFilename is a filename not subject to Unicode translation, meaning that it - can contain characters not conforming to the Unicode encoding expected from the - filesystem (i.e. non-UTF-8 characters although the VM is started in Unicode - filename mode). - -
- absname(Filename) -> string() + Convert a filename to an absolute name, relative the working directory - - Filename = name() - -

Converts a relative Filename and returns an absolute +

Converts a relative Filename and returns an absolute name. No attempt is made to create the shortest absolute name, because this can give incorrect results on file systems which allow links.

@@ -95,44 +79,33 @@ name() = string() | atom() | DeepList | RawFilename
- absname(Filename, Dir) -> string() + Convert a filename to an absolute name, relative a specified directory - - Filename = name() - Dir = string() -

This function works like absname/1, except that the directory to which the file name should be made relative - is given explicitly in the Dir argument.

+ is given explicitly in the Dir argument.

- absname_join(Dir, Filename) -> string() + Join an absolute directory with a relative filename - - Dir = string() - Filename = name() -

Joins an absolute directory with a relative filename. Similar to join/2, but on platforms with tight restrictions on raw filename length and no support for symbolic links (read: VxWorks), leading parent directory - components in Filename are matched against trailing - directory components in Dir so they can be removed + components in Filename are matched against trailing + directory components in Dir so they can be removed from the result - minimizing its length.

- basename(Filename) -> string() + Return the last component of a filename - - Filename = name() - -

Returns the last component of Filename, or - Filename itself if it does not contain any directory +

Returns the last component of Filename, or + Filename itself if it does not contain any directory separators.

 5> filename:basename("foo").
@@ -144,14 +117,11 @@ name() = string() | atom() | DeepList | RawFilename
       
     
     
-      basename(Filename, Ext) -> string()
+      
       Return the last component of a filename, stripped of the specified extension
-      
-        Filename = Ext = name()
-      
       
-        

Returns the last component of Filename with the - extension Ext stripped. This function should be used +

Returns the last component of Filename with the + extension Ext stripped. This function should be used to remove a specific extension which might, or might not, be there. Use rootname(basename(Filename)) to remove an extension that exists, but you are not sure which one it is.

@@ -169,13 +139,10 @@ name() = string() | atom() | DeepList | RawFilename
- dirname(Filename) -> string() + Return the directory part of a path name - - Filename = name() - -

Returns the directory part of Filename.

+

Returns the directory part of Filename.

 13> filename:dirname("/usr/src/kalle.erl").
 "/usr/src"
@@ -187,13 +154,10 @@ name() = string() | atom() | DeepList | RawFilename
       
     
     
-      extension(Filename) -> string()
+      
       Return the file extension
-      
-        Filename = name()
-      
       
-        

Returns the file extension of Filename, including +

Returns the file extension of Filename, including the period. Returns an empty string if there is no extension.

 15> filename:extension("foo.erl").
@@ -203,11 +167,8 @@ name() = string() | atom() | DeepList | RawFilename
       
     
     
-      flatten(Filename) -> string()
+      
       Convert a filename to a flat string
-      
-        Filename = name()
-      
       
         

Converts a possibly deep list filename consisting of characters and atoms into the corresponding flat string @@ -215,14 +176,11 @@ name() = string() | atom() | DeepList | RawFilename - join(Components) -> string() + Join a list of filename components with directory separators - - Components = [string()] - -

Joins a list of file name Components with directory - separators. If one of the elements of Components +

Joins a list of file name Components with directory + separators. If one of the elements of Components includes an absolute path, for example "/xxx", the preceding elements, if any, are removed from the result.

The result is "normalized":

@@ -242,24 +200,18 @@ name() = string() | atom() | DeepList | RawFilename
- join(Name1, Name2) -> string() + Join two filename components with directory separators - - Name1 = Name2 = string() -

Joins two file name components with directory separators. - Equivalent to join([Name1, Name2]).

+ Equivalent to join([Name1, Name2]).

- nativename(Path) -> string() + Return the native form of a file path - - Path = string() - -

Converts Path to a form accepted by the command shell +

Converts Path to a form accepted by the command shell and native applications on the current platform. On Windows, forward slashes is converted to backward slashes. On all platforms, the name is normalized as done by join/1.

@@ -272,7 +224,7 @@ name() = string() | atom() | DeepList | RawFilename
- pathtype(Path) -> absolute | relative | volumerelative + Return the type of a path

Returns the type of path, one of absolute, @@ -302,16 +254,13 @@ name() = string() | atom() | DeepList | RawFilename - rootname(Filename) -> string() - rootname(Filename, Ext) -> string() + + Remove a filename extension - - Filename = Ext = name() -

Remove a filename extension. rootname/2 works as rootname/1, except that the extension is removed only - if it is Ext.

+ if it is Ext.

 20> filename:rootname("/beam.src/kalle").
 /beam.src/kalle"
@@ -324,15 +273,11 @@ name() = string() | atom() | DeepList | RawFilename
       
     
     
-      split(Filename) -> Components
+      
       Split a filename into its path components
-      
-        Filename = name()
-        Components = [string()]
-      
       
         

Returns a list whose elements are the path components of - Filename.

+ Filename.

 24> filename:split("/usr/local/bin").
 ["/","usr","local","bin"]
@@ -343,47 +288,38 @@ name() = string() | atom() | DeepList | RawFilename
       
     
     
-      find_src(Beam) -> {SourceFile, Options} | {error,{ErrorReason,Module}}
-      find_src(Beam, Rules) -> {SourceFile, Options} | {error,{ErrorReason,Module}}
+      
+      
       Find the filename and compiler options for a module
-      
-        Beam = Module | Filename
-         Module = atom()
-         Filename = string() | atom()
-        SourceFile = string()
-        Options = [Opt]
-         Opt = {i, string()} | {outdir, string()} | {d, atom()}
-	ErrorReason = non_existing | preloaded | interpreted
-      
       
         

Finds the source filename and compiler options for a module. The result can be fed to compile:file/2 in order to compile the file again.

-

The Beam argument, which can be a string or an atom, +

The Beam argument, which can be a string or an atom, specifies either the module name or the path to the source code, with or without the ".erl" extension. In either case, the module must be known by the code server, i.e. - code:which(Module) must succeed.

-

Rules describes how the source directory can be found, + code:which(Module) must succeed.

+

Rules describes how the source directory can be found, when the object code directory is known. It is a list of - tuples {BinSuffix, SourceSuffix} and is interpreted + tuples {BinSuffix, SourceSuffix} and is interpreted as follows: If the end of the directory name where the object - is located matches BinSuffix, then the source code - directory has the same name, but with BinSuffix - replaced by SourceSuffix. Rules defaults to:

+ is located matches BinSuffix, then the source code + directory has the same name, but with BinSuffix + replaced by SourceSuffix. Rules defaults to:

[{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}]

If the source file is found in the resulting directory, then the function returns that location together with - Options. Otherwise, the next rule is tried, and so on.

+ Options. Otherwise, the next rule is tried, and so on.

-

The function returns {SourceFile, Options} if it succeeds. - SourceFile is the absolute path to the source file - without the ".erl" extension. Options include +

The function returns {SourceFile, Options} if it succeeds. + SourceFile is the absolute path to the source file + without the ".erl" extension. Options include the options which are necessary to recompile the file with compile:file/2, but excludes options such as report or verbose which do not change the way - code is generated. The paths in the {outdir, Path} + code is generated. The paths in the {outdir, Path} and {i, Path} options are guaranteed to be absolute.

diff --git a/lib/stdlib/doc/src/gb_sets.xml b/lib/stdlib/doc/src/gb_sets.xml index 60d8bcbfa3..38de51322f 100644 --- a/lib/stdlib/doc/src/gb_sets.xml +++ b/lib/stdlib/doc/src/gb_sets.xml @@ -4,7 +4,7 @@
- 20012010 + 20012011 Ericsson AB. All Rights Reserved. @@ -114,34 +114,32 @@
-
- DATA TYPES - -gb_set() = a GB set -
+ + + gb_set() +

A GB set.

+
+ + +

A GB set iterator.

+
+
- add(Element, Set1) -> Set2 - add_element(Element, Set1) -> Set2 + + Add a (possibly existing) element to a gb_set - - Element = term() - Set1 = Set2 = gb_set() - -

Returns a new gb_set formed from Set1 with - Element inserted. If Element is already an - element in Set1, nothing is changed.

+

Returns a new gb_set formed from Set1 with + Element inserted. If Element is already an + element in Set1, nothing is changed.

- balance(Set1) -> Set2 + Rebalance tree representation of a gb_set - - Set1 = Set2 = gb_set() - -

Rebalances the tree representation of Set1. Note that +

Rebalances the tree representation of Set1. Note that this is rarely necessary, but may be motivated when a large number of elements have been deleted from the tree without further insertions. Rebalancing could then be forced in order @@ -150,208 +148,144 @@ gb_set() = a GB set - delete(Element, Set1) -> Set2 + Remove an element from a gb_set - - Element = term() - Set1 = Set2 = gb_set() - -

Returns a new gb_set formed from Set1 with - Element removed. Assumes that Element is present - in Set1.

+

Returns a new gb_set formed from Set1 with + Element removed. Assumes that Element is present + in Set1.

- delete_any(Element, Set1) -> Set2 - del_element(Element, Set1) -> Set2 + + Remove a (possibly non-existing) element from a gb_set - - Element = term() - Set1 = Set2 = gb_set() - -

Returns a new gb_set formed from Set1 with - Element removed. If Element is not an element - in Set1, nothing is changed.

+

Returns a new gb_set formed from Set1 with + Element removed. If Element is not an element + in Set1, nothing is changed.

- difference(Set1, Set2) -> Set3 - subtract(Set1, Set2) -> Set3 + + Return the difference of two gb_sets - - Set1 = Set2 = Set3 = gb_set() - -

Returns only the elements of Set1 which are not also - elements of Set2.

+

Returns only the elements of Set1 which are not also + elements of Set2.

- empty() -> Set - new() -> Set + + Return an empty gb_set - - Set = gb_set() -

Returns a new empty gb_set.

- filter(Pred, Set1) -> Set2 + Filter gb_set elements - - Pred = fun (E) -> bool() -  E = term() - Set1 = Set2 = gb_set() - -

Filters elements in Set1 using predicate function - Pred.

+

Filters elements in Set1 using predicate function + Pred.

- fold(Function, Acc0, Set) -> Acc1 + Fold over gb_set elements - - Function = fun (E, AccIn) -> AccOut - Acc0 = Acc1 = AccIn = AccOut = term() -  E = term() - Set = gb_set() - -

Folds Function over every element in Set +

Folds Function over every element in Set returning the final value of the accumulator.

- from_list(List) -> Set + Convert a list into a gb_set - - List = [term()] - Set = gb_set() - -

Returns a gb_set of the elements in List, where - List may be unordered and contain duplicates.

+

Returns a gb_set of the elements in List, where + List may be unordered and contain duplicates.

- from_ordset(List) -> Set + Make a gb_set from an ordset list - - List = [term()] - Set = gb_set() - -

Turns an ordered-set list List into a gb_set. The list +

Turns an ordered-set list List into a gb_set. The list must not contain duplicates.

- insert(Element, Set1) -> Set2 + Add a new element to a gb_set - - Element = term() - Set1 = Set2 = gb_set() - -

Returns a new gb_set formed from Set1 with - Element inserted. Assumes that Element is not - present in Set1.

+

Returns a new gb_set formed from Set1 with + Element inserted. Assumes that Element is not + present in Set1.

- intersection(Set1, Set2) -> Set3 + Return the intersection of two gb_sets - - Set1 = Set2 = Set3 = gb_set() - -

Returns the intersection of Set1 and Set2.

+

Returns the intersection of Set1 and Set2.

- intersection(SetList) -> Set + Return the intersection of a list of gb_sets - - SetList = [gb_set()] - Set = gb_set() -

Returns the intersection of the non-empty list of gb_sets.

- is_disjoint(Set1, Set2) -> bool() + Check whether two gb_sets are disjoint - - Set1 = Set2 = gb_set() - -

Returns true if Set1 and - Set2 are disjoint (have no elements in common), +

Returns true if Set1 and + Set2 are disjoint (have no elements in common), and false otherwise.

- is_empty(Set) -> bool() + Test for empty gb_set - - Set = gb_set() - -

Returns true if Set is an empty set, and +

Returns true if Set is an empty set, and false otherwise.

- is_member(Element, Set) -> bool() - is_element(Element, Set) -> bool() + + Test for membership of a gb_set - - Element = term() - Set = gb_set() - -

Returns true if Element is an element of - Set, otherwise false.

+

Returns true if Element is an element of + Set, otherwise false.

- is_set(Term) -> bool() + Test for a gb_set - - Term = term() - -

Returns true if Set appears to be a gb_set, +

Returns true if Term appears to be a gb_set, otherwise false.

- is_subset(Set1, Set2) -> bool() + Test for subset - - Set1 = Set2 = gb_set() - -

Returns true when every element of Set1 is - also a member of Set2, otherwise false.

+

Returns true when every element of Set1 is + also a member of Set2, otherwise false.

- iterator(Set) -> Iter + Return an iterator for a gb_set - - Set = gb_set() - Iter = term() -

Returns an iterator that can be used for traversing the - entries of Set; see next/1. The implementation + entries of Set; see next/1. The implementation of this is very efficient; traversing the whole set using next/1 is only slightly slower than getting the list of all elements using to_list/1 and traversing that. @@ -361,118 +295,84 @@ gb_set() = a GB set - largest(Set) -> term() + Return largest element - - Set = gb_set() - -

Returns the largest element in Set. Assumes that - Set is nonempty.

+

Returns the largest element in Set. Assumes that + Set is nonempty.

- next(Iter1) -> {Element, Iter2} | none + Traverse a gb_set with an iterator - - Iter1 = Iter2 = Element = term() - -

Returns {Element, Iter2} where Element is the - smallest element referred to by the iterator Iter1, - and Iter2 is the new iterator to be used for +

Returns {Element, Iter2} where Element is the + smallest element referred to by the iterator Iter1, + and Iter2 is the new iterator to be used for traversing the remaining elements, or the atom none if no elements remain.

- singleton(Element) -> gb_set() + Return a gb_set with one element - - Element = term() - -

Returns a gb_set containing only the element Element.

+

Returns a gb_set containing only the element Element.

- size(Set) -> int() + Return the number of elements in a gb_set - - Set = gb_set() - -

Returns the number of elements in Set.

+

Returns the number of elements in Set.

- smallest(Set) -> term() + Return smallest element - - Set = gb_set() - -

Returns the smallest element in Set. Assumes that - Set is nonempty.

+

Returns the smallest element in Set. Assumes that + Set is nonempty.

- take_largest(Set1) -> {Element, Set2} + Extract largest element - - Set1 = Set2 = gb_set() - Element = term() - -

Returns {Element, Set2}, where Element is the - largest element in Set1, and Set2 is this set - with Element deleted. Assumes that Set1 is +

Returns {Element, Set2}, where Element is the + largest element in Set1, and Set2 is this set + with Element deleted. Assumes that Set1 is nonempty.

- take_smallest(Set1) -> {Element, Set2} + Extract smallest element - - Set1 = Set2 = gb_set() - Element = term() - -

Returns {Element, Set2}, where Element is the - smallest element in Set1, and Set2 is this set - with Element deleted. Assumes that Set1 is +

Returns {Element, Set2}, where Element is the + smallest element in Set1, and Set2 is this set + with Element deleted. Assumes that Set1 is nonempty.

- to_list(Set) -> List + Convert a gb_set into a list - - Set = gb_set() - List = [term()] - -

Returns the elements of Set as a list.

+

Returns the elements of Set as a list.

- union(Set1, Set2) -> Set3 + Return the union of two gb_sets - - Set1 = Set2 = Set3 = gb_set() - -

Returns the merged (union) gb_set of Set1 and - Set2.

+

Returns the merged (union) gb_set of Set1 and + Set2.

- union(SetList) -> Set + Return the union of a list of gb_sets - - SetList = [gb_set()] - Set = gb_set() -

Returns the merged (union) gb_set of the list of gb_sets.

diff --git a/lib/stdlib/doc/src/gb_trees.xml b/lib/stdlib/doc/src/gb_trees.xml index 94f40c28bd..65c866efbe 100644 --- a/lib/stdlib/doc/src/gb_trees.xml +++ b/lib/stdlib/doc/src/gb_trees.xml @@ -4,7 +4,7 @@
- 20012010 + 20012011 Ericsson AB. All Rights Reserved. @@ -57,20 +57,22 @@ trees. Behaviour is logarithmic (as it should be).

-
- DATA TYPES - -gb_tree() = a GB tree -
+ + + gb_tree() +

A GB tree.

+
+ + +

A GB tree iterator.

+
+
- balance(Tree1) -> Tree2 + Rebalance a tree - - Tree1 = Tree2 = gb_tree() - -

Rebalances Tree1. Note that this is rarely necessary, +

Rebalances Tree1. Note that this is rarely necessary, but may be motivated when a large number of nodes have been deleted from the tree without further insertions. Rebalancing could then be forced in order to minimise lookup times, since @@ -78,139 +80,97 @@ gb_tree() = a GB tree - delete(Key, Tree1) -> Tree2 + Remove a node from a tree - - Key = term() - Tree1 = Tree2 = gb_tree() - -

Removes the node with key Key from Tree1; +

Removes the node with key Key from Tree1; returns new tree. Assumes that the key is present in the tree, crashes otherwise.

- delete_any(Key, Tree1) -> Tree2 + Remove a (possibly non-existing) node from a tree - - Key = term() - Tree1 = Tree2 = gb_tree() - -

Removes the node with key Key from Tree1 if +

Removes the node with key Key from Tree1 if the key is present in the tree, otherwise does nothing; returns new tree.

- empty() -> Tree + Return an empty tree - - Tree = gb_tree() -

Returns a new empty tree

- enter(Key, Val, Tree1) -> Tree2 + Insert or update key with value in a tree - - Key = Val = term() - Tree1 = Tree2 = gb_tree() - -

Inserts Key with value Val into Tree1 if +

Inserts Key with value Val into Tree1 if the key is not present in the tree, otherwise updates - Key to value Val in Tree1. Returns the + Key to value Val in Tree1. Returns the new tree.

- from_orddict(List) -> Tree + Make a tree from an orddict - - List = [{Key, Val}] -  Key = Val = term() - Tree = gb_tree() - -

Turns an ordered list List of key-value tuples into a +

Turns an ordered list List of key-value tuples into a tree. The list must not contain duplicate keys.

- get(Key, Tree) -> Val + Look up a key in a tree, if present - - Key = Val = term() - Tree = gb_tree() - -

Retrieves the value stored with Key in Tree. +

Retrieves the value stored with Key in Tree. Assumes that the key is present in the tree, crashes otherwise.

- lookup(Key, Tree) -> {value, Val} | none + Look up a key in a tree - - Key = Val = term() - Tree = gb_tree() - -

Looks up Key in Tree; returns - {value, Val}, or none if Key is not +

Looks up Key in Tree; returns + {value, Val}, or none if Key is not present.

- insert(Key, Val, Tree1) -> Tree2 + Insert a new key and value in a tree - - Key = Val = term() - Tree1 = Tree2 = gb_tree() - -

Inserts Key with value Val into Tree1; +

Inserts Key with value Val into Tree1; returns the new tree. Assumes that the key is not present in the tree, crashes otherwise.

- is_defined(Key, Tree) -> bool() + Test for membership of a tree - - Tree = gb_tree() - -

Returns true if Key is present in Tree, +

Returns true if Key is present in Tree, otherwise false.

- is_empty(Tree) -> bool() + Test for empty tree - - Tree = gb_tree() - -

Returns true if Tree is an empty tree, and +

Returns true if Tree is an empty tree, and false otherwise.

- iterator(Tree) -> Iter + Return an iterator for a tree - - Tree = gb_tree() - Iter = term() -

Returns an iterator that can be used for traversing the - entries of Tree; see next/1. The implementation + entries of Tree; see next/1. The implementation of this is very efficient; traversing the whole tree using next/1 is only slightly slower than getting the list of all elements using to_list/1 and traversing that. @@ -220,141 +180,99 @@ gb_tree() = a GB tree - keys(Tree) -> [Key] + Return a list of the keys in a tree - - Tree = gb_tree() - Key = term() - -

Returns the keys in Tree as an ordered list.

+

Returns the keys in Tree as an ordered list.

- largest(Tree) -> {Key, Val} + Return largest key and value - - Tree = gb_tree() - Key = Val = term() - -

Returns {Key, Val}, where Key is the largest - key in Tree, and Val is the value associated +

Returns {Key, Val}, where Key is the largest + key in Tree, and Val is the value associated with this key. Assumes that the tree is nonempty.

- map(Function, Tree1) -> Tree2 + Return largest key and value - - Function = fun(K, V1) -> V2 - Tree1 = Tree2 = gb_tree() - -

maps the function F(K, V1) -> V2 to all key-value pairs - of the tree Tree1 and returns a new tree Tree2 with the same set of keys - as Tree1 and the new set of values V2.

+

Maps the function F(K, V1) -> V2 to all key-value pairs + of the tree Tree1 and returns a new tree Tree2 with the same set of keys + as Tree1 and the new set of values V2.

- next(Iter1) -> {Key, Val, Iter2} | none + Traverse a tree with an iterator - - Iter1 = Iter2 = Key = Val = term() - -

Returns {Key, Val, Iter2} where Key is the - smallest key referred to by the iterator Iter1, and - Iter2 is the new iterator to be used for +

Returns {Key, Val, Iter2} where Key is the + smallest key referred to by the iterator Iter1, and + Iter2 is the new iterator to be used for traversing the remaining nodes, or the atom none if no nodes remain.

- size(Tree) -> int() + Return the number of nodes in a tree - - Tree = gb_tree() - -

Returns the number of nodes in Tree.

+

Returns the number of nodes in Tree.

- smallest(Tree) -> {Key, Val} + Return smallest key and value - - Tree = gb_tree() - Key = Val = term() - -

Returns {Key, Val}, where Key is the smallest - key in Tree, and Val is the value associated +

Returns {Key, Val}, where Key is the smallest + key in Tree, and Val is the value associated with this key. Assumes that the tree is nonempty.

- take_largest(Tree1) -> {Key, Val, Tree2} + Extract largest key and value - - Tree1 = Tree2 = gb_tree() - Key = Val = term() - -

Returns {Key, Val, Tree2}, where Key is the - largest key in Tree1, Val is the value - associated with this key, and Tree2 is this tree with +

Returns {Key, Val, Tree2}, where Key is the + largest key in Tree1, Val is the value + associated with this key, and Tree2 is this tree with the corresponding node deleted. Assumes that the tree is nonempty.

- take_smallest(Tree1) -> {Key, Val, Tree2} + Extract smallest key and value - - Tree1 = Tree2 = gb_tree() - Key = Val = term() - -

Returns {Key, Val, Tree2}, where Key is the - smallest key in Tree1, Val is the value - associated with this key, and Tree2 is this tree with +

Returns {Key, Val, Tree2}, where Key is the + smallest key in Tree1, Val is the value + associated with this key, and Tree2 is this tree with the corresponding node deleted. Assumes that the tree is nonempty.

- to_list(Tree) -> [{Key, Val}] + Convert a tree into a list - - Tree = gb_tree() - Key = Val = term() -

Converts a tree into an ordered list of key-value tuples.

- update(Key, Val, Tree1) -> Tree2 + Update a key to new value in a tree - - Key = Val = term() - Tree1 = Tree2 = gb_tree() - -

Updates Key to value Val in Tree1; +

Updates Key to value Val in Tree1; returns the new tree. Assumes that the key is present in the tree.

- values(Tree) -> [Val] + Return a list of the values in a tree - - Tree = gb_tree() - Val = term() - -

Returns the values in Tree as an ordered list, sorted +

Returns the values in Tree as an ordered list, sorted by their corresponding keys. Duplicates are not removed.

diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml index 2234a62ac3..24bcb419fe 100644 --- a/lib/stdlib/doc/src/gen_event.xml +++ b/lib/stdlib/doc/src/gen_event.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -100,6 +100,20 @@ gen_event:stop -----> Module:terminate/2 the specified event manager does not exist or if bad arguments are given.

+ + + + + + + + + + + + + + start_link() -> Result diff --git a/lib/stdlib/doc/src/io.xml b/lib/stdlib/doc/src/io.xml index 41e3e92c59..af9c75d546 100644 --- a/lib/stdlib/doc/src/io.xml +++ b/lib/stdlib/doc/src/io.xml @@ -43,7 +43,7 @@

As of R13A, data supplied to the put_chars function should be in the - chardata() format described below. This means that programs + unicode:chardata() format. This means that programs supplying binaries to this function need to convert them to UTF-8 before trying to output the data on an io_device().

@@ -64,76 +64,84 @@ -
- DATA TYPES - -io_device() - as returned by file:open/2, a process handling IO protocols - - -unicode_binary() = binary() with characters encoded in UTF-8 coding standard -unicode_char() = integer() representing valid unicode codepoint - -chardata() = charlist() | unicode_binary() + + + + +

Either standard_io, standard_error, a + registered name, or a pid handling IO protocols (returned from + file:open/2).

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +

Whatever the I/O-server sends.

+
+
-charlist() = [unicode_char() | unicode_binary() | charlist()] - a unicode_binary is allowed as the tail of the list
-
- columns() -> {ok,int()} | {error, enotsup} - columns(IoDevice) -> {ok,int()} | {error, enotsup} + + Get the number of columns of a device - - IoDevice = io_device() -

Retrieves the number of columns of the - IoDevice (i.e. the width of a terminal). The function + IoDevice (i.e. the width of a terminal). The function only succeeds for terminal devices, for all other devices the function returns {error, enotsup}

- put_chars(IoData) -> ok - put_chars(IoDevice, IoData) -> ok + + Write a list of characters - - IoDevice = io_device() - IoData = chardata() - -

Writes the characters of IoData to the io_server() - (IoDevice).

+

Writes the characters of CharData to the io_server() + (IoDevice).

- nl() -> ok - nl(IoDevice) -> ok + + Write a newline - - IoDevice = io_device() - -

Writes new line to the standard output (IoDevice).

+

Writes new line to the standard output (IoDevice).

- get_chars(Prompt, Count) -> Data | eof - get_chars(IoDevice, Prompt, Count) -> Data | eof + + Read a specified number of characters - - IoDevice = io_device() - Prompt = atom() | string() - Count = int() - Data = [ unicode_char() ] | unicode_binary() - -

Reads Count characters from standard input - (IoDevice), prompting it with Prompt. It +

Reads Count characters from standard input + (IoDevice), prompting it with Prompt. It returns:

- Data + Data

The input characters. If the device supports Unicode, the data may represent codepoints larger than 255 (the @@ -145,7 +153,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]

End of file was encountered.

- {error,Reason} + {error,Reason}

Other (rare) error condition, for instance {error,estale} if reading from an NFS file system.

@@ -154,19 +162,14 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
- get_line(Prompt) -> Data | eof | {error,Reason} - get_line(IoDevice, Prompt) -> Data | eof | {error,Reason} + + Read a line - - IoDevice = io_device() - Prompt = atom() | string() - Data = [ unicode_char() ] | unicode_binary() - -

Reads a line from the standard input (IoDevice), - prompting it with Prompt. It returns:

+

Reads a line from the standard input (IoDevice), + prompting it with Prompt. It returns:

- Data + Data

The characters in the line terminated by a LF (or end of file). If the device supports Unicode, @@ -179,7 +182,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]

End of file was encountered.

- {error,Reason} + {error,Reason}

Other (rare) error condition, for instance {error,estale} if reading from an NFS file system.

@@ -188,15 +191,9 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
- getopts() -> Opts - getopts(IoDevice) -> Opts + + Get the supported options and values from an I/O-server - - IoDevice = io_device() - Opts = [Opt] -   Opt = {atom(),Value} -   Value = term() -

This function requests all available options and their current values for a specific io_device(). Example:

@@ -216,18 +213,11 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
       
     
     
-      setopts(Opts) -> ok | {error, Reason}
-      setopts(IoDevice, Opts) -> ok | {error, Reason}
+      
+      
       Set options
-      
-        IoDevice = io_device()
-        Opts = [Opt]
-          Opt = atom() | {atom(),Value}
-	  Value = term()
-        Reason = term()
-      
       
-        

Set options for the io_device() (IoDevice).

+

Set options for the io_device() (IoDevice).

Possible options and values vary depending on the actual io_device(). For a list of supported options and their current values @@ -236,17 +226,17 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]

The options and values supported by the current OTP io_devices are:

- binary, list or {binary, bool()} + binary, list or {binary, boolean()}

If set in binary mode (binary or {binary,true}), the io_server() sends binary data (encoded in UTF-8) as answers to the get_line, get_chars and, if possible, get_until requests (see the I/O protocol description in STDLIB User's Guide for details). The immediate effect is that get_chars/2,3 and get_line/1,2 return UTF-8 binaries instead of lists of chars for the affected device.

By default, all io_devices in OTP are set in list mode, but the io functions can handle any of these modes and so should other, user written, modules behaving as clients to I/O-servers.

This option is supported by the standard shell (group.erl), the 'oldshell' (user.erl) and the file I/O servers.

- {echo, bool()} + {echo, boolean()}

Denotes if the terminal should echo input. Only supported for the standard shell I/O-server (group.erl)

- {expand_fun, fun()} + {expand_fun, expand_fun()}

Provide a function for tab-completion (expansion) like the erlang shell. This function is called @@ -288,35 +278,24 @@ charlist() = [unicode_char() | unicode_binary() | charlist()] - write(Term) -> ok - write(IoDevice, Term) -> ok + + Write a term - - IoDevice = io_device() - Term = term() - -

Writes the term Term to the standard output - (IoDevice).

+

Writes the term Term to the standard output + (IoDevice).

- read(Prompt) -> Result - read(IoDevice, Prompt) -> Result + + Read a term - - IoDevice = io_device() - Prompt = atom() | string() - Result = {ok, Term} | eof | {error, ErrorInfo} -  Term = term() -  ErrorInfo -- see section Error Information below - -

Reads a term Term from the standard input - (IoDevice), prompting it with Prompt. It +

Reads a term Term from the standard input + (IoDevice), prompting it with Prompt. It returns:

- {ok, Term} + {ok, Term}

The parsing was successful.

@@ -324,7 +303,7 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]

End of file was encountered.

- {error, ErrorInfo} + {error, ErrorInfo}

The parsing failed.

@@ -332,31 +311,22 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
- read(IoDevice, Prompt, StartLine) -> Result + Read a term - - IoDevice = io_device() - Prompt = atom() | string() - StartLine = int() - Result = {ok, Term, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine} -  Term = term() -  EndLine = int() -  ErrorInfo -- see section Error Information below - -

Reads a term Term from IoDevice, prompting it - with Prompt. Reading starts at line number - StartLine. It returns:

+

Reads a term Term from IoDevice, prompting it + with Prompt. Reading starts at line number + StartLine. It returns:

- {ok, Term, EndLine} + {ok, Term, EndLine}

The parsing was successful.

- {eof, EndLine} + {eof, EndLine}

End of file was encountered.

- {error, ErrorInfo, EndLine} + {error, ErrorInfo, ErrorLine}

The parsing failed.

@@ -364,24 +334,19 @@ charlist() = [unicode_char() | unicode_binary() | charlist()]
- fwrite(Format) -> - fwrite(Format, Data) -> ok - fwrite(IoDevice, Format, Data) -> ok - format(Format) -> - format(Format, Data) -> ok - format(IoDevice, Format, Data) -> ok + + + + + + Write formatted output - - IoDevice = io_device() - Format = atom() | string() | binary() - Data = [term()] - -

Writes the items in Data ([]) on the standard - output (IoDevice) in accordance with Format. - Format contains plain characters which are copied to +

Writes the items in Data ([]) on the standard + output (IoDevice) in accordance with Format. + Format contains plain characters which are copied to the output device, and control sequences for formatting, see - below. If Format is an atom or a binary, it is first + below. If Format is an atom or a binary, it is first converted to a list with the aid of atom_to_list/1 or binary_to_list/1.

@@ -474,7 +439,7 @@ ok

Prints the argument with the string syntax. The argument is, if no Unicode translation modifier is present, an - I/O list, a binary, or an atom. If the Unicode translation modifier ('t') is in effect, the argument is chardata(), meaning that binaries are in UTF-8. The characters + I/O list, 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.

@@ -673,23 +638,15 @@ ok
- fread(Prompt, Format) -> Result - fread(IoDevice, Prompt, Format) -> Result + + Read formatted input - - IoDevice = io_device() - Prompt = atom() | string() - Format = string() - Result = {ok, Terms} | eof | {error, What} -  Terms = [term()] -  What = term() - -

Reads characters from the standard input (IoDevice), - prompting it with Prompt. Interprets the characters in - accordance with Format. Format contains control +

Reads characters from the standard input (IoDevice), + prompting it with Prompt. Interprets the characters in + accordance with Format. Format contains control sequences which directs the interpretation of the input.

-

Format may contain:

+

Format may contain:

White space characters (SPACE, TAB and NEWLINE) which @@ -803,19 +760,19 @@ Prompt> <Character beyond latin1 range not printable in this medium>

It returns:

- {ok, Terms} + {ok, Terms} -

The read was successful and Terms is the list +

The read was successful and Terms is the list of successfully matched and read items.

eof

End of file was encountered.

- {error, What} + {error, What}

The read operation failed and the parameter - What gives a hint about the error.

+ What gives a hint about the error.

@@ -834,33 +791,21 @@ enter>: alan : joe
- rows() -> {ok,int()} | {error, enotsup} - rows(IoDevice) -> {ok,int()} | {error, enotsup} + + Get the number of rows of a device - - IoDevice = io_device() -

Retrieves the number of rows of the - IoDevice (i.e. the height of a terminal). The function + IoDevice (i.e. the height of a terminal). The function only succeeds for terminal devices, for all other devices the function returns {error, enotsup}

- scan_erl_exprs(Prompt) -> - scan_erl_exprs(Prompt, StartLine) -> Result - scan_erl_exprs(IoDevice, Prompt, StartLine) -> Result + + + Read and tokenize Erlang expressions - - IoDevice = io_device() - Prompt = atom() | string() - StartLine = int() - Result = {ok, Tokens, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine} -  Tokens -- see erl_scan(3) -  EndLine = int() -  ErrorInfo -- see section Error Information below -

Reads data from the standard input (IoDevice), prompting it with Prompt. Reading starts at line number @@ -876,7 +821,7 @@ enter>: alan : joe

End of file was encountered.

- {error, ErrorInfo, EndLine} + {error, ErrorInfo, ErrorLine}

An error occurred.

@@ -892,23 +837,14 @@ enter>1.0er.
- scan_erl_form(Prompt) -> - scan_erl_form(Prompt, StartLine) -> Result - scan_erl_form(IoDevice, Prompt, StartLine) -> Result + + + Read and tokenize an Erlang form - - IoDevice = io_device() - Prompt = atom() | string() - StartLine = int() - Result = {ok, Tokens, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine} -  Tokens -- see erl_scan(3) -  EndLine = int() -  ErrorInfo -- see section Error Information below - -

Reads data from the standard input (IoDevice), - prompting it with Prompt. Starts reading at line number - StartLine (1). The data is tokenized as if it were an +

Reads data from the standard input (IoDevice), + prompting it with Prompt. Starts reading at line number + StartLine (1). The data is tokenized as if it were an Erlang form - one of the valid Erlang expressions in an Erlang source file - until a final '.' is reached. This last token is also returned. The return values are the @@ -916,27 +852,19 @@ enter>1.0er. - parse_erl_exprs(Prompt) -> - parse_erl_exprs(Prompt, StartLine) -> Result - parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result + + + + Read, tokenize and parse Erlang expressions - - IoDevice = io_device() - Prompt = atom() | string() - StartLine = int() - Result = {ok, Expr_list, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine} -  Expr_list -- see erl_parse(3) -  EndLine = int() -  ErrorInfo -- see section Error Information below - -

Reads data from the standard input (IoDevice), - prompting it with Prompt. Starts reading at line number - StartLine (1). The data is tokenized and parsed as if +

Reads data from the standard input (IoDevice), + prompting it with Prompt. Starts reading at line number + StartLine (1). The data is tokenized and parsed as if it were a sequence of Erlang expressions until a final '.' is reached. It returns:

- {ok, Expr_list, EndLine} + {ok, ExprList, EndLine}

The parsing was successful.

@@ -944,7 +872,7 @@ enter>1.0er.

End of file was encountered.

- {error, ErrorInfo, EndLine} + {error, ErrorInfo, ErrorLine}

An error occurred.

@@ -960,23 +888,15 @@ enter>abc("hey".
- parse_erl_form(Prompt) -> - parse_erl_form(Prompt, StartLine) -> Result - parse_erl_form(IoDevice, Prompt, StartLine) -> Result + + + + Read, tokenize and parse an Erlang form - - IoDevice = io_device() - Prompt = atom() | string() - StartLine = int() - Result = {ok, AbsForm, EndLine} | {eof, EndLine} | {error, ErrorInfo, EndLine} -  AbsForm -- see erl_parse(3) -  EndLine = int() -  ErrorInfo -- see section Error Information below - -

Reads data from the standard input (IoDevice), - prompting it with Prompt. Starts reading at line number - StartLine (1). The data is tokenized and parsed as if +

Reads data from the standard input (IoDevice), + prompting it with Prompt. Starts reading at line number + StartLine (1). The data is tokenized and parsed as if it were an Erlang form - one of the valid Erlang expressions in an Erlang source file - until a final '.' is reached. It returns:

@@ -989,7 +909,7 @@ enter>abc("hey".

End of file was encountered.

- {error, ErrorInfo, EndLine} + {error, ErrorInfo, ErrorLine}

An error occurred.

diff --git a/lib/stdlib/doc/src/io_lib.xml b/lib/stdlib/doc/src/io_lib.xml index 399f968c5f..506c1792f1 100644 --- a/lib/stdlib/doc/src/io_lib.xml +++ b/lib/stdlib/doc/src/io_lib.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -38,14 +38,22 @@ flattening deep lists.

-
- DATA TYPES - -chars() = [char() | chars()] -
+ + + + + + +

A continuation as returned by fread/3.

+
+
+ + + +
- nl() -> chars() + Write a newline

Returns a character list which represents a new line @@ -53,16 +61,12 @@ chars() = [char() | chars()] - write(Term) -> - write(Term, Depth) -> chars() + + Write a term - - Term = term() - Depth = int() - -

Returns a character list which represents Term. The - Depth (-1) argument controls the depth of the +

Returns a character list which represents Term. The + Depth (-1) argument controls the depth of the structures written. When the specified depth is reached, everything below this level is replaced by "...". For example:

@@ -74,36 +78,26 @@ chars() = [char() | chars()]
- print(Term) -> - print(Term, Column, LineLength, Depth) -> chars() + + Pretty print a term - - Term = term() - Column = LineLenght = Depth = int() -

Also returns a list of characters which represents - Term, but breaks representations which are longer than + Term, but breaks representations which are longer than one line into many lines and indents each line sensibly. It also tries to detect and output lists of printable characters - as strings. Column is the starting column (1), - LineLength the maximum line length (80), and - Depth (-1) the maximum print depth.

+ as strings. Column is the starting column (1), + LineLength the maximum line length (80), and + Depth (-1) the maximum print depth.

- fwrite(Format, Data) -> - format(Format, Data) -> chars() | UnicodeList + + Write formatted output - - Format = atom() | string() | binary() - Data = [term()] - UnicodeList = [Unicode] - Unicode = int() representing valid unicode codepoint - -

Returns a character list which represents Data - formatted in accordance with Format. See +

Returns a character list which represents Data + formatted in accordance with Format. See io:fwrite/1,2,3 for a detailed description of the available formatting options. A fault is generated if there is an error in the format string or @@ -119,42 +113,32 @@ chars() = [char() | chars()] - fread(Format, String) -> Result + Read formatted input - - Format = String = string() - Result = {ok, InputList, LeftOverChars} | {more, RestFormat, Nchars, InputStack} | {error, What} -  InputList = chars() -  LeftOverChars = string() -  RestFormat = string() -  Nchars = int() -  InputStack = chars() -  What = term() - -

Tries to read String in accordance with the control - sequences in Format. See +

Tries to read String in accordance with the control + sequences in Format. See io:fread/3 for a detailed description of the available formatting options. It is - assumed that String contains whole lines. It returns:

+ assumed that String contains whole lines. It returns:

- {ok, InputList, LeftOverChars} + {ok, InputList, LeftOverChars} -

The string was read. InputList is the list of +

The string was read. InputList is the list of successfully matched and read items, and - LeftOverChars are the input characters not used.

+ LeftOverChars are the input characters not used.

- {more, RestFormat, Nchars, InputStack} + {more, RestFormat, Nchars, InputStack}

The string was read, but more input is needed in order - to complete the original format string. RestFormat - is the remaining format string, NChars the number - of characters scanned, and InputStack is the + to complete the original format string. RestFormat + is the remaining format string, Nchars the number + of characters scanned, and InputStack is the reversed list of inputs matched up to that point.

- {error, What} + {error, What} -

The read operation failed and the parameter What +

The read operation failed and the parameter What gives a hint about the error.

@@ -165,17 +149,8 @@ chars() = [char() | chars()]
- fread(Continuation, String, Format) -> Return + Re-entrant formatted reader - - Continuation = see below - String = Format = string() - Return = {done, Result, LeftOverChars} | {more, Continuation} -  Result = {ok, InputList} | eof | {error, What} -   InputList = chars() -   What = term()() -  LeftOverChars = string() -

This is the re-entrant formatted reader. The continuation of the first call to the functions must be []. Refer to @@ -184,114 +159,92 @@ chars() = [char() | chars()] re-entrant input scheme works.

The function returns:

- {done, Result, LeftOverChars} + {done, Result, LeftOverChars}

The input is complete. The result is one of the following:

- {ok, InputList} + {ok, InputList} -

The string was read. InputList is the list of +

The string was read. InputList is the list of successfully matched and read items, and - LeftOverChars are the remaining characters.

+ LeftOverChars are the remaining characters.

eof

End of file has been encountered. - LeftOverChars are the input characters not + LeftOverChars are the input characters not used.

- {error, What} + {error, What} -

An error occurred and the parameter What gives +

An error occurred and the parameter What gives a hint about the error.

- {more, Continuation} + {more, Continuation}

More data is required to build a term. - Continuation must be passed to fread/3, + Continuation must be passed to fread/3, when more data becomes available.

- write_atom(Atom) -> chars() + Write an atom - - Atom = atom() -

Returns the list of characters needed to print the atom - Atom.

+ Atom.

- write_string(String) -> chars() + Write a string - - String = string() - -

Returns the list of characters needed to print String +

Returns the list of characters needed to print String as a string.

- write_char(Integer) -> chars() + Write a character - - Integer = int() -

Returns the list of characters needed to print a character constant in the ISO-latin-1 character set.

- indentation(String, StartIndent) -> int() + Indentation after printing string - - String = string() - StartIndent = int() - -

Returns the indentation if String has been printed, - starting at StartIndent.

+

Returns the indentation if String has been printed, + starting at StartIndent.

- char_list(Term) -> bool() + Test for a list of characters - - Term = term() - -

Returns true if Term is a flat list of +

Returns true if Term is a flat list of characters in the ISO-latin-1 range, otherwise it returns false.

- deep_char_list(Term) -> bool() + Test for a deep list of characters - - Term = term() - -

Returns true if Term is a, possibly deep, list +

Returns true if Term is a, possibly deep, list of characters in the ISO-latin-1 range, otherwise it returns false.

- printable_list(Term) -> bool() + Test for a list of printable ISO-latin-1 characters - - Term = term() - -

Returns true if Term is a flat list of +

Returns true if Term is a flat list of printable ISO-latin-1 characters, otherwise it returns false.

diff --git a/lib/stdlib/doc/src/lib.xml b/lib/stdlib/doc/src/lib.xml index 046add48e8..19fb827cbf 100644 --- a/lib/stdlib/doc/src/lib.xml +++ b/lib/stdlib/doc/src/lib.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -37,27 +37,23 @@ - flush_receive() -> void() + Flush messages

Flushes the message buffer of the current process.

- error_message(Format, Args) -> ok + Print error message - - Format = atom() | string() | binary() - Args = [term()] - -

Prints error message Args in accordance with - Format. Similar to io:format/2, see +

Prints error message Args in accordance with + Format. Similar to io:format/2, see io(3).

- progname() -> atom() + Return name of Erlang start script

Returns the name of the script that started the current @@ -65,37 +61,24 @@ - nonl(String1) -> String2 + Remove last newline - - String1 = String2 = string() -

Removes the last newline character, if any, in - String1.

+ String1.

- send(To, Msg) + Send a message - - To = pid() | Name | {Name,Node} -  Name = Node = atom() - Msg = term() -

This function to makes it possible to send a message using the apply/3 BIF.

- sendw(To, Msg) + Send a message and wait for an answer - - To = pid() | Name | {Name,Node} -  Name = Node = atom() - Msg = term() -

As send/2, but waits for an answer. It is implemented as follows:

diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index 92c4eb4f4c..6f3ed7af98 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -60,58 +60,41 @@ - all(Pred, List) -> bool() + Return true if all elements in the list satisfyPred - - Pred = fun(Elem) -> bool() -  Elem = term() - List = [term()] - -

Returns true if Pred(Elem) returns - true for all elements Elem in List, +

Returns true if Pred(Elem) returns + true for all elements Elem in List, otherwise false.

- any(Pred, List) -> bool() + Return true if any of the elements in the list satisfiesPred - - Pred = fun(Elem) -> bool() -  Elem = term() - List = [term()] - -

Returns true if Pred(Elem) returns - true for at least one element Elem in - List.

+

Returns true if Pred(Elem) returns + true for at least one element Elem in + List.

- append(ListOfLists) -> List1 + Append a list of lists - - ListOfLists = [List] - List = List1 = [term()] -

Returns a list in which all the sub-lists of - ListOfLists have been appended. For example:

+ ListOfLists have been appended. For example:

 > lists:append([[1, 2, 3], [a, b], [4, 5, 6]]).
 [1,2,3,a,b,4,5,6]
- append(List1, List2) -> List3 + Append two lists - - List1 = List2 = List3 = [term()] - -

Returns a new list List3 which is made from - the elements of List1 followed by the elements of - List2. For example:

+

Returns a new list List3 which is made from + the elements of List1 followed by the elements of + List2. For example:

 > lists:append("abc", "def").
 "abcdef"
@@ -119,15 +102,11 @@
- concat(Things) -> string() + Concatenate a list of atoms - - Things = [Thing] -  Thing = atom() | integer() | float() | string() -

Concatenates the text representation of the elements - of Things. The elements of Things can be atoms, + of Things. The elements of Things can be atoms, integers, floats or strings.

 > lists:concat([doc, '/', file, '.', 3]).
@@ -135,87 +114,59 @@
       
     
     
-      delete(Elem, List1) -> List2
+      
       Delete an element from a list
-      
-        Elem = term()
-        List1 = List2 = [term()]
-      
       
-        

Returns a copy of List1 where the first element - matching Elem is deleted, if there is such an +

Returns a copy of List1 where the first element + matching Elem is deleted, if there is such an element.

- dropwhile(Pred, List1) -> List2 + Drop elements from a list while a predicate is true - - Pred = fun(Elem) -> bool() -  Elem = term() - List1 = List2 = [term()] - -

Drops elements Elem from List1 while - Pred(Elem) returns true and returns +

Drops elements Elem from List1 while + Pred(Elem) returns true and returns the remaining list.

- duplicate(N, Elem) -> List + Make N copies of element - - N = int() - Elem = term() - List = [term()] - -

Returns a list which contains N copies of the term - Elem. For example:

+

Returns a list which contains N copies of the term + Elem. For example:

 > lists:duplicate(5, xx).
 [xx,xx,xx,xx,xx]
- filter(Pred, List1) -> List2 + Choose elements which satisfy a predicate - - Pred = fun(Elem) -> bool() -  Elem = term() - List1 = List2 = [term()] - -

List2 is a list of all elements Elem in - List1 for which Pred(Elem) returns +

List2 is a list of all elements Elem in + List1 for which Pred(Elem) returns true.

- flatlength(DeepList) -> int() + Length of flattened deep list - - DeepList = [term() | DeepList] - -

Equivalent to length(flatten(DeepList)), but more +

Equivalent to length(flatten(DeepList)), but more efficient.

- flatmap(Fun, List1) -> List2 + Map and flatten in one pass - - Fun = fun(A) -> [B] - List1 = [A] - List2 = [B] -  A = B = term() - -

Takes a function from As to lists of Bs, and a - list of As (List1) and produces a list of - Bs by applying the function to every element in - List1 and appending the resulting lists.

+

Takes a function from As to lists of Bs, and a + list of As (List1) and produces a list of + Bs by applying the function to every element in + List1 and appending the resulting lists.

That is, flatmap behaves as if it had been defined as follows:

@@ -228,43 +179,29 @@ flatmap(Fun, List1) ->
- flatten(DeepList) -> List + Flatten a deep list - - DeepList = [term() | DeepList] - List = [term()] - -

Returns a flattened version of DeepList.

+

Returns a flattened version of DeepList.

- flatten(DeepList, Tail) -> List + Flatten a deep list - - DeepList = [term() | DeepList] - Tail = List = [term()] - -

Returns a flattened version of DeepList with the tail - Tail appended.

+

Returns a flattened version of DeepList with the tail + Tail appended.

- foldl(Fun, Acc0, List) -> Acc1 + Fold a function over a list - - Fun = fun(Elem, AccIn) -> AccOut -  Elem = term() - Acc0 = Acc1 = AccIn = AccOut = term() - List = [term()] - -

Calls Fun(Elem, AccIn) on successive elements A - of List, starting with AccIn == Acc0. - Fun/2 must return a new accumulator which is passed to +

Calls Fun(Elem, AccIn) on successive elements A + of List, starting with AccIn == Acc0. + Fun/2 must return a new accumulator which is passed to the next call. The function returns the final value of - the accumulator. Acc0 is returned if the list is empty. + the accumulator. Acc0 is returned if the list is empty. For example:

 > lists:foldl(fun(X, Sum) -> X + Sum end, 0, [1,2,3,4,5]).
@@ -274,14 +211,8 @@ flatmap(Fun, List1) ->
       
     
     
-      foldr(Fun, Acc0, List) -> Acc1
+      
       Fold a function over a list
-      
-        Fun = fun(Elem, AccIn) -> AccOut
-         Elem = term()
-        Acc0 = Acc1 = AccIn = AccOut = term()
-        List = [term()]
-      
       
         

Like foldl/3, but the list is traversed from right to left. For example:

@@ -297,33 +228,23 @@ flatmap(Fun, List1) ->
- foreach(Fun, List) -> void() + Apply a function to each element of a list - - Fun = fun(Elem) -> void() -  Elem = term() - List = [term()] - -

Calls Fun(Elem) for each element Elem in - List. This function is used for its side effects and +

Calls Fun(Elem) for each element Elem in + List. This function is used for its side effects and the evaluation order is defined to be the same as the order of the elements in the list.

- keydelete(Key, N, TupleList1) -> TupleList2 + Delete an element from a list of tuples - - Key = term() - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = [Tuple] -  Tuple = tuple() - + 1..tuple_size(Tuple) -

Returns a copy of TupleList1 where the first - occurrence of a tuple whose Nth element compares equal to - Key is deleted, if there is such a tuple.

+

Returns a copy of TupleList1 where the first + occurrence of a tuple whose Nth element compares equal to + Key is deleted, if there is such a tuple.

@@ -343,19 +264,14 @@ flatmap(Fun, List1) -> - keymap(Fun, N, TupleList1) -> TupleList2 + Map a function over a list of tuples - - Fun = fun(Term1) -> Term2 -  Term1 = Term2 = term() - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = [tuple()] - + 1..tuple_size(Tuple)

Returns a list of tuples where, for each tuple in - TupleList1, the Nth element Term1 of the tuple + TupleList1, the Nth element Term1 of the tuple has been replaced with the result of calling - Fun(Term1).

+ Fun(Term1).

Examples:

 > Fun = fun(Atom) -> atom_to_list(Atom) end.
@@ -365,7 +281,7 @@ flatmap(Fun, List1) ->
       
     
     
-      keymember(Key, N, TupleList) -> bool()
+      keymember(Key, N, TupleList) -> boolean()
       Test for membership of a list of tuples
       
         Key = term()
@@ -380,37 +296,28 @@ flatmap(Fun, List1) ->
       
     
     
-      keymerge(N, TupleList1, TupleList2) -> TupleList3
+      
       Merge two key-sorted lists of tuples
-      
-        N = 1..tuple_size(Tuple)
-        TupleList1 = TupleList2 = TupleList3 = [Tuple]
-         Tuple = tuple()
-      
+      1..tuple_size(Tuple)
       
-        

Returns the sorted list formed by merging TupleList1 - and TupleList2. The merge is performed on - the Nth element of each tuple. Both TupleList1 and - TupleList2 must be key-sorted prior to evaluating this +

Returns the sorted list formed by merging TupleList1 + and TupleList2. The merge is performed on + the Nth element of each tuple. Both TupleList1 and + TupleList2 must be key-sorted prior to evaluating this function. When two tuples compare equal, the tuple from - TupleList1 is picked before the tuple from - TupleList2.

+ TupleList1 is picked before the tuple from + TupleList2.

- keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 + Replace an element in a list of tuples - - Key = term() - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = [Tuple] - NewTuple = Tuple = tuple() - + 1..tuple_size(Tuple) -

Returns a copy of TupleList1 where the first - occurrence of a T tuple whose Nth element - compares equal to Key is replaced with - NewTuple, if there is such a tuple T.

+

Returns a copy of TupleList1 where the first + occurrence of a T tuple whose Nth element + compares equal to Key is replaced with + NewTuple, if there is such a tuple T.

@@ -433,95 +340,63 @@ flatmap(Fun, List1) -> - keysort(N, TupleList1) -> TupleList2 + Sort a list of tuples - - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = [Tuple] -  Tuple = tuple() - + 1..tuple_size(Tuple)

Returns a list containing the sorted elements of the list - TupleList1. Sorting is performed on the Nth + TupleList1. Sorting is performed on the Nth element of the tuples. The sort is stable.

- keystore(Key, N, TupleList1, NewTuple) -> TupleList2 + Store an element in a list of tuples - - Key = term() - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = [Tuple] - NewTuple = Tuple = tuple() - - -

Returns a copy of TupleList1 where the first - occurrence of a tuple T whose Nth element - compares equal to Key is replaced with - NewTuple, if there is such a tuple T. If there - is no such tuple T a copy of TupleList1 where - [NewTuple] has been appended to the end is + 1..tuple_size(Tuple) + +

Returns a copy of TupleList1 where the first + occurrence of a tuple T whose Nth element + compares equal to Key is replaced with + NewTuple, if there is such a tuple T. If there + is no such tuple T a copy of TupleList1 where + [NewTuple] has been appended to the end is returned.

- keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} - | false + Extract an element from a list of tuples - - Key = term() - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = [Tuple] - Tuple = tuple() - + 1..tuple_size(Tuple) -

Searches the list of tuples TupleList1 for a tuple - whose Nth element compares equal to Key. - Returns {value, Tuple, TupleList2} if such a tuple is - found, otherwise false. TupleList2 is a copy - of TupleList1 where the first occurrence of - Tuple has been removed.

+

Searches the list of tuples TupleList1 for a tuple + whose Nth element compares equal to Key. + Returns {value, Tuple, TupleList2} if such a tuple is + found, otherwise false. TupleList2 is a copy + of TupleList1 where the first occurrence of + Tuple has been removed.

- last(List) -> Last + Return last element in a list - - List = [term()], length(List) > 0 - Last = term() - -

Returns the last element in List.

+

Returns the last element in List.

- map(Fun, List1) -> List2 + Map a function over a list - - Fun = fun(A) -> B - List1 = [A] - List2 = [B] -  A = B = term() - -

Takes a function from As to Bs, and a list of - As and produces a list of Bs by applying +

Takes a function from As to Bs, and a list of + As and produces a list of Bs by applying the function to every element in the list. This function is used to obtain the return values. The evaluation order is implementation dependent.

- mapfoldl(Fun, Acc0, List1) -> {List2, Acc1} + Map and fold in one pass - - Fun = fun(A, AccIn) -> {B, AccOut} - Acc0 = Acc1 = AccIn = AccOut = term() - List1 = [A] - List2 = [B] -  A = B = term() -

mapfoldl combines the operations of map/2 and foldl/3 into one pass. An example, summing @@ -533,35 +408,24 @@ flatmap(Fun, List1) -> - mapfoldr(Fun, Acc0, List1) -> {List2, Acc1} + Map and fold in one pass - - Fun = fun(A, AccIn) -> {B, AccOut} - Acc0 = Acc1 = AccIn = AccOut = term() - List1 = [A] - List2 = [B] -  A = B = term() -

mapfoldr combines the operations of map/2 and foldr/3 into one pass.

- max(List) -> Max + Return maximum element of a list - - List = [term()], length(List) > 0 - Max = term() - -

Returns the first element of List that compares +

Returns the first element of List that compares greater than or equal to all other elements of - List.

+ List.

- member(Elem, List) -> bool() + member(Elem, List) -> boolean() Test for membership of a list Elem = term() @@ -573,112 +437,84 @@ flatmap(Fun, List1) -> - merge(ListOfLists) -> List1 + Merge a list of sorted lists - - ListOfLists = [List] - List = List1 = [term()] -

Returns the sorted list formed by merging all the sub-lists - of ListOfLists. All sub-lists must be sorted prior to + of ListOfLists. All sub-lists must be sorted prior to evaluating this function. When two elements compare equal, the element from the sub-list with the lowest position in - ListOfLists is picked before the other element.

+ ListOfLists is picked before the other element.

- merge(List1, List2) -> List3 + Merge two sorted lists - - List1 = List2 = List3 = [term()] - -

Returns the sorted list formed by merging List1 and - List2. Both List1 and List2 must be +

Returns the sorted list formed by merging List1 and + List2. Both List1 and List2 must be sorted prior to evaluating this function. When two elements - compare equal, the element from List1 is picked - before the element from List2.

+ compare equal, the element from List1 is picked + before the element from List2.

- merge(Fun, List1, List2) -> List3 + Merge two sorted list - - Fun = fun(A, B) -> bool() - List1 = [A] - List2 = [B] - List3 = [A | B] -  A = B = term() - -

Returns the sorted list formed by merging List1 and - List2. Both List1 and List2 must be +

Returns the sorted list formed by merging List1 and + List2. Both List1 and List2 must be sorted according to the ordering function - Fun prior to evaluating this function. Fun(A, - B) should return true if A compares less - than or equal to B in the ordering, false + Fun prior to evaluating this function. Fun(A, + B) should return true if A compares less + than or equal to B in the ordering, false otherwise. When two elements compare equal, the element from - List1 is picked before the element from - List2.

+ List1 is picked before the element from + List2.

- merge3(List1, List2, List3) -> List4 + Merge three sorted lists - - List1 = List2 = List3 = List4 = [term()] - -

Returns the sorted list formed by merging List1, - List2 and List3. All of List1, - List2 and List3 must be sorted prior to +

Returns the sorted list formed by merging List1, + List2 and List3. All of List1, + List2 and List3 must be sorted prior to evaluating this function. When two elements compare equal, - the element from List1, if there is such an element, + the element from List1, if there is such an element, is picked before the other element, otherwise the element - from List2 is picked before the element from - List3.

+ from List2 is picked before the element from + List3.

- min(List) -> Min + Return minimum element of a list - - List = [term()], length(List) > 0 - Min = term() - -

Returns the first element of List that compares +

Returns the first element of List that compares less than or equal to all other elements of - List.

+ List.

- nth(N, List) -> Elem + Return the Nth element of a list - - N = 1..length(List) - List = [term()] - Elem = term() - + 1..length(List) -

Returns the Nth element of List. For example:

+

Returns the Nth element of List. For example:

 > lists:nth(3, [a, b, c, d, e]).
 c
- nthtail(N, List1) -> Tail + Return the Nth tail of a list - - N = 0..length(List1) - List1 = Tail = [term()] - + 0..length(List) -

Returns the Nth tail of List, that is, the sublist of - List starting at N+1 and continuing up to +

Returns the Nth tail of List, that is, the sublist of + List starting at N+1 and continuing up to the end of the list. For example:

 > lists:nthtail(3, [a, b, c, d, e]).
@@ -692,18 +528,13 @@ c
- partition(Pred, List) -> {Satisfying, NonSatisfying} + Partition a list into two lists based on a predicate - - Pred = fun(Elem) -> bool() -  Elem = term() - List = Satisfying = NonSatisfying = [term()] - -

Partitions List into two lists, where the first list - contains all elements for which Pred(Elem) returns +

Partitions List into two lists, where the first list + contains all elements for which Pred(Elem) returns true, and the second list contains all elements for - which Pred(Elem) returns false.

+ which Pred(Elem) returns false.

Examples:

 > lists:partition(fun(A) -> A rem 2 == 1 end, [1,2,3,4,5,6,7]).
@@ -715,24 +546,18 @@ c
- prefix(List1, List2) -> bool() + Test for list prefix - - List1 = List2 = [term()] - -

Returns true if List1 is a prefix of - List2, otherwise false.

+

Returns true if List1 is a prefix of + List2, otherwise false.

- reverse(List1) -> List2 + Reverse a list - - List1 = List2 = [term()] - -

Returns a list with the top level elements in List1 +

Returns a list with the elements in List1 in reverse order.

@@ -743,7 +568,7 @@ c
List1 = Tail = List2 = [term()] -

Returns a list with the top level elements in List1 +

Returns a list with the elements in List1 in reverse order, with the tail Tail appended. For example:

@@ -752,22 +577,18 @@ c
- seq(From, To) -> Seq - seq(From, To, Incr) -> Seq + + Generate a sequence of integers - - From = To = Incr = int() - Seq = [int()] - -

Returns a sequence of integers which starts with From - and contains the successive results of adding Incr to - the previous element, until To has been reached or - passed (in the latter case, To is not an element of - the sequence). Incr defaults to 1.

-

Failure: If and Incr - is positive, or if To>From-Incr and Incr is - negative, or if Incr==0 and From/=To.

+

Returns a sequence of integers which starts with From + and contains the successive results of adding Incr to + the previous element, until To has been reached or + passed (in the latter case, To is not an element of + the sequence). Incr defaults to 1.

+

Failure: If To<From-Incr and Incr + is positive, or if To>From-Incr and Incr is + negative, or if Incr==0 and From/=To.

The following equalities hold for all sequences:

length(lists:seq(From, To)) == To-From+1 @@ -787,57 +608,41 @@ length(lists:seq(From, To, Incr)) == (To-From+Incr) div Incr
- sort(List1) -> List2 + Sort a list - - List1 = List2 = [term()] -

Returns a list containing the sorted elements of - List1.

+ List1.

- sort(Fun, List1) -> List2 + Sort a list - - Fun = fun(Elem1, Elem2) -> bool() -  Elem1 = Elem2 = term() - List1 = List2 = [term()] -

Returns a list containing the sorted elements of - List1, according to the List1, according to the ordering function - Fun. Fun(A, B) should return true if - A compares less than or equal to B in the + Fun. Fun(A, B) should return true if + A compares less than or equal to B in the ordering, false otherwise.

- split(N, List1) -> {List2, List3} + Split a list into two lists - - N = 0..length(List1) - List1 = List2 = List3 = [term()] - + 0..length(List1) -

Splits List1 into List2 and List3. - List2 contains the first N elements and - List3 the rest of the elements (the Nth tail).

+

Splits List1 into List2 and List3. + List2 contains the first N elements and + List3 the rest of the elements (the Nth tail).

- splitwith(Pred, List) -> {List1, List2} + Split a list into two lists based on a predicate - - Pred = fun(Elem) -> bool() -  Elem = term() - List = List1 = List2 = [term()] - -

Partitions List into two lists according to - Pred. splitwith/2 behaves as if it is defined +

Partitions List into two lists according to + Pred. splitwith/2 behaves as if it is defined as follows:

splitwith(Pred, List) -> @@ -853,31 +658,23 @@ splitwith(Pred, List) ->
- sublist(List1, Len) -> List2 + Return a sub-list of a certain length, starting at the first position - - List1 = List2 = [term()] - Len = int() - -

Returns the sub-list of List1 starting at position 1 - and with (max) Len elements. It is not an error for - Len to exceed the length of the list -- in that case +

Returns the sub-list of List1 starting at position 1 + and with (max) Len elements. It is not an error for + Len to exceed the length of the list -- in that case the whole list is returned.

- sublist(List1, Start, Len) -> List2 + Return a sub-list starting at a given position and with a given number of elements - - List1 = List2 = [term()] - Start = 1..(length(List1)+1) - Len = int() - + 1..(length(List1)+1) -

Returns the sub-list of List1 starting at Start - and with (max) Len elements. It is not an error for - Start+Len to exceed the length of the list.

+

Returns the sub-list of List1 starting at Start + and with (max) Len elements. It is not an error for + Start+Len to exceed the length of the list.

 > lists:sublist([1,2,3,4], 2, 2).
 [2,3]
@@ -888,15 +685,12 @@ splitwith(Pred, List) ->
       
     
     
-      subtract(List1, List2) -> List3
+      
       Subtract the element in one list from another list
-      
-        List1 = List2 = List3 = [term()]
-      
       
-        

Returns a new list List3 which is a copy of - List1, subjected to the following procedure: for each - element in List2, its first occurrence in List1 +

Returns a new list List3 which is a copy of + List1, subjected to the following procedure: for each + element in List2, its first occurrence in List1 is deleted. For example:

 > lists:subtract("123212", "212").
@@ -911,151 +705,112 @@ splitwith(Pred, List) ->
       
     
     
-      suffix(List1, List2) -> bool()
+      
       Test for list suffix
       
-        

Returns true if List1 is a suffix of - List2, otherwise false.

+

Returns true if List1 is a suffix of + List2, otherwise false.

- sum(List) -> number() + Return sum of elements in a list - - List = [number()] - -

Returns the sum of the elements in List.

+

Returns the sum of the elements in List.

- takewhile(Pred, List1) -> List2 + Take elements from a list while a predicate is true - - Pred = fun(Elem) -> bool() -  Elem = term() - List1 = List2 = [term()] - -

Takes elements Elem from List1 while - Pred(Elem) returns true, that is, +

Takes elements Elem from List1 while + Pred(Elem) returns true, that is, the function returns the longest prefix of the list for which all elements satisfy the predicate.

- ukeymerge(N, TupleList1, TupleList2) -> TupleList3 + Merge two key-sorted lists of tuples, removing duplicates - - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = TupleList3 = [Tuple] -  Tuple = tuple() - + 1..tuple_size(Tuple) -

Returns the sorted list formed by merging TupleList1 - and TupleList2. The merge is performed on the - Nth element of each tuple. Both TupleList1 and - TupleList2 must be key-sorted without duplicates +

Returns the sorted list formed by merging TupleList1 + and TupleList2. The merge is performed on the + Nth element of each tuple. Both TupleList1 and + TupleList2 must be key-sorted without duplicates prior to evaluating this function. When two tuples compare - equal, the tuple from TupleList1 is picked and the - one from TupleList2 deleted.

+ equal, the tuple from TupleList1 is picked and the + one from TupleList2 deleted.

- ukeysort(N, TupleList1) -> TupleList2 + Sort a list of tuples, removing duplicates - - N = 1..tuple_size(Tuple) - TupleList1 = TupleList2 = [Tuple] -  Tuple = tuple() - + 1..tuple_size(Tuple)

Returns a list containing the sorted elements of the list - TupleList1 where all but the first tuple of the + TupleList1 where all but the first tuple of the tuples comparing equal have been deleted. Sorting is - performed on the Nth element of the tuples.

+ performed on the Nth element of the tuples.

- umerge(ListOfLists) -> List1 + Merge a list of sorted lists, removing duplicates - - ListOfLists = [List] - List = List1 = [term()] -

Returns the sorted list formed by merging all the sub-lists - of ListOfLists. All sub-lists must be sorted and + of ListOfLists. All sub-lists must be sorted and contain no duplicates prior to evaluating this function. When two elements compare equal, the element from the - sub-list with the lowest position in ListOfLists is + sub-list with the lowest position in ListOfLists is picked and the other one deleted.

- umerge(List1, List2) -> List3 + Merge two sorted lists, removing duplicates - - List1 = List2 = List3 = [term()] - -

Returns the sorted list formed by merging List1 and - List2. Both List1 and List2 must be +

Returns the sorted list formed by merging List1 and + List2. Both List1 and List2 must be sorted and contain no duplicates prior to evaluating this function. When two elements compare equal, the element from - List1 is picked and the one from List2 + List1 is picked and the one from List2 deleted.

- umerge(Fun, List1, List2) -> List3 + Merge two sorted lists, removing duplicates - - Fun = fun(A, B) -> bool() - List1 = [A] - List2 = [B] - List3 = [A | B] -  A = B = term() - -

Returns the sorted list formed by merging List1 and - List2. Both List1 and List2 must be +

Returns the sorted list formed by merging List1 and + List2. Both List1 and List2 must be sorted according to the ordering function Fun and contain no duplicates prior to evaluating - this function. Fun(A, B) should return true if - A compares less than or equal to B in the + this function. Fun(A, B) should return true if + A compares less than or equal to B in the ordering, false otherwise. When two elements compare equal, the element from - List1 is picked and the one from List2 + List1 is picked and the one from List2 deleted.

- umerge3(List1, List2, List3) -> List4 + Merge three sorted lists, removing duplicates - - List1 = List2 = List3 = List4 = [term()] - -

Returns the sorted list formed by merging List1, - List2 and List3. All of List1, - List2 and List3 must be sorted and contain no +

Returns the sorted list formed by merging List1, + List2 and List3. All of List1, + List2 and List3 must be sorted and contain no duplicates prior to evaluating this function. When two - elements compare equal, the element from List1 is + elements compare equal, the element from List1 is picked if there is such an element, otherwise the element - from List2 is picked, and the other one deleted.

+ from List2 is picked, and the other one deleted.

- unzip(List1) -> {List2, List3} + Unzip a list of two-tuples into two lists - - List1 = [{X, Y}] - List2 = [X] - List3 = [Y] -  X = Y = term() -

"Unzips" a list of two-tuples into two lists, where the first list contains the first element of each tuple, and the second @@ -1063,15 +818,8 @@ splitwith(Pred, List) -> - unzip3(List1) -> {List2, List3, List4} + Unzip a list of three-tuples into three lists - - List1 = [{X, Y, Z}] - List2 = [X] - List3 = [Y] - List4 = [Z] -  X = Y = Z = term() -

"Unzips" a list of three-tuples into three lists, where the first list contains the first element of each tuple, @@ -1080,44 +828,30 @@ splitwith(Pred, List) -> - usort(List1) -> List2 + Sort a list, removing duplicates - - List1 = List2 = [term()] -

Returns a list containing the sorted elements of - List1 where all but the first element of the elements + List1 where all but the first element of the elements comparing equal have been deleted.

- usort(Fun, List1) -> List2 + Sort a list, removing duplicates - - Fun = fun(Elem1, Elem2) -> bool() -  Elem1 = Elem2 = term() - List1 = List2 = [term()] -

Returns a list which contains the sorted elements of - List1 where all but the first element of the elements + List1 where all but the first element of the elements comparing equal according to the ordering function - Fun have been deleted. Fun(A, B) should return + Fun have been deleted. Fun(A, B) should return true if A compares less than or equal to B in the ordering, false otherwise.

- zip(List1, List2) -> List3 + Zip two lists into a list of two-tuples - - List1 = [X] - List2 = [Y] - List3 = [{X, Y}] -  X = Y = term() -

"Zips" two lists of equal length into one list of two-tuples, where the first element of each tuple is taken from the first @@ -1126,15 +860,8 @@ splitwith(Pred, List) -> - zip3(List1, List2, List3) -> List4 + Zip three lists into a list of three-tuples - - List1 = [X] - List2 = [Y] - List3 = [Z] - List3 = [{X, Y, Z}] -  X = Y = Z = term() -

"Zips" three lists of equal length into one list of three-tuples, where the first element of each tuple is taken @@ -1145,20 +872,13 @@ splitwith(Pred, List) -> - zipwith(Combine, List1, List2) -> List3 + Zip two lists into one list according to a fun - - Combine = fun(X, Y) -> T - List1 = [X] - List2 = [Y] - List3 = [T] -  X = Y = T = term() -

Combine the elements of two lists of equal length into one - list. For each pair X, Y of list elements from the two + list. For each pair X, Y of list elements from the two lists, the element in the result list will be - Combine(X, Y).

+ Combine(X, Y).

zipwith(fun(X, Y) -> {X,Y} end, List1, List2) is equivalent to zip(List1, List2).

Example:

@@ -1168,21 +888,13 @@ splitwith(Pred, List) ->
- zipwith3(Combine, List1, List2, List3) -> List4 + Zip three lists into one list according to a fun - - Combine = fun(X, Y, Z) -> T - List1 = [X] - List2 = [Y] - List3 = [Z] - List4 = [T] -  X = Y = Z = T = term() -

Combine the elements of three lists of equal length into one - list. For each triple X, Y, Z of list elements from + list. For each triple X, Y, Z of list elements from the three lists, the element in the result list will be - Combine(X, Y, Z).

+ Combine(X, Y, Z).

zipwith3(fun(X, Y, Z) -> {X,Y,Z} end, List1, List2, List3) is equivalent to zip3(List1, List2, List3).

Examples:

diff --git a/lib/stdlib/doc/src/log_mf_h.xml b/lib/stdlib/doc/src/log_mf_h.xml
index f8e11339a7..f2b09b58eb 100644
--- a/lib/stdlib/doc/src/log_mf_h.xml
+++ b/lib/stdlib/doc/src/log_mf_h.xml
@@ -43,28 +43,27 @@
       report files 1, 2, .....
       

+ + + +

Term to be sent to + gen_event:add_handler/3.

+
+
- init(Dir, MaxBytes, MaxFiles) - init(Dir, MaxBytes, MaxFiles, Pred) -> Args + + Initiate the event handler - - Dir = string() - MaxBytes = integer() - MaxFiles = 0 < integer() < 256 - Pred = fun(Event) -> boolean() - Event = term() - Args = args() -

Initiates the event handler. This function returns - Args, which should be used in a call to - gen_event:add_handler(EventMgr, log_mf_h, Args). + Args, which should be used in a call to + gen_event:add_handler(EventMgr, log_mf_h, Args).

-

Dir specifies which directory to use for the log - files. MaxBytes specifies the size of each individual - file. MaxFiles specifies how many files are - used. Pred is a predicate function used to filter the +

Dir specifies which directory to use for the log + files. MaxBytes specifies the size of each individual + file. MaxFiles specifies how many files are + used. Pred is a predicate function used to filter the events. If no predicate function is specified, all events are logged.

diff --git a/lib/stdlib/doc/src/math.xml b/lib/stdlib/doc/src/math.xml index 02e4d6e495..518457d5d8 100644 --- a/lib/stdlib/doc/src/math.xml +++ b/lib/stdlib/doc/src/math.xml @@ -45,7 +45,7 @@ - pi() -> float() + A useful number

A useful number.

diff --git a/lib/stdlib/doc/src/ms_transform.xml b/lib/stdlib/doc/src/ms_transform.xml index ba9f89685b..f81f8bda96 100644 --- a/lib/stdlib/doc/src/ms_transform.xml +++ b/lib/stdlib/doc/src/ms_transform.xml @@ -4,7 +4,7 @@
- 20022010 + 20022011 Ericsson AB. All Rights Reserved. @@ -599,12 +599,9 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]). - parse_transform(Forms,_Options) -> Forms + Transforms Erlang abstract format containing calls to ets/dbg:fun2ms into literal match specifications. - - Forms = Erlang abstract code format, see the erl_parse module description - _Options = Option list, required but not used - + Option list, required but not used.

Implements the actual transformation at compile time. This function is called by the compiler to do the source code @@ -617,29 +614,21 @@ ets:select(Table, [{{'$1',test,'$2'},[],['$_']}]). - transform_from_shell(Dialect,Clauses,BoundEnvironment) -> term() + Used when transforming fun's created in the shell into match_specifications. - - Dialect = ets | dbg - Clauses = Erlang abstract form for a single fun - BoundEnvironment = [{atom(), term()}, ...], list of variable bindings in the shell environment - + List of variable bindings in the shell environment.

Implements the actual transformation when the fun2ms functions are called from the shell. In this case the abstract form is for one single fun (parsed by the Erlang shell), and all imported variables should be in the key-value list passed - as BoundEnvironment. The result is a term, normalized, + as BoundEnvironment. The result is a term, normalized, i.e. not in abstract format.

- format_error(Errcode) -> ErrMessage + Error formatting function as required by the parse_transform interface. - - Errcode = term() - ErrMessage = string() -

Takes an error code returned by one of the other functions in the module and creates a textual description of the diff --git a/lib/stdlib/doc/src/orddict.xml b/lib/stdlib/doc/src/orddict.xml index 1b8b74534b..319083d35b 100644 --- a/lib/stdlib/doc/src/orddict.xml +++ b/lib/stdlib/doc/src/orddict.xml @@ -46,176 +46,123 @@ (==).

-
- DATA TYPES - -ordered_dictionary() - as returned by new/0 -
+ + + +

As returned by new/0.

+
+
+ - append(Key, Value, Orddict1) -> Orddict2 + Append a value to keys in a dictionary - - Key = Value = term() - Orddict1 = Orddict2 = ordered_dictionary() - -

This function appends a new Value to the current list - of values associated with Key. An exception is - generated if the initial value associated with Key is +

This function appends a new Value to the current list + of values associated with Key. An exception is + generated if the initial value associated with Key is not a list of values.

- append_list(Key, ValList, Orddict1) -> Orddict2 + Append new values to keys in a dictionary - - ValList = [Value] - Key = Value = term() - Orddict1 = Orddict2 = ordered_dictionary() - -

This function appends a list of values ValList to - the current list of values associated with Key. An +

This function appends a list of values ValList to + the current list of values associated with Key. An exception is generated if the initial value associated with - Key is not a list of values.

+ Key is not a list of values.

- erase(Key, Orddict1) -> Orddict2 + Erase a key from a dictionary - - Key = term() - Orddict1 = Orddict2 = ordered_dictionary() -

This function erases all items with a given key from a dictionary.

- fetch(Key, Orddict) -> Value + Look-up values in a dictionary - - Key = Value = term() - Orddict = ordered_dictionary() - -

This function returns the value associated with Key - in the dictionary Orddict. fetch assumes that - the Key is present in the dictionary and an exception - is generated if Key is not in the dictionary.

+

This function returns the value associated with Key + in the dictionary Orddict. fetch assumes that + the Key is present in the dictionary and an exception + is generated if Key is not in the dictionary.

- fetch_keys(Orddict) -> Keys + Return all keys in a dictionary - - Orddict = ordered_dictionary() - Keys = [term()] -

This function returns a list of all keys in the dictionary.

- filter(Pred, Orddict1) -> Orddict2 + Choose elements which satisfy a predicate - - Pred = fun(Key, Value) -> bool() -  Key = Value = term() - Orddict1 = Orddict2 = ordered_dictionary() - -

Orddict2 is a dictionary of all keys and values in - Orddict1 for which Pred(Key, Value) is true.

+

Orddict2 is a dictionary of all keys and values in + Orddict1 for which Pred(Key, Value) is true.

- find(Key, Orddict) -> {ok, Value} | error + Search for a key in a dictionary - - Key = Value = term() - Orddict = ordered_dictionary() -

This function searches for a key in a dictionary. Returns - {ok, Value} where Value is the value associated - with Key, or error if the key is not present in + {ok, Value} where Value is the value associated + with Key, or error if the key is not present in the dictionary.

- fold(Fun, Acc0, Orddict) -> Acc1 + Fold a function over a dictionary - - Fun = fun(Key, Value, AccIn) -> AccOut - Key = Value = term() - Acc0 = Acc1 = AccIn = AccOut = term() - Orddict = ordered_dictionary() - -

Calls Fun on successive keys and values of - Orddict together with an extra argument Acc - (short for accumulator). Fun must return a new - accumulator which is passed to the next call. Acc0 is +

Calls Fun on successive keys and values of + Orddict together with an extra argument Acc + (short for accumulator). Fun must return a new + accumulator which is passed to the next call. Acc0 is returned if the list is empty. The evaluation order is undefined.

- from_list(List) -> Orddict + Convert a list of pairs to a dictionary - - List = [{Key, Value}] - Orddict = ordered_dictionary() - -

This function converts the Key - Value list - List to a dictionary.

+

This function converts the Key - Value list + List to a dictionary.

- is_key(Key, Orddict) -> bool() + Test if a key is in a dictionary - - Key = term() - Orddict = ordered_dictionary() - -

This function tests if Key is contained in - the dictionary Orddict.

+

This function tests if Key is contained in + the dictionary Orddict.

- map(Fun, Orddict1) -> Orddict2 + Map a function over a dictionary - - Fun = fun(Key, Value1) -> Value2 -  Key = Value1 = Value2 = term() - Orddict1 = Orddict2 = ordered_dictionary() - -

map calls Func on successive keys and values - of Orddict to return a new value for each key. +

map calls Fun on successive keys and values + of Orddict1 to return a new value for each key. The evaluation order is undefined.

- merge(Fun, Orddict1, Orddict2) -> Orddict3 + Merge two dictionaries - - Fun = fun(Key, Value1, Value2) -> Value -  Key = Value1 = Value2 = Value3 = term() - Orddict1 = Orddict2 = Orddict3 = ordered_dictionary() - -

merge merges two dictionaries, Orddict1 and - Orddict2, to create a new dictionary. All the Key - - Value pairs from both dictionaries are included in +

merge merges two dictionaries, Orddict1 and + Orddict2, to create a new dictionary. All the Key + - Value pairs from both dictionaries are included in the new dictionary. If a key occurs in both dictionaries then - Fun is called with the key and both values to return a + Fun is called with the key and both values to return a new value. merge could be defined as:

merge(Fun, D1, D2) -> @@ -226,75 +173,52 @@ merge(Fun, D1, D2) ->
- new() -> ordered_dictionary() + Create a dictionary

This function creates a new dictionary.

- size(Orddict) -> int() + Return the number of elements in an ordered dictionary - - Orddict = ordered_dictionary() - -

Returns the number of elements in an Orddict.

+

Returns the number of elements in an Orddict.

- store(Key, Value, Orddict1) -> Orddict2 + Store a value in a dictionary - - Key = Value = term() - Orddict1 = Orddict2 = ordered_dictionary() - -

This function stores a Key - Value pair in a - dictionary. If the Key already exists in Orddict1, - the associated value is replaced by Value.

+

This function stores a Key - Value pair in a + dictionary. If the Key already exists in Orddict1, + the associated value is replaced by Value.

- to_list(Orddict) -> List + Convert a dictionary to a list of pairs - - Orddict = ordered_dictionary() - List = [{Key, Value}] -

This function converts the dictionary to a list representation.

- update(Key, Fun, Orddict1) -> Orddict2 + Update a value in a dictionary - - Key = term() - Fun = fun(Value1) -> Value2 -  Value1 = Value2 = term() - Orddict1 = Orddict2 = ordered_dictionary() - -

Update a value in a dictionary by calling Fun on +

Update a value in a dictionary by calling Fun on the value to get a new value. An exception is generated if - Key is not present in the dictionary.

+ Key is not present in the dictionary.

- update(Key, Fun, Initial, Orddict1) -> Orddict2 + Update a value in a dictionary - - Key = Initial = term() - Fun = fun(Value1) -> Value2 -  Value1 = Value2 = term() - Orddict1 = Orddict2 = ordered_dictionary() - -

Update a value in a dictionary by calling Fun on - the value to get a new value. If Key is not present - in the dictionary then Initial will be stored as +

Update a value in a dictionary by calling Fun on + the value to get a new value. If Key is not present + in the dictionary then Initial will be stored as the first value. For example append/3 could be defined as:

@@ -303,17 +227,12 @@ append(Key, Val, D) ->
- update_counter(Key, Increment, Orddict1) -> Orddict2 + Increment a value in a dictionary - - Key = term() - Increment = number() - Orddict1 = Orddict2 = ordered_dictionary() - -

Add Increment to the value associated with Key - and store this value. If Key is not present in - the dictionary then Increment will be stored as +

Add Increment to the value associated with Key + and store this value. If Key is not present in + the dictionary then Increment will be stored as the first value.

This could be defined as:

diff --git a/lib/stdlib/doc/src/ordsets.xml b/lib/stdlib/doc/src/ordsets.xml index a20ab2d879..1e26fc2022 100644 --- a/lib/stdlib/doc/src/ordsets.xml +++ b/lib/stdlib/doc/src/ordsets.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -45,202 +45,141 @@ different if and only if they do not compare equal (==).

-
- DATA TYPES - -ordered_set() - as returned by new/0 -
+ + + +

As returned by new/0.

+
+
- new() -> Ordset + Return an empty set - - Ordset = ordered_set() -

Returns a new empty ordered set.

- is_set(Ordset) -> bool() + Test for an Ordset - - Ordset = term() - -

Returns true if Ordset is an ordered set of +

Returns true if Ordset is an ordered set of elements, otherwise false.

- size(Ordset) -> int() + Return the number of elements in a set - - Ordset = term() - -

Returns the number of elements in Ordset.

+

Returns the number of elements in Ordset.

- to_list(Ordset) -> List + Convert an Ordsetinto a list - - Ordset = ordered_set() - List = [term()] - -

Returns the elements of Ordset as a list.

+

Returns the elements of Ordset as a list.

- from_list(List) -> Ordset + Convert a list into an Ordset - - List = [term()] - Ordset = ordered_set() - -

Returns an ordered set of the elements in List.

+

Returns an ordered set of the elements in List.

- is_element(Element, Ordset) -> bool() + Test for membership of an Ordset - - Element = term() - Ordset = ordered_set() - -

Returns true if Element is an element of - Ordset, otherwise false.

+

Returns true if Element is an element of + Ordset, otherwise false.

- add_element(Element, Ordset1) -> Ordset2 + Add an element to an Ordset - - Element = term() - Ordset1 = Ordset2 = ordered_set() - -

Returns a new ordered set formed from Ordset1 with - Element inserted.

+

Returns a new ordered set formed from Ordset1 with + Element inserted.

- del_element(Element, Ordset1) -> Ordset2 + Remove an element from an Ordset - - Element = term() - Ordset1 = Ordset2 = ordered_set() - -

Returns Ordset1, but with Element removed.

+

Returns Ordset1, but with Element removed.

- union(Ordset1, Ordset2) -> Ordset3 + Return the union of two Ordsets - - Ordset1 = Ordset2 = Ordset3 = ordered_set() - -

Returns the merged (union) set of Ordset1 and - Ordset2.

+

Returns the merged (union) set of Ordset1 and + Ordset2.

- union(OrdsetList) -> Ordset + Return the union of a list of Ordsets - - OrdsetList = [ordered_set()] - Ordset = ordered_set() -

Returns the merged (union) set of the list of sets.

- intersection(Ordset1, Ordset2) -> Ordset3 + Return the intersection of two Ordsets - - Ordset1 = Ordset2 = Ordset3 = ordered_set() - -

Returns the intersection of Ordset1 and - Ordset2.

+

Returns the intersection of Ordset1 and + Ordset2.

- intersection(OrdsetList) -> Ordset + Return the intersection of a list of Ordsets - - OrdsetList = [ordered_set()] - Ordset = ordered_set() -

Returns the intersection of the non-empty list of sets.

- is_disjoint(Ordset1, Ordset2) -> bool() + Check whether two Ordsets are disjoint - - Ordset1 = Ordset2 = ordered_set() - -

Returns true if Ordset1 and - Ordset2 are disjoint (have no elements in common), +

Returns true if Ordset1 and + Ordset2 are disjoint (have no elements in common), and false otherwise.

- subtract(Ordset1, Ordset2) -> Ordset3 + Return the difference of two Ordsets - - Ordset1 = Ordset2 = Ordset3 = ordered_set() - -

Returns only the elements of Ordset1 which are not - also elements of Ordset2.

+

Returns only the elements of Ordset1 which are not + also elements of Ordset2.

- is_subset(Ordset1, Ordset2) -> bool() + Test for subset - - Ordset1 = Ordset2 = ordered_set() - -

Returns true when every element of Ordset1 is - also a member of Ordset2, otherwise false.

+

Returns true when every element of Ordset1 is + also a member of Ordset2, otherwise false.

- fold(Function, Acc0, Ordset) -> Acc1 + Fold over set elements - - Function = fun (E, AccIn) -> AccOut - Acc0 = Acc1 = AccIn = AccOut = term() - Ordset = ordered_set() - -

Fold Function over every element in Ordset +

Fold Function over every element in Ordset returning the final value of the accumulator.

- filter(Pred, Ordset1) -> Set2 + Filter set elements - - Pred = fun (E) -> bool() - Set1 = Set2 = ordered_set() - -

Filter elements in Set1 with boolean function - Fun.

+

Filter elements in Ordset1 with boolean function + Pred.

diff --git a/lib/stdlib/doc/src/pg.xml b/lib/stdlib/doc/src/pg.xml index b174d4f7d4..c56db8c6e6 100644 --- a/lib/stdlib/doc/src/pg.xml +++ b/lib/stdlib/doc/src/pg.xml @@ -51,77 +51,56 @@ - create(PgName) -> ok | {error, Reason} + Create an empty group - - PgName = term() - Reason = already_created | term() - -

Creates an empty group named PgName on the current +

Creates an empty group named PgName on the current node.

- create(PgName, Node) -> ok | {error, Reason} + Create an empty group on another node - - PgName = term() - Node = node() - Reason = already_created | term() - -

Creates an empty group named PgName on the node - Node.

+

Creates an empty group named PgName on the node + Node.

- join(PgName, Pid) -> Members + Join a pid to a process group - - PgName = term() - Pid = pid() - Members = [pid()] - -

Joins the pid Pid to the process group PgName. +

Joins the pid Pid to the process group + PgName. Returns a list of all old members of the group.

- send(PgName, Msg) -> void() + Send a message to all members of a process group - - PgName = Msg = term() -

Sends the tuple {pg_message, From, PgName, Msg} to - all members of the process group PgName.

-

Failure: {badarg, {PgName, Msg}} if PgName is + all members of the process group PgName.

+

Failure: {badarg, {PgName, Msg}} + if PgName is not a process group (a globally registered name).

- esend(PgName, Msg) -> void() + Send a message to all members of a process group, except ourselves - - PgName = Msg = term() -

Sends the tuple {pg_message, From, PgName, Msg} to - all members of the process group PgName, except + all members of the process group PgName, except ourselves.

-

Failure: {badarg, {PgName, Msg}} if PgName is +

Failure: {badarg, {PgName, Msg}} + if PgName is not a process group (a globally registered name).

- members(PgName) -> Members + Return a list of all members of a process group - - PgName = term() - Members = [pid()] -

Returns a list of all members of the process group PgName.

diff --git a/lib/stdlib/doc/src/pool.xml b/lib/stdlib/doc/src/pool.xml index 2b890352eb..39a79e5dc5 100644 --- a/lib/stdlib/doc/src/pool.xml +++ b/lib/stdlib/doc/src/pool.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -48,23 +48,19 @@ - start(Name) -> - start(Name, Args) -> Nodes + + >Start a new pool - - Name = atom() - Args = string() - Nodes = [node()] -

Starts a new pool. The file .hosts.erlang is read to find host names where the pool nodes can be started. See section Files below. The start-up procedure fails if the file is not found.

The slave nodes are started with slave:start/2,3, - passing along Name and, if provided, Args. - Name is used as the first part of the node names, - Args is used to specify command line arguments. See + passing along Name and, if provided, + Args. + Name is used as the first part of the node names, + Args is used to specify command line arguments. See slave(3).

Access rights must be set so that all nodes in the pool have the authority to access each other.

@@ -73,59 +69,45 @@
- attach(Node) -> already_attached | attached + Ensure that a pool master is running - - Node = node() -

This function ensures that a pool master is running and - includes Node in the pool master's pool of nodes.

+ includes Node in the pool master's pool of nodes.

- stop() -> stopped + Stop the pool and kill all the slave nodes

Stops the pool and kills all the slave nodes.

- get_nodes() -> Nodes + Return a list of the current member nodes of the pool - - Nodes = [node()] -

Returns a list of the current member nodes of the pool.

- pspawn(Mod, Fun, Args) -> pid() + Spawn a process on the pool node with expected lowest future load - - Mod = Fun = atom() - Args = [term()] -

Spawns a process on the pool node which is expected to have the lowest future load.

- pspawn_link(Mod, Fun, Args) -> pid() + Spawn and link to a process on the pool node with expected lowest future load - - Mod = Fun = atom() - Args = [term()] -

Spawn links a process on the pool node which is expected to have the lowest future load.

- get_node() -> node() + Return the node with the expected lowest future load

Returns the node with the expected lowest future load.

diff --git a/lib/stdlib/doc/src/proc_lib.xml b/lib/stdlib/doc/src/proc_lib.xml index 791001cb52..abc17c4a91 100644 --- a/lib/stdlib/doc/src/proc_lib.xml +++ b/lib/stdlib/doc/src/proc_lib.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -60,19 +60,33 @@ information regarding other processes which terminate as a result of this process terminating.

+ + + + +

See + erlang:spawn_opt/2,3,4,5.

+
+
+ + + + + + +
- spawn(Fun) -> pid() - spawn(Node, Fun) -> pid() - spawn(Module, Function, Args) -> pid() - spawn(Node, Module, Function, Args) -> pid() + + + + Spawn a new process. - - Node = node() - Fun = fun() -> void() - Module = Function = atom() - Args = [term()] - + + + + +

Spawns a new process and initializes it as described above. The process is spawned using the @@ -80,17 +94,16 @@ - spawn_link(Fun) -> pid() - spawn_link(Node, Fun) -> pid() - spawn_link(Module, Function, Args) -> pid() - spawn_link(Node, Module, Function, Args) -> pid() + + + + Spawn and link to a new process. - - Node = node() - Fun = fun() -> void() - Module = Function = atom() - Args = [term()] - + + + + +

Spawns a new process and initializes it as described above. The process is spawned using the @@ -99,18 +112,17 @@ - spawn_opt(Fun, SpawnOpts) -> pid() - spawn_opt(Node, Fun, SpawnOpts) -> pid() - spawn_opt(Module, Function, Args, SpawnOpts) -> pid() - spawn_opt(Node, Module, Func, Args, SpawnOpts) -> pid() + + + + Spawn a new process with given options. - - Node = node() - Fun = fun() -> void() - Module = Function = atom() - Args = [term()] - SpawnOpts -- see erlang:spawn_opt/2,3,4,5 - + + + + + +

Spawns a new process and initializes it as described above. The process is spawned using the @@ -124,20 +136,13 @@ - start(Module, Function, Args) -> Ret - start(Module, Function, Args, Time) -> Ret - start(Module, Function, Args, Time, SpawnOpts) -> Ret - start_link(Module, Function, Args) -> Ret - start_link(Module, Function, Args, Time) -> Ret - start_link(Module, Function, Args, Time, SpawnOpts) -> Ret + + + + + + Start a new process synchronously. - - Module = Function = atom() - Args = [term()] - Time = int() >= 0 | infinity - SpawnOpts -- see erlang:spawn_opt/2,3,4,5 - Ret = term() | {error, Reason} -

Starts a new process synchronously. Spawns the process and waits for it to start. When the process has started, it @@ -148,13 +153,13 @@ function. At this time, Ret is returned.

If the start_link/3,4,5 function is used and the process crashes before it has called init_ack/1,2, - {error, Reason} is returned if the calling process + {error, Reason} is returned if the calling process traps exits.

-

If Time is specified as an integer, this function - waits for Time milliseconds for the new process to call +

If Time is specified as an integer, this function + waits for Time milliseconds for the new process to call init_ack, or {error, timeout} is returned, and the process is killed.

-

The SpawnOpts argument, if given, will be passed +

The SpawnOpts argument, if given, will be passed as the last argument to the spawn_opt/2,3,4,5 BIF.

Using the spawn option monitor is currently not @@ -164,17 +169,13 @@ - init_ack(Parent, Ret) -> void() - init_ack(Ret) -> void() + + Used by a process when it has started. - - Parent = pid() - Ret = term() -

This function must used by a process that has been started by a start[_link]/3,4,5 - function. It tells Parent that the process has + function. It tells Parent that the process has initialized itself, has started, or has failed to initialize itself.

The init_ack/1 function uses the parent value @@ -205,40 +206,30 @@ init(Parent) -> - format(CrashReport) -> string() + Format a crash report. - - CrashReport = term() -

This function can be used by a user defined event handler to format a crash report. The crash report is sent using - error_logger:error_report(crash_report, CrashReport). + error_logger:error_report(crash_report, CrashReport). That is, the event to be handled is of the format - {error_report, GL, {Pid, crash_report, CrashReport}} + {error_report, GL, {Pid, crash_report, CrashReport}} where GL is the group leader pid of the process Pid which sent the crash report.

- initial_call(Process) -> {Module,Function,Args} | false + Extract the initial call of a proc_libspawned process. - - Process = pid() | {X,Y,Z} | ProcInfo -  X = Y = Z = int() -  ProcInfo = term() - Module = Function = atom() - Args = [atom()] -

Extracts the initial call of a process that was started using one of the spawn or start functions described above. - Process can either be a pid, an integer tuple (from + Process can either be a pid, an integer tuple (from which a pid can be created), or the process information of a process Pid fetched through an erlang:process_info(Pid) function call.

-

The list Args no longer contains the actual arguments, +

The list Args no longer contains the actual arguments, but the same number of atoms as the number of arguments; the first atom is always 'Argument__1', the second 'Argument__2', and so on. The reason is that the argument list could waste a significant @@ -256,23 +247,15 @@ init(Parent) -> - translate_initial_call(Process) -> {Module,Function,Arity} | Fun + Extract and translate the initial call of a proc_libspawned process. - - Process = pid() | {X,Y,Z} | ProcInfo -  X = Y = Z = int() -  ProcInfo = term() - Module = Function = atom() - Arity = int() - Fun = fun() -> void() -

This function is used by the c:i/0 and c:regs/0 functions in order to present process information.

Extracts the initial call of a process that was started using one of the spawn or start functions described above, - and translates it to more useful information. Process + and translates it to more useful information. Process can either be a pid, an integer tuple (from which a pid can be created), or the process information of a process Pid fetched through an erlang:process_info(Pid) @@ -280,15 +263,15 @@ init(Parent) ->

If the initial call is to one of the system defined behaviors such as gen_server or gen_event, it is translated to more useful information. If a gen_server - is spawned, the returned Module is the name of - the callback module and Function is init + is spawned, the returned Module is the name of + the callback module and Function is init (the function that initiates the new server).

A supervisor and a supervisor_bridge are also gen_server processes. In order to return information that this process is a supervisor and the name of the - call-back module, Module is supervisor and - Function is the name of the supervisor callback - module. Arity is 1 since the init/1 + call-back module, Module is supervisor and + Function is the name of the supervisor callback + module. Arity is 1 since the init/1 function is called initially in the callback module.

By default, {proc_lib,init_p,5} is returned if no information about the initial call can be found. It is @@ -297,12 +280,8 @@ init(Parent) -> - hibernate(Module, Function, Args) + Hibernate a process until a message is sent to it - - Module = Function = atom() - Args = [term()] -

This function does the same as (and does call) the BIF hibernate/3, diff --git a/lib/stdlib/doc/src/proplists.xml b/lib/stdlib/doc/src/proplists.xml index 9f1c5b24ad..225c5e97eb 100644 --- a/lib/stdlib/doc/src/proplists.xml +++ b/lib/stdlib/doc/src/proplists.xml @@ -4,7 +4,7 @@

- 20022010 + 20022011 Ericsson AB. All Rights Reserved. @@ -48,14 +48,15 @@ words, numbers are compared literally rather than by value, so that, for instance, 1 and 1.0 are different keys.

+ + + + + - append_values(Key, List) -> List + - - Key = term() - List = [term()] -

Similar to get_all_values/2, but each value is wrapped in a list unless it is already itself a list, and the @@ -65,11 +66,8 @@ - compact(List) -> List + - - List = [term()] -

Minimizes the representation of all entries in the list. This is equivalent to .

@@ -77,33 +75,24 @@
- delete(Key, List) -> List + - - Key = term() - List = [term()] - -

Deletes all entries associated with Key from - List.

+

Deletes all entries associated with Key from + List.

- expand(Expansions, List) -> List + - - Key = term() - Expansions = [{Property,[term()]}] - Property = atom() | tuple() -

Expands particular properties to corresponding sets of - properties (or other terms). For each pair {Property, Expansion} in Expansions, if E is - the first entry in List with the same key as - Property, and E and Property + properties (or other terms). For each pair {Property, Expansion} in Expansions, if E is + the first entry in List with the same key as + Property, and E and Property have equivalent normal forms, then E is replaced with - the terms in Expansion, and any following entries with - the same key are deleted from List.

+ the terms in Expansion, and any following entries with + the same key are deleted from List.

For example, the following expressions all return [fie, bar, baz, fum]:

expand([{foo, [bar, baz]}], @@ -120,103 +109,75 @@

Note that if the original property term is to be preserved in the result when expanded, it must be included in the expansion list. The inserted terms are not expanded recursively. If - Expansions contains more than one property with the same + Expansions contains more than one property with the same key, only the first occurrence is used.

See also: normalize/2.

- get_all_values(Key, List) -> [term()] + - - Key = term() - List = [term()] -

Similar to get_value/2, but returns the list of values for all entries {Key, Value} in - List. If no such entry exists, the result is the empty + List. If no such entry exists, the result is the empty list.

See also: get_value/2.

- get_bool(Key, List) -> bool() + - - Key = term() - List = [term()] -

Returns the value of a boolean key/value option. If - lookup(Key, List) would yield {Key, true}, + lookup(Key, List) would yield {Key, true}, this function returns true; otherwise false is returned.

See also: get_value/2, lookup/2.

- get_keys(List) -> [term()] + - - List = [term()] - -

Returns an unordered list of the keys used in List, +

Returns an unordered list of the keys used in List, not containing duplicates.

- get_value(Key, List) -> term() + - - Key = term() - List = [term()] - -

Equivalent to get_value(Key, List, undefined).

+

Equivalent to get_value(Key, List, undefined).

- get_value(Key, List, Default) -> term() + - - Key = term() - Default = term() - List = [term()] -

Returns the value of a simple key/value property in - List. If lookup(Key, List) would yield - {Key, Value}, this function returns the corresponding - Value, otherwise Default is returned.

+ List. If lookup(Key, List) would yield + {Key, Value}, this function returns the corresponding + Value, otherwise Default is returned.

See also: get_all_values/2, get_bool/2, get_value/2, lookup/2.

- is_defined(Key, List) -> bool() + - - Key = term() - List = [term()] - -

Returns true if List contains at least - one entry associated with Key, otherwise +

Returns true if List contains at least + one entry associated with Key, otherwise false is returned.

- lookup(Key, List) -> none | tuple() + - - Key = term() - List = [term()] - -

Returns the first entry associated with Key in - List, if one exists, otherwise returns +

Returns the first entry associated with Key in + List, if one exists, otherwise returns none. For an atom A in the list, the tuple {A, true} is the entry associated with A.

See also: get_bool/2, get_value/2, @@ -224,34 +185,20 @@ - lookup_all(Key, List) -> [tuple()] + - - Key = term() - List = [term()] - -

Returns the list of all entries associated with Key - in List. If no such entry exists, the result is the +

Returns the list of all entries associated with Key + in List. If no such entry exists, the result is the empty list.

See also: lookup/2.

- normalize(List, Stages) -> List + - - List = [term()] - Stages = [Operation] - Operation = {aliases, Aliases} | {negations, Negations} | {expand, Expansions} - Aliases = [{Key, Key}] - Negations = [{Key, Key}] - Key = term() - Expansions = [{Property, [term()]}] - Property = atom() | tuple() - -

Passes List through a sequence of +

Passes List through a sequence of substitution/expansion stages. For an aliases operation, the function substitute_aliases/2 is applied using the given list of aliases; for a negations operation, @@ -270,51 +217,37 @@ - property(Property) -> Property + - - Property = atom() | tuple() -

Creates a normal form (minimal) representation of a property. If - Property is {Key, true} where Key is + Property is {Key, true} where Key is an atom, this returns Key, otherwise the whole term - Property is returned.

+ Property is returned.

See also: property/2.

- property(Key, Value) -> Property + - - Key = term() - Value = term() - Property = atom() | tuple() -

Creates a normal form (minimal) representation of a simple - key/value property. Returns Key if Value is - true and Key is an atom, otherwise a tuple - {Key, Value} is returned.

+ key/value property. Returns Key if Value is + true and Key is an atom, otherwise a tuple + {Key, Value} is returned.

See also: property/1.

- split(List, Keys) -> {Lists, Rest} + - - List = [term()] - Keys = [term()] - Lists = [[term()]] - Rest = [term()] - -

Partitions List into a list of sublists and a - remainder. Lists contains one sublist for each key in - Keys, in the corresponding order. The relative order of +

Partitions List into a list of sublists and a + remainder. Lists contains one sublist for each key in + Keys, in the corresponding order. The relative order of the elements in each sublist is preserved from the original - List. Rest contains the elements in - List that are not associated with any of the given keys, + List. Rest contains the elements in + List that are not associated with any of the given keys, also with their original relative order preserved.

Example: split([{c, 2}, {e, 1}, a, {c, 3, 4}, d, {b, 5}, b], [a, b, c])

@@ -323,19 +256,14 @@
- substitute_aliases(Aliases, List) -> List + - - Aliases = [{Key, Key}] - Key = term() - List = [term()] -

Substitutes keys of properties. For each entry in - List, if it is associated with some key K1 - such that {K1, K2} occurs in Aliases, the - key of the entry is changed to Key2. If the same - K1 occurs more than once in Aliases, only + List, if it is associated with some key K1 + such that {K1, K2} occurs in Aliases, the + key of the entry is changed to K2. If the same + K1 occurs more than once in Aliases, only the first occurrence is used.

Example: substitute_aliases([{color, colour}], L) will replace all tuples {color, ...} in L @@ -345,24 +273,19 @@ - substitute_negations(Negations, List) -> List + - - Negations = [{Key, Key}] - Key = term() - List = [term()] -

Substitutes keys of boolean-valued properties and simultaneously negates their values. For each entry in - List, if it is associated with some key K1 - such that {K1, K2} occurs in Negations, then + List, if it is associated with some key K1 + such that {K1, K2} occurs in Negations, then if the entry was {K1, true} it will be replaced with {K2, false}, otherwise it will be replaced with {K2, true}, thus changing the name of the option and simultaneously negating the value given by get_bool(List). If the same K1 occurs more - than once in Negations, only the first occurrence is + than once in Negations, only the first occurrence is used.

Example: substitute_negations([{no_foo, foo}], L) will replace any atom no_foo or tuple @@ -374,13 +297,10 @@ - unfold(List) -> List + - - List = [term()] - -

Unfolds all occurrences of atoms in List to tuples +

Unfolds all occurrences of atoms in List to tuples {Atom, true}.

diff --git a/lib/stdlib/doc/src/qlc.xml b/lib/stdlib/doc/src/qlc.xml index da24ee9914..6a45ade447 100644 --- a/lib/stdlib/doc/src/qlc.xml +++ b/lib/stdlib/doc/src/qlc.xml @@ -4,7 +4,7 @@
- 20042009 + 20042011 Ericsson AB. All Rights Reserved. @@ -45,7 +45,9 @@ tables. Typical QLC tables are ETS, Dets, and Mnesia tables. There is also support for user defined tables, see the Implementing a QLC - table section. A query is stated using + table section. + A query is stated using Query List Comprehensions (QLCs). The answers to a query are determined by data in QLC tables that fulfill the constraints expressed by the QLCs of the query. QLCs are similar @@ -55,10 +57,11 @@ fact, in the absence of optimizations and options such as cache and unique (see below), every QLC free of QLC tables evaluates to the same list of answers as the - identical ordinary list comprehension.

+ identical ordinary list comprehension.

While ordinary list comprehensions evaluate to lists, calling - qlc:q/1,2 returns a Query + qlc:q/1,2 returns a Query Handle. To obtain all the answers to a query, qlc:eval/1,2 should be called with the query handle as first argument. Query handles are essentially @@ -69,7 +72,8 @@ Code replacement is described in the Erlang Reference Manual. The list of answers can also be traversed in - chunks by use of a Query Cursor. Query cursors are + chunks by use of a Query Cursor. Query cursors are created by calling qlc:cursor/1,2 with a query handle as first argument. Query cursors are essentially Erlang processes. @@ -226,75 +230,6 @@

-
Common data types - - -

QueryCursor = {qlc_cursor, term()}

-
-

QueryHandle = {qlc_handle, term()}

-
-

QueryHandleOrList = QueryHandle | list()

-
-

Answers = [Answer]

-
-

Answer = term()

-
-

AbstractExpression =  - parse trees - for Erlang expressions, see the abstract format - documentation in the ERTS User's Guide -

-
-

MatchExpression =  - - match specifications, see the match specification - documentation in the ERTS User's Guide and ms_transform(3) -

-
-

SpawnOptions = default | spawn_options()

-
-

SortOptions = [SortOption] | SortOption

-
-

SortOption = {compressed, bool()} - | {no_files, NoFiles} - | {order, Order} - | {size, Size} - | {tmpdir, TempDirectory} - | {unique, bool()}  - - see file_sorter(3) -

-
-

Order = ascending | descending | OrderFun

-
-

OrderFun = fun(term(), term()) -> bool()

-
-

TempDirectory = "" | filename()

-
-

Size = int() > 0

-
-

NoFiles = int() > 1

-
-

KeyPos = int() > 0 | [int() > 0]

-
-

MaxListSize = int() >= 0

-
-

bool() = true | false

-
-

Cache = ets | list | no

-
-

TmpFileUsage = allowed | not_allowed | info_msg - | warning_msg | error_msg

-
-

filename() =  - see filename(3) -

-
-

spawn_options() =  - see erlang(3) -

-
- -
- -
-
Getting started

As already mentioned @@ -679,34 +614,105 @@ ets:match_spec_run(ets:lookup(86033, {2,2}),

+ + + +

Parse trees for Erlang expression, see the abstract format + documentation in the ERTS User's Guide.

+
+ + + + + + + + + + + +

Match specification, see the match specification + documentation in the ERTS User's Guide and ms_transform(3).

+
+ + +

Actually an integer > 1.

+
+ + + + + + + + + + + + + + +

A query cursor.

+
+
+ + +

A query handle.

+
+
+ + + + + +

A literal + query + list comprehension.

+
+ + + + + + + + +

See file_sorter(3).

+
+ + + + + +

+
+
+ - append(QHL) -> QH + Return a query handle. - - QHL = [QueryHandleOrList] - QH = QueryHandle -

Returns a query handle. When evaluating the query handle - QH all answers to the first query handle in - QHL is returned followed by all answers to the rest - of the query handles in QHL.

+ QH all answers to the first query handle in + QHL are returned followed by all answers + to the rest of the query handles in QHL.

- append(QH1, QH2) -> QH3 + Return a query handle. - - QH1 = QH2 = QueryHandleOrList - QH3 = QueryHandle -

Returns a query handle. When evaluating the query handle - QH3 all answers to QH1 are returned followed - by all answers to QH2.

+ QH3 all answers to + QH1 are returned followed by all answers + to QH2.

append(QH1, QH2) is equivalent to append([QH1, QH2]).

@@ -714,17 +720,9 @@ ets:match_spec_run(ets:lookup(86033, {2,2}),
- cursor(QueryHandleOrList [, Options]) -> QueryCursor + + Create a query cursor. - - Options = [Option] | Option - Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {spawn_options, SpawnOptions} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all -

Creates a query cursor and makes the calling process the owner of the cursor. The @@ -746,11 +744,13 @@ ets:match_spec_run(ets:lookup(86033, {2,2}), [{b,1},{b,2}] 4> qlc:delete_cursor(QC). ok +

cursor(QH) is equivalent to + cursor(QH, []).

- delete_cursor(QueryCursor) -> ok + Delete a query cursor.

Deletes a query cursor. Only the owner of the cursor can @@ -759,19 +759,11 @@ ok - eval(QueryHandleOrList [, Options]) -> Answers | Error - e(QueryHandleOrList [, Options]) -> Answers + + + + Return all answers to a query. - - Options = [Option] | Option - Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all - Error = {error, module(), Reason} - Reason = - as returned by file_sorter(3) - -

Evaluates a query handle in the calling process and collects all answers in a list.

@@ -780,47 +772,39 @@ ok 1> QH = qlc:q([{X,Y} || X <- [a,b], Y <- [1,2]]), qlc:eval(QH). [{a,1},{a,2},{b,1},{b,2}] +

eval(QH) is equivalent to + eval(QH, []).

- fold(Function, Acc0, QueryHandleOrList [, Options]) -> - Acc1 | Error + + Fold a function over the answers to a query. - - Function = fun(Answer, AccIn) -> AccOut - Acc0 = Acc1 = AccIn = AccOut = term() - Options = [Option] | Option - Option = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all - Error = {error, module(), Reason} - Reason = - as returned by file_sorter(3) - - -

Calls Function on successive answers to the query - handle together with an extra argument AccIn. The - query handle and the function are evaluated in the calling - process. Function must return a new accumulator which - is passed to the next call. Acc0 is returned if there - are no answers to the query handle.

+

Calls Function on successive answers to + the query handle together with an extra argument + AccIn. The query handle and the function + are evaluated in the calling process. + Function must return a new accumulator + which is passed to the next call. + Acc0 is returned if there are no answers + to the query handle.

 1> QH = [1,2,3,4,5,6],
 qlc:fold(fun(X, Sum) -> X + Sum end, 0, QH).
 21
+

fold(Function, Acc0, + QH) is equivalent to + fold(Function, Acc0, + QH, []).

- format_error(Error) -> Chars + Return an English description of a an error tuple. - - Error = {error, module(), term()} - Chars = [char() | Chars] -

Returns a descriptive string in English of an error tuple returned by some of the functions of the qlc module @@ -830,25 +814,9 @@ ok - info(QueryHandleOrList [, Options]) -> Info + + Return code describing a query handle. - - Options = [Option] | Option - Option = EvalOption | ReturnOption - EvalOption = {cache_all, Cache} | cache_all - | {max_list_size, MaxListSize} - | {tmpdir_usage, TmpFileUsage} - | {tmpdir, TempDirectory} - | {unique_all, bool()} | unique_all - ReturnOption = {depth, Depth} - | {flat, bool()} - | {format, Format} - | {n_elements, NElements} - Depth = infinity | int() >= 0 - Format = abstract_code | string - NElements = infinity | int() > 0 - Info = AbstractExpression | string() -

Returns information about a query handle. The information describes the simplifications @@ -879,18 +847,18 @@ ok io:format("~s~n", [qlc:info(QH, unique_all)]). begin V1 = - qlc:q([ + qlc:q([ SQV || SQV <- [x,y] ], [{unique,true}]), V2 = - qlc:q([ + qlc:q([ SQV || SQV <- [a,b] ], [{unique,true}]), - qlc:q([ + qlc:q([ {X,Y} || X <- V1, Y <- V2 @@ -913,19 +881,19 @@ end io:format("~s~n", [qlc:info(Q)]). begin V1 = - qlc:q([ + qlc:q([ P0 || P0 = {W,Y} <- ets:table(17) ]), V2 = - qlc:q([ + qlc:q([ [G1|G2] || G2 <- V1, G1 <- ets:table(16), element(2, G1) =:= element(1, G2) ], [{join,lookup}]), - qlc:q([ + qlc:q([ {X,Z,W} || [{X,Z}|{W,Y}] <- V2 ]) @@ -936,44 +904,43 @@ end method chosen. A convention is used for lookup join: the first generator (G2) is the one traversed, the second one (G1) is the table where constants are looked up.

+ +

info(QH) is equivalent to + info(QH, []).

- keysort(KeyPos, QH1 [, SortOptions]) -> QH2 + + Return a query handle. - - QH1 = QueryHandleOrList - QH2 = QueryHandle -

Returns a query handle. When evaluating the query handle - QH2 the answers to the query handle QH1 are - sorted by QH2 the answers to the query handle + QH1 are sorted by file_sorter:keysort/4 according to the options.

-

The sorter will use temporary files only if QH1 does - not evaluate to a list and the size of the binary - representation of the answers exceeds Size bytes, - where Size is the value of the size option.

+

The sorter will use temporary files only if + QH1 does not evaluate to a list and the + size of the binary representation of the answers exceeds + Size bytes, where Size is the value of the + size option.

+ +

keysort(KeyPos, QH1) + is equivalent to + keysort(KeyPos, QH1, []).

- next_answers(QueryCursor [, NumberOfAnswers]) -> - Answers | Error + + Return some or all answers to a query. - - NumberOfAnswers = all_remaining | int() > 0 - Error = {error, module(), Reason} - Reason = - as returned by file_sorter(3) - -

Returns some or all of the remaining answers to a query - cursor. Only the owner of Cursor can retrieve - answers.

- + cursor. Only the owner of QueryCursor can + retrieve answers.

The optional argument NumberOfAnswersdetermines the maximum number of answers returned. The default value is 10. If less than the requested number of answers is @@ -983,21 +950,9 @@ end - q(QueryListComprehension [, Options]) -> QueryHandle + + Return a handle for a query list comprehension. - - QueryListComprehension =  - - literal query listcomprehension - - Options = [Option] | Option - Option = {max_lookup, MaxLookup} - | {cache, Cache} | cache - | {join, Join} - | {lookup, Lookup} - | {unique, bool()} | unique - MaxLookup = int() >= 0 | infinity - Join = any | lookup | merge | nested_loop - Lookup = bool() | any -

Returns a query handle for a query list comprehension. The query list comprehension must be the @@ -1024,7 +979,7 @@ end

 ...
-A = [X || {X} <- [{1},{2}]], 
+A = [X || {X} <- [{1},{2}]],
 QH = qlc:q(A),
 ...
@@ -1034,6 +989,9 @@ QH = qlc:q(A), list comprehension"); the shell process stops with a badarg reason.

+

q(QLC) is equivalent to + q(QLC, []).

+

The {cache, ets} option can be used to cache the answers to a query list comprehension. The answers are stored in one ETS table for each cached query list @@ -1092,26 +1050,26 @@ QH = qlc:q(A), io:format("~s~n", [qlc:info(Q)]). begin V1 = - qlc:q([ + qlc:q([ P0 || P0 = {X,Z} <- qlc:keysort(1, [{a,1},{b,4},{c,6}], []) ]), V2 = - qlc:q([ + qlc:q([ P0 || P0 = {W,Y} <- qlc:keysort(2, [{2,a},{3,b},{4,c}], []) ]), V3 = - qlc:q([ + qlc:q([ [G1|G2] || G1 <- V1, G2 <- V2, element(1, G1) == element(2, G2) ], [{join,merge},{cache,list}]), - qlc:q([ + qlc:q([ {A,X,Z,W} || A <- [a,b,c], [{X,Z}|{W,Y}] <- V3, @@ -1170,7 +1128,7 @@ ets:match_spec_run( elements of the key {X, Y} are compared separately.

The {lookup, true} option can be used to ensure - that the qlc module will look up constants in some + that the qlc module will look up constants in some QLC table. If there are more than one QLC table among the generators' list expressions, constants have to be looked up in at least one @@ -1190,7 +1148,7 @@ ets:match_spec_run( {join, nested_loop} invokes the method of matching every pair of objects from two handles. The last method is mostly very slow. The evaluation of the query - fails if the qlc module cannot carry out the chosen + fails if the qlc module cannot carry out the chosen join method. The default value is any which means that some fast join method will be used if possible.

@@ -1198,47 +1156,33 @@ ets:match_spec_run(
- sort(QH1 [, SortOptions]) -> QH2 + + Return a query handle. - - QH1 = QueryHandleOrList - QH2 = QueryHandle -

Returns a query handle. When evaluating the query handle - QH2 the answers to the query handle QH1 are - sorted by QH2 the answers to the query handle + QH1 are sorted by file_sorter:sort/3 according to the options.

-

The sorter will use temporary files only if QH1 does - not evaluate to a list and the size of the binary - representation of the answers exceeds Size bytes, - where Size is the value of the size option.

+

The sorter will use temporary files only if + QH1 does not evaluate to a list and the + size of the binary representation of the answers exceeds + Size bytes, where Size is the value of the + size option.

+ +

sort(QH1) is equivalent to + sort(QH1, []).

+
- string_to_handle(QueryString [, Options [, Bindings]]) -> - QueryHandle | Error + + + Return a handle for a query list comprehension. - - QueryString = string() - Options = [Option] | Option - Option = {max_lookup, MaxLookup} - | {cache, Cache} | cache - | {join, Join} - | {lookup, Lookup} - | {unique, bool()} | unique - MaxLookup = int() >= 0 | infinity - Join = any | lookup | merge | nested_loop - Lookup = bool() | any - Bindings = - as returned by - erl_eval:bindings/1 - - Error = {error, module(), Reason} - Reason =  - ErrorInfo as returned by - erl_scan:string/1 or erl_parse:parse_exprs/1 - -

A string version of qlc:q/1,2. When the query handle is evaluated the fun created by the parse transform is @@ -1253,57 +1197,24 @@ ets:match_spec_run( qlc:eval(QH). [2,3,4] +

string_to_handle(QueryString) + is equivalent to + string_to_handle(QueryString, []).

+ +

string_to_handle(QueryString, + Options) + is equivalent to + string_to_handle(QueryString, + Options, erl_eval:new_bindings()).

+

This function is probably useful mostly when called from outside of Erlang, for instance from a driver written in C.

- table(TraverseFun, Options) -> QueryHandle + Return a query handle for a table. - - TraverseFun = TraverseFun0 | TraverseFun1 - TraverseFun0 = fun() -> TraverseResult - TraverseFun1 = fun(MatchExpression) -> TraverseResult - TraverseResult = Objects | term() - Objects = [] | [term() | ObjectList] - ObjectList = TraverseFun0 | Objects - Options = [Option] | Option - Option = {format_fun, FormatFun} - | {info_fun, InfoFun} - | {lookup_fun, LookupFun} - | {parent_fun, ParentFun} - | {post_fun, PostFun} - | {pre_fun, PreFun} - | {key_equality, KeyComparison} - FormatFun = undefined | fun(SelectedObjects) -> FormatedTable - SelectedObjects = all - | {all, NElements, DepthFun} - | {match_spec, MatchExpression} - | {lookup, Position, Keys} - | {lookup, Position, Keys, NElements, DepthFun} - NElements = infinity | int() > 0 - DepthFun = fun(term()) -> term() - FormatedTable = {Mod, Fun, Args} - | AbstractExpression - | character_list() - InfoFun = undefined | fun(InfoTag) -> InfoValue - InfoTag = indices | is_unique_objects | keypos | num_of_objects - InfoValue = undefined | term() - LookupFun = undefined | fun(Position, Keys) -> LookupResult - LookupResult = [term()] | term() - ParentFun = undefined | fun() -> ParentFunValue - PostFun = undefined | fun() -> void() - PreFun = undefined | fun([PreArg]) -> void() - PreArg = {parent_value, ParentFunValue} | {stop_fun, StopFun} - ParentFunValue = undefined | term() - StopFun = undefined | fun() -> void() - KeyComparison = '=:=' | '==' - Position = int() > 0 - Keys = [term()] - Mod = Fun = atom() - Args = [term()] -

Returns a query handle for a QLC table. In Erlang/OTP there is support for ETS, Dets and @@ -1315,77 +1226,90 @@ ets:match_spec_run( as well as properties of the table are handled by callback functions provided as options to qlc:table/2.

-

The callback function TraverseFun is used for - traversing the table. It is to return a list of objects - terminated by either [] or a nullary fun to be used - for traversing the not yet traversed objects of the table. - Any other return value is immediately returned as value of - the query evaluation. Unary TraverseFuns are to - accept a match specification as argument. The match - specification is created by the parse transform by analyzing - the pattern of the generator calling qlc:table/2 and - filters using variables introduced in the pattern. If the - parse transform cannot find a match specification equivalent - to the pattern and filters, TraverseFun will be - called with a match specification returning every object. - Modules that can utilize match specifications for optimized +

The callback function TraverseFun is + used for traversing the table. It is to return a list of + objects terminated by either [] or a nullary fun to + be used for traversing the not yet traversed objects of the + table. Any other return value is immediately returned as + value of the query evaluation. Unary + TraverseFuns are to accept a match + specification as argument. The match specification is + created by the parse transform by analyzing the pattern of + the generator calling qlc:table/2 and filters using + variables introduced in the pattern. If the parse transform + cannot find a match specification equivalent to the pattern + and filters, TraverseFun will be called + with a match specification returning every object. Modules + that can utilize match specifications for optimized traversal of tables should call qlc:table/2 with a - unary TraverseFun while other modules can provide a - nullary TraverseFun. ets:table/2 is an example - of the former; gb_table:table/1 in the Implementing a QLC - table section is an example of the latter.

- -

PreFun is a unary callback function that is called - once before the table is read for the first time. If the - call fails, the query evaluation fails. Similarly, the - nullary callback function PostFun is called once - after the table was last read. The return value, which is - caught, is ignored. If PreFun has been called for a - table, PostFun is guaranteed to be called for that - table, even if the evaluation of the query fails for some - reason. The order in which pre (post) functions for + unary + TraverseFun while other modules can + provide a nullary + TraverseFun. ets:table/2 is an + example of the former; gb_table:table/1 in the + Implementing a + QLC table section is an example of the latter.

+ +

PreFun is a unary callback function + that is called once before the table is read for the first + time. If the call fails, the query evaluation fails. + Similarly, the nullary callback function + PostFun is called once after the table + was last read. The return value, which is caught, is + ignored. If PreFun has been called for a + table, + PostFun is guaranteed to be called for + that table, even if the evaluation of the query fails for + some reason. The order in which pre (post) functions for different tables are evaluated is not specified. Other table - access than reading, such as calling InfoFun, is - assumed to be OK at any time. The argument PreArgs is - a list of tagged values. Currently there are two tags, + access than reading, such as calling + InfoFun, is assumed to be OK at any + time. The argument PreArgs is a list of + tagged values. Currently there are two tags, parent_value and stop_fun, used by Mnesia for managing transactions. The value of parent_value is - the value returned by ParentFun, or undefined - if there is no ParentFun. ParentFun is called - once just before the call of PreFun in the context of - the process calling eval, fold, or + the value returned by ParentFun, or + undefined if there is no ParentFun. + ParentFun is called once just before the + call of + PreFun in the context of the process + calling + eval, fold, or cursor. The value of stop_fun is a nullary fun that deletes the cursor if called from the parent, or undefined if there is no cursor.

The binary callback - function LookupFun is used for looking up objects in - the table. The first argument Position is the key - position or an indexed position and the second argument - Keys is a sorted list of unique values. The return - value is to be a list of all objects (tuples) such that the - element at Position is a member of Keys. Any - other return value is immediately returned as value of the - query evaluation. LookupFun is called instead of + function LookupFun is used for looking + up objects in the table. The first argument + Position is the key position or an + indexed position and the second argument + Keys is a sorted list of unique values. + The return value is to be a list of all objects (tuples) + such that the element at Position is a member of + Keys. Any other return value is + immediately returned as value of the query evaluation. + LookupFun is called instead of traversing the table if the parse transform at compile time can find out that the filters match and compare the element - at Position in such a way that only Keys need - to be looked up in order to find all potential answers. The - key position is obtained by calling InfoFun(keypos) - and the indexed positions by calling - InfoFun(indices). If the key position can be used for - lookup it is always chosen, otherwise the indexed position - requiring the least number of lookups is chosen. If there is - a tie between two indexed positions the one occurring first - in the list returned by InfoFun is chosen. Positions - requiring more than max_lookup lookups are - ignored.

- -

The unary callback function InfoFun is to return - information about the table. undefined should be - returned if the value of some tag is unknown:

+ at Position in such a way that only + Keys need to be looked up in order to + find all potential answers. The key position is obtained by + calling + InfoFun(keypos) and the indexed + positions by calling + InfoFun(indices). If the key position + can be used for lookup it is always chosen, otherwise the + indexed position requiring the least number of lookups is + chosen. If there is a tie between two indexed positions the + one occurring first in the list returned by + InfoFun is chosen. Positions requiring + more than max_lookup + lookups are ignored.

+ +

The unary callback function InfoFun is + to return information about the table. undefined + should be returned if the value of some tag is unknown:

indices. Returns a list of indexed @@ -1406,20 +1330,22 @@ ets:match_spec_run( -

The unary callback function FormatFun is used by - qlc:info/1,2 for - displaying the call that created the table's query handle. - The default value, undefined, means that +

The unary callback function FormatFun + is used by qlc:info/1,2 + for displaying the call that created the table's query + handle. The default value, undefined, means that info/1,2 displays a call to '$MOD':'$FUN'/0. - It is up to FormatFun to present the selected objects - of the table in a suitable way. However, if a character list - is chosen for presentation it must be an Erlang expression - that can be scanned and parsed (a trailing dot will be added - by qlc:info though). FormatFun is called with - an argument that describes the selected objects based on - optimizations done as a result of analyzing the filters of - the QLC where the call to qlc:table/2 occurs. The - possible values of the argument are:

+ It is up to FormatFun to present the + selected objects of the table in a suitable way. However, if + a character list is chosen for presentation it must be an + Erlang expression that can be scanned and parsed (a trailing + dot will be added by qlc:info though). + FormatFun is called with an argument + that describes the selected objects based on optimizations + done as a result of analyzing the filters of the QLC where + the call to + qlc:table/2 occurs. The possible values of the + argument are:

{lookup, Position, Keys, NElements, DepthFun}. @@ -1443,10 +1369,12 @@ ets:match_spec_run( can be used for limiting the size of terms; calling DepthFun(Term) substitutes '...' for parts of Term below the depth specified by the info/1,2 - option depth. If calling FormatFun with an - argument including NElements and DepthFun - fails, FormatFun is called once again with an - argument excluding NElements and DepthFun + option depth. If calling + FormatFun with an argument including + NElements and DepthFun fails, + FormatFun is called once again with an + argument excluding + NElements and DepthFun ({lookup, Position, Keys} or all).

@@ -1458,7 +1386,7 @@ ets:match_spec_run(

See ets(3), dets(3) and - mnesia(3) + mnesia(3) for the various options recognized by table/1,2 in respective module.

@@ -1472,12 +1400,12 @@ ets:match_spec_run( Erlang Reference Manual, erl_eval(3), - erlang(3), + erlang(3), ets(3), - file(3), - error_logger(3), + file(3), + error_logger(3), file_sorter(3), - mnesia(3), + mnesia(3), Programming Examples, shell(3)

diff --git a/lib/stdlib/doc/src/queue.xml b/lib/stdlib/doc/src/queue.xml index 5ada1c2c57..383f52d10d 100644 --- a/lib/stdlib/doc/src/queue.xml +++ b/lib/stdlib/doc/src/queue.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -88,122 +88,94 @@ Original API + + + queue() +

As returned by new/0.

+
+
+ - new() -> Q + Create an empty queue - - Q = queue() -

Returns an empty queue.

- is_queue(Term) -> true | false + Test if a term is a queue - - Term = term() - -

Tests if Q is a queue and returns true if so and +

Tests if Term is a queue and returns true if so and false otherwise.

- is_empty(Q) -> true | false + Test if a queue is empty - - Q = queue() - -

Tests if Q is empty and returns true if so and +

Tests if Q is empty and returns true if so and false otherwise.

- len(Q) -> N + Get the length of a queue - - Q = queue() - N = integer() - -

Calculates and returns the length of queue Q.

+

Calculates and returns the length of queue Q.

- in(Item, Q1) -> Q2 + Insert an item at the rear of a queue - - Item = term() - Q1 = Q2 = queue() - -

Inserts Item at the rear of queue Q1. - Returns the resulting queue Q2.

+

Inserts Item at the rear of queue Q1. + Returns the resulting queue Q2.

- in_r(Item, Q1) -> Q2 + Insert an item at the front of a queue - - Item = term() - Q1 = Q2 = queue() - -

Inserts Item at the front of queue Q1. - Returns the resulting queue Q2.

+

Inserts Item at the front of queue Q1. + Returns the resulting queue Q2.

- out(Q1) -> Result + Remove the front item from a queue - - Result = {{value, Item}, Q2} | {empty, Q1} - Q1 = Q2 = queue() - -

Removes the item at the front of queue Q1. Returns the - tuple {{value, Item}, Q2}, where Item is the - item removed and Q2 is the resulting queue. If Q1 is - empty, the tuple {empty, Q1} is returned.

+

Removes the item at the front of queue Q1. Returns the + tuple {{value, Item}, Q2}, where Item is the + item removed and Q2 is the resulting queue. If Q1 is + empty, the tuple {empty, Q1} is returned.

- out_r(Q1) -> Result + Remove the rear item from a queue - - Result = {{value, Item}, Q2} | {empty, Q1} - Q1 = Q2 = queue() - -

Removes the item at the rear of the queue Q1. Returns the - tuple {{value, Item}, Q2}, where Item is the - item removed and Q2 is the new queue. If Q1 is - empty, the tuple {empty, Q1} is returned.

+

Removes the item at the rear of the queue Q1. Returns the + tuple {{value, Item}, Q2}, where Item is the + item removed and Q2 is the new queue. If Q1 is + empty, the tuple {empty, Q1} is returned.

- from_list(L) -> queue() + Convert a list to a queue - - L = list() - -

Returns a queue containing the items in L in the +

Returns a queue containing the items in L in the same order; the head item of the list will become the front item of the queue.

- to_list(Q) -> list() + Convert a queue to a list - - Q = queue() -

Returns a list of the items in the queue in the same order; the front item of the queue will become the head of the list.

@@ -211,57 +183,43 @@
- reverse(Q1) -> Q2 + Reverse a queue - - Q1 = Q2 = queue() - -

Returns a queue Q2 that contains the items of - Q1 in the reverse order.

+

Returns a queue Q2 that contains the items of + Q1 in the reverse order.

- split(N, Q1) -> {Q2,Q3} + Split a queue in two - - N = integer() - Q1 = Q2 = Q3 = queue() - -

Splits Q1 in two. The N front items - are put in Q2 and the rest in Q3

+

Splits Q1 in two. The N front items + are put in Q2 and the rest in Q3

- join(Q1, Q2) -> Q3 + Join two queues - - Q1 = Q2 = Q3 = queue() - -

Returns a queue Q3 that is the result of joining - Q1 and Q2 with Q1 in front of - Q2.

+

Returns a queue Q3 that is the result of joining + Q1 and Q2 with Q1 in front of + Q2.

- filter(Fun, Q1) -> Q2 + Filter a queue - - Fun = fun(Item) -> bool() | list() - Q1 = Q2 = queue() - -

Returns a queue Q2 that is the result of calling - Fun(Item) on all items in Q1, +

Returns a queue Q2 that is the result of calling + Fun(Item) on all items in Q1, in order from front to rear.

-

If Fun(Item) returns true, Item +

If Fun(Item) returns true, Item is copied to the result queue. If it returns false, - Item is not copied. If it returns a list + Item is not copied. If it returns a list the list elements are inserted instead of Item in the result queue.

-

So, Fun(Item) returning [Item] is thereby +

So, Fun(Item) returning [Item] is thereby semantically equivalent to returning true, just as returning [] is semantically equivalent to returning false. But returning a list builds @@ -269,15 +227,11 @@ - member(Item, Q) -> bool() + Test if an item is in a queue - - Item = term() - Q = queue() - -

Returns true if Item matches some element - in Q, otherwise false.

+

Returns true if Item matches some element + in Q, otherwise false.

@@ -290,77 +244,53 @@ - get(Q) -> Item + Return the front item of a queue - - Item = term() - Q = queue() - -

Returns Item at the front of queue Q.

-

Fails with reason empty if Q is empty.

+

Returns Item at the front of queue Q.

+

Fails with reason empty if Q is empty.

- get_r(Q) -> Item + Return the rear item of a queue - - Item = term() - Q = queue() - -

Returns Item at the rear of queue Q.

-

Fails with reason empty if Q is empty.

+

Returns Item at the rear of queue Q.

+

Fails with reason empty if Q is empty.

- drop(Q1) -> Q2 + Remove the front item from a queue - - Item = term() - Q1 = Q2 = queue() - -

Returns a queue Q2 that is the result of removing - the front item from Q1.

-

Fails with reason empty if Q1 is empty.

+

Returns a queue Q2 that is the result of removing + the front item from Q1.

+

Fails with reason empty if Q1 is empty.

- drop_r(Q1) -> Q2 + Remove the rear item from a queue - - Item = term() - Q1 = Q2 = queue() - -

Returns a queue Q2 that is the result of removing - the rear item from Q1.

-

Fails with reason empty if Q1 is empty.

+

Returns a queue Q2 that is the result of removing + the rear item from Q1.

+

Fails with reason empty if Q1 is empty.

- peek(Q) -> {value,Item} | empty + Return the front item of a queue - - Item = term() - Q = queue() - -

Returns the tuple {value, Item} where Item is the - front item of Q, or empty if Q1 is empty.

+

Returns the tuple {value, Item} where Item is the + front item of Q, or empty if Q is empty.

- peek_r(Q) -> {value,Item} | empty + Return the rear item of a queue - - Item = term() - Q = queue() - -

Returns the tuple {value, Item} where Item is the - rear item of Q, or empty if Q1 is empty.

+

Returns the tuple {value, Item} where Item is the + rear item of Q, or empty if Q is empty.

@@ -372,80 +302,56 @@ - cons(Item, Q1) -> Q2 + Insert an item at the head of a queue - - Item = term() - Q1 = Q2 = queue() - -

Inserts Item at the head of queue Q1. Returns - the new queue Q2.

+

Inserts Item at the head of queue Q1. Returns + the new queue Q2.

- head(Q) -> Item + Return the item at the head of a queue - - Item = term() - Q = queue() - -

Returns Item from the head of queue Q.

-

Fails with reason empty if Q is empty.

+

Returns Item from the head of queue Q.

+

Fails with reason empty if Q is empty.

- tail(Q1) -> Q2 + Remove the head item from a queue - - Item = term() - Q1 = Q2 = queue() - -

Returns a queue Q2 that is the result of removing - the head item from Q1.

-

Fails with reason empty if Q1 is empty.

+

Returns a queue Q2 that is the result of removing + the head item from Q1.

+

Fails with reason empty if Q1 is empty.

- snoc(Q1, Item) -> Q2 + Insert an item at the tail of a queue - - Item = term() - Q1 = Q2 = queue() - -

Inserts Item as the tail item of queue Q1. Returns - the new queue Q2.

+

Inserts Item as the tail item of queue Q1. Returns + the new queue Q2.

- daeh(Q) -> Item - last(Q) -> Item + + Return the tail item of a queue - - Item = term() - Q = queue() - -

Returns the tail item of queue Q.

-

Fails with reason empty if Q is empty.

+

Returns the tail item of queue Q.

+

Fails with reason empty if Q is empty.

- liat(Q1) -> Q2 - init(Q1) -> Q2 - lait(Q1) -> Q2 + + + Remove the tail item from a queue - - Item = term() - Q1 = Q2 = queue() - -

Returns a queue Q2 that is the result of removing - the tail item from Q1.

-

Fails with reason empty if Q1 is empty.

+

Returns a queue Q2 that is the result of removing + the tail item from Q1.

+

Fails with reason empty if Q1 is empty.

The name lait/1 is a misspelling - do not use it anymore.

diff --git a/lib/stdlib/doc/src/random.xml b/lib/stdlib/doc/src/random.xml index dcc6d756e1..93affc3191 100644 --- a/lib/stdlib/doc/src/random.xml +++ b/lib/stdlib/doc/src/random.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -49,9 +49,15 @@ strong. If a strong cryptographic random number generator is needed for example crypto:rand_bytes/1 could be used instead.

+ + + +

The state.

+
+
- seed() -> ran() + Seeds random number generation with default values

Seeds random number generation with default (fixed) values @@ -59,11 +65,8 @@ - seed(A1, A2, A3) -> undefined | ran() + Seeds random number generator - - A1 = A2 = A3 = integer() -

Seeds random number generation with integer values in the process dictionary, and returns the old state.

@@ -76,26 +79,23 @@
- seed({A1, A2, A3}) -> undefined | ran() + Seeds random number generator - - A1 = A2 = A3 = integer() -

- seed({A1, A2, A3}) is equivalent to seed(A1, A2, A3). + seed({A1, A2, A3}) is equivalent to seed(A1, A2, A3).

- seed0() -> ran() + Return default state for random number generation

Returns the default state.

- uniform()-> float() + Return a random float

Returns a random float uniformly distributed between 0.0 @@ -103,39 +103,29 @@ - uniform(N) -> integer() + Return a random integer - - N = integer() - -

Given an integer N >= 1, uniform/1 returns a +

Given an integer N >= 1, uniform/1 returns a random integer uniformly distributed between 1 and - N, updating the state in the process dictionary.

+ N, updating the state in the process dictionary.

- uniform_s(State0) -> {float(), State1} + Return a random float - - State0 = State1 = ran() -

Given a state, uniform_s/1returns a random float uniformly distributed between 0.0 and 1.0, and a new state.

- uniform_s(N, State0) -> {integer(), State1} + Return a random integer - - N = integer() - State0 = State1 = ran() - -

Given an integer N >= 1 and a state, uniform_s/2 +

Given an integer N >= 1 and a state, uniform_s/2 returns a random integer uniformly distributed between 1 and - N, and a new state.

+ N, and a new state.

diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index 9091035392..b8db55fc26 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -59,28 +59,24 @@ -
- DATA TYPES - - iodata() = iolist() | binary() - iolist() = [char() | binary() | iolist()] - - a binary is allowed as the tail of the list - - unicode_binary() = binary() with characters encoded in UTF-8 coding standard - unicode_char() = integer() representing a valid unicode codepoint - - chardata() = charlist() | unicode_binary() - - charlist() = [unicode_char() | unicode_binary() | charlist()] - - a unicode_binary is allowed as the tail of the list - - - mp() = Opaque datatype containing a compiled regular expression. - - The mp() is guaranteed to be a tuple() having the atom + + + + +

Opaque datatype containing a compiled regular expression. + The mp() is guaranteed to be a tuple() having the atom 're_pattern' as its first element, to allow for matching in guards. The arity of the tuple() or the content of the other fields - may change in future releases. -

+ may change in future releases.

+ + + + + + + + + compile(Regexp) -> {ok, MP} | {error, ErrSpec} @@ -96,14 +92,14 @@ compile(Regexp,Options) -> {ok, MP} | {error, ErrSpec} Compile a regular expression into a match program - Regexp = iodata() | charlist() + Regexp = iodata() | io:charlist() Options = [ Option ] Option = unicode | anchored | caseless | dollar_endonly | dotall | extended | firstline | multiline | no_auto_capture | dupnames | ungreedy | {newline, NLSpec}| bsr_anycrlf | bsr_unicode - NLSpec = cr | crlf | lf | anycrlf | any - MP = mp() + NLSpec = nl_spec() + MP = mp() ErrSpec = {ErrString, Position} ErrString = string() - Position = int() + Position = non_neg_integer()

This function compiles a regular expression with the syntax @@ -116,7 +112,7 @@ time one wants to match.

When the unicode option is given, the regular expression should be given as a valid unicode charlist(), otherwise as any valid iodata().

-

The options have the following meanings:

+

The options have the following meanings:

unicode The regular expression is given as a unicode charlist() and the resulting regular expression code is to be run against a valid unicode charlist() subject. @@ -173,10 +169,10 @@ This option makes it possible to include comments inside complicated patterns. N run(Subject,RE) -> {match, Captured} | nomatch Match a subject against regular expression and capture subpatterns - Subject = iodata() | charlist() - RE = mp() | iodata() | charlist() + Subject = iodata() | io:charlist() + RE = mp() | iodata() | io:charlist() Captured = [ CaptureData ] - CaptureData = {int(),int()} + CaptureData = {integer(),integer()}

The same as run(Subject,RE,[]).

@@ -186,18 +182,19 @@ This option makes it possible to include comments inside complicated patterns. N run(Subject,RE,Options) -> {match, Captured} | match | nomatch Match a subject against regular expression and capture subpatterns - Subject = iodata() | charlist() - RE = mp() | iodata() | charlist() + Subject = iodata() | io:charlist() + RE = mp() | iodata() | io:charlist() Options = [ Option ] - Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt + Option = anchored | global | notbol | noteol | notempty | {offset, integer() >= 0} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | {capture, ValueSpec, Type} | CompileOpt Type = index | list | binary ValueSpec = all | all_but_first | first | none | ValueList ValueList = [ ValueID ] - ValueID = int() | string() | atom() - CompileOpt = see compile/2 above - NLSpec = cr | crlf | lf | anycrlf | any + ValueID = integer() | string() | atom() + CompileOpt = compile_option() + See compile/2 above. + NLSpec = nl_spec() Captured = [ CaptureData ] | [ [ CaptureData ] ... ] - CaptureData = {int(),int()} | ListConversionData | binary() + CaptureData = {integer(),integer()} | ListConversionData | binary() ListConversionData = string() | {error, string(), binary()} | {incomplete, string(), binary()} @@ -217,7 +214,7 @@ This option makes it possible to include comments inside complicated patterns. N

If the regular expression is previously compiled, the option list can only contain the options anchored, global, notbol, noteol, - notempty, {offset, int()}, {newline, + notempty, {offset, integer() >= 0}, {newline, NLSpec} and {capture, ValueSpec}/{capture, ValueSpec, Type}. Otherwise all options valid for the re:compile/2 function are allowed as well. Options @@ -360,7 +357,7 @@ This option makes it possible to include comments inside complicated patterns. N behavior of the dollar metacharacter. It does not affect \Z or \z. - {offset, int()} + {offset, integer() >= 0} Start matching at the offset (position) given in the subject string. The offset is zero-based, so that the default is @@ -503,42 +500,27 @@ This option makes it possible to include comments inside complicated patterns. N - replace(Subject,RE,Replacement) -> iodata() | charlist() + Match a subject against regular expression and replace matching elements with Replacement - - Subject = iodata() | charlist() - RE = mp() | iodata() - Replacement = iodata() | charlist() - -

The same as replace(Subject,RE,Replacement,[]).

+

The same as replace(Subject,RE,Replacement,[]).

- replace(Subject,RE,Replacement,Options) -> iodata() | charlist() | binary() | list() + Match a subject against regular expression and replace matching elements with Replacement - - Subject = iodata() | charlist() - RE = mp() | iodata() | charlist() - Replacement = iodata() | charlist() - Options = [ Option ] - Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {return, ReturnType} | CompileOpt - ReturnType = iodata | list | binary - CompileOpt = see compile/2 above - NLSpec = cr | crlf | lf | anycrlf | any - -

Replaces the matched part of the Subject string with the contents of Replacement.

+

Replaces the matched part of the Subject string with the contents of Replacement.

The permissible options are the same as for re:run/3, except that the capture option is not allowed. - Instead a {return, ReturnType} is present. The default return type is iodata, constructed in a + Instead a {return, ReturnType} is present. The default return type is iodata, constructed in a way to minimize copying. The iodata result can be used directly in many i/o-operations. If a flat list() is desired, specify {return, list} and if a binary is preferred, specify {return, binary}.

As in the re:run/3 function, an mp() compiled - with the unicode option requires the Subject to be + with the unicode option requires the Subject to be a Unicode charlist(). If compilation is done implicitly and the unicode compilation option is given to this - function, both the regular expression and the Subject + function, both the regular expression and the Subject should be given as valid Unicode charlist()s.

The replacement string can contain the special character @@ -565,34 +547,17 @@ This option makes it possible to include comments inside complicated patterns. N - split(Subject,RE) -> SplitList + Split a string by tokens specified as a regular expression - - Subject = iodata() | charlist() - RE = mp() | iodata() - SplitList = [ iodata() | charlist() ] - -

The same as split(Subject,RE,[]).

+

The same as split(Subject,RE,[]).

- split(Subject,RE,Options) -> SplitList + Split a string by tokens specified as a regular expression - - Subject = iodata() | charlist() - RE = mp() | iodata() | charlist() - Options = [ Option ] - Option = anchored | global | notbol | noteol | notempty | {offset, int()} | {newline, NLSpec} | bsr_anycrlf | bsr_unicode | {return, ReturnType} | {parts, NumParts} | group | trim | CompileOpt - NumParts = int() | infinity - ReturnType = iodata | list | binary - CompileOpt = see compile/2 above - NLSpec = cr | crlf | lf | anycrlf | any - SplitList = [ RetData ] | [ GroupedRetData ] - GroupedRetData = [ RetData ] - RetData = iodata() | charlist() | binary() | list() - + See compile/2 above.

This function splits the input into parts by finding tokens according to the regular expression supplied.

@@ -602,10 +567,10 @@ This option makes it possible to include comments inside complicated patterns. N of the string is removed from the output.

As in the re:run/3 function, an mp() compiled - with the unicode option requires the Subject to be + with the unicode option requires the Subject to be a Unicode charlist(). If compilation is done implicitly and the unicode compilation option is given to this - function, both the regular expression and the Subject + function, both the regular expression and the Subject should be given as valid Unicode charlist()s.

The result is given as a list of "strings", the @@ -722,7 +687,7 @@ This option makes it possible to include comments inside complicated patterns. N

Summary of options not previously described for the re:run/3 function:

- {return,ReturnType} + {return,ReturnType}

Specifies how the parts of the original string are presented in the result list. The possible types are:

iodata diff --git a/lib/stdlib/doc/src/regexp.xml b/lib/stdlib/doc/src/regexp.xml index 8c4191c88f..35d8e1c3f8 100644 --- a/lib/stdlib/doc/src/regexp.xml +++ b/lib/stdlib/doc/src/regexp.xml @@ -4,7 +4,7 @@
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -40,176 +40,150 @@

This module contains functions for regular expression matching and substitution.

+ + + + + + +

Internal representation of a regular expression.

+
+
- match(String, RegExp) -> MatchRes + Match a regular expression - - String = RegExp = string() - MatchRes = {match,Start,Length} | nomatch | {error,errordesc()} - Start = Length = integer() - -

Finds the first, longest match of the regular expression RegExp in String. This function searches for the longest possible match and returns the first one found if there are several expressions of the same length. It returns as follows:

+

Finds the first, longest match of the regular expression RegExp in String. This function searches for the longest possible match and returns the first one found if there are several expressions of the same length. It returns as follows:

- {match,Start,Length} + {match,Start,Length} -

if the match succeeded. Start is the starting - position of the match, and Length is the length of +

if the match succeeded. Start is the starting + position of the match, and Length is the length of the matching string.

nomatch

if there were no matching characters.

- {error,Error} + {error,Error} -

if there was an error in RegExp.

+

if there was an error in RegExp.

- first_match(String, RegExp) -> MatchRes + Match a regular expression - - String = RegExp = string() - MatchRes = {match,Start,Length} | nomatch | {error,errordesc()} - Start = Length = integer() - -

Finds the first match of the regular expression RegExp in String. This call is +

Finds the first match of the regular expression RegExp in String. This call is usually faster than match and it is also a useful way to ascertain that a match exists. It returns as follows:

- {match,Start,Length} + {match,Start,Length} -

if the match succeeded. Start is the starting - position of the match and Length is the length of +

if the match succeeded. Start is the starting + position of the match and Length is the length of the matching string.

nomatch

if there were no matching characters.

- {error,Error} + {error,Error} -

if there was an error in RegExp.

+

if there was an error in RegExp.

- matches(String, RegExp) -> MatchRes + Match a regular expression - - String = RegExp = string() - MatchRes = {match, Matches} | {error, errordesc()} - Matches = list() -

Finds all non-overlapping matches of the - expression RegExp in String. + expression RegExp in String. It returns as follows:

- {match, Matches} + {match, Matches}

if the regular expression was correct. - The list will be empty if there was no match. Each element in the list looks like {Start, Length}, where Start is the starting position of the match, and Length is the length of the matching string.

+ The list will be empty if there was no match. Each element in the list looks like {Start, Length}, where Start is the starting position of the match, and Length is the length of the matching string.

- {error,Error} + {error,Error} -

if there was an error in RegExp.

+

if there was an error in RegExp.

- sub(String, RegExp, New) -> SubRes + Substitute the first occurrence of a regular expression - - String = RegExp = New = string() - SubRes = {ok,NewString,RepCount} | {error,errordesc()} - RepCount = integer() - -

Substitutes the first occurrence of a substring matching RegExp in String with the string New. A in the string New is replaced by the matched substring of String. puts a literal into the replacement string. It returns as follows:

+

Substitutes the first occurrence of a substring matching RegExp in String with the string New. A in the string New is replaced by the matched substring of String. puts a literal into the replacement string. It returns as follows:

- {ok,NewString,RepCount} + {ok,NewString,RepCount} -

if RegExp is correct. RepCount is the number of replacements which have been made +

if RegExp is correct. RepCount is the number of replacements which have been made (this will be either 0 or 1).

- {error, Error} + {error, Error} -

if there is an error in RegExp.

+

if there is an error in RegExp.

- gsub(String, RegExp, New) -> SubRes + Substitute all occurrences of a regular expression - - String = RegExp = New = string() - SubRes = {ok,NewString,RepCount} | {error,errordesc()} - RepCount = integer() -

The same as sub, except that all non-overlapping occurrences of a substring matching - RegExp in String are replaced by the string New. It returns:

+ RegExp in String are replaced by the string New. It returns:

- {ok,NewString,RepCount} + {ok,NewString,RepCount} -

if RegExp is correct. RepCount is the number of replacements which have been made.

+

if RegExp is correct. RepCount is the number of replacements which have been made.

- {error, Error} + {error, Error} -

if there is an error in RegExp.

+

if there is an error in RegExp.

- split(String, RegExp) -> SplitRes + Split a string into fields - - String = RegExp = string() - SubRes = {ok,FieldList} | {error,errordesc()} - Fieldlist = [string()] - -

String is split into fields (sub-strings) by the - regular expression RegExp.

+

String is split into fields (sub-strings) by the + regular expression RegExp.

If the separator expression is " " (a single space), then the fields are separated by blanks and/or tabs and leading and trailing blanks and tabs are discarded. For all other values of the separator, leading and trailing blanks and tabs are not discarded. It returns:

- {ok, FieldList} + {ok, FieldList}

to indicate that the string has been split up into the fields of - FieldList.

+ FieldList.

- {error, Error} + {error, Error} -

if there is an error in RegExp.

+

if there is an error in RegExp.

- sh_to_awk(ShRegExp) -> AwkRegExp + Convert an shregular expression into an AWKone - - ShRegExp AwkRegExp = string() - SubRes = {ok,NewString,RepCount} | {error,errordesc()} - RepCount = integer() -

Converts the sh type regular expression - ShRegExp into a full AWK regular + ShRegExp into a full AWK regular expression. Returns the converted regular expression string. sh expressions are used in the shell for matching file names and have the following special @@ -236,40 +210,32 @@ - parse(RegExp) -> ParseRes + Parse a regular expression - - RegExp = string() - ParseRes = {ok,RE} | {error,errordesc()} - -

Parses the regular expression RegExp and builds the +

Parses the regular expression RegExp and builds the internal representation used in the other regular expression functions. Such representations can be used in all of the other functions instead of a regular expression string. This is more efficient when the same regular expression is used in many strings. It returns:

- {ok, RE}if RegExpis correct and REis the internal representation. + {ok, RE} -

+

if RegExp is correct and RE is the internal representation.

- {error, Error}if there is an error in RegExpString. + {error, Error} -

+

if there is an error in RegExp.

- format_error(ErrorDescriptor) -> Chars + Format an error descriptor - - ErrorDescriptor = errordesc() - Chars = [char() | Chars] - -

Returns a string which describes the error ErrorDescriptor +

Returns a string which describes the error ErrorDescriptor returned when there is an error in a regular expression.

diff --git a/lib/stdlib/doc/src/sets.xml b/lib/stdlib/doc/src/sets.xml index 3610bb0184..071ee437cb 100644 --- a/lib/stdlib/doc/src/sets.xml +++ b/lib/stdlib/doc/src/sets.xml @@ -4,7 +4,7 @@
- 20002009 + 20002011 Ericsson AB. All Rights Reserved. @@ -43,202 +43,141 @@ different if and only if they do not compare equal (==).

-
- DATA TYPES - -set() - as returned by new/0 -
+ + + set() +

As returned by new/0.

+
+
- new() -> Set + Return an empty set - - Set = set() -

Returns a new empty set.

- is_set(Set) -> bool() + Test for an Set - - Set = term() - -

Returns true if Set is a set of +

Returns true if Set is a set of elements, otherwise false.

- size(Set) -> int() + Return the number of elements in a set - - Set = term() - -

Returns the number of elements in Set.

+

Returns the number of elements in Set.

- to_list(Set) -> List + Convert an Setinto a list - - Set = set() - List = [term()] - -

Returns the elements of Set as a list.

+

Returns the elements of Set as a list.

- from_list(List) -> Set + Convert a list into an Set - - List = [term()] - Set = set() - -

Returns an set of the elements in List.

+

Returns an set of the elements in List.

- is_element(Element, Set) -> bool() + Test for membership of an Set - - Element = term() - Set = set() - -

Returns true if Element is an element of - Set, otherwise false.

+

Returns true if Element is an element of + Set, otherwise false.

- add_element(Element, Set1) -> Set2 + Add an element to an Set - - Element = term() - Set1 = Set2 = set() - -

Returns a new set formed from Set1 with - Element inserted.

+

Returns a new set formed from Set1 with + Element inserted.

- del_element(Element, Set1) -> Set2 + Remove an element from an Set - - Element = term() - Set1 = Set2 = set() - -

Returns Set1, but with Element removed.

+

Returns Set1, but with Element removed.

- union(Set1, Set2) -> Set3 + Return the union of two Sets - - Set1 = Set2 = Set3 = set() - -

Returns the merged (union) set of Set1 and - Set2.

+

Returns the merged (union) set of Set1 and + Set2.

- union(SetList) -> Set + Return the union of a list of Sets - - SetList = [set()] - Set = set() -

Returns the merged (union) set of the list of sets.

- intersection(Set1, Set2) -> Set3 + Return the intersection of two Sets - - Set1 = Set2 = Set3 = set() - -

Returns the intersection of Set1 and - Set2.

+

Returns the intersection of Set1 and + Set2.

- intersection(SetList) -> Set + Return the intersection of a list of Sets - - SetList = [set()] - Set = set() -

Returns the intersection of the non-empty list of sets.

- is_disjoint(Set1, Set2) -> bool() + Check whether two Sets are disjoint - - Set1 = Set2 = set() - -

Returns true if Set1 and - Set2 are disjoint (have no elements in common), +

Returns true if Set1 and + Set2 are disjoint (have no elements in common), and false otherwise.

- subtract(Set1, Set2) -> Set3 + Return the difference of two Sets - - Set1 = Set2 = Set3 = set() - -

Returns only the elements of Set1 which are not - also elements of Set2.

+

Returns only the elements of Set1 which are not + also elements of Set2.

- is_subset(Set1, Set2) -> bool() + Test for subset - - Set1 = Set2 = set() - -

Returns true when every element of Set1 is - also a member of Set2, otherwise false.

+

Returns true when every element of Set11 is + also a member of Set2, otherwise false.

- fold(Function, Acc0, Set) -> Acc1 + Fold over set elements - - Function = fun (E, AccIn) -> AccOut - Acc0 = Acc1 = AccIn = AccOut = term() - Set = set() - -

Fold Function over every element in Set +

Fold Function over every element in Set returning the final value of the accumulator.

- filter(Pred, Set1) -> Set2 + Filter set elements - - Pred = fun (E) -> bool() - Set1 = Set2 = set() - -

Filter elements in Set1 with boolean function - Fun.

+

Filter elements in Set1 with boolean function + Pred.

diff --git a/lib/stdlib/doc/src/shell.xml b/lib/stdlib/doc/src/shell.xml index 73cc1b33bd..bc2120c37d 100644 --- a/lib/stdlib/doc/src/shell.xml +++ b/lib/stdlib/doc/src/shell.xml @@ -4,7 +4,7 @@
- 19962010 + 19962011 Ericsson AB. All Rights Reserved. @@ -763,26 +763,20 @@ loop(N) -> - history(N) -> integer() + Sets the number of previous commands to keep - - N = integer() -

Sets the number of previous commands to keep in the - history list to N. The previous number is returned. + history list to N. The previous number is returned. The default number is 20.

- results(N) -> integer() + Sets the number of previous results to keep - - N = integer() -

Sets the number of results from previous commands to keep in - the history list to N. The previous number is returned. + the history list to N. The previous number is returned. The default number is 20.

@@ -790,7 +784,7 @@ loop(N) -> catch_exception(Bool) -> Bool Sets the exception handling of the shell - Bool = bool() + Bool = boolean()

Sets the exception handling of the evaluator process. The @@ -804,38 +798,29 @@ loop(N) -> - prompt_func(PromptFunc) -> prompt_func() + Sets the shell prompt - - PromptFunc = prompt_func() - prompt_func() = default | {Mod, Func} - Mod = Func = atom() -

Sets the shell prompt function to PromptFunc. The previous prompt function is returned.

- start_restricted(Module) -> ok | {error, Reason} + Exits a normal shell and starts a restricted shell. - - Module = atom() - Reason = atom() -

Exits a normal shell and starts a restricted - shell. Module specifies the callback module for the + shell. Module specifies the callback module for the functions local_allowed/3 and non_local_allowed/3. The function is meant to be called from the shell.

If the callback module cannot be loaded, an error tuple is - returned. The Reason in the error tuple is the one + returned. The Reason in the error tuple is the one returned by the code loader when trying to load the code of the callback module.

- stop_restricted() -> ok + Exits a restricted shell and starts a normal shell.

Exits a restricted shell and starts a normal shell. The function diff --git a/lib/stdlib/doc/src/slave.xml b/lib/stdlib/doc/src/slave.xml index 168d83f301..15b6711731 100644 --- a/lib/stdlib/doc/src/slave.xml +++ b/lib/stdlib/doc/src/slave.xml @@ -4,7 +4,7 @@

- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -56,22 +56,16 @@ - start(Host) -> - start(Host, Name) -> - start(Host, Name, Args) -> {ok, Node} | {error, Reason} + + + Start a slave node on a host - - Host = Name = atom() - Args = string() - Node = node() - Reason = timeout | no_rsh | {already_running, Node} - -

Starts a slave node on the host Host. Host names need +

Starts a slave node on the host Host. Host names need not necessarily be specified as fully qualified names; short names can also be used. This is the same condition that applies to names of distributed Erlang nodes.

-

The name of the started node will be Name@Host. If no +

The name of the started node will be Name@Host. If no name is provided, the name will be the same as the node which executes the call (with the exception of the host name part of the node name).

@@ -79,7 +73,7 @@ terminal I/O which is produced at the slave is automatically relayed to the master. Also, the file process will be relayed to the master.

-

The Args argument is used to set erl command +

The Args argument is used to set erl command line arguments. If provided, it is passed to the new node and can be used for a variety of purposes. See erl(1)

@@ -103,9 +97,9 @@ E = " -env DISPLAY " ++ net_adm:localhost() ++ ":0 ", Arg = "-mnesia_dir " ++ M ++ " -pa " ++ Dir ++ E, slave:start(H, Name, Arg).
-

If successful, the function returns {ok, Node}, - where Node is the name of the new node. Otherwise it - returns {error, Reason}, where Reason can be +

If successful, the function returns {ok, Node}, + where Node is the name of the new node. Otherwise it + returns {error, Reason}, where Reason can be one of:

timeout @@ -123,24 +117,18 @@ slave:start(H, Name, Arg).

There is no rsh program on the computer.

- {already_running, Node} + {already_running, Node} -

A node with the name Name@Host already exists.

+

A node with the name Name@Host already exists.

- start_link(Host) -> - start_link(Host, Name) -> - start_link(Host, Name, Args) -> {ok, Node} | {error, Reason} + + + Start and link to a slave node on a host - - Host = Name = atom() - Args = string() - Node = node() - Reason = timeout | no_rsh | {already_running, Node} -

Starts a slave node in the same way as start/1,2,3, except that the slave node is linked to the currently @@ -151,11 +139,8 @@ slave:start(H, Name, Arg). - stop(Node) -> ok + Stop (kill) a node - - Node = node() -

Stops (kills) a node.

@@ -177,12 +162,8 @@ slave:start(H, Name, Arg).
- pseudo(Master, ServerList) -> ok + Start a number of pseudo servers - - Master = node() - ServerList = [atom()] -

Starts a number of pseudo servers. A pseudo server is a server with a registered name which does absolutely nothing @@ -198,16 +179,13 @@ rpc:call(N, slave, pseudo, [node(), [pxw_server]]). - relay(Pid) + Run a pseudo server - - Pid = pid() -

Runs a pseudo server. This function never returns any value and the process which executes the function will receive messages. All messages received will simply be passed on to - Pid.

+ Pid.

diff --git a/lib/stdlib/doc/src/sofs.xml b/lib/stdlib/doc/src/sofs.xml index 729df1e678..2e7768a1df 100644 --- a/lib/stdlib/doc/src/sofs.xml +++ b/lib/stdlib/doc/src/sofs.xml @@ -4,7 +4,7 @@
- 20012010 + 20012011 Ericsson AB. All Rights Reserved. @@ -181,10 +181,11 @@ the canonical map is the function that maps every element of X onto its equivalence class.

-

Relations as defined above (as sets of ordered pairs) will from - now on be referred to as binary relations. We call a - set of ordered sets (x[1], ..., x[n]) - an (n-ary) relation, and say that the relation is a subset of +

Relations as defined above + (as sets of ordered pairs) will from now on be referred to as + binary relations. We call a set of ordered sets + (x[1], ..., x[n]) an + (n-ary) relation, and say that the relation is a subset of the Cartesian product X[1] × ... × X[n] where x[i] is an element of X[i], 1 <= i <= n. @@ -293,7 +294,8 @@ partition_family/2, projection/2, restriction/3, substitution/2) accept an Erlang function as a means to modify each element of a given unordered - set. Such a function, called SetFun in the following, can be + set. Such a function, called + SetFun in the following, can be specified as a functional object (fun), a tuple {external, Fun}, or an integer. If SetFun is specified as a fun, the fun is applied to each element of the @@ -337,34 +339,73 @@ fun(S) -> sofs:partition(1, S) end message when given badly formed arguments or sets the types of which are not compatible.

When comparing external sets the operator ==/2 is used.

-

Types

-
-anyset() = - an unordered, ordered or atomic set -
-binary_relation() = - a binary relation -
-bool() = true | false
-external_set() = - an external set -
-family() = - a family (of subsets) -
-function() = - a function -
-ordset() = - an ordered set -
-relation() = - an n-ary relation -
-set() = - an unordered set -
-set_of_sets() = - an unordered set of set() -
-set_fun() = integer() >= 1
-          | {external, fun(external_set()) -> external_set()}
-          | fun(anyset()) -> anyset()
-spec_fun() = {external, fun(external_set()) -> bool()}
-           | fun(anyset()) -> bool()
-type() = - a type - 
+ + + +

Any kind of set (also included are the atomic sets).

+
+ + +

A binary + relation.

+
+ + +

An external + set.

+
+ + +

A family (of subsets).

+
+
+ + +

A function.

+
+ + +

An ordered + set.

+
+ + +

An n-ary relation. +

+
+ + +

An unordered + set.

+
+ + +

An unordered + set of unordered sets.

+
+ + +

A SetFun.

+
+ + + + + +

A type.

+
+ + + tuple_of(T) +

A tuple where the elements are of type T.

+
+
- a_function(Tuples [, Type]) -> Function + + Create a function. - - Function = function() - Tuples = [tuple()] - Type = type() -

Creates a function. a_function(F, T) is equivalent to @@ -375,16 +416,12 @@ type() = - a type - - canonical_relation(SetOfSets) -> BinRel + Return the canonical map. - - BinRel = binary_relation() - SetOfSets = set_of_sets() -

Returns the binary relation containing the elements - (E, Set) such that Set belongs to SetOfSets and E - belongs to Set. If SetOfSets is + (E, Set) such that Set belongs to SetOfSets and E + belongs to Set. If SetOfSets is a partition of a set X and R is the equivalence relation in X induced by SetOfSets, then the returned relation is @@ -398,14 +435,12 @@ type() = - a type - - composite(Function1, Function2) -> Function3 + Return the composite of two functions. - - Function1 = Function2 = Function3 = function() -

Returns the composite of - the functions Function1 and Function2.

+ the functions Function1 and + Function2.

 1> F1 = sofs:a_function([{a,1},{b,2},{c,2}]),
 F2 = sofs:a_function([{1,x},{2,y},{3,z}]),
@@ -415,14 +450,9 @@ type() = - a type - 
- constant_function(Set, AnySet) -> Function + Create the function that maps each element of a set onto another set. - - AnySet = anyset() - Function = function() - Set = set() -

Creates the function that maps each element of the set Set onto AnySet.

@@ -435,14 +465,11 @@ type() = - a type -
- converse(BinRel1) -> BinRel2 + Return the converse of a binary relation. - - BinRel1 = BinRel2 = binary_relation() -

Returns the converse - of the binary relation BinRel1.

+ of the binary relation BinRel1.

 1> R1 = sofs:relation([{1,a},{2,b},{3,a}]),
 R2 = sofs:converse(R1),
@@ -451,31 +478,25 @@ type() = - a type - 
- difference(Set1, Set2) -> Set3 + Return the difference of two sets. - - Set1 = Set2 = Set3 = set() -

Returns the difference of - the sets Set1 and Set2.

+ the sets Set1 and Set2.

- digraph_to_family(Graph [, Type]) -> Family + + Create a family from a directed graph. - - Graph = digraph() - see digraph(3) - - Family = family() - Type = type() -

Creates a family from - the directed graph Graph. Each vertex a of Graph is + the directed graph Graph. Each vertex a of + Graph is represented by a pair (a, {b[1], ..., b[n]}) where the b[i]'s are the out-neighbours of a. If no type is explicitly given, [{atom, [atom]}] is used as type of - the family. It is assumed that Type is + the family. It is assumed that Type is a valid type of the external set of the family.

If G is a directed graph, it holds that the vertices and @@ -484,15 +505,11 @@ type() = - a type - - domain(BinRel) -> Set + Return the domain of a binary relation. - - BinRel = binary_relation() - Set = set() -

Returns the domain of - the binary relation BinRel.

+ the binary relation BinRel.

 1> R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),
 S = sofs:domain(R),
@@ -501,16 +518,13 @@ type() = - a type - 
- drestriction(BinRel1, Set) -> BinRel2 + Return a restriction of a binary relation. - - BinRel1 = BinRel2 = binary_relation() - Set = set() - -

Returns the difference between the binary relation BinRel1 +

Returns the difference between the binary relation + BinRel1 and the restriction - of BinRel1 to Set.

+ of BinRel1 to Set.

 1> R1 = sofs:relation([{1,a},{2,b},{3,c}]),
 S = sofs:set([2,4,6]),
@@ -522,16 +536,13 @@ type() = - a type - 
- drestriction(SetFun, Set1, Set2) -> Set3 + Return a restriction of a relation. - - SetFun = set_fun() - Set1 = Set2 = Set3 = set() - - -

Returns a subset of Set1 containing those elements that do - not yield an element in Set2 as the result of applying - SetFun.

+ +

Returns a subset of Set1 containing those elements + that do + not yield an element in Set2 as the result of applying + SetFun.

 1> SetFun = {external, fun({_A,B,C}) -> {B,C} end},
 R1 = sofs:relation([{a,aa,1},{b,bb,2},{c,cc,3}]),
@@ -544,11 +555,8 @@ type() = - a type - 
- empty_set() -> Set + Return the untyped empty set. - - Set = set() -

Returns the untyped empty set. empty_set() is equivalent to @@ -556,19 +564,14 @@ type() = - a type - - extension(BinRel1, Set, AnySet) -> BinRel2 + Extend the domain of a binary relation. - - AnySet = anyset() - BinRel1 = BinRel2 = binary_relation() - Set = set() -

Returns the extension of - BinRel1 such that - for each element E in Set that does not belong to the - domain of BinRel1, - BinRel2 contains the pair (E, AnySet).

+ BinRel1 such that + for each element E in Set that does not belong to the + domain of BinRel1, + BinRel2 contains the pair (E, AnySet).

 1> S = sofs:set([b,c]),
 A = sofs:empty_set(),
@@ -579,13 +582,9 @@ type() = - a type - 
- family(Tuples [, Type]) -> Family + + Create a family of subsets. - - Family = family() - Tuples = [tuple()] - Type = type() -

Creates a family of subsets. family(F, T) is equivalent to @@ -596,18 +595,17 @@ type() = - a type - - family_difference(Family1, Family2) -> Family3 + Return the difference of two families. - - Family1 = Family2 = Family3 = family() - -

If Family1 and Family2 +

If Family1 and Family2 are families, then - Family3 is the family + Family3 is the family such that the index set is equal to the index set of - Family1, and Family3[i] is the difference between Family1[i] - and Family2[i] if Family2 maps i, Family1[i] otherwise.

+ Family1, and Family3[i] is the + difference between Family1[i] + and Family2[i] if Family2 maps i, + Family1[i] otherwise.

 1> F1 = sofs:family([{a,[1,2]},{b,[3,4]}]),
 F2 = sofs:family([{b,[4,5]},{c,[6,7]}]),
@@ -617,17 +615,18 @@ type() = - a type - 
- family_domain(Family1) -> Family2 + Return a family of domains. - - Family1 = Family2 = family() - - -

If Family1 is a family - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the domain of Family1[i].

+ +

If Family1 is + a family + and Family1[i] is a binary relation for every i + in the index set of Family1, + then Family2 is the family with the same index + set as Family1 such + that Family2[i] is + the domain of + Family1[i].

 1> FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),
 F = sofs:family_domain(FR),
@@ -636,17 +635,18 @@ type() = - a type - 
- family_field(Family1) -> Family2 + Return a family of fields. - - Family1 = Family2 = family() - - -

If Family1 is a family - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the field of Family1[i].

+ +

If Family1 is + a family + and Family1[i] is a binary relation for every i + in the index set of Family1, + then Family2 is the family with the same index + set as Family1 such + that Family2[i] is + the field of + Family1[i].

 1> FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),
 F = sofs:family_field(FR),
@@ -657,21 +657,21 @@ type() = - a type - 
- family_intersection(Family1) -> Family2 + Return the intersection of a family of sets of sets. - - Family1 = Family2 = family() - - -

If Family1 is a family - and Family1[i] is a set of sets for every i in the index set - of Family1, then Family2 is the family with the same index - set as Family1 such that Family2[i] is - the intersection of - Family1[i].

-

If Family1[i] is an empty set for some i, then the process - exits with a badarg message.

+ +

If Family1 is + a family + and Family1[i] is a set of sets for every i in + the index set of Family1, + then Family2 is the family with the same index + set as Family1 such + that Family2[i] is + the intersection + of Family1[i].

+

If Family1[i] is an empty set for some i, then + the process exits with a badarg message.

 1> F1 = sofs:from_term([{a,[[1,2,3],[2,3,4]]},{b,[[x,y,z],[x,y]]}]),
 F2 = sofs:family_intersection(F1),
@@ -680,17 +680,16 @@ type() = - a type - 
- family_intersection(Family1, Family2) -> Family3 + Return the intersection of two families. - - Family1 = Family2 = Family3 = family() - - -

If Family1 and Family2 - are families, then Family3 - is the family such that the index set is the intersection of - Family1's and Family2's index sets, and Family3[i] is the - intersection of Family1[i] and Family2[i].

+ +

If Family1 and Family2 + are families, + then Family3 is the family such that the index + set is the intersection of Family1's and + Family2's index sets, + and Family3[i] is the intersection of + Family1[i] and Family2[i].

 1> F1 = sofs:family([{a,[1,2]},{b,[3,4]},{c,[5,6]}]),
 F2 = sofs:family([{b,[4,5]},{c,[7,8]},{d,[9,10]}]),
@@ -700,18 +699,16 @@ type() = - a type - 
- family_projection(SetFun, Family1) -> Family2 + Return a family of modified subsets. - - SetFun = set_fun() - Family1 = Family2 = family() - Set = set() - - -

If Family1 is a family - then Family2 is the family with the same index set as - Family1 such that Family2[i] is the result of calling SetFun - with Family1[i] as argument.

+ +

If Family1 is + a family + then Family2 is the family with the same index + set as Family1 such + that Family2[i] is the result of + calling SetFun with Family1[i] as + argument.

 1> F1 = sofs:from_term([{a,[[1,2],[2,3]]},{b,[[]]}]),
 F2 = sofs:family_projection({sofs, union}, F1),
@@ -720,17 +717,18 @@ type() = - a type - 
- family_range(Family1) -> Family2 + Return a family of ranges. - - Family1 = Family2 = family() - - -

If Family1 is a family - and Family1[i] is a binary relation for every i in the index - set of Family1, then Family2 is the family with the same - index set as Family1 such that Family2[i] is - the range of Family1[i].

+ +

If Family1 is + a family + and Family1[i] is a binary relation for every i + in the index set of Family1, + then Family2 is the family with the same index + set as Family1 such + that Family2[i] is + the range of + Family1[i].

 1> FR = sofs:from_term([{a,[{1,a},{2,b},{3,c}]},{b,[]},{c,[{4,d},{5,e}]}]),
 F = sofs:family_range(FR),
@@ -739,22 +737,21 @@ type() = - a type - 
- family_specification(Fun, Family1) -> Family2 + Select a subset of a family using a predicate. - - Fun = spec_fun() - Family1 = Family2 = family() - - -

If Family1 is a family, - then Family2 is - the restriction of - Family1 to those elements i of the - index set for which Fun applied to Family1[i] returns - true. If Fun is a tuple {external, Fun2}, - Fun2 is applied to - the external set of - Family1[i], otherwise Fun is applied to Family1[i].

+ +

If Family1 is + a family, + then Family2 is + the restriction of + Family1 to those elements i of the index set + for which Fun applied + to Family1[i] returns + true. If Fun is a + tuple {external, Fun2}, Fun2 is applied to + the external set + of Family1[i], otherwise Fun is + applied to Family1[i].

 1> F1 = sofs:family([{a,[1,2,3]},{b,[1,2]},{c,[1]}]),
 SpecFun = fun(S) -> sofs:no_elements(S) =:= 2 end,
@@ -764,24 +761,22 @@ type() = - a type - 
- family_to_digraph(Family [, GraphType]) -> Graph + + Create a directed graph from a family. - - Graph = digraph() - Family = family() - GraphType = - see digraph(3) - -

Creates a directed graph from - the family Family. For each - pair (a, {b[1], ..., b[n]}) of Family, the vertex + the family Family. + For each pair (a, {b[1], ..., b[n]}) + of Family, the vertex a as well the edges (a, b[i]) for 1 <= i <= n are added to a newly created directed graph.

-

If no graph type is given, digraph:new/1 is used for - creating the directed graph, otherwise the GraphType +

If no graph type is given + digraph:new/0 is used for + creating the directed graph, otherwise the GraphType argument is passed on as second argument to - digraph:new/2.

+ digraph:new/1.

It F is a family, it holds that F is a subset of digraph_to_family(family_to_digraph(F), type(F)). Equality holds if union_of_family(F) is a subset of @@ -791,17 +786,15 @@ type() = - a type - - family_to_relation(Family) -> BinRel + Create a binary relation from a family. - - Family = family() - BinRel = binary_relation() - - -

If Family is a family, - then BinRel is the binary relation containing all pairs - (i, x) such that i belongs to the index set of Family - and x belongs to Family[i].

+ +

If Family is + a family, + then BinRel is the binary relation containing + all pairs (i, x) such that i belongs to the index set + of Family and x belongs + to Family[i].

 1> F = sofs:family([{a,[]}, {b,[1]}, {c,[2,3]}]),
 R = sofs:family_to_relation(F),
@@ -810,17 +803,18 @@ type() = - a type - 
- family_union(Family1) -> Family2 + Return the union of a family of sets of sets. - - Family1 = Family2 = family() - - -

If Family1 is a family - and Family1[i] is a set of sets for each i in the index set - of Family1, then Family2 is the family with the same index - set as Family1 such that Family2[i] is - the union of Family1[i].

+ +

If Family1 is + a family + and Family1[i] is a set of sets for each i in + the index set of Family1, + then Family2 is the family with the same index + set as Family1 such + that Family2[i] is + the union of + Family1[i].

 1> F1 = sofs:from_term([{a,[[1,2],[2,3]]},{b,[[]]}]),
 F2 = sofs:family_union(F1),
@@ -831,18 +825,18 @@ type() = - a type - 
- family_union(Family1, Family2) -> Family3 + Return the union of two families. - - Family1 = Family2 = Family3 = family() - - -

If Family1 and Family2 - are families, then Family3 - is the family such that the index set is the union of Family1's - and Family2's index sets, and Family3[i] is the union of - Family1[i] and Family2[i] if both maps i, Family1[i] or - Family2[i] otherwise.

+ +

If Family1 and Family2 + are families, + then Family3 is the family such that the index + set is the union of Family1's + and Family2's index sets, + and Family3[i] is the union + of Family1[i] and Family2[i] if + both maps i, Family1[i] + or Family2[i] otherwise.

 1> F1 = sofs:family([{a,[1,2]},{b,[3,4]},{c,[5,6]}]),
 F2 = sofs:family([{b,[4,5]},{c,[7,8]},{d,[9,10]}]),
@@ -852,15 +846,11 @@ type() = - a type - 
- field(BinRel) -> Set + Return the field of a binary relation. - - BinRel = binary_relation() - Set = set() -

Returns the field of the - binary relation BinRel.

+ binary relation BinRel.

 1> R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),
 S = sofs:field(R),
@@ -871,31 +861,24 @@ type() = - a type - 
- from_external(ExternalSet, Type) -> AnySet + Create a set. - - ExternalSet = external_set() - AnySet = anyset() - Type = type() -

Creates a set from the external - set ExternalSet - and the type Type. It is - assumed that Type is a valid - type of ExternalSet.

+ set ExternalSet + and the type Type. + It is assumed that Type is + a valid + type of ExternalSet.

- from_sets(ListOfSets) -> Set + Create a set out of a list of sets. - - Set = set() - ListOfSets = [anyset()] -

Returns the unordered - set containing the sets of the list ListOfSets.

+ set containing the sets of the list + ListOfSets.

 1> S1 = sofs:relation([{a,1},{b,2}]),
 S2 = sofs:relation([{x,3},{y,4}]),
@@ -905,38 +888,33 @@ type() = - a type - 
- from_sets(TupleOfSets) -> Ordset + Create an ordered set out of a tuple of sets. - - Ordset = ordset() - TupleOfSets = tuple-of(anyset()) -

Returns the ordered set containing the sets of the non-empty tuple - TupleOfSets.

+ TupleOfSets.

- from_term(Term [, Type]) -> AnySet + + Create a set. - - AnySet = anyset() - Term = term() - Type = type() -

Creates an element of Sets by - traversing the term Term, sorting lists, removing duplicates and + traversing the term Term, sorting lists, + removing duplicates and deriving or verifying a valid type for the so obtained external set. An - explicitly given type Type + explicitly given type + Type can be used to limit the depth of the traversal; an atomic type stops the traversal, as demonstrated by this example where "foo" and {"foo"} are left unmodified:

-1> S = sofs:from_term([{{"foo"},[1,1]},{"foo",[2,2]}], [{atom,[atom]}]),
+1> S = sofs:from_term([{{"foo"},[1,1]},{"foo",[2,2]}],
+[{atom,[atom]}]),
 sofs:to_external(S).
 [{{"foo"},[1]},{"foo",[2]}]

from_term can be used for creating atomic or ordered @@ -963,15 +941,12 @@ type() = - a type - - image(BinRel, Set1) -> Set2 + Return the image of a set under a binary relation. - - BinRel = binary_relation() - Set1 = Set2 = set() -

Returns the image of the - set Set1 under the binary relation BinRel.

+ set Set1 under the binary + relation BinRel.

 1> R = sofs:relation([{1,a},{2,b},{2,c},{3,d}]),
 S1 = sofs:set([1,2]),
@@ -981,42 +956,32 @@ type() = - a type - 
- intersection(SetOfSets) -> Set + Return the intersection of a set of sets. - - Set = set() - SetOfSets = set_of_sets() -

Returns the intersection of - the set of sets SetOfSets.

+ the set of sets SetOfSets.

Intersecting an empty set of sets exits the process with a badarg message.

- intersection(Set1, Set2) -> Set3 + Return the intersection of two sets. - - Set1 = Set2 = Set3 = set() -

Returns the intersection of - Set1 and Set2.

+ Set1 and Set2.

- intersection_of_family(Family) -> Set + Return the intersection of a family. - - Family = family() - Set = set() -

Returns the intersection of - the family Family.

+ the family Family. +

Intersecting an empty family exits the process with a badarg message.

@@ -1027,14 +992,11 @@ type() = - a type - 
- inverse(Function1) -> Function2 + Return the inverse of a function. - - Function1 = Function2 = function() -

Returns the inverse - of the function Function1.

+ of the function Function1.

 1> R1 = sofs:relation([{1,a},{2,b},{3,c}]),
 R2 = sofs:inverse(R1),
@@ -1043,16 +1005,13 @@ type() = - a type - 
- inverse_image(BinRel, Set1) -> Set2 + Return the inverse image of a set under a binary relation. - - BinRel = binary_relation() - Set1 = Set2 = set() - -

Returns the inverse - image of Set1 under the binary relation BinRel.

+

Returns the inverse + image of Set1 under the binary + relation BinRel.

 1> R = sofs:relation([{1,a},{2,b},{2,c},{3,d}]),
 S1 = sofs:set([c,d,e]),
@@ -1062,52 +1021,38 @@ type() = - a type - 
- is_a_function(BinRel) -> Bool + Test for a function. - - Bool = bool() - BinRel = binary_relation() - -

Returns true if the binary relation BinRel is a - function or the +

Returns true if the binary relation BinRel + is a function or the untyped empty set, false otherwise.

- is_disjoint(Set1, Set2) -> Bool + Test for disjoint sets. - - Bool = bool() - Set1 = Set2 = set() - -

Returns true if Set1 and Set2 +

Returns true if Set1 + and Set2 are disjoint, false otherwise.

- is_empty_set(AnySet) -> Bool + Test for an empty set. - - AnySet = anyset() - Bool = bool() - -

Returns true if Set is an empty unordered set, - false otherwise.

+

Returns true if AnySet is an empty + unordered set, false otherwise.

- is_equal(AnySet1, AnySet2) -> Bool + Test two sets for equality. - - AnySet1 = AnySet2 = anyset() - Bool = bool() - -

Returns true if the AnySet1 and AnySet2 +

Returns true if the AnySet1 + and AnySet2 are equal, false otherwise. This example shows that ==/2 is used when comparing sets for equality:

@@ -1119,67 +1064,49 @@ true
- is_set(AnySet) -> Bool + Test for an unordered set. - - AnySet = anyset() - Bool = bool() - -

Returns true if AnySet is +

Returns true if AnySet is an unordered set, and - false if AnySet is an ordered set or an atomic set.

+ false if AnySet is an ordered set or an + atomic set.

- is_sofs_set(Term) -> Bool + Test for an unordered set. - - Bool = bool() - Term = term() - -

Returns true if Term is +

Returns true if Term is an unordered set, an ordered set or an atomic set, false otherwise.

- is_subset(Set1, Set2) -> Bool + Test two sets for subset. - - Bool = bool() - Set1 = Set2 = set() - -

Returns true if Set1 is - a subset of Set2, false - otherwise.

+

Returns true if Set1 is + a subset + of Set2, false otherwise.

- is_type(Term) -> Bool + Test for a type. - - Bool = bool() - Term = term() - -

Returns true if the term Term is +

Returns true if the term Term is a type.

- join(Relation1, I, Relation2, J) -> Relation3 + Return the join of two relations. - - Relation1 = Relation2 = Relation3 = relation() - I = J = integer() > 0 -

Returns the natural - join of the relations Relation1 and Relation2 on - coordinates I and J.

+ join of the relations Relation1 + and Relation2 on coordinates I and + J.

 1> R1 = sofs:relation([{a,x,1},{b,y,2}]),
 R2 = sofs:relation([{1,f,g},{1,h,i},{2,3,4}]),
@@ -1189,20 +1116,17 @@ true
- multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2 + Return the multiple relative product of a tuple of binary relations and a relation. - - TupleOfBinRels = tuple-of(BinRel) - BinRel = BinRel1 = BinRel2 = binary_relation() - - -

If TupleOfBinRels is a non-empty tuple - {R[1], ..., R[n]} of binary relations and BinRel1 - is a binary relation, then BinRel2 is + +

If TupleOfBinRels is a non-empty tuple + {R[1], ..., R[n]} of binary relations + and BinRel1 is a binary relation, + then BinRel2 is the multiple relative product of the ordered set - (R[i], ..., R[n]) and BinRel1.

+ (R[i], ..., R[n]) and BinRel1.

 1> Ri = sofs:relation([{a,1},{b,2},{c,3}]),
 R = sofs:relation([{a,b},{b,c},{c,a}]),
@@ -1212,29 +1136,21 @@ true
- no_elements(ASet) -> NoElements + Return the number of elements of a set. - - ASet = set() | ordset() - NoElements = integer() >= 0 -

Returns the number of elements of the ordered or unordered - set ASet.

+ set ASet.

- partition(SetOfSets) -> Partition + Return the coarsest partition given a set of sets. - - SetOfSets = set_of_sets() - Partition = set() -

Returns the partition of - the union of the set of sets SetOfSets such that two + the union of the set of sets SetOfSets such that two elements are considered equal if they belong to the same - elements of SetOfSets.

+ elements of SetOfSets.

 1> Sets1 = sofs:from_term([[a,b,c],[d,e,f],[g,h,i]]),
 Sets2 = sofs:from_term([[b,c,d],[e,f,g],[h,i,j]]),
@@ -1244,17 +1160,12 @@ true
- partition(SetFun, Set) -> Partition + Return a partition of a set. - - SetFun = set_fun() - Partition = set() - Set = set() -

Returns the partition of - Set such that two elements are considered equal if the - results of applying SetFun are equal.

+ Set such that two elements are considered equal + if the results of applying SetFun are equal.

 1> Ss = sofs:from_term([[a],[b],[c,d],[e,f]]),
 SetFun = fun(S) -> sofs:from_term(sofs:no_elements(S)) end,
@@ -1264,19 +1175,16 @@ true
- partition(SetFun, Set1, Set2) -> {Set3, Set4} + Return a partition of a set. - - SetFun = set_fun() - Set1 = Set2 = Set3 = Set4 = set() -

Returns a pair of sets that, regarded as constituting a set, forms a partition of - Set1. If the - result of applying SetFun to an element of Set1 yields an - element in Set2, the element belongs to Set3, otherwise the - element belongs to Set4.

+ Set1. If the + result of applying SetFun to an element + of Set1 yields an element in Set2, + the element belongs to Set3, otherwise the + element belongs to Set4.

 1> R1 = sofs:relation([{1,a},{2,b},{3,c}]),
 S = sofs:set([2,4,6]),
@@ -1289,21 +1197,17 @@ true
- partition_family(SetFun, Set) -> Family + Return a family indexing a partition. - - Family = family() - SetFun = set_fun() - Set = set() -

Returns the family - Family where the indexed set is - a partition of Set - such that two elements are considered equal if the results - of applying SetFun are the same value i. This i is the index - that Family maps onto - the equivalence + Family where the indexed set is + a partition + of Set such that two elements are considered + equal if the results of applying SetFun are the + same value i. This i is the index that Family + maps onto + the equivalence class.

 1> S = sofs:relation([{a,a,a,a},{a,a,b,b},{a,b,b,b}]),
@@ -1314,18 +1218,15 @@ true
- product(TupleOfSets) -> Relation + Return the Cartesian product of a tuple of sets. - - Relation = relation() - TupleOfSets = tuple-of(set()) -

Returns the Cartesian product of the non-empty tuple of sets - TupleOfSets. If (x[1], ..., x[n]) is an element of - the n-ary relation Relation, then x[i] is drawn from element - i of TupleOfSets.

+ TupleOfSets. If (x[1], ..., x[n]) is + an element of the n-ary relation Relation, then + x[i] is drawn from element i + of TupleOfSets.

 1> S1 = sofs:set([a,b]),
 S2 = sofs:set([1,2]),
@@ -1336,15 +1237,12 @@ true
- product(Set1, Set2) -> BinRel + Return the Cartesian product of two sets. - - BinRel = binary_relation() - Set1 = Set2 = set() -

Returns the Cartesian - product of Set1 and Set2.

+ product of Set1 + and Set2.

 1> S1 = sofs:set([1,2]),
 S2 = sofs:set([a,b]),
@@ -1356,19 +1254,16 @@ true
- projection(SetFun, Set1) -> Set2 + Return a set of substituted elements. - - SetFun = set_fun() - Set1 = Set2 = set() -

Returns the set created by substituting each element of - Set1 by the result of applying SetFun to the element.

-

If SetFun is a number i >= 1 and Set1 is a - relation, then the returned set is - the projection of Set1 - onto coordinate i.

+ Set1 by the result of + applying SetFun to the element.

+

If SetFun is a number i >= 1 and + Set1 is a relation, then the returned set is + the projection of + Set1 onto coordinate i.

 1> S1 = sofs:from_term([{1,a},{2,b},{3,a}]),
 S2 = sofs:projection(2, S1),
@@ -1377,15 +1272,11 @@ true
- range(BinRel) -> Set + Return the range of a binary relation. - - BinRel = binary_relation() - Set = set() -

Returns the range of the - binary relation BinRel.

+ binary relation BinRel.

 1> R = sofs:relation([{1,a},{1,b},{2,b},{2,c}]),
 S = sofs:range(R),
@@ -1394,41 +1285,33 @@ true
- relation(Tuples [, Type]) -> Relation + + Create a relation. - - N = integer() - Type = N | type() - Relation = relation() - Tuples = [tuple()] -

Creates a relation. relation(R, T) is equivalent to from_term(R, T), if T is a type and the result is a - relation. If Type is an integer N, then + relation. If Type is an integer N, then [{atom, ..., atom}]), where the size of the tuple is N, is used as type of the relation. If no type is - explicitly given, the size of the first tuple of Tuples is + explicitly given, the size of the first tuple of + Tuples is used if there is such a tuple. relation([]) is equivalent to relation([], 2).

- relation_to_family(BinRel) -> Family + Create a family from a binary relation. - - Family = family() - BinRel = binary_relation() -

Returns the family - Family such that the index set is equal to + Family such that the index set is equal to the domain of the binary - relation BinRel, and Family[i] is + relation BinRel, and Family[i] is the image of the set of i - under BinRel.

+ under BinRel.

 1> R = sofs:relation([{b,1},{c,2},{c,3}]),
 F = sofs:relation_to_family(R),
@@ -1437,63 +1320,57 @@ true
- relative_product(TupleOfBinRels [, BinRel1]) -> BinRel2 - Return the relative product of a tuple of binary relations + + + Return the relative product of a list of binary relations and a binary relation. - - TupleOfBinRels = tuple-of(BinRel) - BinRel = BinRel1 = BinRel2 = binary_relation() - - -

If TupleOfBinRels is a non-empty tuple - {R[1], ..., R[n]} of binary relations and BinRel1 - is a binary relation, then BinRel2 is - the relative - product of the ordered set (R[i], ..., R[n]) - and BinRel1.

-

If BinRel1 is omitted, the relation of equality between the - elements of - the Cartesian - product of the ranges of R[i], + +

If ListOfBinRels is a non-empty list + [R[1], ..., R[n]] of binary relations and + BinRel1 + is a binary relation, then BinRel2 is the relative product + of the ordered set (R[i], ..., R[n]) and + BinRel1.

+

If BinRel1 is omitted, the relation of equality + between the elements of + the Cartesian + product of the ranges of R[i], range R[1] × ... × range R[n], is used instead (intuitively, nothing is "lost").

 1> TR = sofs:relation([{1,a},{1,aa},{2,b}]),
 R1 = sofs:relation([{1,u},{2,v},{3,c}]),
-R2 = sofs:relative_product({TR, R1}),
+R2 = sofs:relative_product([TR, R1]),
 sofs:to_external(R2).
 [{1,{a,u}},{1,{aa,u}},{2,{b,v}}]
-

Note that relative_product({R1}, R2) is +

Note that relative_product([R1], R2) is different from relative_product(R1, R2); the - tuple of one element is not identified with the element + list of one element is not identified with the element itself.

- relative_product(BinRel1, BinRel2) -> BinRel3 + Return the relative product of two binary relations. - - BinRel1 = BinRel2 = BinRel3 = binary_relation() -

Returns the relative - product of the binary relations BinRel1 and BinRel2.

+ product of the binary relations BinRel1 + and BinRel2.

- relative_product1(BinRel1, BinRel2) -> BinRel3 + Return the relative_product of two binary relations. - - BinRel1 = BinRel2 = BinRel3 = binary_relation() -

Returns the relative product of the converse of the - binary relation BinRel1 and the binary relation BinRel2.

+ binary relation BinRel1 and the binary + relation BinRel2.

 1> R1 = sofs:relation([{1,a},{1,aa},{2,b}]),
 R2 = sofs:relation([{1,u},{2,v},{3,c}]),
@@ -1505,15 +1382,12 @@ true
- restriction(BinRel1, Set) -> BinRel2 + Return a restriction of a binary relation. - - BinRel1 = BinRel2 = binary_relation() - Set = set() -

Returns the restriction of - the binary relation BinRel1 to Set.

+ the binary relation BinRel1 + to Set.

 1> R1 = sofs:relation([{1,a},{2,b},{3,c}]),
 S = sofs:set([1,2,4]),
@@ -1523,15 +1397,12 @@ true
- restriction(SetFun, Set1, Set2) -> Set3 + Return a restriction of a set. - - SetFun = set_fun() - Set1 = Set2 = Set3 = set() - -

Returns a subset of Set1 containing those elements that - yield an element in Set2 as the result of applying SetFun.

+

Returns a subset of Set1 containing those + elements that yield an element in Set2 as the + result of applying SetFun.

 1> S1 = sofs:relation([{1,a},{2,b},{3,c}]),
 S2 = sofs:set([b,c,d]),
@@ -1541,13 +1412,9 @@ true
- set(Terms [, Type]) -> Set + + Create a set of atoms or any type of sets. - - Set = set() - Terms = [term()] - Type = type() -

Creates an unordered set. set(L, T) is equivalent to @@ -1557,18 +1424,16 @@ true - specification(Fun, Set1) -> Set2 + Select a subset using a predicate. - - Fun = spec_fun() - Set1 = Set2 = set() - -

Returns the set containing every element of Set1 for which - Fun returns true. If Fun is a tuple +

Returns the set containing every element + of Set1 for which Fun + returns true. If Fun is a tuple {external, Fun2}, Fun2 is applied to the external set of - each element, otherwise Fun is applied to each element.

+ each element, otherwise Fun is applied to each + element.

 1> R1 = sofs:relation([{a,1},{b,2}]),
 R2 = sofs:relation([{x,1},{x,2},{y,3}]),
@@ -1579,15 +1444,13 @@ true
- strict_relation(BinRel1) -> BinRel2 + Return the strict relation corresponding to a given relation. - - BinRel1 = BinRel2 = binary_relation() -

Returns the strict - relation corresponding to the binary relation BinRel1.

+ relation corresponding to the binary + relation BinRel1.

 1> R1 = sofs:relation([{1,1},{1,2},{2,1},{2,2}]),
 R2 = sofs:strict_relation(R1),
@@ -1596,16 +1459,13 @@ true
- substitution(SetFun, Set1) -> Set2 + Return a function with a given set as domain. - - SetFun = set_fun() - Set1 = Set2 = set() - - -

Returns a function, the domain of which is Set1. The value - of an element of the domain is the result of applying SetFun - to the element.

+ +

Returns a function, the domain of which + is Set1. The value of an element of the domain + is the result of applying SetFun to the + element.

 1> L = [{a,1},{b,2}].
 [{a,1},{b,2}]
@@ -1647,14 +1507,12 @@ images2(SetOfSets, BinRel) ->
       
     
     
-      symdiff(Set1, Set2) -> Set3
+      
       Return the symmetric difference of two sets.
-      
-        Set1 = Set2 = Set3 = set()
-      
       
         

Returns the symmetric - difference (or the Boolean sum) of Set1 and Set2.

+ difference (or the Boolean sum) + of Set1 and Set2.

 1> S1 = sofs:set([1,2,3]),
 S2 = sofs:set([2,3,4]),
@@ -1664,88 +1522,67 @@ images2(SetOfSets, BinRel) ->
       
     
     
-      symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5}
+      
       Return a partition of two sets.
-      
-        Set1 = Set2 = Set3 = Set4 = Set5 = set()
-      
       
-        

Returns a triple of sets: Set3 contains the elements - of Set1 that do not belong to Set2; Set4 contains the - elements of Set1 that belong to Set2; Set5 contains the - elements of Set2 that do not belong to Set1.

+

Returns a triple of sets: Set3 contains the + elements of Set1 that do not belong + to Set2; Set4 contains the + elements of Set1 that belong + to Set2; Set5 contains the + elements of Set2 that do not belong + to Set1.

- to_external(AnySet) -> ExternalSet + Return the elements of a set. - - ExternalSet = external_set() - AnySet = anyset() -

Returns the external set of an atomic, ordered or unordered set.

- to_sets(ASet) -> Sets + Return a list or a tuple of the elements of set. - - ASet = set() | ordset() - Sets = tuple_of(AnySet) | [AnySet] - -

Returns the elements of the ordered set ASet as a tuple of - sets, and the elements of the unordered set ASet as a sorted - list of sets without duplicates.

+

Returns the elements of the ordered set ASet + as a tuple of sets, and the elements of the unordered set + ASet as a sorted list of sets without + duplicates.

- type(AnySet) -> Type + Return the type of a set. - - AnySet = anyset() - Type = type() -

Returns the type of an atomic, ordered or unordered set.

- union(SetOfSets) -> Set + Return the union of a set of sets. - - Set = set() - SetOfSets = set_of_sets() -

Returns the union of the - set of sets SetOfSets.

+ set of sets SetOfSets.

- union(Set1, Set2) -> Set3 + Return the union of two sets. - - Set1 = Set2 = Set3 = set() -

Returns the union of - Set1 and Set2.

+ Set1 and Set2.

- union_of_family(Family) -> Set + Return the union of a family. - - Family = family() - Set = set() -

Returns the union of - the family Family.

+ the family Family. +

 1> F = sofs:family([{a,[0,2,4]},{b,[0,1,2]},{c,[2,3]}]),
 S = sofs:union_of_family(F),
@@ -1754,17 +1591,15 @@ images2(SetOfSets, BinRel) ->
       
     
     
-      weak_relation(BinRel1) -> BinRel2
+      
       Return the weak relation corresponding to 
         a given relation.
-      
-        BinRel1 = BinRel2 = binary_relation()
-      
       
         

Returns a subset S of the weak relation W - corresponding to the binary relation BinRel1. Let F be the - field of BinRel1. The + corresponding to the binary relation BinRel1. + Let F be the field of + BinRel1. The subset S is defined so that x S y if x W y for some x in F and for some y in F.

diff --git a/lib/stdlib/doc/src/specs.xml b/lib/stdlib/doc/src/specs.xml
new file mode 100644
index 0000000000..98338b5ec2
--- /dev/null
+++ b/lib/stdlib/doc/src/specs.xml
@@ -0,0 +1,63 @@
+
+
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+  
+
diff --git a/lib/stdlib/doc/src/string.xml b/lib/stdlib/doc/src/string.xml
index de1b99a2d5..48867ffe72 100644
--- a/lib/stdlib/doc/src/string.xml
+++ b/lib/stdlib/doc/src/string.xml
@@ -4,7 +4,7 @@
 
   
- 19962009 + 19962011 Ericsson AB. All Rights Reserved. @@ -38,65 +38,46 @@ - len(String) -> Length + Return the length of a string - - String = string() - Length = integer() -

Returns the number of characters in the string.

- equal(String1, String2) -> bool() + Test string equality - - String1 = String2 = string() -

Tests whether two strings are equal. Returns true if they are, otherwise false.

- concat(String1, String2) -> String3 + Concatenate two strings - - String1 = String2 = String3 = string() -

Concatenates two strings to form a new string. Returns the new string.

- chr(String, Character) -> Index - rchr(String, Character) -> Index + + Return the index of the first/last occurrence ofCharacterin String - - String = string() - Character = char() - Index = integer() -

Returns the index of the first/last occurrence of - Character in String. 0 is returned if Character does not + Character in String. 0 is returned if Character does not occur.

- str(String, SubString) -> Index - rstr(String, SubString) -> Index + + Find the index of a substring - - String = SubString = string() - Index = integer() -

Returns the position where the first/last occurrence of - SubString begins in String. 0 is returned if SubString - does not exist in String. + SubString begins in String. 0 is returned if SubString + does not exist in String. For example:

> string:str(" Hello Hello World World ", "Hello World"). @@ -104,17 +85,13 @@
- span(String, Chars) -> Length - cspan(String, Chars) -> Length + + Span characters at start of string - - String = Chars = string() - Length = integer() -

Returns the length of the maximum initial segment of - String, which consists entirely of characters from (not - from) Chars.

+ String, which consists entirely of characters from (not + from) Chars.

For example:

> string:span("\t abcdef", " \t"). @@ -124,17 +101,13 @@
- substr(String, Start) -> SubString - substr(String, Start, Length) -> Substring + + Return a substring of String - - String = SubString = string() - Start = Length = integer() - -

Returns a substring of String, starting at the - position Start, and ending at the end of the string or - at length Length.

+

Returns a substring of String, starting at the + position Start, and ending at the end of the string or + at length Length.

For example:

> substr("Hello World", 4, 5). @@ -142,15 +115,11 @@
- tokens(String, SeparatorList) -> Tokens + Split string into tokens - - String = SeparatorList = string() - Tokens = [string()] - -

Returns a list of tokens in String, separated by the - characters in SeparatorList.

+

Returns a list of tokens in String, separated by the + characters in SeparatorList.

For example:

> tokens("abc defxxghix jkl", "x "). @@ -158,15 +127,11 @@
- join(StringList, Separator) -> String + Join a list of strings with separator - - StringList = [string()] - Separator = string() - -

Returns a string with the elements of StringList - separated by the string in Seperator.

+

Returns a string with the elements of StringList + separated by the string in Separator.

For example:

> join(["one", "two", "three"], ", "). @@ -174,44 +139,30 @@
- chars(Character, Number) -> String - chars(Character, Number, Tail) -> String + + Returns a string consisting of numbers of characters - - Character = char() - Number = integer() - String = string() - -

Returns a string consisting of Number of characters - Character. Optionally, the string can end with the - string Tail.

+

Returns a string consisting of Number of characters + Character. Optionally, the string can end with the + string Tail.

- copies(String, Number) -> Copies + Copy a string - - String = Copies = string() - Number = integer() - -

Returns a string containing String repeated - Number times.

+

Returns a string containing String repeated + Number times.

- words(String) -> Count - words(String, Character) -> Count + + Count blank separated words - - String = string() - Character = char() - Count = integer() - -

Returns the number of words in String, separated by - blanks or Character.

+

Returns the number of words in String, separated by + blanks or Character.

For example:

> words(" Hello old boy!", $o). @@ -219,17 +170,12 @@
- sub_word(String, Number) -> Word - sub_word(String, Number, Character) -> Word + + Extract subword - - String = Word = string() - Character = char() - Number = integer() - -

Returns the word in position Number of String. - Words are separated by blanks or Characters.

+

Returns the word in position Number of String. + Words are separated by blanks or Characters.

For example:

> string:sub_word(" Hello old boy !",3,$o). @@ -237,19 +183,14 @@
- strip(String) -> Stripped - strip(String, Direction) -> Stripped - strip(String, Direction, Character) -> Stripped + + + Strip leading or trailing characters - - String = Stripped = string() - Direction = left | right | both - Character = char() -

Returns a string, where leading and/or trailing blanks or a - number of Character have been removed. - Direction can be left, right, or + number of Character have been removed. + Direction can be left, right, or both and indicates from which direction blanks are to be removed. The function strip/1 is equivalent to strip(String, both).

@@ -260,19 +201,14 @@
- left(String, Number) -> Left - left(String, Number, Character) -> Left + + Adjust left end of string - - String = Left = string() - Character = char - Number = integer() - -

Returns the String with the length adjusted in - accordance with Number. The left margin is - fixed. If the length(String) < Number, - String is padded with blanks or Characters.

+

Returns the String with the length adjusted in + accordance with Number. The left margin is + fixed. If the length(String) < Number, + String is padded with blanks or Characters.

For example:

> string:left("Hello",10,$.). @@ -280,19 +216,14 @@
- right(String, Number) -> Right - right(String, Number, Character) -> Right + + Adjust right end of string - - String = Right = string() - Character = char - Number = integer() - -

Returns the String with the length adjusted in - accordance with Number. The right margin is - fixed. If the length of (String) < Number, - String is padded with blanks or Characters.

+

Returns the String with the length adjusted in + accordance with Number. The right margin is + fixed. If the length of (String) < Number, + String is padded with blanks or Characters.

For example:

> string:right("Hello", 10, $.). @@ -300,32 +231,23 @@
- centre(String, Number) -> Centered - centre(String, Number, Character) -> Centered + + Center a string - - String = Centered = string() - Character = char - Number = integer() - -

Returns a string, where String is centred in the +

Returns a string, where String is centred in the string and surrounded by blanks or characters. The resulting - string will have the length Number.

+ string will have the length Number.

- sub_string(String, Start) -> SubString - sub_string(String, Start, Stop) -> SubString + + Extract a substring - - String = SubString = string() - Start = Stop = integer() - -

Returns a substring of String, starting at the - position Start to the end of the string, or to and - including the Stop position.

+

Returns a substring of String, starting at the + position Start to the end of the string, or to and + including the Stop position.

For example:

sub_string("Hello World", 4, 8). @@ -383,15 +305,15 @@ sub_string("Hello World", 4, 8).
- to_lower(String) -> Result - to_lower(Char) -> CharResult - to_upper(String) -> Result - to_upper(Char) -> CharResult + + + + Convert case of string (ISO/IEC 8859-1) - - String = Result = string() - Char = CharResult = integer() - + + + +

The given string or character is case-converted. Note that the supported character set is ISO/IEC 8859-1 (a.k.a. Latin 1), diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index d6203bdaa0..009aa60faa 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -199,51 +199,81 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} + + + + + + +

Not a pid().

+ + + + + + +

A (the argument list) has the value + undefined if Restart is temporary.

+
+
+ + + + + + + + + + + + + + + + + + + - start_link(Module, Args) -> Result - start_link(SupName, Module, Args) -> Result + + Create a supervisor process. - - SupName = {local,Name} | {global,Name} -  Name = atom() - Module = atom() - Args = term() - Result = {ok,Pid} | ignore | {error,Error} -  Pid = pid() -  Error = {already_started,Pid}} | shutdown | term() - + + +

Creates a supervisor process as part of a supervision tree. The function will, among other things, ensure that the supervisor is linked to the calling process (its supervisor).

-

The created supervisor process calls Module:init/1 to +

The created supervisor process calls Module:init/1 to find out about restart strategy, maximum restart frequency and child processes. To ensure a synchronized start-up procedure, start_link/2,3 does not return until - Module:init/1 has returned and all child processes + Module:init/1 has returned and all child processes have been started.

-

If SupName={local,Name} the supervisor is registered +

If SupName={local,Name} the supervisor is registered locally as Name using register/2. If - SupName={global,Name} the supervisor is registered + SupName={global,Name} the supervisor is registered globally as Name using global:register_name/2. If no name is provided, the supervisor is not registered.

-

Module is the name of the callback module.

-

Args is an arbitrary term which is passed as - the argument to Module:init/1.

+

Module is the name of the callback module.

+

Args is an arbitrary term which is passed as + the argument to Module:init/1.

If the supervisor and its child processes are successfully created (i.e. if all child process start functions return {ok,Child}, {ok,Child,Info}, or ignore) the function returns {ok,Pid}, where Pid is the pid of the supervisor. If there already exists a process - with the specified SupName the function returns + with the specified SupName the function returns {error,{already_started,Pid}}, where Pid is the pid of that process.

-

If Module:init/1 returns ignore, this function +

If Module:init/1 returns ignore, this function returns ignore as well and the supervisor terminates with reason normal. - If Module:init/1 fails or returns an incorrect value, + If Module:init/1 fails or returns an incorrect value, this function returns {error,Term} where Term is a term with information about the error, and the supervisor terminates with reason Term.

@@ -255,21 +285,15 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
- start_child(SupRef, ChildSpec) -> Result + Dynamically add a child process to a supervisor. - - SupRef = Name | {Name,Node} | {global,Name} | pid() -  Name = Node = atom() - ChildSpec = child_spec() | [term()] - Result = {ok,Child} | {ok,Child,Info} | {error,Error} -  Child = pid() | undefined -  Info = term() -  Error = already_present | {already_started,Child} | term() - + + +

Dynamically adds a child specification to the supervisor - SupRef which starts the corresponding child process.

-

SupRef can be:

+ SupRef which starts the corresponding child process.

+

SupRef can be:

the pid, Name, if the supervisor is locally registered, @@ -278,26 +302,26 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} {global,Name}, if the supervisor is globally registered. -

ChildSpec should be a valid child specification +

ChildSpec should be a valid child specification (unless the supervisor is a simple_one_for_one supervisor, see below). The child process will be started by using the start function as defined in the child specification.

If the case of a simple_one_for_one supervisor, the child specification defined in Module:init/1 will - be used and ChildSpec should instead be an arbitrary - list of terms List. The child process will then be - started by appending List to the existing start + be used and ChildSpec should instead be an arbitrary + list of terms List. The child process will then be + started by appending List to the existing start function arguments, i.e. by calling - apply(M, F, A++List) where {M,F,A} is the start + apply(M, F, A++List) where {M,F,A} is the start function defined in the child specification.

If there already exists a child specification with - the specified Id, ChildSpec is discarded and + the specified Id, ChildSpec is discarded and the function returns {error,already_present} or - {error,{already_started,Child}}, depending on if + {error,{already_started,Child}}, depending on if the corresponding child process is running or not.

-

If the child process start function returns {ok,Child} - or {ok,Child,Info}, the child specification and pid is +

If the child process start function returns {ok,Child} + or {ok,Child,Info}, the child specification and pid is added to the supervisor and the function returns the same value.

If the child process start function returns ignore, @@ -312,27 +336,20 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} - terminate_child(SupRef, Id) -> Result + Terminate a child process belonging to a supervisor. - - SupRef = Name | {Name,Node} | {global,Name} | pid() -  Name = Node = atom() - Id = pid() | term() - Result = ok | {error,Error} -  Error = not_found | simple_one_for_one - -

Tells the supervisor SupRef to terminate the given +

Tells the supervisor SupRef to terminate the given child.

If the supervisor is not simple_one_for_one, - Id must be the child specification identifier. The + Id must be the child specification identifier. The process, if there is one, is terminated but the child specification is kept by the supervisor. The child process may later be restarted by the supervisor. The child process can also be restarted explicitly by calling restart_child/2. Use delete_child/2 to remove the child specification.

-

If the supervisor is simple_one_for_one, Id +

If the supervisor is simple_one_for_one, Id must be the child process' pid(). I the specified process is alive, but is not a child of the given supervisor, the function will return @@ -340,119 +357,93 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} identifier is given instead instead of a pid(), the function will return {error,simple_one_for_one}.

If successful, the function returns ok. If there is - no child specification with the specified Id, the + no child specification with the specified Id, the function returns {error,not_found}.

See start_child/2 for a description of - SupRef.

+ SupRef.

- delete_child(SupRef, Id) -> Result + Delete a child specification from a supervisor. - - SupRef = Name | {Name,Node} | {global,Name} | pid() -  Name = Node = atom() - Id = term() - Result = ok | {error,Error} -  Error = running | not_found | simple_one_for_one - -

Tells the supervisor SupRef to delete the child - specification identified by Id. The corresponding child +

Tells the supervisor SupRef to delete the child + specification identified by Id. The corresponding child process must not be running, use terminate_child/2 to terminate it.

-

See start_child/2 for a description of SupRef.

+

See start_child/2 for a description of + SupRef.

If successful, the function returns ok. If the child - specification identified by Id exists but + specification identified by Id exists but the corresponding child process is running, the function returns {error,running}. If the child specification - identified by Id does not exist, the function returns + identified by Id does not exist, the function returns {error,not_found}.

- restart_child(SupRef, Id) -> Result + Restart a terminated child process belonging to a supervisor. - - SupRef = Name | {Name,Node} | {global,Name} | pid() -  Name = Node = atom() - Id = term() - Result = {ok,Child} | {ok,Child,Info} | {error,Error} -  Child = pid() | undefined -  Error = running | not_found | simple_one_for_one | term() - -

Tells the supervisor SupRef to restart a child process +

Tells the supervisor SupRef to restart a child process corresponding to the child specification identified by - Id. The child specification must exist and + Id. The child specification must exist and the corresponding child process must not be running.

-

See start_child/2 for a description of SupRef.

-

If the child specification identified by Id does not +

See start_child/2 for a description of + SupRef.

+

If the child specification identified by Id does not exist, the function returns {error,not_found}. If the child specification exists but the corresponding process is already running, the function returns {error,running}.

-

If the child process start function returns {ok,Child} - or {ok,Child,Info}, the pid is added to the supervisor +

If the child process start function returns {ok,Child} + or {ok,Child,Info}, the pid is added to the supervisor and the function returns the same value.

If the child process start function returns ignore, the pid remains set to undefined and the function returns {ok,undefined}.

If the child process start function returns an error tuple or an erroneous value, or if it fails, the function returns - {error,Error} where Error is a term containing + {error,Error} where Error is a term containing information about the error.

- which_children(SupRef) -> [{Id,Child,Type,Modules}] + Return information about all children specifications and child processes belonging to a supervisor. - - SupRef = Name | {Name,Node} | {global,Name} | pid() -  Name = Node = atom() - Id = term() | undefined - Child = pid() | undefined - Type = worker | supervisor - Modules = [Module] | dynamic -  Module = atom() -

Returns a newly created list with information about all child specifications and child processes belonging to - the supervisor SupRef.

+ the supervisor SupRef.

Note that calling this function when supervising a large number of children under low memory conditions can cause an out of memory exception.

-

See start_child/2 for a description of SupRef.

+

See start_child/2 for a description of + SupRef.

The information given for each child specification/process is:

-

Id - as defined in the child specification or +

Id - as defined in the child specification or undefined in the case of a simple_one_for_one supervisor.

-

Child - the pid of the corresponding child +

Child - the pid of the corresponding child process, or undefined if there is no such process.

-

Type - as defined in the child specification.

+

Type - as defined in the child specification.

-

Modules - as defined in the child specification.

+

Modules - as defined in the child specification.

- count_children(SupRef) -> PropListOfCounts + Return counts for the number of childspecs, active children, supervisors and workers. - - SupRef = Name | {Name,Node} | {global,Name} | pid() -  Name = Node = atom() - PropListOfCounts = [{specs, ChildSpecCount}, {active, ActiveProcessCount}, {supervisors, ChildSupervisorCount}, {workers, ChildWorkerCount}] -

Returns a property list (see proplists) containing the counts for each of the following elements of the supervisor's @@ -479,17 +470,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} - check_childspecs([ChildSpec]) -> Result + Check if children specifications are syntactically correct. - - ChildSpec = child_spec() - Result = ok | {error,Error} -  Error = term() -

This function takes a list of child specification as argument and returns ok if all of them are syntactically - correct, or {error,Error} otherwise.

+ correct, or {error,Error} otherwise.

@@ -506,9 +492,9 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} Args = term() Result = {ok,{{RestartStrategy,MaxR,MaxT},[ChildSpec]}} | ignore -  RestartStrategy = one_for_all | one_for_one | rest_for_one | simple_one_for_one -  MaxR = MaxT = int()>=0 -  ChildSpec = child_spec() +  RestartStrategy = strategy() +  MaxR = MaxT = integer()>=0 +  ChildSpec = child_spec()

Whenever a supervisor is started using diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml index cbd0d9230b..c1a5e7947f 100644 --- a/lib/stdlib/doc/src/supervisor_bridge.xml +++ b/lib/stdlib/doc/src/supervisor_bridge.xml @@ -50,46 +50,37 @@ - start_link(Module, Args) -> Result - start_link(SupBridgeName, Module, Args) -> Result + + Create a supervisor bridge process. - - SupBridgeName = {local,Name} | {global,Name} -  Name = atom() - Module = atom() - Args = term() - Result = {ok,Pid} | ignore | {error,Error} -  Pid = pid() -  Error = {already_started,Pid} | term() -

Creates a supervisor_bridge process, linked to the calling - process, which calls Module:init/1 to start the subsystem. + process, which calls Module:init/1 to start the subsystem. To ensure a synchronized start-up procedure, this function does - not return until Module:init/1 has returned.

-

If SupBridgeName={local,Name} the supervisor_bridge is - registered locally as Name using register/2. - If SupBridgeName={global,Name} the supervisor_bridge is - registered globally as Name using + not return until Module:init/1 has returned.

+

If SupBridgeName={local,Name} the supervisor_bridge is + registered locally as Name using register/2. + If SupBridgeName={global,Name} the supervisor_bridge is + registered globally as Name using global:register_name/2. If no name is provided, the supervisor_bridge is not registered. If there already exists a process with the specified - SupBridgeName the function returns - {error,{already_started,Pid}}, where Pid is the pid + SupBridgeName the function returns + {error,{already_started,Pid}}, where Pid is the pid of that process.

-

Module is the name of the callback module.

-

Args is an arbitrary term which is passed as the argument - to Module:init/1.

+

Module is the name of the callback module.

+

Args is an arbitrary term which is passed as the argument + to Module:init/1.

If the supervisor_bridge and the subsystem are successfully - started the function returns {ok,Pid}, where Pid is + started the function returns {ok,Pid}, where Pid is is the pid of the supervisor_bridge.

-

If Module:init/1 returns ignore, this function +

If Module:init/1 returns ignore, this function returns ignore as well and the supervisor_bridge terminates with reason normal. - If Module:init/1 fails or returns an error tuple or an - incorrect value, this function returns {error,Term} where - Term is a term with information about the error, and - the supervisor_bridge terminates with reason Term.

+ If Module:init/1 fails or returns an error tuple or an + incorrect value, this function returns {error,Errorr} where + Error is a term with information about the error, and + the supervisor_bridge terminates with reason Error.

diff --git a/lib/stdlib/doc/src/sys.xml b/lib/stdlib/doc/src/sys.xml index efa8922a9d..073faf2df2 100644 --- a/lib/stdlib/doc/src/sys.xml +++ b/lib/stdlib/doc/src/sys.xml @@ -39,24 +39,12 @@

Functions used for implementation of processes should also understand system messages such as debugging messages and code change. These functions must be used to implement the use of system messages for a process; either directly, or through standard behaviours, such as gen_server.

-

The following types are used in the functions defined below:

- - -

Name = pid() | atom() | {global, atom()}

-
- -

Timeout = int() >= 0 | infinity

-
- -

system_event() = {in, Msg} | {in, Msg, From} | {out, Msg, To} | term()

-
-

The default timeout is 5000 ms, unless otherwise specified. The timeout defines the time period to wait for the process to respond to a request. If the process does not respond, the function evaluates exit({timeout, {M, F, A}}).

-

The functions make reference to a debug structure. +

The functions make reference to a debug structure. The debug structure is a list of dbg_opt(). dbg_opt() is an internal data type used by the handle_system_msg/6 function. No debugging is performed if it is an empty list. @@ -113,20 +101,31 @@ own system events. It is always up to the process itself to format these events.

+ + + + + + + + + +

See above.

+
+ + + +
- log(Name,Flag) - log(Name,Flag,Timeout) -> ok | {ok, [system_event()]} + + Log system events in memory - - Flag = true | {true, N} | false | get | print - N = integer() > 0 -

Turns the logging of system events On or Off. If On, a - maximum of N events are kept in the - debug structure (the default is 10). If Flag is get, a list of all - logged events is returned. If Flag is print, the + maximum of N events are kept in the + debug structure (the default is 10). If Flag is get, a list of all + logged events is returned. If Flag is print, the logged events are printed to standard_io. The events are formatted with a function that is defined by the process that generated the event (with a call to @@ -134,13 +133,9 @@ - log_to_file(Name,Flag) - log_to_file(Name,Flag,Timeout) -> ok | {error, open_file} + + Log system events to the specified file - - Flag = FileName | false - FileName = string() -

Enables or disables the logging of all system events in textual format to the file. The events are formatted with a function that is @@ -149,27 +144,18 @@ - statistics(Name,Flag) - statistics(Name,Flag,Timeout) -> ok | {ok, Statistics} + + Enable or disable the collections of statistics - - Flag = true | false | get - Statistics = [{start_time, {Date1, Time1}}, {current_time, {Date, Time2}}, {reductions, integer()}, {messages_in, integer()}, {messages_out, integer()}] - Date1 = Date2 = {Year, Month, Day} - Time1 = Time2 = {Hour, Min, Sec} - -

Enables or disables the collection of statistics. If Flag is +

Enables or disables the collection of statistics. If Flag is get, the statistical collection is returned.

- trace(Name,Flag) - trace(Name,Flag,Timeout) -> void() + + Print all system events on standard_io - - Flag = boolean() -

Prints all system events on standard_io. The events are formatted with a function that is defined by the process that @@ -178,8 +164,8 @@ - no_debug(Name) - no_debug(Name,Timeout) -> void() + + Turn off debugging

Turns off all debugging for the process. This includes @@ -188,8 +174,8 @@ - suspend(Name) - suspend(Name,Timeout) -> void() + + Suspend the process

Suspends the process. When the process is suspended, it @@ -198,49 +184,37 @@ - resume(Name) - resume(Name,Timeout) -> void() + + Resume a suspended process

Resumes a suspended process.

- change_code(Name, Module, OldVsn, Extra) - change_code(Name, Module, OldVsn, Extra, Timeout) -> ok | {error, Reason} + + Send the code change system message to the process - - OldVsn = undefined | term() - Module = atom() - Extra = term() -

Tells the process to change code. The process must be - suspended to handle this message. The Extra argument is + suspended to handle this message. The Extra argument is reserved for each process to use as its own. The function - Mod:system_code_change/4 is called. OldVsn is - the old version of the Module.

+ Module:system_code_change/4 is called. OldVsn is + the old version of the Module.

- get_status(Name) - get_status(Name,Timeout) -> {status, Pid, {module, Mod}, [PDict, SysState, Parent, Dbg, Misc]} + + Get the status of the process - - PDict = [{Key, Value}] - SysState = running | suspended - Parent = pid() - Dbg = [dbg_opt()] - Misc = term() -

Gets the status of the process.

-

The value of Misc varies for different types of +

The value of Misc varies for different types of processes. For example, a gen_server process returns the callback module's state, and a gen_fsm process returns information such as its current state name. Callback modules for gen_server and gen_fsm can also - customise the value of Misc by exporting + customise the value of Misc by exporting a format_status/2 function that contributes module-specific information; see gen_server:format_status/2 @@ -249,17 +223,9 @@ - install(Name,{Func,FuncState}) - install(Name,{Func,FuncState},Timeout) + + Install a debug function in the process - - Func = dbg_fun() - dbg_fun() = fun(FuncState, Event, ProcState) -> done | NewFuncState - FuncState = term() - Event = system_event() - ProcState = term() - NewFuncState = term() -

This function makes it possible to install other debug functions than the ones defined above. An example of such a @@ -267,22 +233,19 @@ special event and performs some action when the event is generated. This could, for example, be turning on low level tracing.

-

Func is called whenever a system event is +

Func is called whenever a system event is generated. This function should return done, or a new func state. In the first case, the function is removed. It is removed if the function fails.

- remove(Name,Func) - remove(Name,Func,Timeout) -> void() + + Remove a debug function from the process - - Func = dbg_fun() -

Removes a previously installed debug function from the - process. Func must be the same as previously + process. Func must be the same as previously installed.

@@ -296,86 +259,65 @@ - debug_options(Options) -> [dbg_opt()] + Convert a list of options to a debug structure - - Options = [Opt] - Opt = trace | log | statistics | {log_to_file, FileName} | {install, {Func, FuncState}} - Func = dbg_fun() - FuncState = term() -

This function can be used by a process that initiates a debug structure from a list of options. The values of the - Opt argument are the same as the corresponding + Opt argument are the same as the corresponding functions.

- get_debug(Item,Debug,Default) -> term() + Get the data associated with a debug option - - Item = log | statistics - Debug = [dbg_opt()] - Default = term() - -

This function gets the data associated with a debug option. Default is returned if the - Item is not found. Can be +

This function gets the data associated with a debug option. Default is returned if the + Item is not found. Can be used by the process to retrieve debug data for printing before it terminates.

- handle_debug([dbg_opt()],FormFunc,Extra,Event) -> [dbg_opt()] + Generate a system event - - FormFunc = dbg_fun() - Extra = term() - Event = system_event() - -

This function is called by a process when it generates a system event. FormFunc is a formatting function which is called as FormFunc(Device, Event, Extra) in order to print the events, which is necessary if tracing is activated. Extra is any - extra information which the process needs in the format function, for example the name of the process.

+

This function is called by a process when it generates a + system event. FormFunc is a formatting + function which is called as FormFunc(Device, + Event, Extra) in order to print + the events, which is necessary if tracing is activated. + Extra is any extra information which the + process needs in the format function, for example the name + of the process.

- handle_system_msg(Msg,From,Parent,Module,Debug,Misc) + Take care of system messages - - Msg = term() - From = pid() - Parent = pid() - Module = atom() - Debug = [dbg_opt()] - Misc = term() -

This function is used by a process module that wishes to take care of system - messages. The process receives a {system, From, Msg} - message and passes the Msg and From to this + messages. The process receives a {system, From, Msg} + message and passes the Msg and From to this function.

This function never returns. It calls the function - Module:system_continue(Parent, NDebug, Misc) where the + Module:system_continue(Parent, NDebug, Misc) where the process continues the execution, or - Module:system_terminate(Reason, Parent, Debug, Misc) if - the process should terminate. The Module must export + Module:system_terminate(Reason, Parent, Debug, Misc) if + the process should terminate. The Module must export system_continue/3, system_terminate/4, and system_code_change/4 (see below).

-

The Misc argument can be used to save internal data +

The Misc argument can be used to save internal data in a process, for example its state. It is sent to - Module:system_continue/3 or - Module:system_terminate/4

+ Module:system_continue/3 or + Module:system_terminate/4

- print_log(Debug) -> void() + Print the logged events in the debug structure - - Debug = [dbg_opt()] -

Prints the logged system events in the debug structure using FormFunc as defined when the event was @@ -383,11 +325,11 @@ - Mod:system_continue(Parent, Debug, Misc) + Mod:system_continue(Parent, Debug, Misc) -> none() Called when the process should continue its execution Parent = pid() - Debug = [dbg_opt()] + Debug = [dbg_opt()] Misc = term() @@ -397,12 +339,12 @@ - Mod:system_terminate(Reason, Parent, Debug, Misc) + Mod:system_terminate(Reason, Parent, Debug, Misc) -> none() Called when the process should terminate Reason = term() Parent = pid() - Debug = [dbg_opt()] + Debug = [dbg_opt()] Misc = term() diff --git a/lib/stdlib/doc/src/timer.xml b/lib/stdlib/doc/src/timer.xml index cae655f801..b741ab7db1 100644 --- a/lib/stdlib/doc/src/timer.xml +++ b/lib/stdlib/doc/src/timer.xml @@ -49,9 +49,19 @@ as requested.

+ + + +

Time in milliseconds.

+
+ + +

A timer reference.

+
+
- start() -> ok + Start a global timer server (named timer_server).

Starts the timer server. Normally, the server does not need @@ -62,214 +72,181 @@ - apply_after(Time, Module, Function, Arguments) -> {ok, Tref} | {error, Reason} + Apply Module:Function(Arguments)after a specified Time. - - Time = integer() in Milliseconds - Module = Function = atom() - Arguments = [term()] - -

Evaluates apply(M, F, A) after Time amount of time - has elapsed. Returns {ok, TRef}, or {error, Reason}.

+

Evaluates apply(Module, Function, Arguments) after Time amount of time + has elapsed. Returns {ok, TRef}, or {error, Reason}.

- send_after(Time, Pid, Message) -> {ok, TRef} | {error,Reason} - send_after(Time, Message) -> {ok, TRef} | {error,Reason} + + Send Messageto Pidafter a specified Time. - - Time = integer() in Milliseconds - Pid = pid() | atom() - Message = term() - Result = {ok, TRef} | {error, Reason} -

send_after/3 -

Evaluates Pid ! Message after Time amount - of time has elapsed. (Pid can also be an atom of a - registered name.) Returns {ok, TRef}, or - {error, Reason}.

+

Evaluates Pid ! Message after Time amount + of time has elapsed. (Pid can also be an atom of a + registered name.) Returns {ok, TRef}, or + {error, Reason}.

send_after/2 -

Same as send_after(Time, self(), Message).

+

Same as send_after(Time, self(), Message).

- exit_after(Time, Pid, Reason1) -> {ok, TRef} | {error,Reason2} - exit_after(Time, Reason1) -> {ok, TRef} | {error,Reason2} - kill_after(Time, Pid)-> {ok, TRef} | {error,Reason2} - kill_after(Time) -> {ok, TRef} | {error,Reason2} + + + + Send an exit signal with Reasonafter a specified Time. - - Time = integer() in milliseconds - Pid = pid() | atom() - Reason1 = Reason2 = term() -

exit_after/3 -

Send an exit signal with reason Reason1 to Pid - Pid. Returns {ok, TRef}, or - {error, Reason2}.

+

Send an exit signal with reason Reason1 to Pid + Pid. Returns {ok, TRef}, or + {error, Reason2}.

exit_after/2 -

Same as exit_after(Time, self(), Reason1).

+

Same as exit_after(Time, self(), Reason1).

kill_after/2 -

Same as exit_after(Time, Pid, kill).

+

Same as exit_after(Time, Pid, kill).

kill_after/1 -

Same as exit_after(Time, self(), kill).

+

Same as exit_after(Time, self(), kill).

- apply_interval(Time, Module, Function, Arguments) -> {ok, TRef} | {error, Reason} + Evaluate Module:Function(Arguments)repeatedly at intervals of Time. - - Time = integer() in milliseconds - Module = Function = atom() - Arguments = [term()] - -

Evaluates apply(Module, Function, Arguments) repeatedly at - intervals of Time. Returns {ok, TRef}, or - {error, Reason}.

+

Evaluates apply(Module, Function, Arguments) repeatedly at + intervals of Time. Returns {ok, TRef}, or + {error, Reason}.

- send_interval(Time, Pid, Message) -> {ok, TRef} | {error, Reason} - send_interval(Time, Message) -> {ok, TRef} | {error, Reason} + + Send Messagerepeatedly at intervals of Time. - - Time = integer() in milliseconds - Pid = pid() | atom() - Message = term() - Reason = term() -

send_interval/3 -

Evaluates Pid ! Message repeatedly after Time - amount of time has elapsed. (Pid can also be an atom of - a registered name.) Returns {ok, TRef} or - {error, Reason}.

+

Evaluates Pid ! Message repeatedly after Time + amount of time has elapsed. (Pid can also be an atom of + a registered name.) Returns {ok, TRef} or + {error, Reason}.

send_interval/2 -

Same as send_interval(Time, self(), Message).

+

Same as send_interval(Time, self(), Message).

- cancel(TRef) -> {ok, cancel} | {error, Reason} + Cancel a previously requested timeout identified by TRef. -

Cancels a previously requested timeout. TRef is a unique +

Cancels a previously requested timeout. TRef is a unique timer reference returned by the timer function in question. Returns - {ok, cancel}, or {error, Reason} when TRef + {ok, cancel}, or {error, Reason} when TRef is not a timer reference.

- sleep(Time) -> ok + Suspend the calling process for Timeamount of milliseconds. - - Time = integer() in milliseconds or the atom infinity - -

Suspends the process calling this function for Time amount +

Suspends the process calling this function for Time amount of milliseconds and then returns ok, or suspend the process - forever if Time is the atom infinity. Naturally, this + forever if Time is the atom infinity. Naturally, this function does not return immediately.

- tc(Module, Function, Arguments) -> {Time, Value} - tc(Fun, Arguments) -> {Time, Value} + + + Measure the real time it takes to evaluate apply(Module, Function, Arguments) or apply(Fun, Arguments) - - Module = Function = atom() - Fun = fun() - Arguments = [term()] - Time = integer() in microseconds - Value = term() - + In microseconds

tc/3 -

Evaluates apply(Module, Function, Arguments) and measures - the elapsed real time as reported by now/0. - Returns {Time, Value}, where - Time is the elapsed real time in microseconds, - and Value is what is returned from the apply.

+

Evaluates apply(Module, Function, Arguments) and measures + the elapsed real time as reported by os:timestamp/0. + Returns {Time, Value}, where + Time is the elapsed real time in microseconds, + and Value is what is returned from the apply.

tc/2 -

Evaluates apply(Fun, Arguments). Otherwise works +

Evaluates apply(Fun, Arguments). Otherwise works like tc/3.

+ tc/1 + +

Evaluates Fun(). Otherwise works like tc/2.

+
+
- now_diff(T2, T1) -> Tdiff + Calculate time difference between now/0timestamps - - T1 = T2 = {MegaSecs, Secs, MicroSecs} - Tdiff = MegaSecs = Secs = MicroSecs = integer() - + In microseconds -

Calculates the time difference Tdiff = T2 - T1 in - microseconds, where T1 and T2 probably +

Calculates the time difference Tdiff = T2 - T1 in + microseconds, where T1 and T2 probably are timestamp tuples returned from erlang:now/0.

- seconds(Seconds) -> Milliseconds + Convert Secondsto Milliseconds. -

Returns the number of milliseconds in Seconds.

+

Returns the number of milliseconds in Seconds.

- minutes(Minutes) -> Milliseconds + Converts Minutesto Milliseconds. -

Return the number of milliseconds in Minutes.

+

Return the number of milliseconds in Minutes.

- hours(Hours) -> Milliseconds + Convert Hoursto Milliseconds. -

Returns the number of milliseconds in Hours.

+

Returns the number of milliseconds in Hours.

- hms(Hours, Minutes, Seconds) -> Milliseconds + Convert Hours+Minutes+Secondsto Milliseconds. -

Returns the number of milliseconds in Hours + Minutes + Seconds.

+

Returns the number of milliseconds in Hours + Minutes + Seconds.

diff --git a/lib/stdlib/doc/src/unicode.xml b/lib/stdlib/doc/src/unicode.xml index cb1cfa8ed0..d02763f75c 100644 --- a/lib/stdlib/doc/src/unicode.xml +++ b/lib/stdlib/doc/src/unicode.xml @@ -38,50 +38,83 @@

It is recommended to only use external encodings for communication with external entities where this is required. When working inside the Erlang/OTP environment, it is recommended to keep binaries in UTF-8 when representing Unicode characters. Latin1 encoding is supported both for backward compatibility and for communication with external entities not supporting Unicode character sets.

-
- DATA TYPES - - -unicode_binary() = binary() with characters encoded in UTF-8 coding standard -unicode_char() = integer() representing valid unicode codepoint - -chardata() = charlist() | unicode_binary() - -charlist() = [unicode_char() | unicode_binary() | charlist()] - a unicode_binary is allowed as the tail of the list - - -external_unicode_binary() = binary() - with characters coded in a user specified Unicode encoding other - than UTF-8 (UTF-16 or UTF-32) - -external_chardata() = external_charlist() | external_unicode_binary() - -external_charlist() = [unicode_char() | external_unicode_binary() | external_charlist()] - an external_unicode_binary is allowed as the tail of the list - - -latin1_binary() = binary() with characters coded in iso-latin-1 -latin1_char() = integer() representing valid latin1 character (0-255) - -latin1_chardata() = latin1_charlist() | latin1_binary() + + + + + + + + + + +

A binary() with characters encoded in the UTF-8 coding standard.

+
+
+ + + +

An integer() representing a valid unicode codepoint.

+
+
+ + + + + + +

A unicode_binary is allowed as the tail of the list.

+
+
+ + + +

A binary() with characters coded in a user specified Unicode + encoding other than UTF-8 (UTF-16 or UTF-32).

+
+
+ + + + + + +

An external_unicode_binary() is allowed as the tail + of the list.

+
+
+ + +

A binary() with characters coded in iso-latin-1.

+
+
+ + +

An integer() representing valid latin1 + character (0-255).

+
+
+ + + + + +

A latin1_binary() is allowed as the tail of + the list.

+
+
+
-latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] - a latin1_binary is allowed as the tail of the list
-
- bom_to_encoding(Bin) -> {Encoding,Length} + Identify UTF byte order marks in a binary. - - Bin = binary() of byte_size 4 or more - Encoding = latin1 | utf8 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big} - Length = int() - + + A binary() of byte_size 4 or more.

Check for a UTF byte order mark (BOM) in the beginning of a - binary. If the supplied binary Bin begins with a valid + binary. If the supplied binary Bin begins with a valid byte order mark for either UTF-8, UTF-16 or UTF-32, the function returns the encoding identified along with the length of the BOM in bytes.

@@ -90,23 +123,24 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()]
- characters_to_list(Data) -> list() | {error, list(), RestData} | {incomplete, list(), binary()} + Convert a collection of characters to list of Unicode characters - - Data = latin1_chardata() | chardata() | external_chardata() - RestData = latin1_chardata() | chardata() | external_chardata() - -

Same as characters_to_list(Data,unicode).

+

Same as characters_to_list(Data,unicode).

- characters_to_list(Data, InEncoding) -> list() | {error, list(), RestData} | {incomplete, list(), binary()} + characters_to_list(Data, InEncoding) -> Result Convert a collection of characters to list of Unicode characters - Data = latin1_chardata() | chardata() | external_chardata() - RestData = latin1_chardata() | chardata() | external_chardata() - InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big} + Data = latin1_chardata() + | chardata() + | external_chardata() + Result = list() | {error, list(), RestData} | {incomplete, list(), binary()} + RestData = latin1_chardata() + | chardata() + | external_chardata() + InEncoding = encoding() @@ -234,44 +268,42 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] - characters_to_binary(Data) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} - Convert a collection of characters to an UTF-8 binary - Data = latin1_chardata() | chardata() | external_chardata() - RestData = latin1_chardata() | chardata() | external_chardata() - + + Convert a collection of characters to an UTF-8 binary

Same as characters_to_binary(Data, unicode, unicode).

- characters_to_binary(Data,InEncoding) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} - Convert a collection of characters to an UTF-8 binary - Data = latin1_chardata() | chardata() | external_chardata() - RestData = latin1_chardata() | chardata() | external_chardata() - InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big} + characters_to_binary(Data,InEncoding) -> Result + Convert a collection of characters to an UTF-8 binary + + + Data = latin1_chardata() + | chardata() + | external_chardata() + Result = binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} + RestData = latin1_chardata() + | chardata() + | external_chardata() + InEncoding = encoding()

Same as characters_to_binary(Data, InEncoding, unicode).

- characters_to_binary(Data, InEncoding, OutEncoding) -> binary() | {error, binary(), RestData} | {incomplete, binary(), binary()} + Convert a collection of characters to an UTF-8 binary - - Data = latin1_chardata() | chardata() | external_chardata() - RestData = latin1_chardata() | chardata() | external_chardata() - InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big} - OutEncoding = latin1 | unicode | utf8 | utf16 | utf32| {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big} -

This function behaves as characters_to_list/2, but produces an binary instead of a unicode list. The - InEncoding defines how input is to be interpreted if + InEncoding defines how input is to be interpreted if binaries are present in the Data, while - OutEncoding defines in what format output is to be + OutEncoding defines in what format output is to be generated.

The option unicode is an alias for utf8, as this is the @@ -291,17 +323,13 @@ latin1_charlist() = [latin1_char() | latin1_binary() | latin1_charlist()] - encoding_to_bom(InEncoding) -> Bin + Create a binary UTF byte order mark from encoding. - - Bin = binary() of byte_size 4 or less - InEncoding = latin1 | unicode | utf8 | utf16 | utf32 | {utf16,little} | {utf16,big} | {utf32,little} | {utf32,big} - Length = int() - + A binary() of byte_size 4 or more.

Create an UTF byte order mark (BOM) as a binary from the - supplied InEncoding. The BOM is, if supported at all, + supplied InEncoding. The BOM is, if supported at all, expected to be placed first in UTF encoded files or messages.

diff --git a/lib/stdlib/doc/src/win32reg.xml b/lib/stdlib/doc/src/win32reg.xml index 28960cd098..99fd7fdeb6 100644 --- a/lib/stdlib/doc/src/win32reg.xml +++ b/lib/stdlib/doc/src/win32reg.xml @@ -4,7 +4,7 @@
- 20002009 + 20002011 Ericsson AB. All Rights Reserved. @@ -85,14 +85,22 @@ hkdd HKEY_DYN_DATA

For additional information on the Windows registry consult the Win32 Programmer's Reference.

+ + + +

As returned by open/1.

+
+ + + + + + +
- change_key(RegHandle, Key) -> ReturnValue + Move to a key in the registry - - RegHandle = term() - Key = string() -

Changes the current key to another key. Works like cd. The key can be specified as a relative path or as an @@ -100,12 +108,8 @@ hkdd HKEY_DYN_DATA

- change_key_create(RegHandle, Key) -> ReturnValue + Move to a key, create it if it is not there - - RegHandle = term() - Key = string() -

Creates a key, or just changes to it, if it is already there. Works like a combination of mkdir and cd. Calls the Win32 API function @@ -114,23 +118,16 @@ hkdd HKEY_DYN_DATA

- close(RegHandle)-> ReturnValue + Close the registry. - - RegHandle = term() - -

Closes the registry. After that, the RegHandle cannot +

Closes the registry. After that, the RegHandle cannot be used.

- current_key(RegHandle) -> ReturnValue + Return the path to the current key. - - RegHandle = term() - ReturnValue = {ok, string()} -

Returns the path to the current key. This is the equivalent of pwd.

Note that the current key is stored in the driver, and might be @@ -138,12 +135,8 @@ hkdd HKEY_DYN_DATA

- delete_key(RegHandle) -> ReturnValue + Delete the current key - - RegHandle = term() - ReturnValue = ok | {error, ErrorId} -

Deletes the current key, if it is valid. Calls the Win32 API function RegDeleteKey(). Note that this call does not change the current key, @@ -152,12 +145,8 @@ hkdd HKEY_DYN_DATA - delete_value(RegHandle, Name) -> ReturnValue + Delete the named value on the current key. - - RegHandle = term() - ReturnValue = ok | {error, ErrorId} -

Deletes a named value on the current key. The atom default is used for the the default value.

@@ -165,12 +154,8 @@ hkdd HKEY_DYN_DATA
- expand(String) -> ExpandedString + Expand a string with environment variables - - String = string() - ExpandedString = string() -

Expands a string containing environment variables between percent characters. Anything between two % is taken for a environment @@ -180,23 +165,15 @@ hkdd HKEY_DYN_DATA - format_error(ErrorId) -> ErrorString + Convert an POSIX errorcode to a string - - ErrorId = atom() - ErrorString = string() -

Convert an POSIX errorcode to a string (by calling erl_posix_msg:message).

- open(OpenModeList)-> ReturnValue + Open the registry for reading or writing - - OpenModeList = [OpenMode] - OpenMode = read | write -

Opens the registry for reading or writing. The current key will be the root (HKEY_CLASSES_ROOT). The read flag in the mode list can be omitted.

@@ -204,12 +181,8 @@ hkdd HKEY_DYN_DATA
- set_value(RegHandle, Name, Value) -> ReturnValue + Set value at the current registry key with specified name. - - Name = string() | default - Value = string() | integer() | binary() -

Sets the named (or default) value to value. Calls the Win32 API function RegSetValueEx(). The value can be of three types, and @@ -221,13 +194,8 @@ hkdd HKEY_DYN_DATA - sub_keys(RegHandle) -> ReturnValue + Get subkeys to the current key. - - ReturnValue = {ok, SubKeys} | {error, ErrorId} - SubKeys = [SubKey] - SubKey = string() -

Returns a list of subkeys to the current key. Calls the Win32 API function EnumRegKeysEx().

@@ -235,13 +203,8 @@ hkdd HKEY_DYN_DATA
- value(RegHandle, Name) -> ReturnValue + Get the named value on the current key. - - Name = string() | default - ReturnValue = {ok, Value} - Value = string() | integer() | binary() -

Retrieves the named value (or default) on the current key. Registry values of type REG_SZ, are returned as strings. Type REG_DWORD @@ -249,15 +212,8 @@ hkdd HKEY_DYN_DATA - values(RegHandle) -> ReturnValue + Get all values on the current key. - - ReturnValue = {ok, ValuePairs} | {ok, ErrorId} - ValuePairs = [ValuePair] - ValuePair = {Name, Value} - Name = string | default - Value = string() | integer() | binary() -

Retrieves a list of all values on the current key. The values have types corresponding to the registry types, see value. diff --git a/lib/stdlib/doc/src/zip.xml b/lib/stdlib/doc/src/zip.xml index 529a70a23d..b03fc7f4e2 100644 --- a/lib/stdlib/doc/src/zip.xml +++ b/lib/stdlib/doc/src/zip.xml @@ -4,7 +4,7 @@

- 20062010 + 20062011 Ericsson AB. All Rights Reserved. @@ -85,67 +85,55 @@ recreated.

-
- DATA TYPES - -zip_file() -

The record zip_file contains the following fields.

- - name = string() - -

the name of the file

-
- info = file_info() - -

file info as in - file:read_file_info/1

-
- comment = string() - -

the comment for the file in the zip archive

-
- offset = integer() - -

the offset of the file in the zip archive (used internally)

-
- comp_size = integer() - -

the compressed size of the file (the uncompressed size is found - in info)

-
-
- zip_comment -

The record zip_comment just contains the archive comment for - a zip archive

- - comment = string() - -

the comment for the zip archive

-
-
-
+ + + + +

The record zip_comment just contains the archive comment for + a zip archive

+
+
+ + + +

The record zip_file contains the following fields.

+ + name + +

the name of the file

+
+ info + +

file info as in + file:read_file_info/1

+
+ comment + +

the comment for the file in the zip archive

+
+ offset + +

the offset of the file in the zip archive (used internally)

+
+ comp_size + +

the compressed size of the file (the uncompressed size is found + in info)

+
+
+
+
+
- zip(Name, FileList) -> RetValue - zip(Name, FileList, Options) -> RetValue - create(Name, FileList) -> RetValue - create(Name, FileList, Options) -> RetValue + + + + Create a zip archive with options - - Name = filename() - FileList = [FileSpec] - FileSpec = filename() | {filename(), binary()} | {filename(), binary(), #file_info{}} - Options = [Option] - Option = memory | cooked | verbose | {comment, Comment} | {cwd, CWD} | {compress, What} | {uncompress, What} - What = all | [Extension] | {add, [Extension]} | {del, [Extension]} - Extension = string() - Comment = CWD = string() - RetValue = {ok, Name} | {ok, {Name, binary()}} | {error, Reason} - Reason = term() -

The zip function creates a - zip archive containing the files specified in FileList.

+ zip archive containing the files specified in FileList.

As synonyms, the functions create/2 and create/3 are provided, to make it resemble the erl_tar module.

The file-list is a list of files, with paths relative to the @@ -161,9 +149,9 @@ zip_file() .Z, .zip, .zoo, .arc, .lzh, .arj.

It is possible to override the default behavior and - explicitly control what types of files should be - compressed by using the {compress, What} and - {uncompress, What} options. It is possible to have + explicitly control what types of files that should be + compressed by using the {compress, What} and + {uncompress, What} options. It is possible to have several compress and uncompress options. In order to trigger compression of a file, its extension must match with the @@ -191,22 +179,22 @@ zip_file() memory

The output will not be to a file, but instead as a tuple - {FileName, binary()}. The binary will be a full zip + {FileName, binary()}. The binary will be a full zip archive with header, and can be extracted with for instance unzip/2.

- {comment, Comment} + {comment, Comment}

Add a comment to the zip-archive.

- {cwd, CWD} + {cwd, CWD}

Use the given directory as current directory, it will be prepended to file names when adding them, although it will not be in the zip-archive. (Acting like a file:set_cwd/1, but without changing the global cwd property.)

- {compress, What} + {compress, What}

Controls what types of files will be compressed. It is by default set to all. The @@ -215,18 +203,18 @@ zip_file() all

means that all files will be compressed (as long as they pass the uncompress condition).

- [Extension] + [Extension]

means that only files with exactly these extensions will be compressed.

- {add,[Extension]} + {add,[Extension]}

adds these extensions to the list of compress extensions.

- {del,[Extension]} + {del,[Extension]}

deletes these extensions from the list of compress extensions.

- {uncompress, What} + {uncompress, What}

Controls what types of files will be uncompressed. It is by default set to [".Z",".zip",".zoo",".arc",".lzh",".arj"]. @@ -234,13 +222,13 @@ zip_file() all

means that no files will be compressed.

- [Extension] + [Extension]

means that files with these extensions will be uncompressed.

- {add,[Extension]} + {add,[Extension]}

adds these extensions to the list of uncompress extensions.

- {del,[Extension]} + {del,[Extension]}

deletes these extensions from the list of uncompress extensions.

@@ -249,23 +237,11 @@ zip_file() - unzip(Archive) -> RetValue - unzip(Archive, Options) -> RetValue - extract(Archive) -> RetValue - extract(Archive, Options) -> RetValue + + + + Extract files from a zip archive - - Archive = filename() | binary() - Options = [Option] - Option = {file_list, FileList} | keep_old_files | verbose | memory | {file_filter, FileFilter} | {cwd, CWD} - FileList = [filename()] - FileBinList = [{filename(),binary()}] - FileFilter = fun(ZipFile) -> true | false - CWD = string() - ZipFile = zip_file() - RetValue = {ok,FileList} | {ok,FileBinList} | {error, Reason} | {error, {Name, Reason}} - Reason = term() -

The unzip/1 function extracts @@ -273,17 +249,17 @@ zip_file() unzip/2 function provides options to extract some files, and more.

-

If the Archive argument is given as a binary, +

If the Archive argument is given as a binary, the contents of the binary is assumed to be a zip archive, otherwise it should be a filename.

The following options are available:

- {file_list, FileList} + {file_list, FileList}

By default, all files will be extracted from the zip - archive. With the {file_list,FileList} option, + archive. With the {file_list, FileList} option, the unzip/2 function will only extract the files - whose names are included in FileList. The full + whose names are included in FileList. The full paths, including the names of all sub directories within the zip archive, must be specified.

@@ -329,29 +305,29 @@ zip_file()
- foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason} + Fold a function over all files in a zip archive - - Fun = fun(FileInArchive, GetInfo, GetBin, AccIn) -> AccOut - FileInArchive = filename() - GetInfo = fun() -> #file_info{} - GetBin = fun() -> binary() - Acc0 = Acc1 = AccIn = AccOut = term() - Archive = filename() | {filename(), binary()} -

The foldl/3 function - calls Fun(FileInArchive, GetInfo, GetBin, AccIn) on - successive files in the Archive, starting with AccIn - == Acc0. FileInArchive is the name that the file - has in the archive. GetInfo is a fun that returns info - about the the file. GetBin returns the contents of the - file. Both GetInfo and GetBin must be called - within the Fun. Their behavior is undefined if they are - called outside the context of the Fun. The Fun + calls Fun(FileInArchive, GetInfo + , GetBin, AccIn) on + successive files in the Archive, starting with + AccIn + == Acc0. FileInArchive is + the name that the file + has in the archive. GetInfo is a fun that + returns info + about the the file. GetBin returns the contents + of the + file. Both GetInfo and GetBin + must be called + within the Fun. Their behavior is undefined if + they are + called outside the context of the Fun. + The Fun must return a new accumulator which is passed to the next call. foldl/3 returns the final value of the - accumulator. Acc0 is returned if the archive is + accumulator. Acc0 is returned if the archive is empty. It is not necessary to iterate over all files in the archive. The iteration may be ended prematurely in a controlled manner by throwing an exception.

@@ -387,26 +363,16 @@ zip_file()
- list_dir(Archive) -> RetValue - list_dir(Archive, Options) - table(Archive) -> RetValue - table(Archive, Options) + + + + Retrieve the name of all files in a zip archive - - Archive = filename() | binary() - RetValue = {ok, [Comment, Files]} | {error, Reason} - Comment = zip_comment() - Files = [zip_file()] - Options = [Option] - Option = cooked - Reason = term() - -

The -list_dir/1 function retrieves - the names of all files in the zip archive Archive. The - -list_dir/2 function provides options.

+

The list_dir/1 + function retrieves the names of all files in the zip archive + Archive. The + list_dir/2 function provides options.

As synonyms, the functions table/2 and table/3 are provided, to make it resemble the erl_tar module.

The result value is the tuple {ok, List}, where List @@ -425,43 +391,27 @@ zip_file() - t(Archive) + Print the name of each file in a zip archive - - Archive = filename() | binary() | ZipHandle - ZipHandle = pid() - -

The -t/1 function prints the names - of all files in the zip archive Archive to the Erlang shell. +

The t/1 function prints the names + of all files in the zip archive Archive to the Erlang shell. (Similar to "tar t".)

- tt(Archive) + Print name and information for each file in a zip archive - - Archive = filename() | binary() - -

The -tt/1 function prints names and - information about all files in the zip archive Archive to +

The tt/1 function prints names and + information about all files in the zip archive Archive to the Erlang shell. (Similar to "tar tv".)

- zip_open(Archive) -> {ok, ZipHandle} | {error, Reason} - zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason} + + Open an archive and return a handle to it - - Archive = filename() | binary() - Options = [Option] - Options = cooked | memory | {cwd, CWD} - CWD = string() - ZipHandle = pid() -

The zip_open function opens a @@ -472,29 +422,19 @@ zip_file() - zip_list_dir(ZipHandle) -> Result | {error, Reason} + Return a table of files in open zip archive - - Result = [ZipComment, ZipFile...] - ZipComment = #zip_comment{} - ZipFile = #zip_file{} - ZipHandle = pid() - -

The -zip_list_dir/1 function - returns the file list of an open zip archive.

+

The + zip_list_dir/1 function + returns the file list of an open zip archive. The first returned + element is the zip archive comment.

- zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason} - zip_get(FileName, ZipHandle) -> {ok, Result} | {error, Reason} + + Extract files from an open archive - - FileName = filename() - ZipHandle = pid() - Result = filename() | {filename(), binary()} -

The zip_get function extracts @@ -505,11 +445,8 @@ zip_file() - zip_close(ZipHandle) -> ok | {error, einval} + Close an open archive - - ZipHandle = pid() -

The zip_close/1 function closes -- cgit v1.2.3 From d3d5b4fcf3e07c22e61c2c9a410d365178b12945 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 May 2011 14:06:12 +0200 Subject: Calling gen_tcp:connect with option {ip, {127,0,0,1}} results in an exit with reason badarg. Neither SSL nor INETS catches this, resulting in crashes with incomprehensible reasons. OTP-9289 --- lib/inets/doc/src/notes.xml | 37 +++++++++++++++++++++---------- lib/inets/src/http_lib/http_transport.erl | 30 ++++++++++++++++++++++--- lib/inets/src/inets_app/inets.appup.src | 8 +++---- lib/ssl/src/ssl.erl | 4 +++- lib/ssl/vsn.mk | 2 +- 5 files changed, 60 insertions(+), 21 deletions(-) diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index edb994a91b..0926df8581 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -104,18 +104,6 @@

*** POTENTIAL INCOMPATIBILITY ***

- -

[httpc] httpc manager crashes. - When a request results in a retry, the request id will be "reused" - in the previous implementation a race condition could occur causing - the manager to crash.

-

This is now avoided by using proc_lib:init_ack and - gen_server:enter_loop to allow mor advanced initialization of - httpc_handlers without blocking the httpc_manger and eliminating - extra processes that can cause race conditions.

-

Own Id: OTP-9246

-
- @@ -147,6 +135,31 @@

Bernard Duggan

Own Id: OTP-9158

+ + +

[httpc] httpc manager crashes. + When a request results in a retry, the request id will be "reused" + in the previous implementation a race condition could occur causing + the manager to crash.

+

This is now avoided by using proc_lib:init_ack and + gen_server:enter_loop to allow mor advanced initialization of + httpc_handlers without blocking the httpc_manger and eliminating + extra processes that can cause race conditions.

+

Own Id: OTP-9246

+
+ + +

[httpc] Issuing a request (httpc:request) to an + host with the ssl option + {ip, {127,0,0,1}} results in an handler crash. + The reason was that the connect call resulted in an exit with + reason badarg + (this was the same for both ssl and gen_tcp).

+

Exits was not catched. This has now been improved.

+

Own Id: OTP-9289

+

Aux Id: seq11845

+
+ diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 173911b868..8cabfe3c71 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -110,7 +110,17 @@ connect(ip_comm = _SocketType, {Host, Port}, Opts0, Timeout) Opts = [binary, {packet, 0}, {active, false}, {reuseaddr, true} | Opts0], ?hlrt("connect using gen_tcp", [{host, Host}, {port, Port}, {opts, Opts}, {timeout, Timeout}]), - gen_tcp:connect(Host, Port, Opts, Timeout); + try gen_tcp:connect(Host, Port, Opts, Timeout) of + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + catch + exit:{badarg, _} -> + {error, {eoptions, Opts}}; + exit:badarg -> + {error, {eoptions, Opts}} + end; %% Wrapper for backaward compatibillity connect({ssl, SslConfig}, Address, Opts, Timeout) -> @@ -123,7 +133,14 @@ connect({ossl, SslConfig}, {Host, Port}, _, Timeout) -> {port, Port}, {ssl_config, SslConfig}, {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout); + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end; connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig, @@ -132,7 +149,14 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> {port, Port}, {ssl_config, SslConfig}, {timeout, Timeout}]), - ssl:connect(Host, Port, Opts, Timeout). + case (catch ssl:connect(Host, Port, Opts, Timeout)) of + {'EXIT', Reason} -> + {error, {eoptions, Reason}}; + {ok, _} = OK -> + OK; + {error, _} = ERROR -> + ERROR + end. %%------------------------------------------------------------------------- diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 91fb064eec..47f3fbba58 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -20,12 +20,12 @@ [ {"5.5.2", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5.1", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5", @@ -42,12 +42,12 @@ [ {"5.5.2", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5.1", [ - {restart_application, inets} + {restart_application, inets} ] }, {"5.5", diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 7b1fda4cf9..38877ece7e 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -612,8 +612,10 @@ do_new_connect(Address, Port, catch exit:{function_clause, _} -> {error, {eoptions, {cb_info, CbInfo}}}; + exit:badarg -> + {error, {eoptions, {inet_options, UserOpts}}}; exit:{badarg, _} -> - {error,{eoptions, {inet_options, UserOpts}}} + {error, {eoptions, {inet_options, UserOpts}}} end. old_connect(Address, Port, Options, Timeout) -> diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 2f1edfa186..0e80e42637 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 4.1.4 +SSL_VSN = 4.1.5 -- cgit v1.2.3 From 30919cea3c641148389a46e94af20805e55f684c Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Thu, 12 May 2011 14:15:37 +0200 Subject: Calling gen_tcp:connect with option {ip, {127,0,0,1}} results in an exit with reason badarg. Neither SSL nor INETS catches this, resulting in crashes with incomprehensible reasons. OTP-9289 --- lib/ssl/doc/src/notes.xml | 84 ++++++++++++++++++++--------------------------- lib/ssl/src/ssl.appup.src | 6 ++-- 2 files changed, 40 insertions(+), 50 deletions(-) diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 52ee9c086a..b2d17925fd 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -1,4 +1,4 @@ - + @@ -28,59 +28,47 @@ G notes.xml
-

This document describes the changes made to the SSL application. -

- -
SSL 4.1.4 - +

This document describes the changes made to the SSL application.

+ +
+ SSL 4.1.5 +
Improvements and New Features - - -

- Reduced memory footprint of an ssl connection.

-

- Handshake hashes, premaster secret and "public_key_info" - does not need to be saved when the connection has been - established. The own certificate is no longer duplicated - in the state.

-

- Own Id: OTP-9021

-
- -

- Add the option {hibernate_after, int()} to ssl:connect - and ssl:listen

-

- Own Id: OTP-9106

-
-
+ + +

Calling gen_tcp:connect with option {ip, {127,0,0,1}} results in + an exit with reason badarg. Neither SSL nor INETS This was not + catched, resulting in crashes with incomprehensible reasons.

+

Own Id: OTP-9289 Aux Id: seq11845

+
+
- -
- -
SSL 4.1.3 - + +
+ +
+ SSL 4.1.3 +
Fixed Bugs and Malfunctions - - -

- Fixed error in cache-handling fix from ssl-4.1.2

-

- Own Id: OTP-9018 Aux Id: seq11739

-
- -

- Verification of a critical extended_key_usage-extension - corrected

-

- Own Id: OTP-9029 Aux Id: seq11541

-
-
+ + +

+ Fixed error in cache-handling fix from ssl-4.1.2

+

+ Own Id: OTP-9018 Aux Id: seq11739

+
+ +

Verification of a critical extended_key_usage-extension + corrected

+

Own Id: OTP-9029 Aux Id: seq11541

+
+
-
+
-
SSL 4.1.2 +
+ SSL 4.1.2
Fixed Bugs and Malfunctions diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index d3e426f254..cf8867245b 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,17 +1,19 @@ %% -*- erlang -*- {"%VSN%", [ + {"4.1.4", [{restart_application, ssl}]}, {"4.1.3", [{restart_application, ssl}]}, {"4.1.2", [{restart_application, ssl}]}, {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ], [ + {"4.1.4", [{restart_application, ssl}]}, {"4.1.3", [{restart_application, ssl}]}, {"4.1.2", [{restart_application, ssl}]}, {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, + {"4.1", [{restart_application, ssl}]}, {"4.0.1", [{restart_application, ssl}]} ]}. -- cgit v1.2.3 From 76ca320fd37cecdcf225ddcc094bc72a607b0453 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 6 May 2011 15:11:15 +0200 Subject: Types and specifications have been modified and added --- lib/stdlib/include/zip.hrl | 4 +- lib/stdlib/src/array.erl | 111 +++---- lib/stdlib/src/base64.erl | 24 +- lib/stdlib/src/beam_lib.erl | 117 +++++--- lib/stdlib/src/binary.erl | 37 ++- lib/stdlib/src/c.erl | 66 ++-- lib/stdlib/src/calendar.erl | 91 ++++-- lib/stdlib/src/dets.erl | 301 +++++++++++++++++-- lib/stdlib/src/dict.erl | 99 ++++-- lib/stdlib/src/digraph.erl | 159 +++++++--- lib/stdlib/src/digraph_utils.erl | 84 ++++-- lib/stdlib/src/epp.erl | 43 ++- lib/stdlib/src/erl_eval.erl | 134 ++++++++- lib/stdlib/src/erl_expand_records.erl | 6 +- lib/stdlib/src/erl_internal.erl | 39 ++- lib/stdlib/src/erl_lint.erl | 31 ++ lib/stdlib/src/erl_parse.yrl | 39 ++- lib/stdlib/src/erl_pp.erl | 65 ++++ lib/stdlib/src/erl_scan.erl | 115 ++++--- lib/stdlib/src/ets.erl | 148 ++++++--- lib/stdlib/src/file_sorter.erl | 119 +++++++- lib/stdlib/src/filelib.erl | 39 ++- lib/stdlib/src/filename.erl | 84 ++++-- lib/stdlib/src/gb_sets.erl | 147 ++++++--- lib/stdlib/src/gb_trees.erl | 109 +++++-- lib/stdlib/src/gen_event.erl | 11 +- lib/stdlib/src/io.erl | 195 ++++++++---- lib/stdlib/src/io_lib.erl | 78 +++-- lib/stdlib/src/io_lib_fread.erl | 41 ++- lib/stdlib/src/lib.erl | 20 +- lib/stdlib/src/lists.erl | 425 +++++++++++++++++++++----- lib/stdlib/src/log_mf_h.erl | 21 +- lib/stdlib/src/ms_transform.erl | 16 + lib/stdlib/src/orddict.erl | 98 ++++-- lib/stdlib/src/ordsets.erl | 72 ++++- lib/stdlib/src/pg.erl | 28 +- lib/stdlib/src/pool.erl | 24 +- lib/stdlib/src/proc_lib.erl | 138 +++++++-- lib/stdlib/src/proplists.erl | 151 +++++----- lib/stdlib/src/qlc.erl | 304 +++++++++++++++++-- lib/stdlib/src/qlc_pt.erl | 18 +- lib/stdlib/src/queue.erl | 123 ++++++-- lib/stdlib/src/random.erl | 24 +- lib/stdlib/src/re.erl | 47 +++ lib/stdlib/src/regexp.erl | 69 ++++- lib/stdlib/src/sets.erl | 74 +++-- lib/stdlib/src/shell.erl | 23 +- lib/stdlib/src/slave.erl | 52 +++- lib/stdlib/src/sofs.erl | 545 +++++++++++++++++++++++++++------- lib/stdlib/src/string.erl | 175 ++++++++--- lib/stdlib/src/supervisor.erl | 98 ++++-- lib/stdlib/src/supervisor_bridge.erl | 18 +- lib/stdlib/src/sys.erl | 182 +++++++++++- lib/stdlib/src/timer.erl | 142 +++++++-- lib/stdlib/src/unicode.erl | 51 +++- lib/stdlib/src/win32reg.erl | 70 +++-- lib/stdlib/src/zip.erl | 185 +++++++++++- lib/stdlib/test/erl_eval_SUITE.erl | 2 +- lib/stdlib/test/erl_pp_SUITE.erl | 2 +- lib/stdlib/test/sofs_SUITE.erl | 27 +- 60 files changed, 4616 insertions(+), 1144 deletions(-) diff --git a/lib/stdlib/include/zip.hrl b/lib/stdlib/include/zip.hrl index 2b5ddc1dfe..6e3ed9c78a 100644 --- a/lib/stdlib/include/zip.hrl +++ b/lib/stdlib/include/zip.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. 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 @@ -19,7 +19,7 @@ -record(zip_file, { name :: string(), % file name - info :: #file_info{}, + info :: file:file_info(), comment :: string(), % zip file comment offset :: non_neg_integer(), % offset of file's local header in archive comp_size :: non_neg_integer() % compressed size diff --git a/lib/stdlib/src/array.erl b/lib/stdlib/src/array.erl index 83576c9fd3..2f69e2b0a4 100644 --- a/lib/stdlib/src/array.erl +++ b/lib/stdlib/src/array.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. 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 @@ -163,17 +163,17 @@ -type array_indx() :: non_neg_integer(). --type array_opt() :: 'fixed' | non_neg_integer() - | {'default', term()} | {'fixed', boolean()} - | {'size', non_neg_integer()}. +-type array_opt() :: {'fixed', boolean()} | 'fixed' + | {'default', Value :: term()} + | {'size', N :: non_neg_integer()} + | (N :: non_neg_integer()). -type array_opts() :: array_opt() | [array_opt()]. --type indx_pair() :: {array_indx(), term()}. +-type indx_pair() :: {Index :: array_indx(), Value :: term()}. -type indx_pairs() :: [indx_pair()]. %%-------------------------------------------------------------------------- -%% @spec () -> array() %% @doc Create a new, extendible array with initial size zero. %% @equiv new([]) %% @@ -185,7 +185,6 @@ new() -> new([]). -%% @spec (Options::term()) -> array() %% @doc Create a new array according to the given options. By default, %% the array is extendible and has initial size zero. Array indices %% start at 0. @@ -224,12 +223,11 @@ new() -> %% @see from_list/2 %% @see fix/1 --spec new(array_opts()) -> array(). +-spec new(Options :: array_opts()) -> array(). new(Options) -> new_0(Options, 0, false). -%% @spec (Size::integer(), Options::term()) -> array() %% @doc Create a new array according to the given size and options. If %% `Size' is not a nonnegative integer, the call fails with reason %% `badarg'. By default, the array has fixed size. Note that any size @@ -245,7 +243,7 @@ new(Options) -> %% %% @see new/1 --spec new(non_neg_integer(), array_opts()) -> array(). +-spec new(Size :: non_neg_integer(), Options :: array_opts()) -> array(). new(Size, Options) when is_integer(Size), Size >= 0 -> new_0(Options, Size, true); @@ -293,13 +291,12 @@ find_max(_I, M) -> M. -%% @spec (X::term()) -> boolean() %% @doc Returns `true' if `X' appears to be an array, otherwise `false'. %% Note that the check is only shallow; there is no guarantee that `X' %% is a well-formed array representation even if this function returns %% `true'. --spec is_array(term()) -> boolean(). +-spec is_array(X :: term()) -> boolean(). is_array(#array{size = Size, max = Max}) when is_integer(Size), is_integer(Max) -> @@ -308,25 +305,23 @@ is_array(_) -> false. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array. Entries are numbered %% from 0 to `size(Array)-1'; hence, this is also the index of the first %% entry that is guaranteed to not have been previously set. %% @see set/3 %% @see sparse_size/1 --spec size(array()) -> non_neg_integer(). +-spec size(Array :: array()) -> non_neg_integer(). size(#array{size = N}) -> N; size(_) -> erlang:error(badarg). -%% @spec (array()) -> term() %% @doc Get the value used for uninitialized entries. %% %% @see new/2 --spec default(array()) -> term(). +-spec default(Array :: array()) -> term(). default(#array{default = D}) -> D; default(_) -> erlang:error(badarg). @@ -405,23 +400,21 @@ new_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Fix the size of the array. This prevents it from growing %% automatically upon insertion; see also {@link set/3}. %% @see relax/1 --spec fix(array()) -> array(). +-spec fix(Array :: array()) -> array(). fix(#array{}=A) -> A#array{max = 0}. -%% @spec (array()) -> boolean() %% @doc Check if the array has fixed size. %% Returns `true' if the array is fixed, otherwise `false'. %% @see fix/1 --spec is_fix(array()) -> boolean(). +-spec is_fix(Array :: array()) -> boolean(). is_fix(#array{max = 0}) -> true; is_fix(#array{}) -> false. @@ -455,12 +448,11 @@ fix_test_() -> -endif. -%% @spec (array()) -> array() %% @doc Make the array resizable. (Reverses the effects of {@link %% fix/1}.) %% @see fix/1 --spec relax(array()) -> array(). +-spec relax(Array :: array()) -> array(). relax(#array{size = N}=A) -> A#array{max = find_max(N-1, ?LEAFSIZE)}. @@ -481,12 +473,11 @@ relax_test_() -> -endif. -%% @spec (integer(), array()) -> array() %% @doc Change the size of the array. If `Size' is not a nonnegative %% integer, the call fails with reason `badarg'. If the given array has %% fixed size, the resulting array will also have fixed size. --spec resize(non_neg_integer(), array()) -> array(). +-spec resize(Size :: non_neg_integer(), Array :: array()) -> array(). resize(Size, #array{size = N, max = M, elements = E}=A) when is_integer(Size), Size >= 0 -> @@ -510,8 +501,6 @@ resize(_Size, _) -> erlang:error(badarg). -%% @spec (array()) -> array() - %% @doc Change the size of the array to that reported by {@link %% sparse_size/1}. If the given array has fixed size, the resulting %% array will also have fixed size. @@ -519,7 +508,7 @@ resize(_Size, _) -> %% @see resize/2 %% @see sparse_size/1 --spec resize(array()) -> array(). +-spec resize(Array :: array()) -> array(). resize(Array) -> resize(sparse_size(Array), Array). @@ -559,7 +548,6 @@ resize_test_() -> -endif. -%% @spec (integer(), term(), array()) -> array() %% @doc Set entry `I' of the array to `Value'. If `I' is not a %% nonnegative integer, or if the array has fixed size and `I' is larger %% than the maximum index, the call fails with reason `badarg'. @@ -570,7 +558,7 @@ resize_test_() -> %% @see get/2 %% @see reset/2 --spec set(array_indx(), term(), array()) -> array(). +-spec set(I :: array_indx(), Value :: term(), Array :: array()) -> array(). set(I, Value, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -624,7 +612,6 @@ expand(I, _S, X, D) -> setelement(I+1, ?NEW_LEAF(D), X). -%% @spec (integer(), array()) -> term() %% @doc Get the value of entry `I'. If `I' is not a nonnegative %% integer, or if the array has fixed size and `I' is larger than the %% maximum index, the call fails with reason `badarg'. @@ -634,7 +621,7 @@ expand(I, _S, X, D) -> %% @see set/3 --spec get(array_indx(), array()) -> term(). +-spec get(I :: array_indx(), Array :: array()) -> term(). get(I, #array{size = N, max = M, elements = E, default = D}) when is_integer(I), I >= 0 -> @@ -660,7 +647,6 @@ get_1(I, E, _D) -> element(I+1, E). -%% @spec (integer(), array()) -> array() %% @doc Reset entry `I' to the default value for the array. %% If the value of entry `I' is the default value the array will be %% returned unchanged. Reset will never change size of the array. @@ -675,7 +661,7 @@ get_1(I, E, _D) -> %% TODO: a reset_range function --spec reset(array_indx(), array()) -> array(). +-spec reset(I :: array_indx(), Array :: array()) -> array(). reset(I, #array{size = N, max = M, default = D, elements = E}=A) when is_integer(I), I >= 0 -> @@ -756,13 +742,12 @@ set_get_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list. %% %% @see from_list/2 %% @see sparse_to_list/1 --spec to_list(array()) -> list(). +-spec to_list(Array :: array()) -> list(). to_list(#array{size = 0}) -> []; @@ -831,12 +816,11 @@ to_list_test_() -> -endif. -%% @spec (array()) -> list() %% @doc Converts the array to a list, skipping default-valued entries. %% %% @see to_list/1 --spec sparse_to_list(array()) -> list(). +-spec sparse_to_list(Array :: array()) -> list(). sparse_to_list(#array{size = 0}) -> []; @@ -901,15 +885,13 @@ sparse_to_list_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_list(List, undefined) --spec from_list(list()) -> array(). +-spec from_list(List :: list()) -> array(). from_list(List) -> from_list(List, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert a list to an extendible array. `Default' is used as the value %% for uninitialized entries of the array. If `List' is not a proper list, %% the call fails with reason `badarg'. @@ -917,7 +899,7 @@ from_list(List) -> %% @see new/2 %% @see to_list/1 --spec from_list(list(), term()) -> array(). +-spec from_list(List :: list(), Default :: term()) -> array(). from_list([], Default) -> new({default,Default}); @@ -1011,13 +993,12 @@ from_list_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}'. %% %% @see from_orddict/2 %% @see sparse_to_orddict/1 --spec to_orddict(array()) -> indx_pairs(). +-spec to_orddict(Array :: array()) -> indx_pairs(). to_orddict(#array{size = 0}) -> []; @@ -1104,13 +1085,12 @@ to_orddict_test_() -> -endif. -%% @spec (array()) -> [{Index::integer(), Value::term()}] %% @doc Convert the array to an ordered list of pairs `{Index, Value}', %% skipping default-valued entries. %% %% @see to_orddict/1 --spec sparse_to_orddict(array()) -> indx_pairs(). +-spec sparse_to_orddict(Array :: array()) -> indx_pairs(). sparse_to_orddict(#array{size = 0}) -> []; @@ -1188,15 +1168,13 @@ sparse_to_orddict_test_() -> -endif. -%% @spec (list()) -> array() %% @equiv from_orddict(Orddict, undefined) --spec from_orddict(indx_pairs()) -> array(). +-spec from_orddict(Orddict :: indx_pairs()) -> array(). from_orddict(Orddict) -> from_orddict(Orddict, undefined). -%% @spec (list(), term()) -> array() %% @doc Convert an ordered list of pairs `{Index, Value}' to a %% corresponding extendible array. `Default' is used as the value for %% uninitialized entries of the array. If `List' is not a proper, @@ -1206,7 +1184,7 @@ from_orddict(Orddict) -> %% @see new/2 %% @see to_orddict/1 --spec from_orddict(indx_pairs(), term()) -> array(). +-spec from_orddict(Orddict :: indx_pairs(), Default :: term()) -> array(). from_orddict([], Default) -> new({default,Default}); @@ -1392,7 +1370,6 @@ from_orddict_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array. The %% elements are visited in order from the lowest index to the highest. @@ -1402,7 +1379,8 @@ from_orddict_test_() -> %% @see foldr/3 %% @see sparse_map/2 --spec map(fun((array_indx(), _) -> _), array()) -> array(). +-spec map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1485,7 +1463,6 @@ map_test_() -> -endif. -%% @spec (Function, array()) -> array() %% Function = (Index::integer(), Value::term()) -> term() %% @doc Map the given function onto each element of the array, skipping %% default-valued entries. The elements are visited in order from the @@ -1494,7 +1471,8 @@ map_test_() -> %% %% @see map/2 --spec sparse_map(fun((array_indx(), _) -> _), array()) -> array(). +-spec sparse_map(Function, Array :: array()) -> array() when + Function :: fun((Index :: array_indx(), Value :: _) -> _). sparse_map(Function, Array=#array{size = N, elements = E, default = D}) when is_function(Function, 2) -> @@ -1580,9 +1558,6 @@ sparse_map_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value. The elements are visited in order from the %% lowest index to the highest. If `Function' is not a function, the @@ -1592,7 +1567,8 @@ sparse_map_test_() -> %% @see map/2 %% @see sparse_foldl/3 --spec foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1656,9 +1632,6 @@ foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array using the given function and %% initial accumulator value, skipping default-valued entries. The %% elements are visited in order from the lowest index to the highest. @@ -1667,7 +1640,8 @@ foldl_test_() -> %% @see foldl/3 %% @see sparse_foldr/3 --spec sparse_foldl(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldl(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldl(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1735,9 +1709,6 @@ sparse_foldl_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value. The elements are visited in %% order from the highest index to the lowest. If `Function' is not a @@ -1746,7 +1717,8 @@ sparse_foldl_test_() -> %% @see foldl/3 %% @see map/2 --spec foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1815,9 +1787,6 @@ foldr_test_() -> -endif. -%% @spec (Function, InitialAcc::term(), array()) -> term() -%% Function = (Index::integer(), Value::term(), Acc::term()) -> -%% term() %% @doc Fold the elements of the array right-to-left using the given %% function and initial accumulator value, skipping default-valued %% entries. The elements are visited in order from the highest index to @@ -1827,7 +1796,8 @@ foldr_test_() -> %% @see foldr/3 %% @see sparse_foldl/3 --spec sparse_foldr(fun((array_indx(), _, A) -> B), A, array()) -> B. +-spec sparse_foldr(Function, InitialAcc :: A, Array :: array()) -> B when + Function :: fun((Index :: array_indx(), Value :: _, Acc :: A) -> B). sparse_foldr(Function, A, #array{size = N, elements = E, default = D}) when is_function(Function, 3) -> @@ -1870,7 +1840,6 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> end. -%% @spec (array()) -> integer() %% @doc Get the number of entries in the array up until the last %% non-default valued entry. In other words, returns `I+1' if `I' is the %% last non-default valued entry in the array, or zero if no such entry @@ -1878,7 +1847,7 @@ sparse_foldr_3(I, T, Ix, A, F, D) -> %% @see size/1 %% @see resize/1 --spec sparse_size(array()) -> non_neg_integer(). +-spec sparse_size(Array :: array()) -> non_neg_integer(). sparse_size(A) -> F = fun (I, _V, _A) -> throw({value, I}) end, diff --git a/lib/stdlib/src/base64.erl b/lib/stdlib/src/base64.erl index a14a72ac6d..5d800e87b8 100644 --- a/lib/stdlib/src/base64.erl +++ b/lib/stdlib/src/base64.erl @@ -38,7 +38,9 @@ %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode_to_string(string() | binary()) -> ascii_string(). +-spec encode_to_string(Data) -> Base64String when + Data :: string() | binary(), + Base64String :: ascii_string(). encode_to_string(Bin) when is_binary(Bin) -> encode_to_string(binary_to_list(Bin)); @@ -53,7 +55,9 @@ encode_to_string(List) when is_list(List) -> %% Description: Encodes a plain ASCII string (or binary) into base64. %%------------------------------------------------------------------------- --spec encode(string() | binary()) -> binary(). +-spec encode(Data) -> Base64 when + Data :: string() | binary(), + Base64 :: binary(). encode(Bin) when is_binary(Bin) -> encode_binary(Bin); @@ -102,14 +106,18 @@ encode_binary(Bin) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode(string() | binary()) -> binary(). +-spec decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). decode(Bin) when is_binary(Bin) -> decode_binary(<<>>, Bin); decode(List) when is_list(List) -> list_to_binary(decode_l(List)). --spec mime_decode(string() | binary()) -> binary(). +-spec mime_decode(Base64) -> Data when + Base64 :: string() | binary(), + Data :: binary(). mime_decode(Bin) when is_binary(Bin) -> mime_decode_binary(<<>>, Bin); @@ -139,14 +147,18 @@ mime_decode_l(List) -> %% whereas decode crashes if an illegal character is found %%------------------------------------------------------------------------- --spec decode_to_string(string() | binary()) -> string(). +-spec decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). decode_to_string(Bin) when is_binary(Bin) -> decode_to_string(binary_to_list(Bin)); decode_to_string(List) when is_list(List) -> decode_l(List). --spec mime_decode_to_string(string() | binary()) -> string(). +-spec mime_decode_to_string(Base64) -> DataString when + Base64 :: string() | binary(), + DataString :: string(). mime_decode_to_string(Bin) when is_binary(Bin) -> mime_decode_to_string(binary_to_list(Bin)); diff --git a/lib/stdlib/src/beam_lib.erl b/lib/stdlib/src/beam_lib.erl index 74d4ad3da7..d9c645d787 100644 --- a/lib/stdlib/src/beam_lib.erl +++ b/lib/stdlib/src/beam_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-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 @@ -54,13 +54,9 @@ -type beam() :: module() | file:filename() | binary(). -%% XXX: THE FOLLOWING SHOULD BE IMPORTED FROM SOMEWHERE ELSE --type forms() :: term(). +-type forms() :: [erl_parse:abstract_form()]. --type abst_vsn() :: atom(). --type abst_code() :: {abst_vsn(), forms()} | 'no_abstract_code'. --type attribute() :: atom(). --type attrvalue() :: term(). +-type abst_code() :: {AbstVersion :: atom(), forms()} | 'no_abstract_code'. -type dataB() :: binary(). -type index() :: non_neg_integer(). -type label() :: integer(). @@ -74,9 +70,9 @@ | 'atoms'. -type chunkref() :: chunkname() | chunkid(). --type attrib_entry() :: {attribute(), [attrvalue()]}. --type compinfo_entry() :: {atom(), term()}. --type labeled_entry() :: {atom(), arity(), label()}. +-type attrib_entry() :: {Attribute :: atom(), [AttributeValue :: term()]}. +-type compinfo_entry() :: {InfoKey :: atom(), term()}. +-type labeled_entry() :: {Function :: atom(), arity(), label()}. -type chunkdata() :: {chunkid(), dataB()} | {'abstract_code', abst_code()} @@ -85,20 +81,17 @@ | {'exports', [{atom(), arity()}]} | {'labeled_exports', [labeled_entry()]} | {'imports', [mfa()]} - | {'indexed_imports', [{index(), module(), atom(), arity()}]} + | {'indexed_imports', [{index(), module(), Function :: atom(), arity()}]} | {'locals', [{atom(), arity()}]} | {'labeled_locals', [labeled_entry()]} | {'atoms', [{integer(), atom()}]}. --type info_pair() :: {'file', file:filename()} - | {'binary', binary()} - | {'module', module()} - | {'chunks', [{chunkid(), integer(), integer()}]}. - %% Error reasons -type info_rsn() :: {'chunk_too_big', file:filename(), - chunkid(), integer(), integer()} - | {'invalid_beam_file', file:filename(), integer()} + chunkid(), ChunkSize :: non_neg_integer(), + FileSize :: non_neg_integer()} + | {'invalid_beam_file', file:filename(), + Position :: non_neg_integer()} | {'invalid_chunk', file:filename(), chunkid()} | {'missing_chunk', file:filename(), chunkid()} | {'not_a_beam_file', file:filename()} @@ -118,20 +111,34 @@ %% Exported functions %% --spec info(beam()) -> [info_pair()] | {'error', 'beam_lib', info_rsn()}. +-spec info(Beam) -> [InfoPair] | {'error', 'beam_lib', info_rsn()} when + Beam :: beam(), + InfoPair :: {'file', Filename :: file:filename()} + | {'binary', Binary :: binary()} + | {'module', Module :: module()} + | {'chunks', [{ChunkId :: chunkid(), + Pos :: non_neg_integer(), + Size :: non_neg_integer()}]}. info(File) -> read_info(beam_filename(File)). --spec chunks(beam(), [chunkref()]) -> - {'ok', {module(), [chunkdata()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs) -> + {'ok', {module(), [chunkdata()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()]. chunks(File, Chunks) -> read_chunk_data(File, Chunks). --spec chunks(beam(), [chunkref()], ['allow_missing_chunks']) -> - {'ok', {module(), [{chunkref(), chunkdata() | 'missing_chunk'}]}} - | {'error', 'beam_lib', chnk_rsn()}. +-spec chunks(Beam, ChunkRefs, Options) -> + {'ok', {module(), [ChunkResult]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + ChunkRefs :: [chunkref()], + Options :: ['allow_missing_chunks'], + ChunkResult :: chunkdata() | {ChunkRef :: chunkref(), 'missing_chunk'}. chunks(File, Chunks, Options) -> try read_chunk_data(File, Chunks, Options) @@ -142,49 +149,65 @@ chunks(File, Chunks, Options) -> all_chunks(File) -> read_all_chunks(File). --spec cmp(beam(), beam()) -> 'ok' | {'error', 'beam_lib', cmp_rsn()}. +-spec cmp(Beam1, Beam2) -> 'ok' | {'error', 'beam_lib', cmp_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). cmp(File1, File2) -> try cmp_files(File1, File2) catch Error -> Error end. --spec cmp_dirs(atom() | file:filename(), atom() | file:filename()) -> - {[file:filename()], [file:filename()], - [{file:filename(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec cmp_dirs(Dir1, Dir2) -> + {Only1, Only2, Different} | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Only1 :: [file:filename()], + Only2 :: [file:filename()], + Different :: [{Filename1 :: file:filename(), Filename2 :: file:filename()}], + Reason :: {'not_a_directory', term()} | info_rsn(). cmp_dirs(Dir1, Dir2) -> catch compare_dirs(Dir1, Dir2). --spec diff_dirs(atom() | file:filename(), atom() | file:filename()) -> - 'ok' | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. +-spec diff_dirs(Dir1, Dir2) -> 'ok' | {'error', 'beam_lib', Reason} when + Dir1 :: atom() | file:filename(), + Dir2 :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). diff_dirs(Dir1, Dir2) -> catch diff_directories(Dir1, Dir2). --spec strip(beam()) -> - {'ok', {module(), beam()}} | {'error', 'beam_lib', info_rsn()}. +-spec strip(Beam1) -> + {'ok', {module(), Beam2}} | {'error', 'beam_lib', info_rsn()} when + Beam1 :: beam(), + Beam2 :: beam(). strip(FileName) -> try strip_file(FileName) catch Error -> Error end. --spec strip_files([beam()]) -> - {'ok', [{module(), beam()}]} | {'error', 'beam_lib', info_rsn()}. +-spec strip_files(Files) -> + {'ok', [{module(), Beam}]} | {'error', 'beam_lib', info_rsn()} when + Files :: [beam()], + Beam :: beam(). strip_files(Files) when is_list(Files) -> try strip_fils(Files) catch Error -> Error end. --spec strip_release(atom() | file:filename()) -> +-spec strip_release(Dir) -> {'ok', [{module(), file:filename()}]} - | {'error', 'beam_lib', {'not_a_directory', term()} | info_rsn()}. + | {'error', 'beam_lib', Reason} when + Dir :: atom() | file:filename(), + Reason :: {'not_a_directory', term()} | info_rsn(). strip_release(Root) -> catch strip_rel(Root). --spec version(beam()) -> - {'ok', {module(), [term()]}} | {'error', 'beam_lib', chnk_rsn()}. +-spec version(Beam) -> + {'ok', {module(), [Version :: term()]}} | + {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(). version(File) -> case catch read_chunk_data(File, [attributes]) of @@ -195,8 +218,10 @@ version(File) -> Error end. --spec md5(beam()) -> - {'ok', {module(), binary()}} | {'error', 'beam_lib', chnk_rsn()}. +-spec md5(Beam) -> + {'ok', {module(), MD5}} | {'error', 'beam_lib', chnk_rsn()} when + Beam :: beam(), + MD5 :: binary(). md5(File) -> case catch read_significant_chunks(File) of @@ -207,7 +232,8 @@ md5(File) -> Error end. --spec format_error(term()) -> [char() | string()]. +-spec format_error(Reason) -> io_lib:chars() when + Reason :: term(). format_error({error, Error}) -> format_error(Error); @@ -260,12 +286,15 @@ format_error(E) -> | {'debug_info', mode(), module(), file:filename()}. -type crypto_fun() :: fun((crypto_fun_arg()) -> term()). --spec crypto_key_fun(crypto_fun()) -> 'ok' | {'error', term()}. +-spec crypto_key_fun(CryptoKeyFun) -> 'ok' | {'error', Reason} when + CryptoKeyFun :: crypto_fun(), + Reason :: badfun | exists | term(). crypto_key_fun(F) -> call_crypto_server({crypto_key_fun, F}). --spec clear_crypto_key_fun() -> 'undefined' | {'ok', term()}. +-spec clear_crypto_key_fun() -> 'undefined' | {'ok', Result} when + Result :: 'undefined' | term(). clear_crypto_key_fun() -> call_crypto_server(clear_crypto_key_fun). diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl index f6489788b2..cb1e12ae46 100644 --- a/lib/stdlib/src/binary.erl +++ b/lib/stdlib/src/binary.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-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,11 +38,28 @@ %% Implemented in this module: -export([split/2,split/3,replace/3,replace/4]). +-opaque cp() :: tuple(). +-type part() :: {Start :: non_neg_integer(), Length :: integer()}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% split %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec split(Subject, Pattern) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Parts :: [binary()]. + split(H,N) -> split(H,N,[]). + +-spec split(Subject, Pattern, Options) -> Parts when + Subject :: binary(), + Pattern :: binary() | [binary()] | cp(), + Options :: [Option], + Option :: {scope, part()} | trim | global, + Parts :: [binary()]. + split(Haystack,Needles,Options) -> try {Part,Global,Trim} = get_opts_split(Options,{no,false,false}), @@ -89,8 +106,26 @@ do_split(H,[{A,B}|T],N,Trim) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% replace %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-spec replace(Subject, Pattern, Replacement) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Result :: binary(). + replace(H,N,R) -> replace(H,N,R,[]). + +-spec replace(Subject, Pattern, Replacement, Options) -> Result when + Subject :: binary(), + Pattern :: binary() | [ binary() ] | cp(), + Replacement :: binary(), + Options :: [Option], + Option :: global | {scope, part()} | {insert_replaced, InsPos}, + InsPos :: OnePos | [ OnePos ], + OnePos :: non_neg_integer(), + Result :: binary(). + replace(Haystack,Needles,Replacement,Options) -> try true = is_binary(Replacement), % Make badarg instead of function clause diff --git a/lib/stdlib/src/c.erl b/lib/stdlib/src/c.erl index 235ea939a8..febfdd6285 100644 --- a/lib/stdlib/src/c.erl +++ b/lib/stdlib/src/c.erl @@ -71,11 +71,16 @@ help() -> %% c(FileName) %% Compile a file/module. --spec c(file:name()) -> {'ok', module()} | 'error'. +-spec c(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). c(File) -> c(File, []). --spec c(file:name(), [compile:option()]) -> {'ok', module()} | 'error'. +-spec c(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [compile:option()], + Module :: module(). c(File, Opts0) when is_list(Opts0) -> Opts = [report_errors,report_warnings|Opts0], @@ -140,7 +145,8 @@ check_load(_, Mod) -> {ok, Mod}. %% with constant c2 defined, c1=v1 (v1 must be a term!), include dir %% IDir, outdir ODir. --spec lc([erl_compile:cmd_line_arg()]) -> 'ok' | 'error'. +-spec lc(Files) -> 'ok' | 'error' when + Files :: [File :: erl_compile:cmd_line_arg()]. lc(Args) -> case catch split(Args, [], []) of @@ -205,12 +211,17 @@ make_term(Str) -> throw(error) end. --spec nc(file:name()) -> {'ok', module()} | 'error'. +-spec nc(File) -> {'ok', Module} | 'error' when + File :: file:name(), + Module :: module(). nc(File) -> nc(File, []). --spec nc(file:name(), [compile:option()] | compile:option()) -> - {'ok', module} | 'error'. +-spec nc(File, Options) -> {'ok', Module} | 'error' when + File :: file:name(), + Options :: [Option] | Option, + Option:: compile:option(), + Module :: module(). nc(File, Opts0) when is_list(Opts0) -> Opts = Opts0 ++ [report_errors, report_warnings], @@ -234,14 +245,17 @@ nc(File, Opt) when is_atom(Opt) -> %% l(Mod) %% Reload module Mod from file of same name --spec l(module()) -> code:load_ret(). +-spec l(Module) -> code:load_ret() when + Module :: module(). l(Mod) -> code:purge(Mod), code:load_file(Mod). %% Network version of l/1 -%%-spec nl(module()) -> +-spec nl(Module) -> abcast | error when + Module :: module(). + nl(Mod) -> case code:get_object_code(Mod) of {_Module, Bin, Fname} -> @@ -396,15 +410,20 @@ fetch(Key, Info) -> false -> 0 end. --spec pid(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> pid(). +-spec pid(X, Y, Z) -> pid() when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). pid(X, Y, Z) -> list_to_pid("<" ++ integer_to_list(X) ++ "." ++ integer_to_list(Y) ++ "." ++ integer_to_list(Z) ++ ">"). --spec i(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> - [{atom(), term()}]. +-spec i(X, Y, Z) -> [{atom(), term()}] when + X :: non_neg_integer(), + Y :: non_neg_integer(), + Z :: non_neg_integer(). i(X, Y, Z) -> pinfo(pid(X, Y, Z)). @@ -413,7 +432,8 @@ i(X, Y, Z) -> pinfo(pid(X, Y, Z)). q() -> init:stop(). --spec bt(pid()) -> 'ok' | 'undefined'. +-spec bt(Pid) -> 'ok' | 'undefined' when + Pid :: pid(). bt(Pid) -> case catch erlang:process_display(Pid, backtrace) of @@ -476,7 +496,8 @@ bi(I) -> %% %% Short and nice form of module info %% --spec m(module()) -> 'ok'. +-spec m(Module) -> 'ok' when + Module :: module(). m(M) -> L = M:module_info(), @@ -664,7 +685,8 @@ pwd() -> ok = io:format("Cannot determine current directory\n") end. --spec cd(file:name()) -> 'ok'. +-spec cd(Dir) -> 'ok' when + Dir :: file:name(). cd(Dir) -> file:set_cwd(Dir), @@ -679,7 +701,8 @@ cd(Dir) -> ls() -> ls("."). --spec ls(file:name()) -> 'ok'. +-spec ls(Dir) -> 'ok' when + Dir :: file:name(). ls(Dir) -> case file:list_dir(Dir) of @@ -729,12 +752,19 @@ w(X) -> %% memory/[0,1] %% --spec memory() -> [{atom(), non_neg_integer()}]. +-spec memory() -> [{Type, Size}] when + Type :: atom(), + Size :: non_neg_integer(). memory() -> erlang:memory(). --spec memory(atom()) -> non_neg_integer() - ; ([atom()]) -> [{atom(), non_neg_integer()}]. +-spec memory(Type) -> Size when + Type :: atom(), + Size :: non_neg_integer() + ; (Types) -> [{Type, Size}] when + Types :: [Type], + Type :: atom(), + Size :: non_neg_integer(). memory(TypeSpec) -> erlang:memory(TypeSpec). diff --git a/lib/stdlib/src/calendar.erl b/lib/stdlib/src/calendar.erl index 33725d999c..8d1071209e 100644 --- a/lib/stdlib/src/calendar.erl +++ b/lib/stdlib/src/calendar.erl @@ -63,6 +63,8 @@ %% Types %%---------------------------------------------------------------------- +-export_type([t_now/0]). + -type year() :: non_neg_integer(). -type year1970() :: 1970..10000. % should probably be 1970.. -type month() :: 1..12. @@ -74,7 +76,9 @@ -type ldom() :: 28 | 29 | 30 | 31. % last day of month -type weeknum() :: 1..53. --type t_now() :: {non_neg_integer(),non_neg_integer(),non_neg_integer()}. +-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()}. @@ -106,7 +110,11 @@ %% January 1st. %% %% df/2 catches the case Year<0 --spec date_to_gregorian_days(year(),month(),day()) -> non_neg_integer(). +-spec date_to_gregorian_days(Year, Month, Day) -> Days when + Year :: year(), + Month :: month(), + Day :: day(), + Days :: non_neg_integer(). date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> Last = last_day_of_the_month(Year, Month), if @@ -114,7 +122,9 @@ date_to_gregorian_days(Year, Month, Day) when is_integer(Day), Day > 0 -> dy(Year) + dm(Month) + df(Year, Month) + Day - 1 end. --spec date_to_gregorian_days(t_date()) -> non_neg_integer(). +-spec date_to_gregorian_days(Date) -> Days when + Date :: t_date(), + Days :: non_neg_integer(). date_to_gregorian_days({Year, Month, Day}) -> date_to_gregorian_days(Year, Month, Day). @@ -124,7 +134,9 @@ date_to_gregorian_days({Year, Month, Day}) -> %% Computes the total number of seconds starting from year 0, %% January 1st. %% --spec datetime_to_gregorian_seconds(t_datetime()) -> non_neg_integer(). +-spec datetime_to_gregorian_seconds(DateTime) -> Seconds when + DateTime :: t_datetime(), + Seconds :: non_neg_integer(). datetime_to_gregorian_seconds({Date, Time}) -> ?SECONDS_PER_DAY*date_to_gregorian_days(Date) + time_to_seconds(Time). @@ -135,18 +147,23 @@ datetime_to_gregorian_seconds({Date, Time}) -> %% %% Returns: 1 | .. | 7. Monday = 1, Tuesday = 2, ..., Sunday = 7. %% --spec day_of_the_week(year(), month(), day()) -> daynum(). +-spec day_of_the_week(Year, Month, Day) -> daynum() when + Year :: year(), + Month :: month(), + Day :: day(). day_of_the_week(Year, Month, Day) -> (date_to_gregorian_days(Year, Month, Day) + 5) rem 7 + 1. --spec day_of_the_week(t_date()) -> daynum(). +-spec day_of_the_week(Date) -> daynum() when + Date:: t_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(non_neg_integer()) -> t_date(). +-spec gregorian_days_to_date(Days) -> t_date() when + Days :: non_neg_integer(). gregorian_days_to_date(Days) -> {Year, DayOfYear} = day_to_year(Days), {Month, DayOfMonth} = year_day_to_date(Year, DayOfYear), @@ -155,7 +172,8 @@ gregorian_days_to_date(Days) -> %% gregorian_seconds_to_datetime(Secs) %% --spec gregorian_seconds_to_datetime(non_neg_integer()) -> t_datetime(). +-spec gregorian_seconds_to_datetime(Seconds) -> t_datetime() when + Seconds :: non_neg_integer(). gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> Days = Secs div ?SECONDS_PER_DAY, Rest = Secs rem ?SECONDS_PER_DAY, @@ -164,7 +182,8 @@ gregorian_seconds_to_datetime(Secs) when Secs >= 0 -> %% is_leap_year(Year) = true | false %% --spec is_leap_year(year()) -> boolean(). +-spec is_leap_year(Year) -> boolean() when + Year :: year(). is_leap_year(Y) when is_integer(Y), Y >= 0 -> is_leap_year1(Y). @@ -188,7 +207,8 @@ iso_week_number() -> %% %% Calculates the iso week number for the given date. %% --spec iso_week_number(t_date()) -> t_yearweeknum(). +-spec iso_week_number(Date) -> t_yearweeknum() when + Date :: t_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), @@ -216,7 +236,10 @@ iso_week_number({Year, Month, Day}) -> %% %% Returns the number of days in a month. %% --spec last_day_of_the_month(year(), month()) -> ldom(). +-spec last_day_of_the_month(Year, Month) -> LastDay when + Year :: year(), + Month :: month(), + LastDay :: ldom(). last_day_of_the_month(Y, M) when is_integer(Y), Y >= 0 -> last_day_of_the_month1(Y, M). @@ -244,7 +267,9 @@ local_time() -> %% local_time_to_universal_time(DateTime) %% --spec local_time_to_universal_time(t_datetime1970()) -> t_datetime1970(). +-spec local_time_to_universal_time(DateTime1) -> DateTime2 when + DateTime1 :: t_datetime1970(), + DateTime2 :: t_datetime1970(). local_time_to_universal_time(DateTime) -> erlang:localtime_to_universaltime(DateTime). @@ -254,7 +279,9 @@ local_time_to_universal_time(DateTime) -> local_time_to_universal_time(DateTime, IsDst) -> erlang:localtime_to_universaltime(DateTime, IsDst). --spec local_time_to_universal_time_dst(t_datetime1970()) -> [t_datetime1970()]. +-spec local_time_to_universal_time_dst(DateTime1) -> [DateTime] when + DateTime1 :: t_datetime1970(), + DateTime :: t_datetime1970(). local_time_to_universal_time_dst(DateTime) -> UtDst = erlang:localtime_to_universaltime(DateTime, true), Ut = erlang:localtime_to_universaltime(DateTime, false), @@ -282,12 +309,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(t_now()) -> t_datetime1970(). +-spec now_to_datetime(Now) -> t_datetime1970() when + Now :: t_now(). 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(t_now()) -> t_datetime1970(). +-spec now_to_universal_time(Now) -> t_datetime1970() when + Now :: t_now(). now_to_universal_time(Now) -> now_to_datetime(Now). @@ -296,7 +325,8 @@ now_to_universal_time(Now) -> %% %% Args: Now = now() %% --spec now_to_local_time(t_now()) -> t_datetime1970(). +-spec now_to_local_time(Now) -> t_datetime1970() when + Now :: t_now(). now_to_local_time({MSec, Sec, _uSec}) -> erlang:universaltime_to_localtime( now_to_universal_time({MSec, Sec, _uSec})). @@ -305,7 +335,10 @@ now_to_local_time({MSec, Sec, _uSec}) -> %% seconds_to_daystime(Secs) = {Days, {Hour, Minute, Second}} %% --spec seconds_to_daystime(integer()) -> {integer(), t_time()}. +-spec seconds_to_daystime(Seconds) -> {Days, Time} when + Seconds :: integer(), + Days :: integer(), + Time :: t_time(). seconds_to_daystime(Secs) -> Days0 = Secs div ?SECONDS_PER_DAY, Secs0 = Secs rem ?SECONDS_PER_DAY, @@ -323,7 +356,8 @@ seconds_to_daystime(Secs) -> %% Wraps. %% -type secs_per_day() :: 0..?SECONDS_PER_DAY. --spec seconds_to_time(secs_per_day()) -> t_time(). +-spec seconds_to_time(Seconds) -> t_time() when + Seconds :: secs_per_day(). seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> Secs0 = Secs rem ?SECONDS_PER_DAY, Hour = Secs0 div ?SECONDS_PER_HOUR, @@ -340,8 +374,11 @@ seconds_to_time(Secs) when Secs >= 0, Secs < ?SECONDS_PER_DAY -> %% Date = {Year, Month, Day}, Time = {Hour, Minute, Sec}, %% Year = Month = Day = Hour = Minute = Sec = integer() %% --type timediff() :: {integer(), t_time()}. --spec time_difference(t_datetime(), t_datetime()) -> timediff(). +-spec time_difference(T1, T2) -> {Days, Time} when + T1 :: t_datetime(), + T2 :: t_datetime(), + Days :: integer(), + Time :: t_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}}) - @@ -352,7 +389,8 @@ time_difference({{Y1, Mo1, D1}, {H1, Mi1, S1}}, %% %% time_to_seconds(Time) %% --spec time_to_seconds(t_time()) -> secs_per_day(). +-spec time_to_seconds(Time) -> secs_per_day() when + Time :: t_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. @@ -368,7 +406,8 @@ universal_time() -> %% universal_time_to_local_time(DateTime) %% --spec universal_time_to_local_time(t_datetime()) -> t_datetime(). +-spec universal_time_to_local_time(DateTime) -> t_datetime() when + DateTime :: t_datetime1970(). universal_time_to_local_time(DateTime) -> erlang:universaltime_to_localtime(DateTime). @@ -376,7 +415,10 @@ universal_time_to_local_time(DateTime) -> %% valid_date(Year, Month, Day) = true | false %% valid_date({Year, Month, Day}) = true | false %% --spec valid_date(integer(), integer(), integer()) -> boolean(). +-spec valid_date(Year, Month, Day) -> boolean() when + Year :: integer(), + Month :: integer(), + Day :: integer(). valid_date(Y, M, D) when is_integer(Y), is_integer(M), is_integer(D) -> valid_date1(Y, M, D). @@ -386,7 +428,8 @@ valid_date1(Y, M, D) when Y >= 0, M > 0, M < 13, D > 0 -> valid_date1(_, _, _) -> false. --spec valid_date({integer(),integer(),integer()}) -> boolean(). +-spec valid_date(Date) -> boolean() when + Date :: t_date(). valid_date({Y, M, D}) -> valid_date(Y, M, D). diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 6c91f1efb7..1b81862940 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.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 @@ -97,10 +97,6 @@ -include("dets.hrl"). --type object() :: tuple(). --type pattern() :: atom() | tuple(). --type tab_name() :: atom() | reference(). - %%% This is the implementation of the mnesia file storage. Each (non %%% ram-copy) table is maintained in a corresponding .DAT file. The %%% dat file is organized as a segmented linear hashlist. The head of @@ -179,6 +175,21 @@ %%-define(PROFILE(C), C). -define(PROFILE(C), void). +-type access() :: 'read' | 'read_write'. +-type auto_save() :: 'infinity' | non_neg_integer(). +-opaque bindings_cont() :: #dets_cont{}. +-opaque cont() :: #dets_cont{}. +-type keypos() :: pos_integer(). +-type match_spec() :: ets:match_spec(). +-type object() :: tuple(). +-type no_slots() :: non_neg_integer() | 'default'. +-opaque object_cont() :: #dets_cont{}. +-type pattern() :: atom() | tuple(). +-opaque select_cont() :: #dets_cont{}. +-type tab_name() :: atom() | reference(). +-type type() :: 'bag' | 'duplicate_bag' | 'set'. +-type version() :: 8 | 9 | 'default'. + %%% Some further debug code was added in R12B-1 (stdlib-1.15.1): %%% - there is a new open_file() option 'debug'; %%% - there is a new OS environment variable 'DETS_DEBUG'; @@ -203,9 +214,13 @@ add_user(Pid, Tab, Args) -> all() -> dets_server:all(). --type cont() :: #dets_cont{}. --spec bchunk(tab_name(), 'start' | cont()) -> - {cont(), binary() | tuple()} | '$end_of_table' | {'error', term()}. +-spec bchunk(Name, Continuation) -> + {Continuation2, Data} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Continuation :: 'start' | cont(), + Continuation2 :: cont(), + Data :: binary() | tuple(), + Reason :: term(). bchunk(Tab, start) -> badarg(treq(Tab, {bchunk_init, Tab}), [Tab, start]); @@ -214,7 +229,9 @@ bchunk(Tab, #dets_cont{what = bchunk, tab = Tab} = State) -> bchunk(Tab, Term) -> erlang:error(badarg, [Tab, Term]). --spec close(tab_name()) -> 'ok' | {'error', term()}. +-spec close(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). close(Tab) -> case dets_server:close(Tab) of @@ -224,12 +241,17 @@ close(Tab) -> Reply end. --spec delete(tab_name(), term()) -> 'ok' | {'error', term()}. +-spec delete(Name, Key) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). delete(Tab, Key) -> badarg(treq(Tab, {delete_key, [Key]}), [Tab, Key]). --spec delete_all_objects(tab_name()) -> 'ok' | {'error', term()}. +-spec delete_all_objects(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). delete_all_objects(Tab) -> case treq(Tab, delete_all_objects) of @@ -241,7 +263,10 @@ delete_all_objects(Tab) -> Reply end. --spec delete_object(tab_name(), object()) -> 'ok' | {'error', term()}. +-spec delete_object(Name, Object) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Object :: object(), + Reason :: term(). delete_object(Tab, O) -> badarg(treq(Tab, {delete_object, [O]}), [Tab, O]). @@ -264,23 +289,42 @@ fsck(Fname, Version) -> end end. --spec first(tab_name()) -> term() | '$end_of_table'. +-spec first(Name) -> Key | '$end_of_table' when + Name :: tab_name(), + Key :: term(). first(Tab) -> badarg_exit(treq(Tab, first), [Tab]). --spec foldr(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldr(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldr(Fun, Acc, Tab) -> foldl(Fun, Acc, Tab). --spec foldl(fun((object(), Acc) -> Acc), Acc, tab_name()) -> Acc | {'error', term()}. +-spec foldl(Function, Acc0, Name) -> Acc | {'error', Reason} when + Name :: tab_name(), + Function :: fun((Object :: object(), AccIn) -> AccOut), + Acc0 :: term(), + Acc :: term(), + AccIn :: term(), + AccOut :: term(), + Reason :: term(). foldl(Fun, Acc, Tab) -> Ref = make_ref(), do_traverse(Fun, Acc, Tab, Ref). --spec from_ets(tab_name(), ets:tab()) -> 'ok' | {'error', term()}. +-spec from_ets(Name, EtsTab) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). from_ets(DTab, ETab) -> ets:safe_fixtable(ETab, true), @@ -304,6 +348,15 @@ from_ets_fun(LC, ETab) -> {L, from_ets_fun(ets:select(C), ETab)} end. +-spec info(Name) -> InfoList | 'undefined' when + Name :: tab_name(), + InfoList :: [InfoTuple], + InfoTuple :: {'file_size', non_neg_integer()} + | {'filename', file:name()} + | {'keypos', keypos()} + | {'size', non_neg_integer()} + | {'type', type()}. + info(Tab) -> case catch dets_server:get_pid(Tab) of {'EXIT', _Reason} -> @@ -312,6 +365,14 @@ info(Tab) -> undefined(req(Pid, info)) end. +-spec info(Name, Item) -> Value | 'undefined' when + Name :: tab_name(), + Item :: 'access' | 'auto_save' | 'bchunk_format' + | 'hash' | 'file_size' | 'filename' | 'keypos' | 'memory' + | 'no_keys' | 'no_objects' | 'no_slots' | 'owner' | 'ram_file' + | 'safe_fixed' | 'size' | 'type' | 'version', + Value :: term(). + info(Tab, owner) -> case catch dets_server:get_pid(Tab) of Pid when is_pid(Pid) -> @@ -334,9 +395,26 @@ info(Tab, Tag) -> undefined(req(Pid, {info, Tag})) end. +-spec init_table(Name, InitFun) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun) -> init_table(Tab, InitFun, []). +-spec init_table(Name, InitFun, Options) -> ok | {'error', Reason} when + Name :: tab_name(), + InitFun :: fun((Arg) -> Res), + Arg :: read | close, + Res :: end_of_input | {[object()], InitFun} | {Data, InitFun} | term(), + Options :: [{min_no_slots,no_slots()} | {format,term | bchunk}], + Reason :: term(), + Data :: binary() | tuple(). + init_table(Tab, InitFun, Options) when is_function(InitFun) -> case options(Options, [format, min_no_slots]) of {badarg,_} -> @@ -350,11 +428,20 @@ init_table(Tab, InitFun, Options) when is_function(InitFun) -> init_table(Tab, InitFun, Options) -> erlang:error(badarg, [Tab, InitFun, Options]). +-spec insert(Name, Objects) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Objects :: object() | [object()], + Reason :: term(). + insert(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert, Objs}), [Tab, Objs]); insert(Tab, Obj) -> badarg(treq(Tab, {insert, [Obj]}), [Tab, Obj]). +-spec insert_new(Name, Objects) -> boolean() when + Name :: tab_name(), + Objects :: object() | [object()]. + insert_new(Tab, Objs) when is_list(Objs) -> badarg(treq(Tab, {insert_new, Objs}), [Tab, Objs]); insert_new(Tab, Obj) -> @@ -366,9 +453,17 @@ internal_close(Pid) -> internal_open(Pid, Ref, Args) -> req(Pid, {internal_open, Ref, Args}). +-spec is_compatible_bchunk_format(Name, BchunkFormat) -> boolean() when + Name :: tab_name(), + BchunkFormat :: binary(). + is_compatible_bchunk_format(Tab, Term) -> badarg(treq(Tab, {is_compatible_bchunk_format, Term}), [Tab, Term]). +-spec is_dets_file(Filename) -> boolean() | {'error', Reason} when + Filename :: file:name(), + Reason :: term(). + is_dets_file(FileName) -> case catch read_file_header(FileName, read, false) of {ok, Fd, FH} -> @@ -382,6 +477,12 @@ is_dets_file(FileName) -> Other end. +-spec lookup(Name, Key) -> Objects | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Objects :: [object()], + Reason :: term(). + lookup(Tab, Key) -> badarg(treq(Tab, {lookup_keys, [Key]}), [Tab, Key]). @@ -394,19 +495,43 @@ lookup_keys(Tab, Keys) -> erlang:error(badarg, [Tab, Keys]) end. +-spec match(Name, Pattern) -> [Match] | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat) -> badarg(safe_match(Tab, Pat, bindings), [Tab, Pat]). +-spec match(Name, Pattern, N) -> + {[Match], Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, bindings, N), [Tab, Pat, N]). +-spec match(Continuation) -> + {[Match], Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: bindings_cont(), + Continuation2 :: bindings_cont(), + Match :: [term()], + Reason :: term(). + match(State) when State#dets_cont.what =:= bindings -> badarg(chunk_match(State), [State]); match(Term) -> erlang:error(badarg, [Term]). --spec match_delete(tab_name(), pattern()) -> - non_neg_integer() | 'ok' | {'error', term()}. +-spec match_delete(Name, Pattern) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Reason :: term(). match_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, delete), [Tab, Pat]). @@ -434,23 +559,60 @@ do_match_delete(Tab, _Proc, Error, _What, _N) -> safe_fixtable(Tab, false), Error. +-spec match_object(Name, Pattern) -> Objects | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat) -> badarg(safe_match(Tab, Pat, object), [Tab, Pat]). +-spec match_object(Name, Pattern, N) -> + {Objects, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + Pattern :: pattern(), + N :: 'default' | non_neg_integer(), + Continuation :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, object, N), [Tab, Pat, N]). +-spec match_object(Continuation) -> + {Objects, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: object_cont(), + Continuation2 :: object_cont(), + Objects :: [object()], + Reason :: term(). + match_object(State) when State#dets_cont.what =:= object -> badarg(chunk_match(State), [State]); match_object(Term) -> erlang:error(badarg, [Term]). +-spec member(Name, Key) -> boolean() | {'error', Reason} when + Name :: tab_name(), + Key :: term(), + Reason :: term(). + member(Tab, Key) -> badarg(treq(Tab, {member, Key}), [Tab, Key]). +-spec next(Name, Key1) -> Key2 | '$end_of_table' when + Name :: tab_name(), + Key1 :: term(), + Key2 :: term(). + next(Tab, Key) -> badarg_exit(treq(Tab, {next, Key}), [Tab, Key]). +-spec open_file(Filename) -> {'ok', Reference} | {'error', Reason} when + Filename :: file:name(), + Reference :: reference(), + Reason :: term(). + %% Assuming that a file already exists, open it with the %% parameters as already specified in the file itself. %% Return a ref leading to the file. @@ -462,6 +624,22 @@ open_file(File) -> einval(Reply, [File]) end. +-spec open_file(Name, Args) -> {'ok', Name} | {'error', Reason} when + Name :: tab_name(), + Args :: [OpenArg], + OpenArg :: {'access', access()} + | {'auto_save', auto_save()} + | {'estimated_no_objects', non_neg_integer()} + | {'file', file:name()} + | {'max_no_slots', no_slots()} + | {'min_no_slots', no_slots()} + | {'keypos', keypos()} + | {'ram_file', boolean()} + | {'repair', boolean() | 'force'} + | {'type', type()} + | {'version', version()}, + Reason :: term(). + open_file(Tab, Args) when is_list(Args) -> case catch defaults(Tab, Args) of OpenArgs when is_record(OpenArgs, open_args) -> @@ -477,12 +655,21 @@ open_file(Tab, Args) when is_list(Args) -> open_file(Tab, Arg) -> open_file(Tab, [Arg]). +-spec pid2name(Pid) -> {'ok', Name} | 'undefined' when + Pid :: pid(), + Name :: tab_name(). + pid2name(Pid) -> dets_server:pid2name(Pid). remove_user(Pid, From) -> req(Pid, {close, From}). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation2 when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + MatchSpec :: match_spec(). + repair_continuation(#dets_cont{match_program = B}=Cont, MS) when is_binary(B) -> case ets:is_compiled_ms(B) of @@ -496,25 +683,63 @@ repair_continuation(#dets_cont{}=Cont, _MS) -> repair_continuation(T, MS) -> erlang:error(badarg, [T, MS]). +-spec safe_fixtable(Name, Fix) -> 'ok' when + Name :: tab_name(), + Fix :: boolean(). + safe_fixtable(Tab, Bool) when Bool; not Bool -> badarg(treq(Tab, {safe_fixtable, Bool}), [Tab, Bool]); safe_fixtable(Tab, Term) -> erlang:error(badarg, [Tab, Term]). +-spec select(Name, MatchSpec) -> Selection | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat) -> badarg(safe_match(Tab, Pat, select), [Tab, Pat]). +-spec select(Name, MatchSpec, N) -> + {Selection, Continuation} | '$end_of_table' | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: 'default' | non_neg_integer(), + Continuation :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(Tab, Pat, N) -> badarg(init_chunk_match(Tab, Pat, select, N), [Tab, Pat, N]). +-spec select(Continuation) -> + {Selection, Continuation2} | '$end_of_table' | {'error', Reason} when + Continuation :: select_cont(), + Continuation2 :: select_cont(), + Selection :: [term()], + Reason :: term(). + select(State) when State#dets_cont.what =:= select -> badarg(chunk_match(State), [State]); select(Term) -> erlang:error(badarg, [Term]). +-spec select_delete(Name, MatchSpec) -> N | {'error', Reason} when + Name :: tab_name(), + MatchSpec :: match_spec(), + N :: non_neg_integer(), + Reason :: term(). + select_delete(Tab, Pat) -> badarg(match_delete(Tab, Pat, select), [Tab, Pat]). +-spec slot(Name, I) -> '$end_of_table' | Objects | {'error', Reason} when + Name :: tab_name(), + I :: non_neg_integer(), + Objects :: [object()], + Reason :: term(). + slot(Tab, Slot) when is_integer(Slot), Slot >= 0 -> badarg(treq(Tab, {slot, Slot}), [Tab, Slot]); slot(Tab, Term) -> @@ -529,12 +754,29 @@ stop() -> istart_link(Server) -> {ok, proc_lib:spawn_link(dets, init, [self(), Server])}. +-spec sync(Name) -> 'ok' | {'error', Reason} when + Name :: tab_name(), + Reason :: term(). + sync(Tab) -> badarg(treq(Tab, sync), [Tab]). +-spec table(Name) -> QueryHandle when + Name :: tab_name(), + QueryHandle :: qlc:query_handle(). + table(Tab) -> table(Tab, []). +-spec table(Name, Options) -> QueryHandle when + Name :: tab_name(), + Options :: Option | [Option], + Option :: {'n_objects', Limit} + | {'traverse', TraverseMethod}, + Limit :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'select' | {'select', match_spec()}, + QueryHandle :: qlc:query_handle(). + table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of {badarg,_} -> @@ -612,6 +854,11 @@ table_info(_Tab, _) -> %% End of table/2. +-spec to_ets(Name, EtsTab) -> EtsTab | {'error', Reason} when + Name :: tab_name(), + EtsTab :: ets:tab(), + Reason :: term(). + to_ets(DTab, ETab) -> case ets:info(ETab, protection) of undefined -> @@ -621,6 +868,16 @@ to_ets(DTab, ETab) -> foldl(Fun, ETab, DTab) end. +-spec traverse(Name, Fun) -> Return | {'error', Reason} when + Name :: tab_name(), + Fun :: fun((Object) -> FunReturn), + FunReturn :: 'continue' | {'continue', Val} | {'done', Value}, + Val :: term(), + Value :: term(), + Object :: object(), + Return :: [term()], + Reason :: term(). + traverse(Tab, Fun) -> Ref = make_ref(), TFun = @@ -638,6 +895,14 @@ traverse(Tab, Fun) -> end, do_traverse(TFun, [], Tab, Ref). +-spec update_counter(Name, Key, Increment) -> Result when + Name :: tab_name(), + Key :: term(), + Increment :: {Pos, Incr} | Incr, + Pos :: integer(), + Incr :: integer(), + Result :: integer(). + update_counter(Tab, Key, C) -> badarg(treq(Tab, {update_counter, Key, C}), [Tab, Key, C]). diff --git a/lib/stdlib/src/dict.erl b/lib/stdlib/src/dict.erl index 7e51141098..2e9eba4bfa 100644 --- a/lib/stdlib/src/dict.erl +++ b/lib/stdlib/src/dict.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-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 @@ -80,7 +80,9 @@ new() -> Empty = mk_seg(?seg_size), #dict{empty=Empty,segs={Empty}}. --spec is_key(term(), dict()) -> boolean(). +-spec is_key(Key, Dict) -> boolean() when + Key :: term(), + Dict :: dict(). is_key(Key, D) -> Slot = get_slot(D, Key), @@ -91,21 +93,29 @@ find_key(K, [?kv(K,_Val)|_]) -> true; find_key(K, [_|Bkt]) -> find_key(K, Bkt); find_key(_, []) -> false. --spec to_list(dict()) -> [{term(), term()}]. +-spec to_list(Dict) -> List when + Dict :: dict(), + List :: [{Key :: term(), Value :: term()}]. to_list(D) -> fold(fun (Key, Val, List) -> [{Key,Val}|List] end, [], D). --spec from_list([{term(), term()}]) -> dict(). +-spec from_list(List) -> Dict when + List :: [{Key :: term(), Value :: term()}], + Dict :: dict(). from_list(L) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, new(), L). --spec size(dict()) -> non_neg_integer(). +-spec size(Dict) -> non_neg_integer() when + Dict :: dict(). size(#dict{size=N}) when is_integer(N), N >= 0 -> N. --spec fetch(term(), dict()) -> term(). +-spec fetch(Key, Dict) -> Value when + Key :: term(), + Dict :: dict(), + Value :: term(). fetch(Key, D) -> Slot = get_slot(D, Key), @@ -119,7 +129,10 @@ fetch_val(K, [?kv(K,Val)|_]) -> Val; fetch_val(K, [_|Bkt]) -> fetch_val(K, Bkt); fetch_val(_, []) -> throw(badarg). --spec find(term(), dict()) -> {'ok', term()} | 'error'. +-spec find(Key, Dict) -> {'ok', Value} | 'error' when + Key :: term(), + Dict :: dict(), + Value :: term(). find(Key, D) -> Slot = get_slot(D, Key), @@ -130,12 +143,17 @@ find_val(K, [?kv(K,Val)|_]) -> {ok,Val}; find_val(K, [_|Bkt]) -> find_val(K, Bkt); find_val(_, []) -> error. --spec fetch_keys(dict()) -> [term()]. +-spec fetch_keys(Dict) -> Keys when + Dict :: dict(), + Keys :: [term()]. fetch_keys(D) -> fold(fun (Key, _Val, Keys) -> [Key|Keys] end, [], D). --spec erase(term(), dict()) -> dict(). +-spec erase(Key, Dict1) -> Dict2 when + Key :: term(), + Dict1 :: dict(), + Dict2 :: dict(). %% Erase all elements with key Key. erase(Key, D0) -> @@ -150,7 +168,11 @@ erase_key(Key, [E|Bkt0]) -> {[E|Bkt1],Dc}; erase_key(_, []) -> {[],0}. --spec store(term(), term(), dict()) -> dict(). +-spec store(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). store(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -166,7 +188,11 @@ store_bkt_val(Key, New, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; store_bkt_val(Key, New, []) -> {[?kv(Key,New)],1}. --spec append(term(), term(), dict()) -> dict(). +-spec append(Key, Value, Dict1) -> Dict2 when + Key :: term(), + Value :: term(), + Dict1 :: dict(), + Dict2 :: dict(). append(Key, Val, D0) -> Slot = get_slot(D0, Key), @@ -182,7 +208,11 @@ append_bkt(Key, Val, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; append_bkt(Key, Val, []) -> {[?kv(Key,[Val])],1}. --spec append_list(term(), [term()], dict()) -> dict(). +-spec append_list(Key, ValList, Dict1) -> Dict2 when + Key :: term(), + ValList :: [Value :: term()], + Dict1 :: dict(), + Dict2 :: dict(). append_list(Key, L, D0) -> Slot = get_slot(D0, Key), @@ -252,7 +282,11 @@ app_list_bkt(Key, L, []) -> {[?kv(Key,L)],1}. %% {Bkt1,Dc} = on_key_bkt(Key, F, Bkt0), %% {[Other|Bkt1],Dc}. --spec update(term(), fun((term()) -> term()), dict()) -> dict(). +-spec update(Key, Fun, Dict1) -> Dict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, D0) -> Slot = get_slot(D0, Key), @@ -271,7 +305,12 @@ update_bkt(Key, F, [Other|Bkt0]) -> update_bkt(_Key, _F, []) -> throw(badarg). --spec update(term(), fun((term()) -> term()), term(), dict()) -> dict(). +-spec update(Key, Fun, Initial, Dict1) -> Dict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). update(Key, F, Init, D0) -> Slot = get_slot(D0, Key), @@ -286,7 +325,11 @@ update_bkt(Key, F, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; update_bkt(Key, F, I, []) when is_function(F, 1) -> {[?kv(Key,I)],1}. --spec update_counter(term(), number(), dict()) -> dict(). +-spec update_counter(Key, Increment, Dict1) -> Dict2 when + Key :: term(), + Increment :: number(), + Dict1 :: dict(), + Dict2 :: dict(). update_counter(Key, Incr, D0) when is_number(Incr) -> Slot = get_slot(D0, Key), @@ -301,20 +344,38 @@ counter_bkt(Key, I, [Other|Bkt0]) -> {[Other|Bkt1],Ic}; counter_bkt(Key, I, []) -> {[?kv(Key,I)],1}. --spec fold(fun((term(), term(), term()) -> term()), term(), dict()) -> term(). +-spec fold(Fun, Acc0, Dict) -> Acc1 when + Fun :: fun((Key, Value, AccIn) -> AccOut), + Key :: term(), + Value :: term(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Dict :: dict(). %% Fold function Fun over all "bags" in Table and return Accumulator. fold(F, Acc, D) -> fold_dict(F, Acc, D). --spec map(fun((term(), term()) -> term()), dict()) -> dict(). +-spec map(Fun, Dict1) -> Dict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Dict1 :: dict(), + Dict2 :: dict(). map(F, D) -> map_dict(F, D). --spec filter(fun((term(), term()) -> boolean()), dict()) -> dict(). +-spec filter(Pred, Dict1) -> Dict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Dict1 :: dict(), + Dict2 :: dict(). filter(F, D) -> filter_dict(F, D). --spec merge(fun((term(), term(), term()) -> term()), dict(), dict()) -> dict(). +-spec merge(Fun, Dict1, Dict2) -> Dict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Dict1 :: dict(), + Dict2 :: dict(), + Dict3 :: dict(). merge(F, D1, D2) when D1#dict.size < D2#dict.size -> fold_dict(fun (K, V1, D) -> diff --git a/lib/stdlib/src/digraph.erl b/lib/stdlib/src/digraph.erl index 5edc868a94..e3f87d2c57 100644 --- a/lib/stdlib/src/digraph.erl +++ b/lib/stdlib/src/digraph.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 @@ -53,7 +53,8 @@ -type label() :: term(). -type vertex() :: term(). --type add_edge_err_rsn() :: {'bad_edge', [vertex()]} | {'bad_vertex', vertex()}. +-type add_edge_err_rsn() :: {'bad_edge', Path :: [vertex()]} + | {'bad_vertex', V :: vertex()}. %% %% Type is a list of @@ -70,7 +71,8 @@ new() -> new([]). --spec new([d_type()]) -> digraph(). +-spec new(Type) -> digraph() when + Type :: [d_type()]. new(Type) -> case check_type(Type, protected, []) of @@ -113,16 +115,20 @@ set_type([], G) -> G. %% Data access functions --spec delete(digraph()) -> 'true'. +-spec delete(G) -> 'true' when + G :: digraph(). delete(G) -> ets:delete(G#digraph.vtab), ets:delete(G#digraph.etab), ets:delete(G#digraph.ntab). --spec info(digraph()) -> [{'cyclicity', d_cyclicity()} | - {'memory', non_neg_integer()} | - {'protection', d_protection()}]. +-spec info(G) -> InfoList when + G :: digraph(), + InfoList :: [{'cyclicity', Cyclicity :: d_cyclicity()} | + {'memory', NoWords :: non_neg_integer()} | + {'protection', Protection :: d_protection()}]. + info(G) -> VT = G#digraph.vtab, ET = G#digraph.etab, @@ -135,32 +141,45 @@ info(G) -> Memory = ets:info(VT, memory) + ets:info(ET, memory) + ets:info(NT, memory), [{cyclicity, Cyclicity}, {memory, Memory}, {protection, Protection}]. --spec add_vertex(digraph()) -> vertex(). +-spec add_vertex(G) -> vertex() when + G :: digraph(). add_vertex(G) -> do_add_vertex({new_vertex_id(G), []}, G). --spec add_vertex(digraph(), vertex()) -> vertex(). +-spec add_vertex(G, V) -> vertex() when + G :: digraph(), + V :: vertex(). add_vertex(G, V) -> do_add_vertex({V, []}, G). --spec add_vertex(digraph(), vertex(), label()) -> vertex(). +-spec add_vertex(G, V, Label) -> vertex() when + G :: digraph(), + V :: vertex(), + Label :: label(). add_vertex(G, V, D) -> do_add_vertex({V, D}, G). --spec del_vertex(digraph(), vertex()) -> 'true'. +-spec del_vertex(G, V) -> 'true' when + G :: digraph(), + V :: vertex(). del_vertex(G, V) -> do_del_vertex(V, G). --spec del_vertices(digraph(), [vertex()]) -> 'true'. +-spec del_vertices(G, Vertices) -> 'true' when + G :: digraph(), + Vertices :: [vertex()]. del_vertices(G, Vs) -> do_del_vertices(Vs, G). --spec vertex(digraph(), vertex()) -> {vertex(), label()} | 'false'. +-spec vertex(G, V) -> {V, Label} | 'false' when + G :: digraph(), + V :: vertex(), + Label :: label(). vertex(G, V) -> case ets:lookup(G#digraph.vtab, V) of @@ -168,12 +187,15 @@ vertex(G, V) -> [Vertex] -> Vertex end. --spec no_vertices(digraph()) -> non_neg_integer(). +-spec no_vertices(G) -> non_neg_integer() when + G :: digraph(). no_vertices(G) -> ets:info(G#digraph.vtab, size). --spec vertices(digraph()) -> [vertex()]. +-spec vertices(G) -> Vertices when + G :: digraph(), + Vertices :: [vertex()]. vertices(G) -> ets:select(G#digraph.vtab, [{{'$1', '_'}, [], ['$1']}]). @@ -188,85 +210,125 @@ source_vertices(G) -> sink_vertices(G) -> collect_vertices(G, out). --spec in_degree(digraph(), vertex()) -> non_neg_integer(). +-spec in_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). in_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {in, V})). --spec in_neighbours(digraph(), vertex()) -> [vertex()]. +-spec in_neighbours(G, V) -> Vertex when + G :: digraph(), + V :: vertex(), + Vertex :: [vertex()]. in_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {in, V}), ET, 2). --spec in_edges(digraph(), vertex()) -> [edge()]. +-spec in_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. in_edges(G, V) -> ets:select(G#digraph.ntab, [{{{in, V}, '$1'}, [], ['$1']}]). --spec out_degree(digraph(), vertex()) -> non_neg_integer(). +-spec out_degree(G, V) -> non_neg_integer() when + G :: digraph(), + V :: vertex(). out_degree(G, V) -> length(ets:lookup(G#digraph.ntab, {out, V})). --spec out_neighbours(digraph(), vertex()) -> [vertex()]. +-spec out_neighbours(G, V) -> Vertices when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex()]. out_neighbours(G, V) -> ET = G#digraph.etab, NT = G#digraph.ntab, collect_elems(ets:lookup(NT, {out, V}), ET, 3). --spec out_edges(digraph(), vertex()) -> [edge()]. +-spec out_edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. out_edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V}, '$1'}, [], ['$1']}]). --spec add_edge(digraph(), vertex(), vertex()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). add_edge(G, V1, V2) -> do_add_edge({new_edge_id(G), V1, V2, []}, G). --spec add_edge(digraph(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, V1, V2, D) -> do_add_edge({new_edge_id(G), V1, V2, D}, G). --spec add_edge(digraph(), edge(), vertex(), vertex(), label()) -> - edge() | {'error', add_edge_err_rsn()}. +-spec add_edge(G, E, V1, V2, Label) -> edge() | {'error', add_edge_err_rsn()} when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). add_edge(G, E, V1, V2, D) -> do_add_edge({E, V1, V2, D}, G). --spec del_edge(digraph(), edge()) -> 'true'. +-spec del_edge(G, E) -> 'true' when + G :: digraph(), + E :: edge(). del_edge(G, E) -> do_del_edges([E], G). --spec del_edges(digraph(), [edge()]) -> 'true'. +-spec del_edges(G, Edges) -> 'true' when + G :: digraph(), + Edges :: [edge()]. del_edges(G, Es) -> do_del_edges(Es, G). --spec no_edges(digraph()) -> non_neg_integer(). +-spec no_edges(G) -> non_neg_integer() when + G :: digraph(). no_edges(G) -> ets:info(G#digraph.etab, size). --spec edges(digraph()) -> [edge()]. +-spec edges(G) -> Edges when + G :: digraph(), + Edges :: [edge()]. edges(G) -> ets:select(G#digraph.etab, [{{'$1', '_', '_', '_'}, [], ['$1']}]). --spec edges(digraph(), vertex()) -> [edge()]. +-spec edges(G, V) -> Edges when + G :: digraph(), + V :: vertex(), + Edges :: [edge()]. edges(G, V) -> ets:select(G#digraph.ntab, [{{{out, V},'$1'}, [], ['$1']}, {{{in, V}, '$1'}, [], ['$1']}]). --spec edge(digraph(), edge()) -> {edge(),vertex(),vertex(),label()} | 'false'. +-spec edge(G, E) -> {E, V1, V2, Label} | 'false' when + G :: digraph(), + E :: edge(), + V1 :: vertex(), + V2 :: vertex(), + Label :: label(). edge(G, E) -> case ets:lookup(G#digraph.etab,E) of @@ -277,7 +339,7 @@ edge(G, E) -> %% %% Generate a "unique" edge identifier (relative to this graph) %% --spec new_edge_id(digraph()) -> nonempty_improper_list('$e', non_neg_integer()). +-spec new_edge_id(digraph()) -> edge(). new_edge_id(G) -> NT = G#digraph.ntab, @@ -289,7 +351,7 @@ new_edge_id(G) -> %% %% Generate a "unique" vertex identifier (relative to this graph) %% --spec new_vertex_id(digraph()) -> nonempty_improper_list('$v', non_neg_integer()). +-spec new_vertex_id(digraph()) -> vertex(). new_vertex_id(G) -> NT = G#digraph.ntab, @@ -444,7 +506,10 @@ acyclic_add_edge(E, V1, V2, Label, G) -> %% Delete all paths from vertex V1 to vertex V2 %% --spec del_path(digraph(), vertex(), vertex()) -> 'true'. +-spec del_path(G, V1, V2) -> 'true' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(). del_path(G, V1, V2) -> case get_path(G, V1, V2) of @@ -463,7 +528,10 @@ del_path(G, V1, V2) -> %% been searched. %% --spec get_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_cycle(G, V) -> case one_path(out_neighbours(G, V), V, [], [V], [V], 2, G, 1) of @@ -481,7 +549,11 @@ get_cycle(G, V) -> %% if no path exists false is returned %% --spec get_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_path(G, V1, V2) -> one_path(out_neighbours(G, V1), V2, [], [V1], [V1], 1, G, 1). @@ -516,7 +588,10 @@ one_path([], _, [], _, _, _, _, _Counter) -> false. %% Like get_cycle/2, but a cycle of length one is preferred. %% --spec get_short_cycle(digraph(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_cycle(G, V) -> Vertices | 'false' when + G :: digraph(), + V :: vertex(), + Vertices :: [vertex(),...]. get_short_cycle(G, V) -> get_short_path(G, V, V). @@ -526,7 +601,11 @@ get_short_cycle(G, V) -> %% to find a short path. %% --spec get_short_path(digraph(), vertex(), vertex()) -> [vertex(),...] | 'false'. +-spec get_short_path(G, V1, V2) -> Vertices | 'false' when + G :: digraph(), + V1 :: vertex(), + V2 :: vertex(), + Vertices :: [vertex(),...]. get_short_path(G, V1, V2) -> T = new(), diff --git a/lib/stdlib/src/digraph_utils.erl b/lib/stdlib/src/digraph_utils.erl index 080cae4742..e221be15a1 100644 --- a/lib/stdlib/src/digraph_utils.erl +++ b/lib/stdlib/src/digraph_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. 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 @@ -38,48 +38,66 @@ %% A convenient type alias %% --type vertices() :: [digraph:vertex()]. - %% %% Exported functions %% --spec components(digraph()) -> vertices(). +-spec components(Digraph) -> [Component] when + Digraph :: digraph(), + Component :: [digraph:vertex()]. components(G) -> forest(G, fun inout/3). --spec strong_components(digraph()) -> vertices(). +-spec strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. strong_components(G) -> forest(G, fun in/3, revpostorder(G)). --spec cyclic_strong_components(digraph()) -> vertices(). +-spec cyclic_strong_components(Digraph) -> [StrongComponent] when + Digraph :: digraph(), + StrongComponent :: [digraph:vertex()]. cyclic_strong_components(G) -> remove_singletons(strong_components(G), G, []). --spec reachable(vertices(), digraph()) -> vertices(). +-spec reachable(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, first)). --spec reachable_neighbours(vertices(), digraph()) -> vertices(). +-spec reachable_neighbours(Vertices, Digraph) -> Reachable when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reachable :: [digraph:vertex()]. reachable_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun out/3, Vs, not_first)). --spec reaching(vertices(), digraph()) -> vertices(). +-spec reaching(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, first)). --spec reaching_neighbours(vertices(), digraph()) -> vertices(). +-spec reaching_neighbours(Vertices, Digraph) -> Reaching when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + Reaching :: [digraph:vertex()]. reaching_neighbours(Vs, G) when is_list(Vs) -> lists:append(forest(G, fun in/3, Vs, not_first)). --spec topsort(digraph()) -> vertices() | 'false'. +-spec topsort(Digraph) -> Vertices | 'false' when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. topsort(G) -> L = revpostorder(G), @@ -88,12 +106,15 @@ topsort(G) -> false -> false end. --spec is_acyclic(digraph()) -> boolean(). +-spec is_acyclic(Digraph) -> boolean() when + Digraph :: digraph(). is_acyclic(G) -> loop_vertices(G) =:= [] andalso topsort(G) =/= false. --spec arborescence_root(digraph()) -> 'no' | {'yes', digraph:vertex()}. +-spec arborescence_root(Digraph) -> 'no' | {'yes', Root} when + Digraph :: digraph(), + Root :: digraph:vertex(). arborescence_root(G) -> case digraph:no_edges(G) =:= digraph:no_vertices(G) - 1 of @@ -114,23 +135,30 @@ arborescence_root(G) -> no end. --spec is_arborescence(digraph()) -> boolean(). +-spec is_arborescence(Digraph) -> boolean() when + Digraph :: digraph(). is_arborescence(G) -> arborescence_root(G) =/= no. --spec is_tree(digraph()) -> boolean(). +-spec is_tree(Digraph) -> boolean() when + Digraph :: digraph(). is_tree(G) -> (digraph:no_edges(G) =:= digraph:no_vertices(G) - 1) andalso (length(components(G)) =:= 1). --spec loop_vertices(digraph()) -> vertices(). +-spec loop_vertices(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. loop_vertices(G) -> [V || V <- digraph:vertices(G), is_reflexive_vertex(V, G)]. --spec subgraph(digraph(), vertices()) -> digraph(). +-spec subgraph(Digraph, Vertices) -> SubGraph when + Digraph :: digraph(), + Vertices :: [digraph:vertex()], + SubGraph :: digraph(). subgraph(G, Vs) -> try @@ -140,10 +168,12 @@ subgraph(G, Vs) -> erlang:error(badarg) end. --type option() :: {'type', 'inherit' | [digraph:d_type()]} - | {'keep_labels', boolean()}. - --spec subgraph(digraph(), vertices(), [option()]) -> digraph(). +-spec subgraph(Digraph, Vertices, Options) -> SubGraph when + Digraph :: digraph(), + SubGraph :: digraph(), + Vertices :: [digraph:vertex()], + Options :: [{'type', SubgraphType} | {'keep_labels', boolean()}], + SubgraphType :: 'inherit' | [digraph:d_type()]. subgraph(G, Vs, Opts) -> try @@ -153,7 +183,9 @@ subgraph(G, Vs, Opts) -> erlang:error(badarg) end. --spec condensation(digraph()) -> digraph(). +-spec condensation(Digraph) -> CondensedDigraph when + Digraph :: digraph(), + CondensedDigraph :: digraph(). condensation(G) -> SCs = strong_components(G), @@ -176,12 +208,16 @@ condensation(G) -> ets:delete(I2C), SCG. --spec preorder(digraph()) -> vertices(). +-spec preorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. preorder(G) -> lists:reverse(revpreorder(G)). --spec postorder(digraph()) -> vertices(). +-spec postorder(Digraph) -> Vertices when + Digraph :: digraph(), + Vertices :: [digraph:vertex()]. postorder(G) -> lists:reverse(revpostorder(G)). diff --git a/lib/stdlib/src/epp.erl b/lib/stdlib/src/epp.erl index e5ccaddbb4..d804c1dee5 100644 --- a/lib/stdlib/src/epp.erl +++ b/lib/stdlib/src/epp.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 @@ -29,6 +29,7 @@ %%------------------------------------------------------------------------ -type macros() :: [{atom(), term()}]. +-type epp_handle() :: pid(). %% Epp state record. -record(epp, {file, %Current file @@ -61,14 +62,23 @@ %% parse_file(FileName, IncludePath, PreDefMacros) %% macro_defs(Epp) --spec open(file:name(), [file:name()]) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path) -> open(Name, Path, []). --spec open(file:name(), [file:name()], macros()) -> - {'ok', pid()} | {'error', term()}. +-spec open(FileName, IncludePath, PredefMacros) -> + {'ok', Epp} | {'error', ErrorDescriptor} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + PredefMacros :: macros(), + Epp :: epp_handle(), + ErrorDescriptor :: term(). open(Name, Path, Pdm) -> Self = self(), @@ -80,7 +90,8 @@ open(Name, File, StartLocation, Path, Pdm) -> Epp = spawn(fun() -> server(Self, Name, File, StartLocation,Path,Pdm) end), epp_request(Epp). --spec close(pid()) -> 'ok'. +-spec close(Epp) -> 'ok' when + Epp :: epp_handle(). close(Epp) -> %% Make sure that close is synchronous as a courtesy to test @@ -93,6 +104,13 @@ close(Epp) -> scan_erl_form(Epp) -> epp_request(Epp, scan_erl_form). +-spec parse_erl_form(Epp) -> + {'ok', AbsForm} | {'eof', Line} | {error, ErrorInfo} when + Epp :: epp_handle(), + AbsForm :: erl_parse:abstract_form(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(). + parse_erl_form(Epp) -> case epp_request(Epp, scan_erl_form) of {ok,Toks} -> @@ -107,6 +125,9 @@ macro_defs(Epp) -> %% format_error(ErrorDescriptor) -> String %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: term(). + format_error(cannot_parse) -> io_lib:format("cannot parse file, giving up", []); format_error({bad,W}) -> @@ -146,6 +167,16 @@ format_error(E) -> file:format_error(E). %% parse_file(FileName, IncludePath, [PreDefMacro]) -> %% {ok,[Form]} | {error,OpenError} +-spec parse_file(FileName, IncludePath, PredefMacros) -> + {'ok', [Form]} | {error, OpenError} when + FileName :: file:name(), + IncludePath :: [DirectoryName :: file:name()], + Form :: erl_parse:abstract_form() | {'error', ErrorInfo} | {'eof',Line}, + PredefMacros :: macros(), + Line :: erl_scan:line(), + ErrorInfo :: erl_scan:error_info() | erl_parse:error_info(), + OpenError :: file:posix() | badarg | system_limit. + parse_file(Ifile, Path, Predefs) -> case open(Ifile, Path, Predefs) of {ok,Epp} -> diff --git a/lib/stdlib/src/erl_eval.erl b/lib/stdlib/src/erl_eval.erl index ea1b179ee5..46288cf467 100644 --- a/lib/stdlib/src/erl_eval.erl +++ b/lib/stdlib/src/erl_eval.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -34,6 +34,37 @@ -import(lists, [reverse/1,foldl/3,member/2]). +-export_type([binding_struct/0]). + +-type(expression() :: erl_parse:abstract_expr()). +-type(expressions() :: [erl_parse:abstract_expr()]). +-type(expression_list() :: [expression()]). +-type(clauses() :: [erl_parse:abstract_clause()]). +-type(name() :: term()). +-type(value() :: term()). +-type(bindings() :: [{name(), value()}]). +-opaque(binding_struct() :: orddict:orddict()). + +-type(lfun_value_handler() :: fun((Name :: atom(), + Arguments :: [term()]) -> + Value :: value())). +-type(lfun_eval_handler() :: fun((Name :: atom(), + Arguments :: expression_list(), + Bindings :: binding_struct()) -> + {value, + Value :: value(), + NewBindings :: binding_struct()})). +-type(local_function_handler() :: {value, lfun_value_handler()} + | {eval, lfun_eval_handler()} + | none). + +-type(func_spec() :: {Module :: module(), Function :: atom()} | function()). +-type(nlfun_handler() :: fun((FuncSpec :: func_spec(), + Arguments :: [term()]) -> + term())). +-type(non_local_function_handler() :: {value, nlfun_handler()} + | none). + %% exprs(ExpressionSeq, Bindings) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler) %% exprs(ExpressionSeq, Bindings, LocalFuncHandler, ExternalFuncHandler) @@ -45,6 +76,11 @@ %% that there are valid constructs in Expression to be taken care of %% by a function handler but considerad errors by erl_lint. +-spec(exprs(Expressions, Bindings) -> {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs) -> case check_command(Exprs, Bs) of ok -> @@ -53,9 +89,25 @@ exprs(Exprs, Bs) -> erlang:raise(error, Error, [{?MODULE,exprs,2}]) end. +-spec(exprs(Expressions, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf) -> exprs(Exprs, Bs, Lf, none, none). +-spec(exprs(Expressions, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expressions :: expressions(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). exprs(Exprs, Bs, Lf, Ef) -> exprs(Exprs, Bs, Lf, Ef, none). @@ -75,6 +127,11 @@ exprs([E|Es], Bs0, Lf, Ef, RBs) -> %% %% Only expr/2 checks the command by calling erl_lint. See exprs/2. +-spec(expr(Expression, Bindings) -> {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs) -> case check_command([E], Bs) of ok -> @@ -83,9 +140,25 @@ expr(E, Bs) -> erlang:raise(error, Error, [{?MODULE,expr,2}]) end. +-spec(expr(Expression, Bindings, LocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf) -> expr(E, Bs, Lf, none, none). +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {value, Value, NewBindings} when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + Value :: value(), + NewBindings :: binding_struct()). expr(E, Bs, Lf, Ef) -> expr(E, Bs, Lf, Ef, none). @@ -114,6 +187,16 @@ fun_data(F) when is_function(F) -> fun_data(_T) -> false. +-spec(expr(Expression, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler, ReturnFormat) -> + {value, Value, NewBindings} | Value when + Expression :: expression(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ReturnFormat :: none | value, + Value :: value(), + NewBindings :: binding_struct()). expr({var,_,V}, Bs, _Lf, _Ef, RBs) -> case binding(V, Bs) of {value,Val} -> @@ -384,12 +467,9 @@ local_func(Func, As0, Bs0, {value,F}, value) -> local_func(Func, As0, Bs0, {value,F}, RBs) -> {As1,Bs1} = expr_list(As0, Bs0, {value,F}), ret_expr(F(Func, As1), Bs1, RBs); -local_func(Func, As0, Bs0, {value,F,Eas}, value) -> - {As1,_Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - apply(F, [Func,As1|Eas]); local_func(Func, As0, Bs0, {value,F,Eas}, RBs) -> - {As1,Bs1} = expr_list(As0, Bs0, {value,F,Eas}), - ret_expr(apply(F, [Func,As1|Eas]), Bs1, RBs); + Fun = fun(Name, Args) -> apply(F, [Name,Args|Eas]) end, + local_func(Func, As0, Bs0, {value, Fun}, RBs); local_func(Func, As, Bs, {eval,F}, RBs) -> local_func2(F(Func, As, Bs), RBs); local_func(Func, As, Bs, {eval,F,Eas}, RBs) -> @@ -613,12 +693,33 @@ eval_fun([], As, _Bs, _Lf, _Ef, _RBs) -> %% expr_list(ExpressionList, Bindings, LocalFuncHandler, ExternalFuncHandler) %% Evaluate a list of expressions "in parallel" at the same level. +-spec(expr_list(ExpressionList, Bindings) -> {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs) -> expr_list(Es, Bs, none, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf) -> expr_list(Es, Bs, Lf, none). +-spec(expr_list(ExpressionList, Bindings, LocalFunctionHandler, + NonLocalFunctionHandler) -> + {ValueList, NewBindings} when + ExpressionList :: expression_list(), + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + NonLocalFunctionHandler :: non_local_function_handler(), + ValueList :: [value()], + NewBindings :: binding_struct()). expr_list(Es, Bs, Lf, Ef) -> expr_list(Es, [], Bs, Bs, Lf, Ef). @@ -757,6 +858,15 @@ send_all([], _) -> true. %% match_clause -> {Body, Bindings} or nomatch +-spec(match_clause(Clauses, ValueList, Bindings, LocalFunctionHandler) -> + {Body, NewBindings} | nomatch when + Clauses :: clauses(), + ValueList :: [value()], + Bindings :: binding_struct(), + LocalFunctionHandler :: local_function_handler(), + Body :: expression_list(), + NewBindings :: binding_struct()). + match_clause(Cs, Vs, Bs, Lf) -> match_clause(Cs, Vs, Bs, Lf, none). @@ -973,18 +1083,30 @@ match_list(_, _, _Bs, _BBs) -> %% add_binding(Name, Value, Bindings) %% del_binding(Name, Bindings) +-spec(new_bindings() -> binding_struct()). new_bindings() -> orddict:new(). +-spec(bindings(BindingStruct :: binding_struct()) -> bindings()). bindings(Bs) -> orddict:to_list(Bs). +-spec(binding(Name, BindingStruct) -> {value, value()} | unbound when + Name :: name(), + BindingStruct :: binding_struct()). binding(Name, Bs) -> case orddict:find(Name, Bs) of {ok,Val} -> {value,Val}; error -> unbound end. +-spec(add_binding(Name, Value, BindingStruct) -> binding_struct() when + Name :: name(), + Value :: value(), + BindingStruct :: binding_struct()). add_binding(Name, Val, Bs) -> orddict:store(Name, Val, Bs). +-spec(del_binding(Name, BindingStruct) -> binding_struct() when + Name :: name(), + BindingStruct :: binding_struct()). del_binding(Name, Bs) -> orddict:erase(Name, Bs). add_bindings(Bs1, Bs2) -> diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 61ce41f714..eada563914 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-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,6 +38,10 @@ checked_ra=[] % succesfully accessed records }). +-spec(module(AbsForms, CompileOptions) -> AbsForms when + AbsForms :: [erl_parse:abstract_form()], + CompileOptions :: [compile:option()]). + %% Is is assumed that Fs is a valid list of forms. It should pass %% erl_lint without errors. module(Fs0, Opts0) -> diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index b30b02a96f..478f05e792 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. 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 @@ -54,7 +54,9 @@ %%--------------------------------------------------------------------------- %% Erlang builtin functions allowed in guards. --spec guard_bif(Name::atom(), Arity::arity()) -> boolean(). +-spec guard_bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). guard_bif(abs, 1) -> true; guard_bif(float, 1) -> true; @@ -92,7 +94,9 @@ guard_bif(binary_part, 3) -> true; guard_bif(Name, A) when is_atom(Name), is_integer(A) -> false. %% Erlang type tests. --spec type_test(Name::atom(), Arity::arity()) -> boolean(). +-spec type_test(Name, Arity) -> boolean() when + Name :: atom(), + Arity :: arity(). type_test(Name, Arity) -> new_type_test(Name, Arity) orelse old_type_test(Name, Arity). @@ -135,7 +139,9 @@ old_type_test(record, 2) -> true; old_type_test(function, 1) -> true; old_type_test(Name, A) when is_atom(Name), is_integer(A) -> false. --spec arith_op(Op::atom(), Arity::arity()) -> boolean(). +-spec arith_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). arith_op('+', 1) -> true; arith_op('-', 1) -> true; @@ -153,7 +159,9 @@ arith_op('bsl', 2) -> true; arith_op('bsr', 2) -> true; arith_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec bool_op(Op::atom(), Arity::arity()) -> boolean(). +-spec bool_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). bool_op('not', 1) -> true; bool_op('and', 2) -> true; @@ -161,7 +169,9 @@ bool_op('or', 2) -> true; bool_op('xor', 2) -> true; bool_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec comp_op(Op::atom(), Arity::arity()) -> boolean(). +-spec comp_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). comp_op('==', 2) -> true; comp_op('/=', 2) -> true; @@ -173,18 +183,25 @@ comp_op('=:=', 2) -> true; comp_op('=/=', 2) -> true; comp_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec list_op(Op::atom(), Arity::arity()) -> boolean(). +-spec list_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). list_op('++', 2) -> true; list_op('--', 2) -> true; list_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec send_op(Op::atom(), Arity::arity()) -> boolean(). +-spec send_op(OpName, Arity) -> boolean() when + OpName :: atom(), + Arity :: arity(). send_op('!', 2) -> true; send_op(Op, A) when is_atom(Op), is_integer(A) -> false. --spec op_type(atom(), arity()) -> 'arith' | 'bool' | 'comp' | 'list' | 'send'. +-spec op_type(OpName, Arity) -> Type when + OpName :: atom(), + Arity :: arity(), + Type :: 'arith' | 'bool' | 'comp' | 'list' | 'send'. op_type('+', 1) -> arith; op_type('-', 1) -> arith; @@ -221,7 +238,9 @@ op_type('!', 2) -> send. bif(erlang, Name, Arity) -> bif(Name, Arity); bif(M, F, A) when is_atom(M), is_atom(F), is_integer(A) -> false. --spec bif(Name::atom(), Arity::arity()) -> boolean(). +-spec bif(Name, Arity) -> boolean() when + Name :: atom(), + Arity::arity(). %% Returns true if erlang:Name/Arity is an auto-imported BIF, false otherwise. %% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF %% (meaning implemented in C) or not. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index cfb9f0ca98..dd0b9bc2ab 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -128,10 +128,15 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> }). -type lint_state() :: #lint{}. +-type error_description() :: term(). +-type error_info() :: {erl_scan:line(), module(), error_description()}. %% format_error(Error) %% Return a string describing the error. +-spec format_error(ErrorDescriptor) -> io_lib:chars() when + ErrorDescriptor :: error_description(). + format_error(undefined_module) -> "no module definition"; format_error({bad_module_name, M}) -> @@ -419,16 +424,39 @@ used_vars(Exprs, BindingsList) -> %% apply_lambda/2 has been called to shut lint up. N.B. these lists are %% really all ordsets! +-spec(module(AbsForms) -> {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms) -> Opts = compiler_options(Forms), St = forms(Forms, start("nofile", Opts)), return_status(St). +-spec(module(AbsForms, FileName) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName) -> Opts = compiler_options(Forms), St = forms(Forms, start(FileName, Opts)), return_status(St). +-spec(module(AbsForms, FileName, CompileOptions) -> + {ok, Warnings} | {error, Errors, Warnings} when + AbsForms :: [erl_parse:abstract_form()], + FileName :: atom() | string(), + CompileOptions :: [compile:option()], + Warnings :: [{file:filename(),[ErrorInfo]}], + Errors :: [{FileName2 :: file:filename(),[ErrorInfo]}], + ErrorInfo :: error_info()). + module(Forms, FileName, Opts0) -> %% We want the options given on the command line to take %% precedence over options in the module. @@ -1877,6 +1905,9 @@ gexpr_list(Es, Vt, St) -> %% is_guard_test(Expression) -> boolean(). %% Test if a general expression is a guard test. +-spec is_guard_test(Expr) -> boolean() when + Expr :: erl_parse:abstract_expr(). + is_guard_test(E) -> is_guard_test2(E, dict:new()). diff --git a/lib/stdlib/src/erl_parse.yrl b/lib/stdlib/src/erl_parse.yrl index 15b45d72f4..bd5d65a1e1 100644 --- a/lib/stdlib/src/erl_parse.yrl +++ b/lib/stdlib/src/erl_parse.yrl @@ -511,6 +511,15 @@ Erlang code. %% of the generated .erl file by the HiPE compiler. Please do not remove. -compile([{hipe,[{regalloc,linear_scan}]}]). +-export_type([abstract_clause/0, abstract_expr/0, abstract_form/0, + error_info/0]). + +-type abstract_clause() :: term(). +-type abstract_expr() :: term(). +-type abstract_form() :: term(). +-type error_description() :: term(). +-type error_info() :: {erl_scan:line(), module(), error_description()}. +-type token() :: {Tag :: atom(), Line :: erl_scan:line()}. %% mkop(Op, Arg) -> {op,Line,Op,Arg}. %% mkop(Left, Op, Right) -> {op,Line,Op,Left,Right}. @@ -534,11 +543,19 @@ Erlang code. %% These really suck and are only here until Calle gets multiple %% entry points working. +-spec parse_form(Tokens) -> {ok, AbsForm} | {error, ErrorInfo} when + Tokens :: [token()], + AbsForm :: abstract_form(), + ErrorInfo :: error_info(). parse_form([{'-',L1},{atom,L2,spec}|Tokens]) -> parse([{'-',L1},{'spec',L2}|Tokens]); parse_form(Tokens) -> parse(Tokens). +-spec parse_exprs(Tokens) -> {ok, ExprList} | {error, ErrorInfo} when + Tokens :: [token()], + ExprList :: [abstract_expr()], + ErrorInfo :: error_info(). parse_exprs(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],Exprs}]}} -> @@ -546,6 +563,10 @@ parse_exprs(Tokens) -> {error,_} = Err -> Err end. +-spec parse_term(Tokens) -> {ok, Term} | {error, ErrorInfo} when + Tokens :: [token()], + Term :: term(), + ErrorInfo :: error_info(). parse_term(Tokens) -> case parse([{atom,0,f},{'(',0},{')',0},{'->',0}|Tokens]) of {ok,{function,_Lf,f,0,[{clause,_Lc,[],[],[Expr]}]}} -> @@ -830,6 +851,7 @@ check_clauses(Cs, Name, Arity) -> build_try(L,Es,Scs,{Ccs,As}) -> {'try',L,Es,Scs,Ccs,As}. +-spec ret_err(_, _) -> no_return(). ret_err(L, S) -> {location,Location} = get_attribute(L, location), return_error(Location, S). @@ -846,10 +868,11 @@ mapl(F, [H|T]) -> mapl(_, []) -> []. -%% normalise(AbsTerm) -%% abstract(Term) %% Convert between the abstract form of a term and a term. +-spec normalise(AbsTerm) -> Data when + AbsTerm :: abstract_expr(), + Data :: term(). normalise({char,_,C}) -> C; normalise({integer,_,I}) -> I; normalise({float,_,F}) -> F; @@ -887,6 +910,9 @@ normalise_list([H|T]) -> normalise_list([]) -> []. +-spec abstract(Data) -> AbsTerm when + Data :: term(), + AbsTerm :: abstract_expr(). abstract(T) when is_integer(T) -> {integer,0,T}; abstract(T) when is_float(T) -> {float,0,T}; abstract(T) when is_atom(T) -> {atom,0,T}; @@ -955,13 +981,18 @@ abstract_list([H|T], Line) -> abstract_list([], _Line) -> []. -%% tokens(AbsTerm) -> [Token] -%% tokens(AbsTerm, More) -> [Token] %% Generate a list of tokens representing the abstract term. +-spec tokens(AbsTerm) -> Tokens when + AbsTerm :: abstract_expr(), + Tokens :: [token()]. tokens(Abs) -> tokens(Abs, []). +-spec tokens(AbsTerm, MoreTokens) -> Tokens when + AbsTerm :: abstract_expr(), + MoreTokens :: [token()], + Tokens :: [token()]. tokens({char,L,C}, More) -> [{char,L,C}|More]; tokens({integer,L,N}, More) -> [{integer,L,N}|More]; tokens({float,L,F}, More) -> [{float,L,F}|More]; diff --git a/lib/stdlib/src/erl_pp.erl b/lib/stdlib/src/erl_pp.erl index 66c80a45cb..7dc19f2e9b 100644 --- a/lib/stdlib/src/erl_pp.erl +++ b/lib/stdlib/src/erl_pp.erl @@ -31,25 +31,53 @@ -define(MAXLINE, 72). +-type(hook_function() :: none + | fun((Expr :: erl_parse:abstract_expr(), + CurrentIndentation :: integer(), + CurrentPrecedence :: non_neg_integer(), + HookFunction :: hook_function()) -> + io_lib:chars())). + %%% %%% Exported functions %%% +-spec(form(Form) -> io_lib:chars() when + Form :: erl_parse:abstract_form()). + form(Thing) -> form(Thing, none). +-spec(form(Form, HookFunction) -> io_lib:chars() when + Form :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + form(Thing, Hook) -> frmt(lform(Thing, Hook)). +-spec(attribute(Attribute) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form()). + attribute(Thing) -> attribute(Thing, none). +-spec(attribute(Attribute, HookFunction) -> io_lib:chars() when + Attribute :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + attribute(Thing, Hook) -> frmt(lattribute(Thing, Hook)). +-spec(function(Function) -> io_lib:chars() when + Function :: erl_parse:abstract_form()). + function(F) -> function(F, none). +-spec(function(Function, HookFunction) -> io_lib:chars() when + Function :: erl_parse:abstract_form(), + HookFunction :: hook_function()). + function(F, Hook) -> frmt(lfunction(F, Hook)). @@ -59,30 +87,67 @@ rule(R) -> rule(R, Hook) -> frmt(lrule(R, Hook)). +-spec(guard(Guard) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()]). + guard(Gs) -> guard(Gs, none). +-spec(guard(Guard, HookFunction) -> io_lib:chars() when + Guard :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + guard(Gs, Hook) -> frmt(lguard(Gs, Hook)). +-spec(exprs(Expressions) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()]). + exprs(Es) -> exprs(Es, 0, none). +-spec(exprs(Expressions, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + HookFunction :: hook_function()). + exprs(Es, Hook) -> exprs(Es, 0, Hook). +-spec(exprs(Expressions, Indent, HookFunction) -> io_lib:chars() when + Expressions :: [erl_parse:abstract_expr()], + Indent :: integer(), + HookFunction :: hook_function()). + exprs(Es, I, Hook) -> frmt({seq,[],[],[$,],lexprs(Es, Hook)}, I). +-spec(expr(Expression) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr()). + expr(E) -> frmt(lexpr(E, 0, none)). +-spec(expr(Expression, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + HookFunction :: hook_function()). + expr(E, Hook) -> frmt(lexpr(E, 0, Hook)). +-spec(expr(Expression, Indent, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + HookFunction :: hook_function()). + expr(E, I, Hook) -> frmt(lexpr(E, 0, Hook), I). +-spec(expr(Expression, Indent, Precedence, HookFunction) -> io_lib:chars() when + Expression :: erl_parse:abstract_expr(), + Indent :: integer(), + Precedence :: non_neg_integer(), + HookFunction :: hook_function()). + expr(E, I, P, Hook) -> frmt(lexpr(E, P, Hook), I). diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 18f64c46d0..718ca2e91a 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.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 @@ -99,7 +99,8 @@ %%---------------------------------------------------------------------------- --spec format_error(Error :: term()) -> string(). +-spec format_error(ErrorDescriptor) -> string() when + ErrorDescriptor :: error_description(). format_error({string,Quote,Head}) -> lists:flatten(["unterminated " ++ string_thing(Quote) ++ " starting with " ++ @@ -112,20 +113,33 @@ format_error({base,Base}) -> format_error(Other) -> lists:flatten(io_lib:write(Other)). --type string_return() :: {'ok', tokens(), location()} - | {'error', error_info(), location()}. - --spec string(String :: string()) -> string_return(). +-spec string(String) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + EndLocation :: location(), + ErrorLocation :: location(). string(String) -> string(String, 1, []). --spec string(String :: string(), StartLocation :: location()) -> - string_return(). +-spec string(String, StartLocation) -> Return when + String :: string(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, StartLocation) -> string(String, StartLocation, []). --spec string(String :: string(), StartLocation :: location(), - Options :: options()) -> string_return(). +-spec string(String, StartLocation, Options) -> Return when + String :: string(), + Options :: options(), + Return :: {'ok', Tokens :: tokens(), EndLocation} + | {'error', ErrorInfo :: error_info(), ErrorLocation}, + StartLocation :: location(), + EndLocation :: location(), + ErrorLocation :: location(). string(String, Line, Options) when ?STRING(String), ?ALINE(Line) -> string1(String, options(Options), Line, no_col, []); string(String, {Line,Column}, Options) when ?STRING(String), @@ -136,29 +150,37 @@ string(String, {Line,Column}, Options) when ?STRING(String), -type char_spec() :: string() | 'eof'. -type cont_fun() :: fun((char_spec(), #erl_scan{}, line(), column(), tokens(), any()) -> any()). --opaque return_cont() :: {string(), column(), tokens(), line(), - #erl_scan{}, cont_fun(), any()}. --type cont() :: return_cont() | []. --type tokens_result() :: {'ok', tokens(), location()} - | {'eof', location()} - | {'error', error_info(), location()}. --type tokens_return() :: {'done', tokens_result(), char_spec()} - | {'more', return_cont()}. - --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location()) -> tokens_return(). +-opaque return_cont() :: {erl_scan_continuation, + string(), column(), tokens(), line(), + #erl_scan{}, any(), cont_fun()}. +-type tokens_result() :: {'ok', Tokens :: tokens(), EndLocation :: location()} + | {'eof', EndLocation :: location()} + | {'error', ErrorInfo :: error_info(), + EndLocation :: location()}. + +-spec tokens(Continuation, CharSpec, StartLocation) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens(Cont, CharSpec, StartLocation) -> tokens(Cont, CharSpec, StartLocation, []). --spec tokens(Cont :: cont(), CharSpec :: char_spec(), - StartLocation :: location(), Options :: options()) -> - tokens_return(). +-spec tokens(Continuation, CharSpec, StartLocation, Options) -> Return when + Continuation :: return_cont() | [], + CharSpec :: char_spec(), + StartLocation :: location(), + Options :: options(), + Return :: {'done',Result :: tokens_result(),LeftOverChars :: char_spec()} + | {'more', Continuation1 :: return_cont()}. tokens([], CharSpec, Line, Options) when ?ALINE(Line) -> tokens1(CharSpec, options(Options), Line, no_col, [], fun scan/6, []); tokens([], CharSpec, {Line,Column}, Options) when ?ALINE(Line), ?COLUMN(Column) -> tokens1(CharSpec, options(Options), Line, Column, [], fun scan/6, []); -tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> +tokens({erl_scan_continuation,Cs,Col,Toks,Line,St,Any,Fun}, + CharSpec, _Loc, _Opts) -> tokens1(Cs++CharSpec, St, Line, Col, Toks, Fun, Any). -type attribute_item() :: 'column' | 'length' | 'line' @@ -172,13 +194,22 @@ tokens({Cs,Col,Toks,Line,St,Any,Fun}, CharSpec, _Loc, _Opts) -> -type token_info() :: {'category', category()} | {'symbol', symbol()} | attribute_info(). --spec token_info(token()) -> [token_info()]. +-spec token_info(Token) -> TokenInfo when + Token :: token(), + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(Token) -> Items = [category,column,length,line,symbol,text], % undefined order token_info(Token, Items). --spec token_info(token(), token_item()) -> token_info() | 'undefined'; - (token(), [token_item()]) -> [token_info()]. +-spec token_info(Token, TokenItem) -> TokenInfo | 'undefined' when + Token :: token(), + TokenItem :: token_item(), + TokenInfo :: TokenInfoTuple :: token_info(); + (Token, TokenItems) -> [TokenInfo] when + Token :: token(), + TokenItems :: [TokenItem], + TokenItem :: token_item(), + TokenInfo :: [TokenInfoTuple :: token_info()]. token_info(_Token, []) -> []; token_info(Token, [Item|Items]) when is_atom(Item) -> @@ -201,14 +232,23 @@ token_info({_Category,Attrs}, Item) -> token_info({_Category,Attrs,_Symbol}, Item) -> attributes_info(Attrs, Item). --spec attributes_info(attributes()) -> [attribute_info()]. +-spec attributes_info(Attributes) -> AttributesInfo when + Attributes :: attributes(), + AttributesInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(Attributes) -> Items = [column,length,line,text], % undefined order attributes_info(Attributes, Items). --spec attributes_info(attributes(), attribute_item()) -> - attribute_info() | 'undefined'; - (attributes(), [attribute_item()]) -> [attribute_info()]. +-spec attributes_info(Attributes, AttributeItem) -> + AttributeInfo | 'undefined' when + Attributes :: attributes(), + AttributeItem :: attribute_item(), + AttributeInfo :: AttributeInfoTuple :: attribute_info(); + (Attributes, AttributeItems) -> [AttributeInfo] when + Attributes :: attributes(), + AttributeItems :: [AttributeItem], + AttributeItem :: attribute_item(), + AttributeInfo :: [AttributeInfoTuple :: attribute_info()]. attributes_info(_Attrs, []) -> []; attributes_info(Attrs, [A|As]) when is_atom(A) -> @@ -265,9 +305,10 @@ attributes_info(Attrs, text=Item) -> attributes_info(T1, T2) -> erlang:error(badarg, [T1,T2]). --type setlineattr_fun() :: fun((info_line()) -> info_line()). - --spec set_attribute('line', attributes(), setlineattr_fun()) -> attributes(). +-spec set_attribute(AttributeItem, Attributes, SetAttributeFun) -> Attributes when + AttributeItem :: 'line', + Attributes :: attributes(), + SetAttributeFun :: fun((info_line()) -> info_line()). set_attribute(Tag, Attributes, Fun) when ?SETATTRFUN(Fun) -> set_attr(Tag, Attributes, Fun). @@ -374,7 +415,7 @@ set_attr(T1, T2, T3) -> tokens1(Cs, St, Line, Col, Toks, Fun, Any) when ?STRING(Cs); Cs =:= eof -> case Fun(Cs, St, Line, Col, Toks, Any) of {more,{Cs0,Ncol,Ntoks,Nline,Nany,Nfun}} -> - {more,{Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; + {more,{erl_scan_continuation,Cs0,Ncol,Ntoks,Nline,St,Nany,Nfun}}; {ok,Toks0,eof,Nline,Ncol} -> Res = case Toks0 of [] -> @@ -1285,7 +1326,7 @@ tabs(8) -> "\t\t\t\t\t\t\t\t"; tabs(9) -> "\t\t\t\t\t\t\t\t\t"; tabs(10) -> "\t\t\t\t\t\t\t\t\t\t". --spec reserved_word(atom()) -> boolean(). +-spec reserved_word(Atom :: atom()) -> boolean(). reserved_word('after') -> true; reserved_word('begin') -> true; reserved_word('case') -> true; diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 6e6e949e2c..afa914a456 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.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 @@ -42,7 +42,7 @@ -export([i/0, i/1, i/2, i/3]). --export_type([tab/0, tid/0]). +-export_type([tab/0, tid/0, match_spec/0]). %%----------------------------------------------------------------------------- @@ -51,22 +51,9 @@ %% a similar definition is also in erl_types -opaque tid() :: integer(). --type ext_info() :: 'md5sum' | 'object_count'. --type protection() :: 'private' | 'protected' | 'public'. --type type() :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set'. - --type table_info() :: {'name', atom()} - | {'type', type()} - | {'protection', protection()} - | {'named_table', boolean()} - | {'keypos', non_neg_integer()} - | {'size', non_neg_integer()} - | {'extended_info', [ext_info()]} - | {'version', {non_neg_integer(), non_neg_integer()}}. - %% these ones are also defined in erl_bif_types -type match_pattern() :: atom() | tuple(). --type match_specs() :: [{match_pattern(), [_], [_]}]. +-type match_spec() :: [{match_pattern(), [_], [_]}]. %%----------------------------------------------------------------------------- @@ -86,6 +73,7 @@ %% insert/2 %% is_compiled_ms/1 %% last/1 +%% member/2 %% next/2 %% prev/2 %% rename/2 @@ -101,11 +89,14 @@ %% select/1 %% select/2 %% select/3 +%% select_count/2 %% select_reverse/1 %% select_reverse/2 %% select_reverse/3 %% select_delete/2 +%% setopts/2 %% update_counter/3 +%% update_element/3 %% -opaque comp_match_spec() :: any(). %% this one is REALLY opaque @@ -119,7 +110,9 @@ match_spec_run(List, CompiledMS) -> | {tab(),integer(),integer(),binary(),list(),integer()} | {tab(),_,_,integer(),binary(),list(),integer(),integer()}. --spec repair_continuation(continuation(), match_specs()) -> continuation(). +-spec repair_continuation(Continuation, MatchSpec) -> Continuation when + Continuation :: continuation(), + MatchSpec :: match_spec(). %% $end_of_table is an allowed continuation in ets... repair_continuation('$end_of_table', _) -> @@ -153,7 +146,9 @@ repair_continuation(Untouched = {Table,N1,N2,Bin,L,N3}, MS) {Table,N1,N2,ets:match_spec_compile(MS),L,N3} end. --spec fun2ms(function()) -> match_specs(). +-spec fun2ms(LiteralFun) -> MatchSpec when + LiteralFun :: function(), + MatchSpec :: match_spec(). fun2ms(ShellFun) when is_function(ShellFun) -> %% Check that this is really a shell fun... @@ -177,7 +172,13 @@ fun2ms(ShellFun) when is_function(ShellFun) -> shell]}}) end. --spec foldl(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldl(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldl(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -198,7 +199,13 @@ do_foldl(F, Accu0, Key, T) -> ets:next(T, Key), T) end. --spec foldr(fun((_, term()) -> term()), term(), tab()) -> term(). +-spec foldr(Function, Acc0, Tab) -> Acc1 when + Function :: fun((Element :: term(), AccIn) -> AccOut), + Tab :: tab(), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(). foldr(F, Accu, T) -> ets:safe_fixtable(T, true), @@ -219,7 +226,9 @@ do_foldr(F, Accu0, Key, T) -> ets:prev(T, Key), T) end. --spec from_dets(tab(), dets:tab_name()) -> 'true'. +-spec from_dets(Tab, DetsTab) -> 'true' when + Tab :: tab(), + DetsTab :: dets:tab_name(). from_dets(EtsTable, DetsTable) -> case (catch dets:to_ets(DetsTable, EtsTable)) of @@ -235,7 +244,9 @@ from_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec to_dets(tab(), dets:tab_name()) -> dets:tab_name(). +-spec to_dets(Tab, DetsTab) -> DetsTab when + Tab :: tab(), + DetsTab :: dets:tab_name(). to_dets(EtsTable, DetsTable) -> case (catch dets:from_ets(DetsTable, EtsTable)) of @@ -251,8 +262,11 @@ to_dets(EtsTable, DetsTable) -> erlang:error(Unexpected,[EtsTable,DetsTable]) end. --spec test_ms(tuple(), match_specs()) -> - {'ok', term()} | {'error', [{'warning'|'error', string()}]}. +-spec test_ms(Tuple, MatchSpec) -> {'ok', Result} | {'error', Errors} when + Tuple :: tuple(), + MatchSpec :: match_spec(), + Result :: term(), + Errors :: [{'warning'|'error', string()}]. test_ms(Term, MS) -> case erlang:match_spec_test(Term, MS, table) of @@ -262,7 +276,11 @@ test_ms(Term, MS) -> Error end. --spec init_table(tab(), fun(('read' | 'close') -> term())) -> 'true'. +-spec init_table(Tab, InitFun) -> 'true' when + Tab :: tab(), + InitFun :: fun((Arg) -> Res), + Arg :: 'read' | 'close', + Res :: 'end_of_input' | {Objects :: [term()], InitFun} | term(). init_table(Table, Fun) -> ets:delete_all_objects(Table), @@ -287,7 +305,9 @@ init_table_sub(Table, [H|T]) -> ets:insert(Table, H), init_table_sub(Table, T). --spec match_delete(tab(), match_pattern()) -> 'true'. +-spec match_delete(Tab, Pattern) -> 'true' when + Tab :: tab(), + Pattern :: match_pattern(). match_delete(Table, Pattern) -> ets:select_delete(Table, [{Pattern,[],[true]}]), @@ -295,7 +315,9 @@ match_delete(Table, Pattern) -> %% Produce a list of tuples from a table --spec tab2list(tab()) -> [tuple()]. +-spec tab2list(Tab) -> [Object] when + Tab :: tab(), + Object :: tuple(). tab2list(T) -> ets:match_object(T, '_'). @@ -334,15 +356,21 @@ do_filter(Tab, Key, F, A, Ack) -> md5sum = false :: boolean() }). --type fname() :: string() | atom(). --type t2f_option() :: {'extended_info', [ext_info()]}. - --spec tab2file(tab(), fname()) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Reason :: term(). tab2file(Tab, File) -> tab2file(Tab, File, []). --spec tab2file(tab(), fname(), [t2f_option()]) -> 'ok' | {'error', term()}. +-spec tab2file(Tab, Filename, Options) -> 'ok' | {'error', Reason} when + Tab :: tab(), + Filename :: file:name(), + Options :: [Option], + Option :: {'extended_info', [ExtInfo]}, + ExtInfo :: 'md5sum' | 'object_count', + Reason :: term(). tab2file(Tab, File, Options) -> try @@ -501,14 +529,20 @@ parse_ft_info_options(_,Malformed) -> %% Opt := {verify,boolean()} %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --type f2t_option() :: {'verify', boolean()}. - --spec file2tab(fname()) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Reason :: term(). file2tab(File) -> file2tab(File, []). --spec file2tab(fname(), [f2t_option()]) -> {'ok', tab()} | {'error', term()}. +-spec file2tab(Filename, Options) -> {'ok', Tab} | {'error', Reason} when + Filename :: file:name(), + Tab :: tab(), + Options :: [Option], + Option :: {'verify', boolean()}, + Reason :: term(). file2tab(File, Opts) -> try @@ -895,7 +929,22 @@ named_table(false) -> []. %% information %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec tabfile_info(fname()) -> {'ok', [table_info()]} | {'error', term()}. +-spec tabfile_info(Filename) -> {'ok', TableInfo} | {'error', Reason} when + Filename :: file:name(), + TableInfo :: [InfoItem], + InfoItem :: {'name', atom()} + | {'type', Type} + | {'protection', Protection} + | {'named_table', boolean()} + | {'keypos', non_neg_integer()} + | {'size', non_neg_integer()} + | {'extended_info', [ExtInfo]} + | {'version', {Major :: non_neg_integer(), + Minor :: non_neg_integer()}}, + ExtInfo :: 'md5sum' | 'object_count', + Type :: 'bag' | 'duplicate_bag' | 'ordered_set' | 'set', + Protection :: 'private' | 'protected' | 'public', + Reason :: term(). tabfile_info(File) when is_list(File) ; is_atom(File) -> try @@ -932,20 +981,22 @@ tabfile_info(File) when is_list(File) ; is_atom(File) -> {error,ExReason} end. --type qlc__query_handle() :: term(). %% XXX: belongs in 'qlc' - --type num_objects() :: 'default' | pos_integer(). --type trav_method() :: 'first_next' | 'last_prev' - | 'select' | {'select', match_specs()}. --type table_option() :: {'n_objects', num_objects()} - | {'traverse', trav_method()}. - --spec table(tab()) -> qlc__query_handle(). +-spec table(Tab) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(). table(Tab) -> table(Tab, []). --spec table(tab(), table_option() | [table_option()]) -> qlc__query_handle(). +-spec table(Tab, Options) -> QueryHandle when + Tab :: tab(), + QueryHandle :: qlc:query_handle(), + Options :: [Option] | Option, + Option :: {'n_objects', NObjects} + | {'traverse', TraverseMethod}, + NObjects :: 'default' | pos_integer(), + TraverseMethod :: 'first_next' | 'last_prev' + | 'select' | {'select', MatchSpec :: match_spec()}. table(Tab, Opts) -> case options(Opts, [traverse, n_objects]) of @@ -1135,7 +1186,8 @@ to_string(X) -> lists:flatten(io_lib:format("~p", [X])). %% view a specific table --spec i(tab()) -> 'ok'. +-spec i(Tab) -> 'ok' when + Tab :: tab(). i(Tab) -> i(Tab, 40). diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl index 2a5b08b581..3f31852afc 100644 --- a/lib/stdlib/src/file_sorter.erl +++ b/lib/stdlib/src/file_sorter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -50,12 +50,68 @@ %%% Exported functions %%% +-export_type([reason/0]). + +-type(file_name() :: file:name()). +-type(file_names() :: [file:name()]). +-type(i_command() :: read | close). +-type(i_reply() :: end_of_input | {end_of_input, value()} + | {[object()], infun()} | input_reply()). +-type(infun() :: fun((i_command()) -> i_reply())). +-type(input() :: file_names() | infun()). +-type(input_reply() :: term()). +-type(o_command() :: {value, value()} | [object()] | close). +-type(o_reply() :: outfun() | output_reply()). +-type(object() :: term() | binary()). +-type(outfun() :: fun((o_command()) -> o_reply())). +-type(output() :: file_name() | outfun()). +-type(output_reply() :: term()). +-type(value() :: term()). + +-type(options() :: [option()] | option()). +-type(option() :: {compressed, boolean()} + | {header, header_length()} + | {format, format()} + | {no_files, no_files()} + | {order, order()} + | {size, size()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(format() :: binary_term | term | binary | format_fun()). +-type(format_fun() :: fun((binary()) -> term())). +-type(header_length() :: pos_integer()). +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(no_files() :: pos_integer()). % > 1 +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(size() :: non_neg_integer()). +-type(tmp_directory() :: [] | file:name()). + +-type(reason() :: bad_object + | {bad_object, file_name()} + | {bad_term, file_name()} + | {file_error, file_name(), + file:posix() | badarg | system_limit} + | {premature_eof, file_name()}). + +-spec(sort(FileName) -> Reply when + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(FileName) -> sort([FileName], FileName). +-spec(sort(Input, Output) -> Reply when + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input, Output) -> sort(Input, Output, []). +-spec(sort(Input, Output, Options) -> Reply when + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). sort(Input0, Output0, Options) -> case {is_input(Input0), maybe_output(Output0), options(Options)} of {{true,Input}, {true,Output}, #opts{}=Opts} -> @@ -64,12 +120,27 @@ sort(Input0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Input0, Output0, Options]) end. +-spec(keysort(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, FileName) -> keysort(KeyPos, [FileName], FileName). +-spec(keysort(KeyPos, Input, Output) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input, Output) -> keysort(KeyPos, Input, Output, []). +-spec(keysort(KeyPos, Input, Output, Options) -> Reply when + KeyPos :: key_pos(), + Input :: input(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | input_reply() | output_reply()). keysort(KeyPos, Input0, Output0, Options) -> R = case {is_keypos(KeyPos), is_input(Input0), maybe_output(Output0), options(Options)} of @@ -89,9 +160,18 @@ keysort(KeyPos, Input0, Output0, Options) -> badarg(culprit(O), [KeyPos, Input0, Output0, Options]) end. +-spec(merge(FileNames, Output) -> Reply when + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files, Output) -> merge(Files, Output, []). +-spec(merge(FileNames, Output, Options) -> Reply when + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). merge(Files0, Output0, Options) -> case {is_files(Files0), maybe_output(Output0), options(Options)} of %% size not used @@ -101,9 +181,20 @@ merge(Files0, Output0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Output0, Options]) end. +-spec(keymerge(KeyPos, FileNames, Output) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files, Output) -> keymerge(KeyPos, Files, Output, []). +-spec(keymerge(KeyPos, FileNames, Output, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Output :: output(), + Options :: options(), + Reply :: ok | {error, reason()} | output_reply()). keymerge(KeyPos, Files0, Output0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), maybe_output(Output0), options(Options)} of @@ -123,9 +214,21 @@ keymerge(KeyPos, Files0, Output0, Options) -> badarg(culprit(O), [KeyPos, Files0, Output0, Options]) end. +-spec(check(FileName) -> Reply when + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). check(FileName) -> check([FileName], []). +-spec(check(FileNames, Options) -> Reply when + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). check(Files0, Options) -> case {is_files(Files0), options(Options)} of {{true,Files}, #opts{}=Opts} -> @@ -134,9 +237,23 @@ check(Files0, Options) -> badarg(culprit(tuple_to_list(T)), [Files0, Options]) end. +-spec(keycheck(KeyPos, FileName) -> Reply when + KeyPos :: key_pos(), + FileName :: file_name(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + TermPosition :: pos_integer()). keycheck(KeyPos, FileName) -> keycheck(KeyPos, [FileName], []). +-spec(keycheck(KeyPos, FileNames, Options) -> Reply when + KeyPos :: key_pos(), + FileNames :: file_names(), + Options :: options(), + Reply :: {ok, [Result]} | {error, reason()}, + Result :: {FileName, TermPosition, term()}, + FileName :: file_name(), + TermPosition :: pos_integer()). keycheck(KeyPos, Files0, Options) -> R = case {is_keypos(KeyPos), is_files(Files0), options(Options)} of {_, _, #opts{format = binary}} -> diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index c845b61204..d532cea187 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.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 @@ -40,13 +40,19 @@ erlang:error(UnUsUalVaRiAbLeNaMe) end). +-type filename() :: file:name(). +-type dirname() :: filename(). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec wildcard(file:name()) -> [file:filename()]. +-spec wildcard(Wildcard) -> [file:filename()] when + Wildcard :: filename() | dirname(). wildcard(Pattern) when is_list(Pattern) -> ?HANDLE_ERROR(do_wildcard(Pattern, file)). --spec wildcard(file:name(), file:name() | atom()) -> [file:filename()]. +-spec wildcard(Wildcard, Cwd) -> [file:filename()] when + Wildcard :: filename() | dirname(), + Cwd :: dirname(). wildcard(Pattern, Cwd) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, file)); wildcard(Pattern, Mod) when is_list(Pattern), is_atom(Mod) -> @@ -57,7 +63,8 @@ wildcard(Pattern, Cwd, Mod) when is_list(Pattern), (is_list(Cwd) or is_binary(Cwd)), is_atom(Mod) -> ?HANDLE_ERROR(do_wildcard(Pattern, Cwd, Mod)). --spec is_dir(file:name()) -> boolean(). +-spec is_dir(Name) -> boolean() when + Name :: filename() | dirname(). is_dir(Dir) -> do_is_dir(Dir, file). @@ -65,7 +72,8 @@ is_dir(Dir) -> is_dir(Dir, Mod) when is_atom(Mod) -> do_is_dir(Dir, Mod). --spec is_file(file:name()) -> boolean(). +-spec is_file(Name) -> boolean() when + Name :: filename() | dirname(). is_file(File) -> do_is_file(File, file). @@ -73,7 +81,8 @@ is_file(File) -> is_file(File, Mod) when is_atom(Mod) -> do_is_file(File, Mod). --spec is_regular(file:name()) -> boolean(). +-spec is_regular(Name) -> boolean() when + Name :: filename(). is_regular(File) -> do_is_regular(File, file). @@ -81,7 +90,13 @@ is_regular(File) -> is_regular(File, Mod) when is_atom(Mod) -> do_is_regular(File, Mod). --spec fold_files(file:name(), string(), boolean(), fun((_,_) -> _), _) -> _. +-spec fold_files(Dir, RegExp, Recursive, Fun, AccIn) -> AccOut when + Dir :: dirname(), + RegExp :: string(), + Recursive :: boolean(), + Fun :: fun((F :: file:filename(), AccIn) -> AccOut), + AccIn :: term(), + AccOut :: term(). fold_files(Dir, RegExp, Recursive, Fun, Acc) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, file). @@ -89,7 +104,8 @@ fold_files(Dir, RegExp, Recursive, Fun, Acc) -> fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod) when is_atom(Mod) -> do_fold_files(Dir, RegExp, Recursive, Fun, Acc, Mod). --spec last_modified(file:name()) -> file:date_time() | 0. +-spec last_modified(Name) -> file:date_time() | 0 when + Name :: filename() | dirname(). last_modified(File) -> do_last_modified(File, file). @@ -97,7 +113,8 @@ last_modified(File) -> last_modified(File, Mod) when is_atom(Mod) -> do_last_modified(File, Mod). --spec file_size(file:name()) -> non_neg_integer(). +-spec file_size(Filename) -> non_neg_integer() when + Filename :: filename(). file_size(File) -> do_file_size(File, file). @@ -237,7 +254,9 @@ do_file_size(File, Mod) -> %% +type X = filename() | dirname() %% ensures that the directory name required to create D exists --spec ensure_dir(file:name()) -> 'ok' | {'error', file:posix()}. +-spec ensure_dir(Name) -> 'ok' | {'error', Reason} when + Name :: filename() | dirname(), + Reason :: file:posix(). ensure_dir("/") -> ok; ensure_dir(F) -> diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index 24abf1e977..1cb9e4a25e 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.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 @@ -61,12 +61,15 @@ %% (for WIN32): absname("/") -> "D:/" --spec absname(file:name()) -> file:filename(). +-spec absname(Filename) -> file:filename() when + Filename :: file:name(). absname(Name) -> {ok, Cwd} = file:get_cwd(), absname(Name, Cwd). --spec absname(file:name(), file:filename()) -> file:filename(). +-spec absname(Filename, Dir) -> file:filename() when + Filename :: file:name(), + Dir :: file:filename(). absname(Name, AbsBase) when is_binary(Name), is_list(AbsBase) -> absname(Name,filename_string_to_binary(AbsBase)); absname(Name, AbsBase) when is_list(Name), is_binary(AbsBase) -> @@ -119,7 +122,9 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> %% This is just a join/2, but assumes that %% AbsBase must be absolute and Name must be relative. --spec absname_join(file:filename(), file:name()) -> file:filename(). +-spec absname_join(Dir, Filename) -> file:filename() when + Dir :: file:filename(), + Filename :: file:name(). absname_join(AbsBase, Name) -> join(AbsBase, flatten(Name)). @@ -131,7 +136,8 @@ absname_join(AbsBase, Name) -> %% basename("/usr/foo/") -> "foo" (trailing slashes ignored) %% basename("/") -> [] --spec basename(file:name()) -> file:filename(). +-spec basename(Filename) -> file:filename() when + Filename :: file:name(). basename(Name) when is_binary(Name) -> case os:type() of {win32,_} -> @@ -192,7 +198,9 @@ skip_prefix(Name, _) -> %% rootname(basename("xxx.jam")) -> "xxx" %% rootname(basename("xxx.erl")) -> "xxx" --spec basename(file:name(), file:name()) -> file:filename(). +-spec basename(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). basename(Name, Ext) when is_binary(Name), is_list(Ext) -> basename(Name,filename_string_to_binary(Ext)); basename(Name, Ext) when is_list(Name), is_binary(Ext) -> @@ -240,7 +248,8 @@ basename([], _Ext, Tail, _DrvSep2) -> %% Example: dirname("/usr/src/kalle.erl") -> "/usr/src", %% dirname("kalle.erl") -> "." --spec dirname(file:name()) -> file:filename(). +-spec dirname(Filename) -> file:filename() when + Filename :: file:name(). dirname(Name) when is_binary(Name) -> {Dsep,Drivesep} = separators(), SList = case Dsep of @@ -332,7 +341,8 @@ dirjoin1([H|T],Acc,Sep) -> %% %% On Windows: fn:dirname("\\usr\\src/kalle.erl") -> "/usr/src" --spec extension(file:name()) -> file:filename(). +-spec extension(Filename) -> file:filename() when + Filename :: file:name(). extension(Name) when is_binary(Name) -> {Dsep,_} = separators(), SList = case Dsep of @@ -374,7 +384,8 @@ extension([], Result, _OsType) -> %% Joins a list of filenames with directory separators. --spec join([file:filename()]) -> file:filename(). +-spec join(Components) -> file:filename() when + Components :: [file:filename()]. join([Name1, Name2|Rest]) -> join([join(Name1, Name2)|Rest]); join([Name]) when is_list(Name) -> @@ -386,7 +397,9 @@ join([Name]) when is_atom(Name) -> %% Joins two filenames with directory separators. --spec join(file:filename(), file:filename()) -> file:filename(). +-spec join(Name1, Name2) -> file:filename() when + Name1 :: file:filename(), + Name2 :: file:filename(). join(Name1, Name2) when is_list(Name1), is_list(Name2) -> OsType = major_os_type(), case pathtype(Name2) of @@ -494,7 +507,8 @@ append(Dir, Name) -> %% current working volume. (Windows only) %% Example: a:bar.erl, /temp/foo.erl --spec pathtype(file:name()) -> 'absolute' | 'relative' | 'volumerelative'. +-spec pathtype(Path) -> 'absolute' | 'relative' | 'volumerelative' when + Path :: file:name(). pathtype(Atom) when is_atom(Atom) -> pathtype(atom_to_list(Atom)); pathtype(Name) when is_list(Name) or is_binary(Name) -> @@ -547,7 +561,8 @@ win32_pathtype(_) -> relative. %% Examples: rootname("/jam.src/kalle") -> "/jam.src/kalle" %% rootname("/jam.src/foo.erl") -> "/jam.src/foo" --spec rootname(file:name()) -> file:filename(). +-spec rootname(Filename) -> file:filename() when + Filename :: file:name(). rootname(Name) when is_binary(Name) -> list_to_binary(rootname(binary_to_list(Name))); % No need to handle unicode, . is < 128 rootname(Name0) -> @@ -576,7 +591,9 @@ rootname([], Root, _Ext, _OsType) -> %% Examples: rootname("/jam.src/kalle.jam", ".erl") -> "/jam.src/kalle.jam" %% rootname("/jam.src/foo.erl", ".erl") -> "/jam.src/foo" --spec rootname(file:name(), file:name()) -> file:filename(). +-spec rootname(Filename, Ext) -> file:filename() when + Filename :: file:name(), + Ext :: file:name(). rootname(Name, Ext) when is_binary(Name), is_binary(Ext) -> list_to_binary(rootname(binary_to_list(Name),binary_to_list(Ext))); rootname(Name, Ext) when is_binary(Name) -> @@ -602,7 +619,9 @@ rootname2([Char|Rest], Ext, Result) when is_integer(Char) -> %% split("foo/bar") -> ["foo", "bar"] %% split("a:\\msdev\\include") -> ["a:/", "msdev", "include"] --spec split(file:name()) -> [file:filename()]. +-spec split(Filename) -> Components when + Filename :: file:name(), + Components :: [file:filename()]. split(Name) when is_binary(Name) -> case os:type() of {win32, _} -> win32_splitb(Name); @@ -695,7 +714,8 @@ split([], Comp, Components, OsType) -> %% will be converted to backslashes. On all platforms, the %% name will be normalized as done by join/1. --spec nativename(file:filename()) -> file:filename(). +-spec nativename(Path) -> file:filename() when + Path :: file:filename(). nativename(Name0) -> Name = join([Name0]), %Normalize. case os:type() of @@ -747,12 +767,17 @@ separators() -> %% The paths in the {outdir, Path} and {i, Path} options are guaranteed %% to be absolute. --type rule() :: {string(), string()}. --type ecode() :: 'non_existing' | 'preloaded' | 'interpreted'. --type option() :: {'i', string()} | {'outdir', string()} | {'d', atom()}. - --spec find_src(atom() | string()) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod) -> Default = [{"", ""}, {"ebin", "src"}, {"ebin", "esrc"}], Rules = @@ -763,8 +788,18 @@ find_src(Mod) -> end, find_src(Mod, Rules). --spec find_src(atom() | string(), [rule()]) -> - {string(), [option()]} | {'error', {ecode(), atom()}}. +-spec find_src(Beam, Rules) -> {SourceFile, Options} + | {error, {ErrorReason, Module}} when + Beam :: Module | Filename, + Filename :: atom() | string(), + Rules :: [{BinSuffix :: string(), SourceSuffix :: string()}], + Module :: module(), + SourceFile :: string(), + Options :: [Option], + Option :: {'i', Path :: string()} + | {'outdir', Path :: string()} + | {'d', atom()}, + ErrorReason :: 'non_existing' | 'preloaded' | 'interpreted'. find_src(Mod, Rules) when is_atom(Mod) -> find_src(atom_to_list(Mod), Rules); find_src(File0, Rules) when is_list(File0) -> @@ -890,7 +925,8 @@ major_os_type() -> %% flatten(List) %% Flatten a list, also accepting atoms. --spec flatten(file:name()) -> file:filename(). +-spec flatten(Filename) -> file:filename() when + Filename :: file:name(). flatten(Bin) when is_binary(Bin) -> Bin; flatten(List) -> diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index fc5beb28b0..91d21d869c 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -197,6 +197,7 @@ %% Some types. -type gb_set_node() :: 'nil' | {term(), _, _}. +-opaque iter() :: [gb_set_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -205,38 +206,47 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec empty() -> gb_set(). +-spec empty() -> Set when + Set :: gb_set(). empty() -> {0, nil}. --spec new() -> gb_set(). +-spec new() -> Set when + Set :: gb_set(). new() -> empty(). --spec is_empty(gb_set()) -> boolean(). +-spec is_empty(Set) -> boolean() when + Set :: gb_set(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: gb_set(). size({Size, _}) -> Size. --spec singleton(term()) -> gb_set(). +-spec singleton(Element) -> gb_set() when + Element :: term(). singleton(Key) -> {1, {Key, nil, nil}}. --spec is_element(term(), gb_set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_element(Key, S) -> is_member(Key, S). --spec is_member(term(), gb_set()) -> boolean(). +-spec is_member(Element, Set) -> boolean() when + Element :: term(), + Set :: gb_set(). is_member(Key, {_, T}) -> is_member_1(Key, T). @@ -250,7 +260,10 @@ is_member_1(_, {_, _, _}) -> is_member_1(_, nil) -> false. --spec insert(term(), gb_set()) -> gb_set(). +-spec insert(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). insert(Key, {S, T}) -> S1 = S + 1, @@ -306,7 +319,9 @@ count({_, Sm, Bi}) -> count(nil) -> {1, 0}. --spec balance(gb_set()) -> gb_set(). +-spec balance(Set1) -> Set2 when + Set1 :: gb_set(), + Set2 :: gb_set(). balance({S, T}) -> {S, balance(T, S)}. @@ -331,12 +346,18 @@ balance_list_1([Key | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec add_element(term(), gb_set()) -> gb_set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add_element(X, S) -> add(X, S). --spec add(term(), gb_set()) -> gb_set(). +-spec add(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). add(X, S) -> case is_member(X, S) of @@ -346,23 +367,33 @@ add(X, S) -> insert(X, S) end. --spec from_list([term()]) -> gb_set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: gb_set(). from_list(L) -> from_ordset(ordsets:from_list(L)). --spec from_ordset([term()]) -> gb_set(). +-spec from_ordset(List) -> Set when + List :: [term()], + Set :: gb_set(). from_ordset(L) -> S = length(L), {S, balance_list(L, S)}. --spec del_element(term(), gb_set()) -> gb_set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). del_element(Key, S) -> delete_any(Key, S). --spec delete_any(term(), gb_set()) -> gb_set(). +-spec delete_any(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete_any(Key, S) -> case is_member(Key, S) of @@ -372,7 +403,10 @@ delete_any(Key, S) -> S end. --spec delete(term(), gb_set()) -> gb_set(). +-spec delete(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: gb_set(), + Set2 :: gb_set(). delete(Key, {S, T}) -> {S - 1, delete_1(Key, T)}. @@ -394,7 +428,10 @@ merge(Smaller, Larger) -> {Key, Larger1} = take_smallest1(Larger), {Key, Smaller, Larger1}. --spec take_smallest(gb_set()) -> {term(), gb_set()}. +-spec take_smallest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_smallest({S, T}) -> {Key, Larger} = take_smallest1(T), @@ -406,7 +443,8 @@ take_smallest1({Key, Smaller, Larger}) -> {Key1, Smaller1} = take_smallest1(Smaller), {Key1, {Key, Smaller1, Larger}}. --spec smallest(gb_set()) -> term(). +-spec smallest(Set) -> term() when + Set :: gb_set(). smallest({_, T}) -> smallest_1(T). @@ -416,7 +454,10 @@ smallest_1({Key, nil, _Larger}) -> smallest_1({_Key, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_set()) -> {term(), gb_set()}. +-spec take_largest(Set1) -> {Element, Set2} when + Set1 :: gb_set(), + Set2 :: gb_set(), + Element :: term(). take_largest({S, T}) -> {Key, Smaller} = take_largest1(T), @@ -428,7 +469,8 @@ take_largest1({Key, Smaller, Larger}) -> {Key1, Larger1} = take_largest1(Larger), {Key1, {Key, Smaller, Larger1}}. --spec largest(gb_set()) -> term(). +-spec largest(Set) -> term() when + Set :: gb_set(). largest({_, T}) -> largest_1(T). @@ -438,7 +480,9 @@ largest_1({Key, _Smaller, nil}) -> largest_1({_Key, _Smaller, Larger}) -> largest_1(Larger). --spec to_list(gb_set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: gb_set(), + List :: [term()]. to_list({_, T}) -> to_list(T, []). @@ -449,7 +493,9 @@ to_list({Key, Small, Big}, L) -> to_list(Small, [Key | to_list(Big, L)]); to_list(nil, L) -> L. --spec iterator(gb_set()) -> [term()]. +-spec iterator(Set) -> Iter when + Set :: gb_set(), + Iter :: iter(). iterator({_, T}) -> iterator(T, []). @@ -464,7 +510,10 @@ iterator({_, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([term()]) -> {term(), [term()]} | 'none'. +-spec next(Iter1) -> {Element, Iter2} | 'none' when + Iter1 :: iter(), + Iter2 :: iter(), + Element :: term(). next([{X, _, T} | As]) -> {X, iterator(T, As)}; @@ -494,7 +543,10 @@ next([]) -> %% traversing the elements can be devised, but they all have higher %% overhead. --spec union(gb_set(), gb_set()) -> gb_set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). union({N1, T1}, {N2, T2}) when N2 < N1 -> union(to_list_1(T2), N2, T1, N1); @@ -596,7 +648,9 @@ balance_revlist_1([Key | L], 1) -> balance_revlist_1(L, 0) -> {nil, L}. --spec union([gb_set()]) -> gb_set(). +-spec union(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). union([S | Ss]) -> union_list(S, Ss); @@ -609,7 +663,10 @@ union_list(S, []) -> S. %% The rest is modelled on the above. --spec intersection(gb_set(), gb_set()) -> gb_set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). intersection({N1, T1}, {N2, T2}) when N2 < N1 -> intersection(to_list_1(T2), N2, T1, N1); @@ -657,7 +714,9 @@ intersection_2([], _, As, S) -> intersection_2(_, [], As, S) -> {S, balance_revlist(As, S)}. --spec intersection([gb_set(),...]) -> gb_set(). +-spec intersection(SetList) -> Set when + SetList :: [gb_set(),...], + Set :: gb_set(). intersection([S | Ss]) -> intersection_list(S, Ss). @@ -666,7 +725,9 @@ intersection_list(S, [S1 | Ss]) -> intersection_list(intersection(S, S1), Ss); intersection_list(S, []) -> S. --spec is_disjoint(gb_set(), gb_set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_disjoint({N1, T1}, {N2, T2}) when N1 < N2 -> is_disjoint_1(T1, T2); @@ -694,12 +755,18 @@ is_disjoint_1(_, nil) -> %% the sets. Therefore, we always build a new tree, and thus we need to %% traverse the whole element list of the left operand. --spec subtract(gb_set(), gb_set()) -> gb_set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). subtract(S1, S2) -> difference(S1, S2). --spec difference(gb_set(), gb_set()) -> gb_set(). +-spec difference(Set1, Set2) -> Set3 when + Set1 :: gb_set(), + Set2 :: gb_set(), + Set3 :: gb_set(). difference({N1, T1}, {N2, T2}) -> difference(to_list_1(T1), N1, T2, N2). @@ -747,7 +814,9 @@ difference_2(Xs, [], As, S) -> %% Subset testing is much the same thing as set difference, but %% without the construction of a new set. --spec is_subset(gb_set(), gb_set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: gb_set(), + Set2 :: gb_set(). is_subset({N1, T1}, {N2, T2}) -> is_subset(to_list_1(T1), N1, T2, N2). @@ -788,18 +857,28 @@ is_subset_2(_, []) -> %% For compatibility with `sets': --spec is_set(term()) -> boolean(). +-spec is_set(Term) -> boolean() when + Term :: term(). is_set({0, nil}) -> true; is_set({N, {_, _, _}}) when is_integer(N), N >= 0 -> true; is_set(_) -> false. --spec filter(fun((term()) -> boolean()), gb_set()) -> gb_set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: gb_set(), + Set2 :: gb_set(). filter(F, S) -> from_ordset([X || X <- to_list(S), F(X)]). --spec fold(fun((term(), term()) -> term()), term(), gb_set()) -> term(). +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Set :: gb_set(). fold(F, A, {_, T}) when is_function(F, 2) -> fold_1(F, A, T). diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index d37786a100..6ad861ff5b 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -153,6 +153,7 @@ %% Some types. -type gb_tree_node() :: 'nil' | {_, _, _, _}. +-opaque iter() :: [gb_tree_node()]. %% A declaration equivalent to the following is currently hard-coded %% in erl_types.erl @@ -166,21 +167,26 @@ empty() -> {0, nil}. --spec is_empty(gb_tree()) -> boolean(). +-spec is_empty(Tree) -> boolean() when + Tree :: gb_tree(). is_empty({0, nil}) -> true; is_empty(_) -> false. --spec size(gb_tree()) -> non_neg_integer(). +-spec size(Tree) -> non_neg_integer() when + Tree :: gb_tree(). size({Size, _}) when is_integer(Size), Size >= 0 -> Size. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec lookup(term(), gb_tree()) -> 'none' | {'value', term()}. +-spec lookup(Key, Tree) -> 'none' | {'value', Val} when + Key :: term(), + Val :: term(), + Tree :: gb_tree(). lookup(Key, {_, T}) -> lookup_1(Key, T). @@ -205,7 +211,9 @@ lookup_1(_, nil) -> %% This is a specialized version of `lookup'. --spec is_defined(term(), gb_tree()) -> boolean(). +-spec is_defined(Key, Tree) -> boolean() when + Key :: term(), + Tree :: gb_tree(). is_defined(Key, {_, T}) -> is_defined_1(Key, T). @@ -223,7 +231,10 @@ is_defined_1(_, nil) -> %% This is a specialized version of `lookup'. --spec get(term(), gb_tree()) -> term(). +-spec get(Key, Tree) -> Val when + Key :: term(), + Tree :: gb_tree(), + Val :: term(). get(Key, {_, T}) -> get_1(Key, T). @@ -237,7 +248,11 @@ get_1(_, {_, Value, _, _}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec update(term(), term(), gb_tree()) -> gb_tree(). +-spec update(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). update(Key, Val, {S, T}) -> T1 = update_1(Key, Val, T), @@ -254,7 +269,11 @@ update_1(Key, Value, {_, _, Smaller, Bigger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec insert(term(), term(), gb_tree()) -> gb_tree(). +-spec insert(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). insert(Key, Val, {S, T}) when is_integer(S) -> S1 = S+1, @@ -303,7 +322,11 @@ insert_1(Key, _, _, _) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec enter(term(), term(), gb_tree()) -> gb_tree(). +-spec enter(Key, Val, Tree1) -> Tree2 when + Key :: term(), + Val :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). enter(Key, Val, T) -> case is_defined(Key, T) of @@ -326,7 +349,9 @@ count(nil) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec balance(gb_tree()) -> gb_tree(). +-spec balance(Tree1) -> Tree2 when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). balance({S, T}) -> {S, balance(T, S)}. @@ -351,7 +376,9 @@ balance_list_1([{Key, Val} | L], 1) -> balance_list_1(L, 0) -> {nil, L}. --spec from_orddict([{_,_}]) -> gb_tree(). +-spec from_orddict(List) -> Tree when + List :: [{Key :: term(), Val :: term()}], + Tree :: gb_tree(). from_orddict(L) -> S = length(L), @@ -359,7 +386,10 @@ from_orddict(L) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec delete_any(term(), gb_tree()) -> gb_tree(). +-spec delete_any(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete_any(Key, T) -> case is_defined(Key, T) of @@ -371,7 +401,10 @@ delete_any(Key, T) -> %%% delete. Assumes that key is present. --spec delete(term(), gb_tree()) -> gb_tree(). +-spec delete(Key, Tree1) -> Tree2 when + Key :: term(), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). delete(Key, {S, T}) when is_integer(S), S >= 0 -> {S - 1, delete_1(Key, T)}. @@ -397,7 +430,11 @@ merge(Smaller, Larger) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec take_smallest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_smallest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_smallest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Larger} = take_smallest1(Tree), @@ -409,7 +446,10 @@ take_smallest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Smaller1} = take_smallest1(Smaller), {Key1, Value1, {Key, Value, Smaller1, Larger}}. --spec smallest(gb_tree()) -> {term(), term()}. +-spec smallest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). smallest({_, Tree}) -> smallest_1(Tree). @@ -419,7 +459,11 @@ smallest_1({Key, Value, nil, _Larger}) -> smallest_1({_Key, _Value, Smaller, _Larger}) -> smallest_1(Smaller). --spec take_largest(gb_tree()) -> {term(), term(), gb_tree()}. +-spec take_largest(Tree1) -> {Key, Val, Tree2} when + Tree1 :: gb_tree(), + Tree2 :: gb_tree(), + Key :: term(), + Val :: term(). take_largest({Size, Tree}) when is_integer(Size), Size >= 0 -> {Key, Value, Smaller} = take_largest1(Tree), @@ -431,7 +475,10 @@ take_largest1({Key, Value, Smaller, Larger}) -> {Key1, Value1, Larger1} = take_largest1(Larger), {Key1, Value1, {Key, Value, Smaller, Larger1}}. --spec largest(gb_tree()) -> {term(), term()}. +-spec largest(Tree) -> {Key, Val} when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). largest({_, Tree}) -> largest_1(Tree). @@ -443,7 +490,10 @@ largest_1({_Key, _Value, _Smaller, Larger}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec to_list(gb_tree()) -> [{term(), term()}]. +-spec to_list(Tree) -> [{Key, Val}] when + Tree :: gb_tree(), + Key :: term(), + Val :: term(). to_list({_, T}) -> to_list(T, []). @@ -456,7 +506,9 @@ to_list(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec keys(gb_tree()) -> [term()]. +-spec keys(Tree) -> [Key] when + Tree :: gb_tree(), + Key :: term(). keys({_, T}) -> keys(T, []). @@ -467,7 +519,9 @@ keys(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec values(gb_tree()) -> [term()]. +-spec values(Tree) -> [Val] when + Tree :: gb_tree(), + Val :: term(). values({_, T}) -> values(T, []). @@ -478,7 +532,9 @@ values(nil, L) -> L. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec iterator(gb_tree()) -> [gb_tree_node()]. +-spec iterator(Tree) -> Iter when + Tree :: gb_tree(), + Iter :: iter(). iterator({_, T}) -> iterator_1(T). @@ -496,7 +552,11 @@ iterator({_, _, L, _} = T, As) -> iterator(nil, As) -> As. --spec next([gb_tree_node()]) -> 'none' | {term(), term(), [gb_tree_node()]}. +-spec next(Iter1) -> 'none' | {Key, Val, Iter2} when + Iter1 :: iter(), + Iter2 :: iter(), + Key :: term(), + Val :: term(). next([{X, V, _, T} | As]) -> {X, V, iterator(T, As)}; @@ -505,7 +565,10 @@ next([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec map(fun((term(), term()) -> term()), gb_tree()) -> gb_tree(). +-spec map(Function, Tree1) -> Tree2 when + Function :: fun((K :: term(), V1 :: term()) -> V2 :: term()), + Tree1 :: gb_tree(), + Tree2 :: gb_tree(). map(F, {Size, Tree}) when is_function(F, 2) -> {Size, map_1(F, Tree)}. diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index b00910771f..1c4a73680b 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.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 @@ -44,6 +44,9 @@ system_code_change/4, format_status/2]). +-export_type([handler/0, handler_args/0, add_handler_ret/0, + del_handler_ret/0]). + -import(error_logger, [error_msg/2]). -define(reply(X), From ! {element(2,Tag), X}). @@ -113,7 +116,11 @@ behaviour_info(_Other) -> %%--------------------------------------------------------------------------- --type handler() :: atom() | {atom(), term()}. +-type handler() :: atom() | {atom(), term()}. +-type handler_args() :: term(). +-type add_handler_ret() :: ok | term() | {'EXIT',term()}. +-type del_handler_ret() :: ok | term() | {'EXIT',term()}. + -type emgr_name() :: {'local', atom()} | {'global', atom()}. -type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). -type start_ret() :: {'ok', pid()} | {'error', term()}. diff --git a/lib/stdlib/src/io.erl b/lib/stdlib/src/io.erl index 6aeb076a0b..9f65bbfa3a 100644 --- a/lib/stdlib/src/io.erl +++ b/lib/stdlib/src/io.erl @@ -41,6 +41,7 @@ -type error_description() :: term(). % Whatever the io-server sends. -type request_error() :: {'error',error_description()}. + %% XXX: Some uses of line() in this file may need to read erl_scan:location() -type line() :: pos_integer(). @@ -66,12 +67,15 @@ o_request(Io, Request, Func) -> end. %% Put chars takes mixed *unicode* list from R13 onwards. --spec put_chars(iodata()) -> 'ok'. +-spec put_chars(CharData) -> 'ok' when + CharData :: unicode:chardata(). put_chars(Chars) -> put_chars(default_output(), Chars). --spec put_chars(device(), iodata()) -> 'ok'. +-spec put_chars(IoDevice, IoData) -> 'ok' when + IoDevice :: device(), + IoData :: unicode:chardata(). put_chars(Io, Chars) -> o_request(Io, {put_chars,unicode,Chars}, put_chars). @@ -81,7 +85,8 @@ put_chars(Io, Chars) -> nl() -> nl(default_output()). --spec nl(device()) -> 'ok'. +-spec nl(IoDevice) -> 'ok' when + IoDevice :: device(). nl(Io) -> % o_request(Io, {put_chars,io_lib:nl()}). @@ -92,7 +97,8 @@ nl(Io) -> columns() -> columns(default_output()). --spec columns(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec columns(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). columns(Io) -> case request(Io, {get_geometry,columns}) of @@ -107,7 +113,8 @@ columns(Io) -> rows() -> rows(default_output()). --spec rows(device()) -> {'ok', pos_integer()} | {'error', 'enotsup'}. +-spec rows(IoDevice) -> {'ok', pos_integer()} | {'error', 'enotsup'} when + IoDevice :: device(). rows(Io) -> case request(Io,{get_geometry,rows}) of @@ -117,22 +124,36 @@ rows(Io) -> {error,enotsup} end. --spec get_chars(prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(Prompt, Count) -> Data | 'eof' when + Prompt :: prompt(), + Count :: non_neg_integer(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Prompt, N) -> get_chars(default_input(), Prompt, N). --spec get_chars(device(), prompt(), non_neg_integer()) -> iodata() | 'eof'. +-spec get_chars(IoDevice, Prompt, Count) -> Data | 'eof' | {error, Reason} when + IoDevice :: device(), + Prompt :: prompt(), + Count :: non_neg_integer(), + Reason :: term(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_chars(Io, Prompt, N) when is_integer(N), N >= 0 -> request(Io, {get_chars,unicode,Prompt,N}). --spec get_line(prompt()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(Prompt) -> Data | 'eof' | {'error', Reason} when + Prompt :: prompt(), + Reason :: term(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Prompt) -> get_line(default_input(), Prompt). --spec get_line(device(), prompt()) -> iodata() | 'eof' | {'error', term()}. +-spec get_line(IoDevice, Prompt) -> Data | 'eof' | {'error', term()} when + IoDevice :: device(), + Prompt :: prompt(), + Data :: [unicode:unicode_char()] | unicode:unicode_binary(). get_line(Io, Prompt) -> request(Io, {get_line,unicode,Prompt}). @@ -156,46 +177,62 @@ get_password(Io) -> getopts() -> getopts(default_input()). --spec getopts(device()) -> [opt_pair()]. +-spec getopts(IoDevice) -> [opt_pair()] when + IoDevice :: device(). getopts(Io) -> request(Io, getopts). -type setopt() :: 'binary' | 'list' | opt_pair(). --spec setopts([setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(Opts) -> 'ok' | {'error', Reason} when + Opts :: [setopt()], + Reason :: term(). setopts(Opts) -> setopts(default_input(), Opts). --spec setopts(device(), [setopt()]) -> 'ok' | {'error', term()}. +-spec setopts(IoDevice, Opts) -> 'ok' | {'error', Reason} when + IoDevice :: device(), + Opts :: [setopt()], + Reason :: term(). setopts(Io, Opts) -> request(Io, {setopts, Opts}). %% Writing and reading Erlang terms. --spec write(term()) -> 'ok'. +-spec write(Term) -> 'ok' when + Term :: term(). write(Term) -> write(default_output(), Term). --spec write(device(), term()) -> 'ok'. +-spec write(IoDevice, Term) -> 'ok' when + IoDevice :: device(), + Term :: term(). write(Io, Term) -> o_request(Io, {write,Term}, write). --spec read(prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(Prompt) -> Result when + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan:error_info()}. % Read does not use get_until as erl_scan does not work with unicode % XXX:PaN fixme? read(Prompt) -> read(default_input(), Prompt). --spec read(device(), prompt()) -> - {'ok', term()} | 'eof' | {'error', erl_scan:error_info()}. +-spec read(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: {'ok', Term :: term()} + | 'eof' + | {'error', ErrorInfo :: erl_scan:error_info()}. read(Io, Prompt) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[1]}) of @@ -211,9 +248,13 @@ read(Io, Prompt) -> Other end. --spec read(device(), prompt(), line()) -> - {'ok', term(), line()} | {'eof', line()} | - {'error', erl_scan:error_info(), line()}. +-spec read(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: {'ok', Term :: term(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()}. read(Io, Prompt, StartLine) when is_integer(StartLine) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[StartLine]}) of @@ -239,28 +280,40 @@ conv_reason(_, _Reason) -> badarg. -type format() :: atom() | string() | binary(). --spec fwrite(format()) -> 'ok'. +-spec fwrite(Format) -> 'ok' when + Format :: format(). fwrite(Format) -> format(Format). --spec fwrite(format(), [term()]) -> 'ok'. +-spec fwrite(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fwrite(device(), format(), [term()]) -> 'ok'. +-spec fwrite(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. fwrite(Io, Format, Args) -> format(Io, Format, Args). --spec fread(prompt(), format()) -> {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(Prompt, Format) -> Result when + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Prompt, Format) -> fread(default_input(), Prompt, Format). --spec fread(device(), prompt(), format()) -> - {'ok', [term()]} | 'eof' | {'error',term()}. +-spec fread(IoDevice, Prompt, Format) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Format :: format(), + Result :: {'ok', Terms :: [term()]} | 'eof' | {'error', What :: term()}. fread(Io, Prompt, Format) -> case request(Io, {fread,Prompt,Format}) of @@ -270,73 +323,104 @@ fread(Io, Prompt, Format) -> Other end. --spec format(format()) -> 'ok'. +-spec format(Format) -> 'ok' when + Format :: format(). format(Format) -> format(Format, []). --spec format(format(), [term()]) -> 'ok'. +-spec format(Format, Data) -> 'ok' when + Format :: format(), + Data :: [term()]. format(Format, Args) -> format(default_output(), Format, Args). --spec format(device(), format(), [term()]) -> 'ok'. +-spec format(IoDevice, Format, Data) -> 'ok' when + IoDevice :: device(), + Format :: format(), + Data :: [term()]. format(Io, Format, Args) -> o_request(Io, {format,Format,Args}, format). %% Scanning Erlang code. --spec scan_erl_exprs(prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Prompt) -> scan_erl_exprs(default_input(), Prompt, 1). --spec scan_erl_exprs(device(), prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Device, Prompt) -> Result when + Device :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt) -> scan_erl_exprs(Io, Prompt, 1). --spec scan_erl_exprs(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_exprs(Device, Prompt, StartLine) -> Result when + Device :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_exprs(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). --spec scan_erl_form(prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Prompt) -> scan_erl_form(default_input(), Prompt, 1). --spec scan_erl_form(device(), prompt()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt) -> scan_erl_form(Io, Prompt, 1). --spec scan_erl_form(device(), prompt(), line()) -> erl_scan:tokens_result() | request_error(). +-spec scan_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: erl_scan:tokens_result() | request_error(). scan_erl_form(Io, Prompt, Pos0) -> request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}). %% Parsing Erlang code. --type erl_parse_expr_list() :: [_]. %% XXX: should be imported from erl_parse - --type parse_ret() :: {'ok', erl_parse_expr_list(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()} +-type parse_ret() :: {'ok', ExprList :: erl_parse:abstract_expr(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} | request_error(). --spec parse_erl_exprs(prompt()) -> parse_ret(). +-spec parse_erl_exprs(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Prompt) -> parse_erl_exprs(default_input(), Prompt, 1). --spec parse_erl_exprs(device(), prompt()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt) -> parse_erl_exprs(Io, Prompt, 1). --spec parse_erl_exprs(device(), prompt(), line()) -> parse_ret(). +-spec parse_erl_exprs(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_ret(). parse_erl_exprs(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of @@ -349,24 +433,31 @@ parse_erl_exprs(Io, Prompt, Pos0) -> Other end. --type erl_parse_absform() :: _. %% XXX: should be imported from erl_parse - --type parse_form_ret() :: {'ok', erl_parse_absform(), line()} - | {'eof', line()} - | {'error', erl_scan:error_info(), line()} +-type parse_form_ret() :: {'ok', AbsForm :: erl_parse:abstract_form(), EndLine :: line()} + | {'eof', EndLine :: line()} + | {'error', ErrorInfo :: erl_scan:error_info(), ErrorLine :: line()} | request_error(). --spec parse_erl_form(prompt()) -> parse_form_ret(). +-spec parse_erl_form(Prompt) -> Result when + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Prompt) -> parse_erl_form(default_input(), Prompt, 1). --spec parse_erl_form(device(), prompt()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt) -> parse_erl_form(Io, Prompt, 1). --spec parse_erl_form(device(), prompt(), line()) -> parse_form_ret(). +-spec parse_erl_form(IoDevice, Prompt, StartLine) -> Result when + IoDevice :: device(), + Prompt :: prompt(), + StartLine :: line(), + Result :: parse_form_ret(). parse_erl_form(Io, Prompt, Pos0) -> case request(Io, {get_until,unicode,Prompt,erl_scan,tokens,[Pos0]}) of diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 4ca9d079b7..54c7283abf 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.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 @@ -75,35 +75,57 @@ collect_line/2, collect_line/3, collect_line/4, get_until/3, get_until/4]). --export_type([chars/0]). +-export_type([chars/0, continuation/0]). %%---------------------------------------------------------------------- - %% XXX: overapproximates a deep list of (unicode) characters --type chars() :: [_]. +-type chars() :: [char() | chars()]. -type depth() :: -1 | non_neg_integer(). +-opaque continuation() :: {_, _, _, _}. % XXX: refine + %%---------------------------------------------------------------------- %% Interface calls to sub-modules. --spec fwrite(io:format(), [term()]) -> chars(). +-spec fwrite(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()], + Data :: [term()]. fwrite(Format, Args) -> format(Format, Args). --spec fread(string(), string()) -> io_lib_fread:fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: chars(), LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: chars()} + | {'error', What :: term()}. fread(Chars, Format) -> io_lib_fread:fread(Chars, Format). --spec fread(io_lib_fread:continuation(), string(), string()) -> - io_lib_fread:fread_3_ret(). +-spec fread(Continuation, String, Format) -> Return when + Continuation :: continuation() | [], + String :: string(), + Format :: string(), + Return :: {'more', Continuation1 :: continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: chars()} + | 'eof' + | {'error', What :: term()}. fread(Cont, Chars, Format) -> io_lib_fread:fread(Cont, Chars, Format). --spec format(io:format(), [term()]) -> chars(). +-spec format(Format, Data) -> chars() | UnicodeList when + Format :: io:format(), + Data :: [term()], + UnicodeList :: [unicode:unicode_char()]. format(Format, Args) -> case catch io_lib_format:fwrite(Format, Args) of @@ -113,17 +135,24 @@ format(Format, Args) -> Other end. --spec print(term()) -> chars(). +-spec print(Term) -> chars() when + Term :: term(). print(Term) -> io_lib_pretty:print(Term). --spec print(term(), non_neg_integer(), non_neg_integer(), depth()) -> chars(). +-spec print(Term, Column, LineLength, Depth) -> chars() when + Term :: term(), + Column :: non_neg_integer(), + LineLength :: non_neg_integer(), + Depth :: depth(). print(Term, Column, LineLength, Depth) -> io_lib_pretty:print(Term, Column, LineLength, Depth). --spec indentation(string(), integer()) -> integer(). +-spec indentation(String, StartIndent) -> integer() when + String :: string(), + StartIndent :: integer(). indentation(Chars, Current) -> io_lib_format:indentation(Chars, Current). @@ -158,7 +187,8 @@ format_prompt(Format, Args) -> %% Return a (non-flattened) list of characters giving a printed %% representation of the term. write/3 is for backward compatibility. --spec write(term()) -> chars(). +-spec write(Term) -> chars() when + Term :: term(). write(Term) -> write(Term, -1). @@ -169,7 +199,9 @@ write(Term, D, true) -> write(Term, D, false) -> write(Term, D). --spec write(term(), depth()) -> chars(). +-spec write(Term, Depth) -> chars() when + Term :: term(), + Depth :: depth(). write(_Term, 0) -> "..."; write(Term, _D) when is_integer(Term) -> integer_to_list(Term); @@ -234,7 +266,8 @@ write_binary_body(B, _D) -> %% write_atom(Atom) -> [Char] %% Generate the list of characters needed to print an atom. --spec write_atom(atom()) -> chars(). +-spec write_atom(Atom) -> chars() when + Atom :: atom(). write_atom(Atom) -> Chars = atom_to_list(Atom), @@ -283,7 +316,8 @@ name_char(_) -> false. %% write_string([Char]) -> [Char] %% Generate the list of characters needed to print a string. --spec write_string(string()) -> chars(). +-spec write_string(String) -> chars() when + String :: string(). write_string(S) -> write_string(S, $"). %" @@ -330,7 +364,8 @@ string_char(_,C, _, Tail) when C < $\240-> %Other control characters. %% Generate the list of characters needed to print a character constant. %% Must special case SPACE, $\s, here. --spec write_char(char()) -> chars(). +-spec write_char(Char) -> chars() when + Char :: char(). write_char($\s) -> "$\\s"; %Must special case this. write_char(C) when is_integer(C), C >= $\000, C =< $\377 -> @@ -346,7 +381,8 @@ write_unicode_char(Uni) -> %% Return true if CharList is a (possibly deep) list of characters, else %% false. --spec char_list(term()) -> boolean(). +-spec char_list(Term) -> boolean() when + Term :: term(). char_list([C|Cs]) when is_integer(C), C >= $\000, C =< $\377 -> char_list(Cs); @@ -362,7 +398,8 @@ unicode_char_list([C|Cs]) when is_integer(C), C >= 0, C < 16#D800; unicode_char_list([]) -> true; unicode_char_list(_) -> false. %Everything else is false --spec deep_char_list(term()) -> boolean(). +-spec deep_char_list(Term) -> boolean() when + Term :: term(). deep_char_list(Cs) -> deep_char_list(Cs, []). @@ -399,7 +436,8 @@ deep_unicode_char_list(_, _More) -> %Everything else is false %% Return true if CharList is a list of printable characters, else %% false. --spec printable_list(term()) -> boolean(). +-spec printable_list(Term) -> boolean() when + Term :: term(). printable_list([C|Cs]) when is_integer(C), C >= $\040, C =< $\176 -> printable_list(Cs); diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index 33553692bc..52aa4d073c 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.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 @@ -22,28 +22,8 @@ -export([fread/2,fread/3]). --export_type([continuation/0, fread_2_ret/0, fread_3_ret/0]). - -import(lists, [reverse/1,reverse/2]). -%%----------------------------------------------------------------------- -%% Local types -%%----------------------------------------------------------------------- - --type done_arg2() :: {'ok', io_lib:chars()} | 'eof' | {'error', term()}. - -%%----------------------------------------------------------------------- -%% Types also used in other files -%%----------------------------------------------------------------------- - --type continuation() :: [] | {_, _, _, _}. % XXX: refine - --type fread_2_ret() :: {'ok', io_lib:chars(), string()} - | {'more', string(), non_neg_integer(), io_lib:chars()} - | {'error', term()}. --type fread_3_ret() :: {'more', continuation()} - | {'done', done_arg2(), string()}. - %%----------------------------------------------------------------------- %% fread(Continuation, CharList, FormatString) @@ -51,7 +31,15 @@ %% repeatedly collects lines and calls fread/2 to format the input until %% all the format string has been used. And it counts the characters. --spec fread(io_lib_fread:continuation(), string(), string()) -> fread_3_ret(). +-spec fread(Continuation, String, Format) -> Return when + Continuation :: io_lib:continuation() | [], + String :: string(), + Format :: string(), + Return :: {'more', Continuation1 :: io_lib:continuation()} + | {'done', Result, LeftOverChars :: string()}, + Result :: {'ok', InputList :: io_lib:chars()} + | 'eof' + | {'error', What :: term()}. fread([], Chars, Format) -> %%io:format("FREAD: ~w `~s'~n", [Format,Chars]), @@ -106,7 +94,14 @@ fread_line(Format0, Line, N0, Results0, More, Newline) -> %% WHITE Skip white space %% X Literal X --spec fread(string(), string()) -> fread_2_ret(). +-spec fread(Format, String) -> Result when + Format :: string(), + String :: string(), + Result :: {'ok', InputList :: io_lib:chars(), LeftOverChars :: string()} + | {'more', RestFormat :: string(), + Nchars :: non_neg_integer(), + InputStack :: io_lib:chars()} + | {'error', What :: term()}. fread(Format, Line) -> fread(Format, Line, 0, []). diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index b2cfb00de9..c303ae60b5 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -38,7 +38,9 @@ flush_receive() -> %% %% Functions for doing standard system format i/o. %% --spec error_message(atom() | string() | binary(), [term()]) -> 'ok'. +-spec error_message(Format, Args) -> 'ok' when + Format :: io:format(), + Args :: [term()]. error_message(Format, Args) -> io:format(<<"** ~s **\n">>, [io_lib:format(Format, Args)]). @@ -55,17 +57,23 @@ progname() -> no_prog_name end. --spec nonl(string()) -> string(). +-spec nonl(String1) -> String2 when + String1 :: string(), + String2 :: string(). nonl([10]) -> []; nonl([]) -> []; nonl([H|T]) -> [H|nonl(T)]. --spec send(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec send(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). send(To, Msg) -> To ! Msg. --spec sendw(pid() | atom() | {atom(), node()}, term()) -> term(). +-spec sendw(To, Msg) -> Msg when + To :: pid() | atom() | {atom(), node()}, + Msg :: term(). sendw(To, Msg) -> To ! {self(), Msg}, @@ -89,7 +97,7 @@ eval_str(Str) when is_list(Str) -> true -> case erl_parse:parse_exprs(Toks) of {ok, Exprs} -> - case catch erl_eval:exprs(Exprs, []) of + case catch erl_eval:exprs(Exprs, erl_eval:new_bindings()) of {value, Val, _} -> {ok, Val}; Other -> diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index c669c1f7c1..bba46e4cb6 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.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 @@ -54,13 +54,21 @@ %% append(X, Y) appends lists X and Y --spec append([T], [T]) -> [T]. +-spec append(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). append(L1, L2) -> L1 ++ L2. %% append(L) appends the list of lists L --spec append([[T]]) -> [T]. +-spec append(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). append([E]) -> E; append([H|T]) -> H ++ append(T); @@ -68,13 +76,20 @@ append([]) -> []. %% subtract(List1, List2) subtract elements in List2 form List1. --spec subtract([T], [T]) -> [T]. +-spec subtract(List1, List2) -> List3 when + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). subtract(L1, L2) -> L1 -- L2. %% reverse(L) reverse all elements in the list L. Is now a BIF! --spec reverse([T]) -> [T]. +-spec reverse(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). reverse([] = L) -> L; @@ -93,13 +108,21 @@ reverse([A, B | L]) -> %% nth(N, L) returns the N`th element of the list L %% nthtail(N, L) returns the N`th tail of the list L --spec nth(pos_integer(), [T,...]) -> T. +-spec nth(N, List) -> Elem when + N :: pos_integer(), + List :: [T,...], + Elem :: T, + T :: term(). nth(1, [H|_]) -> H; nth(N, [_|T]) when N > 1 -> nth(N - 1, T). --spec nthtail(non_neg_integer(), [T,...]) -> [T]. +-spec nthtail(N, List) -> Tail when + N :: non_neg_integer(), + List :: [T,...], + Tail :: [T], + T :: term(). nthtail(1, [_|T]) -> T; nthtail(N, [_|T]) when N > 1 -> @@ -108,7 +131,10 @@ nthtail(0, L) when is_list(L) -> L. %% prefix(Prefix, List) -> (true | false) --spec prefix([T], [T]) -> boolean(). +-spec prefix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). prefix([X|PreTail], [X|Tail]) -> prefix(PreTail, Tail); @@ -117,7 +143,10 @@ prefix([_|_], List) when is_list(List) -> false. %% suffix(Suffix, List) -> (true | false) --spec suffix([T], [T]) -> boolean(). +-spec suffix(List1, List2) -> boolean() when + List1 :: [T], + List2 :: [T], + T :: term(). suffix(Suffix, List) -> Delta = length(List) - length(Suffix), @@ -125,7 +154,10 @@ suffix(Suffix, List) -> %% last(List) returns the last element in a list. --spec last([T,...]) -> T. +-spec last(List) -> Last when + List :: [T,...], + Last :: T, + T :: term(). last([E|Es]) -> last(E, Es). @@ -137,7 +169,10 @@ last(E, []) -> E. %% returns the sequence Min..Max %% Min <= Max and Min and Max must be integers --spec seq(integer(), integer()) -> [integer()]. +-spec seq(From, To) -> Seq when + From :: integer(), + To :: integer(), + Seq :: [integer()]. seq(First, Last) when is_integer(First), is_integer(Last), First-1 =< Last -> @@ -152,7 +187,11 @@ seq_loop(1, X, L) -> seq_loop(0, _, L) -> L. --spec seq(integer(), integer(), integer()) -> [integer()]. +-spec seq(From, To, Incr) -> Seq when + From :: integer(), + To :: integer(), + Incr :: integer(), + Seq :: [integer()]. seq(First, Last, Inc) when is_integer(First), is_integer(Last), is_integer(Inc) -> @@ -178,7 +217,8 @@ seq_loop(0, _, _, L) -> %% sum(L) returns the sum of the elements in L --spec sum([number()]) -> number(). +-spec sum(List) -> number() when + List :: [number()]. sum(L) -> sum(L, 0). @@ -188,7 +228,11 @@ sum([], Sum) -> Sum. %% duplicate(N, X) -> [X,X,X,.....,X] (N times) %% return N copies of X --spec duplicate(non_neg_integer(), T) -> [T]. +-spec duplicate(N, Elem) -> List when + N :: non_neg_integer(), + Elem :: T, + List :: [T], + T :: term(). duplicate(N, X) when is_integer(N), N >= 0 -> duplicate(N, X, []). @@ -197,7 +241,10 @@ duplicate(N, X, L) -> duplicate(N-1, X, [X|L]). %% min(L) -> returns the minimum element of the list L --spec min([T,...]) -> T. +-spec min(List) -> Min when + List :: [T,...], + Min :: T, + T :: term(). min([H|T]) -> min(T, H). @@ -207,7 +254,10 @@ min([], Min) -> Min. %% max(L) -> returns the maximum element of the list L --spec max([T,...]) -> T. +-spec max(List) -> Max when + List :: [T,...], + Max :: T, + T :: term(). max([H|T]) -> max(T, H). @@ -218,12 +268,21 @@ max([], Max) -> Max. %% sublist(List, Start, Length) %% Returns the sub-list starting at Start of length Length. --spec sublist([T], pos_integer(), non_neg_integer()) -> [T]. +-spec sublist(List1, Start, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Start :: pos_integer(), + Len :: non_neg_integer(), + T :: term(). sublist(List, S, L) when is_integer(L), L >= 0 -> sublist(nthtail(S-1, List), L). --spec sublist([T], non_neg_integer()) -> [T]. +-spec sublist(List1, Len) -> List2 when + List1 :: [T], + List2 :: [T], + Len :: non_neg_integer(), + T :: term(). sublist(List, L) when is_integer(L), is_list(List) -> sublist_2(List, L). @@ -238,7 +297,11 @@ sublist_2(List, L) when is_list(List), L > 0 -> %% delete(Item, List) -> List' %% Delete the first occurrence of Item from the list L. --spec delete(T, [T]) -> [T]. +-spec delete(Elem, List1) -> List2 when + Elem :: T, + List1 :: [T], + List2 :: [T], + T :: term(). delete(Item, [Item|Rest]) -> Rest; delete(Item, [H|Rest]) -> @@ -248,7 +311,12 @@ delete(_, []) -> []. %% Return [{X0, Y0}, {X1, Y1}, ..., {Xn, Yn}] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zip([A], [B]) -> [{A, B}]. +-spec zip(List1, List2) -> List3 when + List1 :: [A], + List2 :: [B], + List3 :: [{A, B}], + A :: term(), + B :: term(). zip([X | Xs], [Y | Ys]) -> [{X, Y} | zip(Xs, Ys)]; zip([], []) -> []. @@ -256,7 +324,12 @@ zip([], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn]}, for a list [{X0, Y0}, %% {X1, Y1}, ..., {Xn, Yn}]. --spec unzip([{A, B}]) -> {[A], [B]}. +-spec unzip(List1) -> {List2, List3} when + List1 :: [{A, B}], + List2 :: [A], + List3 :: [B], + A :: term(), + B :: term(). unzip(Ts) -> unzip(Ts, [], []). @@ -266,7 +339,14 @@ unzip([], Xs, Ys) -> {reverse(Xs), reverse(Ys)}. %% Return [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}] for lists [X0, %% X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zip3([A], [B], [C]) -> [{A, B, C}]. +-spec zip3(List1, List2, List3) -> List4 when + List1 :: [A], + List2 :: [B], + List3 :: [C], + List4 :: [{A, B, C}], + A :: term(), + B :: term(), + C :: term(). zip3([X | Xs], [Y | Ys], [Z | Zs]) -> [{X, Y, Z} | zip3(Xs, Ys, Zs)]; zip3([], [], []) -> []. @@ -274,7 +354,14 @@ zip3([], [], []) -> []. %% Return {[X0, X1, ..., Xn], [Y0, Y1, ..., Yn], [Z0, Z1, ..., Zn]}, for %% a list [{X0, Y0, Z0}, {X1, Y1, Z1}, ..., {Xn, Yn, Zn}]. --spec unzip3([{A, B, C}]) -> {[A], [B], [C]}. +-spec unzip3(List1) -> {List2, List3, List4} when + List1 :: [{A, B, C}], + List2 :: [A], + List3 :: [B], + List4 :: [C], + A :: term(), + B :: term(), + C :: term(). unzip3(Ts) -> unzip3(Ts, [], [], []). @@ -286,7 +373,14 @@ unzip3([], Xs, Ys, Zs) -> %% Return [F(X0, Y0), F(X1, Y1), ..., F(Xn, Yn)] for lists [X0, X1, ..., %% Xn] and [Y0, Y1, ..., Yn]. --spec zipwith(fun((X, Y) -> R), [X], [Y]) -> [R]. +-spec zipwith(Combine, List1, List2) -> List3 when + Combine :: fun((X, Y) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [T], + X :: term(), + Y :: term(), + T :: term(). zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)]; zipwith(F, [], []) when is_function(F, 2) -> []. @@ -294,7 +388,16 @@ zipwith(F, [], []) when is_function(F, 2) -> []. %% Return [F(X0, Y0, Z0), F(X1, Y1, Z1), ..., F(Xn, Yn, Zn)] for lists %% [X0, X1, ..., Xn], [Y0, Y1, ..., Yn] and [Z0, Z1, ..., Zn]. --spec zipwith3(fun((X, Y, Z) -> R), [X], [Y], [Z]) -> [R]. +-spec zipwith3(Combine, List1, List2, List3) -> List4 when + Combine :: fun((X, Y, Z) -> T), + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [T], + X :: term(), + Y :: term(), + Z :: term(), + T :: term(). zipwith3(F, [X | Xs], [Y | Ys], [Z | Zs]) -> [F(X, Y, Z) | zipwith3(F, Xs, Ys, Zs)]; @@ -303,7 +406,10 @@ zipwith3(F, [], [], []) when is_function(F, 3) -> []. %% sort(List) -> L %% sorts the list L --spec sort([T]) -> [T]. +-spec sort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). sort([X, Y | L] = L0) when X =< Y -> case L of @@ -350,7 +456,11 @@ sort_1(X, [], R) -> %% merge(List) -> L %% merges a list of sorted lists --spec merge([[T]]) -> [T]. +-spec merge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). merge(L) -> mergel(L, []). @@ -358,7 +468,14 @@ merge(L) -> %% merge3(X, Y, Z) -> L %% merges three sorted lists X, Y and Z --spec merge3([X], [Y], [Z]) -> [(X | Y | Z)]. +-spec merge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). merge3(L1, [], L3) -> merge(L1, L3); @@ -382,7 +499,12 @@ rmerge3(L1, [H2 | T2], [H3 | T3]) -> %% merge(X, Y) -> L %% merges two sorted lists X and Y --spec merge([X], [Y]) -> [(X | Y)]. +-spec merge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). merge(T1, []) -> T1; @@ -405,8 +527,9 @@ rmerge(T1, [H2 | T2]) -> %% in L - the elements in L can be atoms, numbers of strings. %% Returns a list of characters. --type concat_thing() :: atom() | integer() | float() | string(). --spec concat([concat_thing()]) -> string(). +-spec concat(Things) -> string() when + Things :: [Thing], + Thing :: atom() | integer() | float() | string(). concat(List) -> flatmap(fun thing_to_list/1, List). @@ -420,12 +543,17 @@ thing_to_list(X) when is_list(X) -> X. %Assumed to be a string %% flatten(List, Tail) %% Flatten a list, adding optional tail. --spec flatten([term()]) -> [term()]. +-spec flatten(DeepList) -> List when + DeepList :: [term() | DeepList], + List :: [term()]. flatten(List) when is_list(List) -> do_flatten(List, []). --spec flatten([term()], [term()]) -> [term()]. +-spec flatten(DeepList, Tail) -> List when + DeepList :: [term() | DeepList], + Tail :: [term()], + List :: [term()]. flatten(List, Tail) when is_list(List), is_list(Tail) -> do_flatten(List, Tail). @@ -440,7 +568,8 @@ do_flatten([], Tail) -> %% flatlength(List) %% Calculate the length of a list of lists. --spec flatlength([term()]) -> non_neg_integer(). +-spec flatlength(DeepList) -> non_neg_integer() when + DeepList :: [term() | DeepList]. flatlength(List) -> flatlength(List, 0). @@ -481,7 +610,12 @@ flatlength([], L) -> L. % keysearch3(Key, N, T); %keysearch3(Key, N, []) -> false. --spec keydelete(term(), pos_integer(), [T]) -> [T] when T :: tuple(). +-spec keydelete(Key, N, TupleList1) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keydelete(K, N, L) when is_integer(N), N > 0 -> keydelete3(K, N, L). @@ -491,7 +625,12 @@ keydelete3(Key, N, [H|T]) -> [H|keydelete3(Key, N, T)]; keydelete3(_, _, []) -> []. --spec keyreplace(term(), pos_integer(), [tuple()], tuple()) -> [tuple()]. +-spec keyreplace(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()], + NewTuple :: tuple(). keyreplace(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keyreplace3(K, N, L, New). @@ -502,8 +641,12 @@ keyreplace3(Key, Pos, [H|T], New) -> [H|keyreplace3(Key, Pos, T, New)]; keyreplace3(_, _, [], _) -> []. --spec keytake(term(), pos_integer(), [tuple()]) -> - {'value', tuple(), [tuple()]} | 'false'. +-spec keytake(Key, N, TupleList1) -> {value, Tuple, TupleList2} | false when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()], + Tuple :: tuple(). keytake(Key, N, L) when is_integer(N), N > 0 -> keytake(Key, N, L, []). @@ -514,7 +657,12 @@ keytake(Key, N, [H|T], L) -> keytake(Key, N, T, [H|L]); keytake(_K, _N, [], _L) -> false. --spec keystore(term(), pos_integer(), [tuple()], tuple()) -> [tuple(),...]. +-spec keystore(Key, N, TupleList1, NewTuple) -> TupleList2 when + Key :: term(), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple(), ...], + NewTuple :: tuple(). keystore(K, N, L, New) when is_integer(N), N > 0, is_tuple(New) -> keystore2(K, N, L, New). @@ -526,7 +674,11 @@ keystore2(Key, N, [H|T], New) -> keystore2(_Key, _N, [], New) -> [New]. --spec keysort(pos_integer(), [T]) -> [T] when T :: tuple(). +-spec keysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). keysort(I, L) when is_integer(I), I > 0 -> case L of @@ -583,8 +735,13 @@ keysort_1(I, X, EX, [Y | L], R) -> keysort_1(_I, X, _EX, [], R) -> lists:reverse(R, [X]). --spec keymerge(pos_integer(), [X], [Y]) -> - [R] when X :: tuple(), Y :: tuple(), R :: tuple(). +-spec keymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: tuple(), + T2 :: tuple(). keymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> case L2 of @@ -611,7 +768,11 @@ rkeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec ukeysort(pos_integer(), [T]) -> [T] when T :: tuple(). +-spec ukeysort(N, TupleList1) -> TupleList2 when + N :: pos_integer(), + TupleList1 :: [Tuple], + TupleList2 :: [Tuple], + Tuple :: tuple(). ukeysort(I, L) when is_integer(I), I > 0 -> case L of @@ -676,8 +837,13 @@ ukeysort_1(I, X, EX, [Y | L]) -> ukeysort_1(_I, X, _EX, []) -> [X]. --spec ukeymerge(pos_integer(), [X], [Y]) -> - [(X | Y)] when X :: tuple(), Y :: tuple(). +-spec ukeymerge(N, TupleList1, TupleList2) -> TupleList3 when + N :: pos_integer(), + TupleList1 :: [T1], + TupleList2 :: [T2], + TupleList3 :: [(T1 | T2)], + T1 :: tuple(), + T2 :: tuple(). ukeymerge(Index, L1, T2) when is_integer(Index), Index > 0 -> case L1 of @@ -704,7 +870,11 @@ rukeymerge(Index, T1, L2) when is_integer(Index), Index > 0 -> lists:reverse(M, []) end. --spec keymap(fun((term()) -> term()), pos_integer(), [tuple()]) -> [tuple()]. +-spec keymap(Fun, N, TupleList1) -> TupleList2 when + Fun :: fun((Term1 :: term()) -> Term2 :: term()), + N :: pos_integer(), + TupleList1 :: [tuple()], + TupleList2 :: [tuple()]. keymap(Fun, Index, [Tup|Tail]) -> [setelement(Index, Tup, Fun(element(Index, Tup)))|keymap(Fun, Index, Tail)]; @@ -713,7 +883,11 @@ keymap(Fun, Index, []) when is_integer(Index), Index >= 1, %%% Suggestion from OTP-2948: sort and merge with Fun. --spec sort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec sort(Fun, List1) -> List2 when + Fun :: fun((A :: T, B :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). sort(Fun, []) when is_function(Fun, 2) -> []; @@ -727,7 +901,13 @@ sort(Fun, [X, Y | T]) -> fsplit_2(Y, X, Fun, T, [], []) end. --spec merge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. +-spec merge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). merge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> lists:reverse(fmerge2_1(T1, H2, Fun, T2, []), []); @@ -743,7 +923,11 @@ rmerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> rmerge(Fun, T1, []) when is_function(Fun, 2) -> T1. --spec usort(fun((T, T) -> boolean()), [T]) -> [T]. +-spec usort(Fun, List1) -> List2 when + Fun :: fun((T, T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). usort(Fun, [_] = L) when is_function(Fun, 2) -> L; @@ -770,7 +954,13 @@ usort_1(Fun, X, [Y | L]) -> ufsplit_2(Y, L, Fun, [X]) end. --spec umerge(fun((X, Y) -> boolean()), [X], [Y]) -> [(X | Y)]. +-spec umerge(Fun, List1, List2) -> List3 when + Fun :: fun((A, B) -> boolean()), + List1 :: [A], + List2 :: [B], + List3 :: [(A | B)], + A :: term(), + B :: term(). umerge(Fun, [], T2) when is_function(Fun, 2) -> T2; @@ -789,7 +979,10 @@ rumerge(Fun, T1, [H2 | T2]) when is_function(Fun, 2) -> %% usort(List) -> L %% sorts the list L, removes duplicates --spec usort([T]) -> [T]. +-spec usort(List1) -> List2 when + List1 :: [T], + List2 :: [T], + T :: term(). usort([X, Y | L] = L0) when X < Y -> case L of @@ -844,7 +1037,11 @@ usort_1(X, []) -> %% umerge(List) -> L %% merges a list of sorted lists without duplicates, removes duplicates --spec umerge([[T]]) -> [T]. +-spec umerge(ListOfLists) -> List1 when + ListOfLists :: [List], + List :: [T], + List1 :: [T], + T :: term(). umerge(L) -> umergel(L). @@ -853,7 +1050,14 @@ umerge(L) -> %% merges three sorted lists X, Y and Z without duplicates, %% removes duplicates --spec umerge3([X], [Y], [Z]) -> [(X | Y | Z)]. +-spec umerge3(List1, List2, List3) -> List4 when + List1 :: [X], + List2 :: [Y], + List3 :: [Z], + List4 :: [(X | Y | Z)], + X :: term(), + Y :: term(), + Z :: term(). umerge3(L1, [], L3) -> umerge(L1, L3); @@ -878,7 +1082,12 @@ rumerge3(L1, [H2 | T2], [H3 | T3]) -> %% umerge(X, Y) -> L %% merges two sorted lists X and Y without duplicates, removes duplicates --spec umerge([X], [Y]) -> [(X | Y)]. +-spec umerge(List1, List2) -> List3 when + List1 :: [X], + List2 :: [Y], + List3 :: [(X | Y)], + X :: term(), + Y :: term(). umerge([], T2) -> T2; @@ -924,7 +1133,10 @@ rumerge(T1, [H2 | T2]) -> %% There are also versions with an extra argument, ExtraArgs, which is a %% list of extra arguments to each call. --spec all(fun((T) -> boolean()), [T]) -> boolean(). +-spec all(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). all(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -933,7 +1145,10 @@ all(Pred, [Hd|Tail]) -> end; all(Pred, []) when is_function(Pred, 1) -> true. --spec any(fun((T) -> boolean()), [T]) -> boolean(). +-spec any(Pred, List) -> boolean() when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + T :: term(). any(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -942,31 +1157,59 @@ any(Pred, [Hd|Tail]) -> end; any(Pred, []) when is_function(Pred, 1) -> false. --spec map(fun((D) -> R), [D]) -> [R]. +-spec map(Fun, List1) -> List2 when + Fun :: fun((A) -> B), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). map(F, [H|T]) -> [F(H)|map(F, T)]; map(F, []) when is_function(F, 1) -> []. --spec flatmap(fun((D) -> [R]), [D]) -> [R]. +-spec flatmap(Fun, List1) -> List2 when + Fun :: fun((A) -> [B]), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). flatmap(F, [Hd|Tail]) -> F(Hd) ++ flatmap(F, Tail); flatmap(F, []) when is_function(F, 1) -> []. --spec foldl(fun((T, term()) -> term()), term(), [T]) -> term(). +-spec foldl(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldl(F, Accu, [Hd|Tail]) -> foldl(F, F(Hd, Accu), Tail); foldl(F, Accu, []) when is_function(F, 2) -> Accu. --spec foldr(fun((T, term()) -> term()), term(), [T]) -> term(). +-spec foldr(Fun, Acc0, List) -> Acc1 when + Fun :: fun((Elem :: T, AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List :: [T], + T :: term(). foldr(F, Accu, [Hd|Tail]) -> F(Hd, foldr(F, Accu, Tail)); foldr(F, Accu, []) when is_function(F, 2) -> Accu. --spec filter(Pred :: fun((T) -> boolean()), List :: [T]) -> [T]. +-spec filter(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). filter(Pred, List) when is_function(Pred, 1) -> [ E || E <- List, Pred(E) ]. @@ -974,7 +1217,12 @@ filter(Pred, List) when is_function(Pred, 1) -> %% Equivalent to {filter(F, L), filter(NotF, L)}, if NotF = 'fun(X) -> %% not F(X) end'. --spec partition(Pred :: fun((T) -> boolean()), List :: [T]) -> {[T], [T]}. +-spec partition(Pred, List) -> {Satisfying, NotSatisfying} when + Pred :: fun((Elem :: T) -> boolean()), + List :: [T], + Satisfying :: [T], + NotSatisfying :: [T], + T :: term(). partition(Pred, L) -> partition(Pred, L, [], []). @@ -1000,14 +1248,26 @@ zf(F, [Hd|Tail]) -> end; zf(F, []) when is_function(F, 1) -> []. --spec foreach(F :: fun((T) -> term()), List :: [T]) -> 'ok'. +-spec foreach(Fun, List) -> ok when + Fun :: fun((Elem :: T) -> term()), + List :: [T], + T :: term(). foreach(F, [Hd|Tail]) -> F(Hd), foreach(F, Tail); foreach(F, []) when is_function(F, 1) -> ok. --spec mapfoldl(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. +-spec mapfoldl(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldl(F, Accu0, [Hd|Tail]) -> {R,Accu1} = F(Hd, Accu0), @@ -1015,7 +1275,16 @@ mapfoldl(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldl(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec mapfoldr(fun((A, term()) -> {B, term()}), term(), [A]) -> {[B], term()}. +-spec mapfoldr(Fun, Acc0, List1) -> {List2, Acc1} when + Fun :: fun((A, AccIn) -> {B, AccOut}), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + List1 :: [A], + List2 :: [B], + A :: term(), + B :: term(). mapfoldr(F, Accu0, [Hd|Tail]) -> {Rs,Accu1} = mapfoldr(F, Accu0, Tail), @@ -1023,7 +1292,11 @@ mapfoldr(F, Accu0, [Hd|Tail]) -> {[R|Rs],Accu2}; mapfoldr(F, Accu, []) when is_function(F, 2) -> {[],Accu}. --spec takewhile(fun((T) -> boolean()), [T]) -> [T]. +-spec takewhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). takewhile(Pred, [Hd|Tail]) -> case Pred(Hd) of @@ -1032,7 +1305,11 @@ takewhile(Pred, [Hd|Tail]) -> end; takewhile(Pred, []) when is_function(Pred, 1) -> []. --spec dropwhile(fun((T) -> boolean()), [T]) -> [T]. +-spec dropwhile(Pred, List1) -> List2 when + Pred :: fun((Elem :: T) -> boolean()), + List1 :: [T], + List2 :: [T], + T :: term(). dropwhile(Pred, [Hd|Tail]=Rest) -> case Pred(Hd) of @@ -1041,7 +1318,12 @@ dropwhile(Pred, [Hd|Tail]=Rest) -> end; dropwhile(Pred, []) when is_function(Pred, 1) -> []. --spec splitwith(fun((T) -> boolean()), [T]) -> {[T], [T]}. +-spec splitwith(Pred, List) -> {List1, List2} when + Pred :: fun((T) -> boolean()), + List :: [T], + List1 :: [T], + List2 :: [T], + T :: term(). splitwith(Pred, List) when is_function(Pred, 1) -> splitwith(Pred, List, []). @@ -1054,7 +1336,12 @@ splitwith(Pred, [Hd|Tail], Taken) -> splitwith(Pred, [], Taken) when is_function(Pred, 1) -> {reverse(Taken),[]}. --spec split(non_neg_integer(), [T]) -> {[T], [T]}. +-spec split(N, List1) -> {List2, List3} when + N :: non_neg_integer(), + List1 :: [T], + List2 :: [T], + List3 :: [T], + T :: term(). split(N, List) when is_integer(N), N >= 0, is_list(List) -> case split(N, List, []) of diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl index 5fa5360fa1..f7f128dac7 100644 --- a/lib/stdlib/src/log_mf_h.erl +++ b/lib/stdlib/src/log_mf_h.erl @@ -27,14 +27,13 @@ %%----------------------------------------------------------------- --type dir() :: file:filename(). -type b() :: non_neg_integer(). -type f() :: 1..255. -type pred() :: fun((term()) -> boolean()). %%----------------------------------------------------------------- --record(state, {dir :: dir(), +-record(state, {dir :: file:filename(), maxB :: b(), maxF :: f(), curB :: b(), @@ -67,11 +66,23 @@ %% EventMgr = pid() | atom(). %%----------------------------------------------------------------- --spec init(dir(), b(), f()) -> {dir(), b(), f(), pred()}. +-opaque args() :: {file:filename(), b(), f(), pred()}. + + +-spec init(Dir, MaxBytes, MaxFiles) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Args :: args(). init(Dir, MaxB, MaxF) -> init(Dir, MaxB, MaxF, fun(_) -> true end). --spec init(dir(), b(), f(), pred()) -> {dir(), b(), f(), pred()}. +-spec init(Dir, MaxBytes, MaxFiles, Pred) -> Args when + Dir :: file:filename(), + MaxBytes :: non_neg_integer(), % b() + MaxFiles :: 1..255, % f() + Pred :: fun((Event :: term()) -> boolean()), % pred() + Args :: args(). init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. @@ -79,7 +90,7 @@ init(Dir, MaxB, MaxF, Pred) -> {Dir, MaxB, MaxF, Pred}. %% Call-back functions from gen_event %%----------------------------------------------------------------- --spec init({dir(), b(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. +-spec init({file:filename(), non_neg_integer(), f(), pred()}) -> {'ok', #state{}} | {'error', term()}. init({Dir, MaxB, MaxF, Pred}) when is_integer(MaxF), MaxF > 0, MaxF < 256 -> First = diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index b565eb20f4..48e22e53fa 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -66,6 +66,11 @@ %% %% Called by compiler or ets/dbg:fun2ms when errors/warnings occur %% + +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). + format_error({?WARN_SHADOW_VAR,Name}) -> lists:flatten( io_lib:format("variable ~p shadowed in ms_transform fun head", @@ -186,6 +191,12 @@ format_error(Else) -> %% %% Called when translating in shell %% + +-spec transform_from_shell(Dialect, Clauses, BoundEnvironment) -> term() when + Dialect :: ets | dbg, + Clauses :: [erl_parse:abstract_clause()], + BoundEnvironment :: erl_eval:binding_struct(). + transform_from_shell(Dialect, Clauses, BoundEnvironment) -> SaveFilename = setup_filename(), case catch ms_clause_list(1,Clauses,Dialect,gb_sets:new()) of @@ -211,6 +222,11 @@ transform_from_shell(Dialect, Clauses, BoundEnvironment) -> %% %% Called when translating during compiling %% + +-spec parse_transform(Forms, Options) -> Forms when + Forms :: [erl_parse:abstract_form()], + Options :: term(). + parse_transform(Forms, _Options) -> SaveFilename = setup_filename(), %io:format("Forms: ~p~n",[Forms]), diff --git a/lib/stdlib/src/orddict.erl b/lib/stdlib/src/orddict.erl index 4e30c9eefd..45d3c84b3e 100644 --- a/lib/stdlib/src/orddict.erl +++ b/lib/stdlib/src/orddict.erl @@ -25,9 +25,11 @@ -export([store/3,append/3,append_list/3,update/3,update/4,update_counter/3]). -export([fold/3,map/2,filter/2,merge/3]). +-export_type([orddict/0]). + %%--------------------------------------------------------------------------- --type orddict() :: [{term(), term()}]. +-type orddict() :: [{Key :: term(), Value :: term()}]. %%--------------------------------------------------------------------------- @@ -35,45 +37,63 @@ new() -> []. --spec is_key(Key::term(), Dictionary::orddict()) -> boolean(). +-spec is_key(Key, Orddict) -> boolean() when + Key :: term(), + Orddict :: orddict(). is_key(Key, [{K,_}|_]) when Key < K -> false; is_key(Key, [{K,_}|Dict]) when Key > K -> is_key(Key, Dict); is_key(_Key, [{_K,_Val}|_]) -> true; %Key == K is_key(_, []) -> false. --spec to_list(orddict()) -> [{term(), term()}]. +-spec to_list(Orddict) -> List when + Orddict :: orddict(), + List :: [{Key :: term(), Value :: term()}]. to_list(Dict) -> Dict. --spec from_list([{term(), term()}]) -> orddict(). +-spec from_list(List) -> Orddict when + List :: [{Key :: term(), Value :: term()}], + Orddict :: orddict(). from_list(Pairs) -> lists:foldl(fun ({K,V}, D) -> store(K, V, D) end, [], Pairs). --spec size(orddict()) -> non_neg_integer(). +-spec size(Orddict) -> non_neg_integer() when + Orddict :: orddict(). size(D) -> length(D). --spec fetch(Key::term(), Dictionary::orddict()) -> term(). +-spec fetch(Key, Orddict) -> Value when + Key :: term(), + Value :: term(), + Orddict :: orddict(). fetch(Key, [{K,_}|D]) when Key > K -> fetch(Key, D); fetch(Key, [{K,Value}|_]) when Key == K -> Value. --spec find(Key::term(), Dictionary::orddict()) -> {'ok', term()} | 'error'. +-spec find(Key, Orddict) -> {'ok', Value} | 'error' when + Key :: term(), + Orddict :: orddict(), + Value :: term(). find(Key, [{K,_}|_]) when Key < K -> error; find(Key, [{K,_}|D]) when Key > K -> find(Key, D); find(_Key, [{_K,Value}|_]) -> {ok,Value}; %Key == K find(_, []) -> error. --spec fetch_keys(Dictionary::orddict()) -> [term()]. +-spec fetch_keys(Orddict) -> Keys when + Orddict :: orddict(), + Keys :: [term()]. fetch_keys([{Key,_}|Dict]) -> [Key|fetch_keys(Dict)]; fetch_keys([]) -> []. --spec erase(Key::term(), Dictionary::orddict()) -> orddict(). +-spec erase(Key, Orddict1) -> Orddict2 when + Key :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). erase(Key, [{K,_}=E|Dict]) when Key < K -> [E|Dict]; erase(Key, [{K,_}=E|Dict]) when Key > K -> @@ -81,7 +101,11 @@ erase(Key, [{K,_}=E|Dict]) when Key > K -> erase(_Key, [{_K,_Val}|Dict]) -> Dict; %Key == K erase(_, []) -> []. --spec store(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec store(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). store(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,New},E|Dict]; @@ -91,7 +115,11 @@ store(Key, New, [{_K,_Old}|Dict]) -> %Key == K [{Key,New}|Dict]; store(Key, New, []) -> [{Key,New}]. --spec append(Key::term(), Value::term(), Dictionary::orddict()) -> orddict(). +-spec append(Key, Value, Orddict1) -> Orddict2 when + Key :: term(), + Value :: term(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). append(Key, New, [{K,_}=E|Dict]) when Key < K -> [{Key,[New]},E|Dict]; @@ -101,7 +129,11 @@ append(Key, New, [{_K,Old}|Dict]) -> %Key == K [{Key,Old ++ [New]}|Dict]; append(Key, New, []) -> [{Key,[New]}]. --spec append_list(Key::term(), ValueList::[term()], orddict()) -> orddict(). +-spec append_list(Key, ValList, Orddict1) -> Orddict2 when + Key :: term(), + ValList :: [Value :: term()], + Orddict1 :: orddict(), + Orddict2 :: orddict(). append_list(Key, NewList, [{K,_}=E|Dict]) when Key < K -> [{Key,NewList},E|Dict]; @@ -112,14 +144,23 @@ append_list(Key, NewList, [{_K,Old}|Dict]) -> %Key == K append_list(Key, NewList, []) -> [{Key,NewList}]. --spec update(Key::term(), Fun::fun((term()) -> term()), orddict()) -> orddict(). +-spec update(Key, Fun, Orddict1) -> Orddict2 when + Key :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, Fun, [{K,_}=E|Dict]) when Key > K -> [E|update(Key, Fun, Dict)]; update(Key, Fun, [{K,Val}|Dict]) when Key == K -> [{Key,Fun(Val)}|Dict]. --spec update(term(), fun((term()) -> term()), term(), orddict()) -> orddict(). +-spec update(Key, Fun, Initial, Orddict1) -> Orddict2 when + Key :: term(), + Initial :: term(), + Fun :: fun((Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update(Key, _, Init, [{K,_}=E|Dict]) when Key < K -> [{Key,Init},E|Dict]; @@ -129,7 +170,11 @@ update(Key, Fun, _Init, [{_K,Val}|Dict]) -> %Key == K [{Key,Fun(Val)}|Dict]; update(Key, _, Init, []) -> [{Key,Init}]. --spec update_counter(Key::term(), Incr::number(), orddict()) -> orddict(). +-spec update_counter(Key, Increment, Orddict1) -> Orddict2 when + Key :: term(), + Increment :: number(), + Orddict1 :: orddict(), + Orddict2 :: orddict(). update_counter(Key, Incr, [{K,_}=E|Dict]) when Key < K -> [{Key,Incr},E|Dict]; @@ -139,19 +184,29 @@ update_counter(Key, Incr, [{_K,Val}|Dict]) -> %Key == K [{Key,Val+Incr}|Dict]; update_counter(Key, Incr, []) -> [{Key,Incr}]. --spec fold(fun((term(),term(),term()) -> term()), term(), orddict()) -> term(). +-spec fold(Fun, Acc0, Orddict) -> Acc1 when + Fun :: fun((Key :: term(), Value :: term(), AccIn :: term()) -> AccOut :: term()), + Acc0 :: term(), + Acc1 :: term(), + Orddict :: orddict(). fold(F, Acc, [{Key,Val}|D]) -> fold(F, F(Key, Val, Acc), D); fold(F, Acc, []) when is_function(F, 3) -> Acc. --spec map(fun((term(), term()) -> term()), orddict()) -> orddict(). +-spec map(Fun, Orddict1) -> Orddict2 when + Fun :: fun((Key :: term(), Value1 :: term()) -> Value2 :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). map(F, [{Key,Val}|D]) -> [{Key,F(Key, Val)}|map(F, D)]; map(F, []) when is_function(F, 2) -> []. --spec filter(fun((term(), term()) -> boolean()), orddict()) -> orddict(). +-spec filter(Pred, Orddict1) -> Orddict2 when + Pred :: fun((Key :: term(), Value :: term()) -> boolean()), + Orddict1 :: orddict(), + Orddict2 :: orddict(). filter(F, [{Key,Val}=E|D]) -> case F(Key, Val) of @@ -160,8 +215,11 @@ filter(F, [{Key,Val}=E|D]) -> end; filter(F, []) when is_function(F, 2) -> []. --spec merge(fun((term(), term(), term()) -> term()), orddict(), orddict()) -> - orddict(). +-spec merge(Fun, Orddict1, Orddict2) -> Orddict3 when + Fun :: fun((Key :: term(), Value1 :: term(), Value2 :: term()) -> Value :: term()), + Orddict1 :: orddict(), + Orddict2 :: orddict(), + Orddict3 :: orddict(). merge(F, [{K1,_}=E1|D1], [{K2,_}=E2|D2]) when K1 < K2 -> [E1|merge(F, D1, [E2|D2])]; diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index 5a1c260703..4a8b1275b2 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -40,7 +40,8 @@ new() -> []. %% is_set(Term) -> boolean(). %% Return 'true' if Set is an ordered set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Ordset) -> boolean() when + Ordset :: term(). is_set([E|Es]) -> is_set(Es, E); is_set([]) -> true; @@ -54,21 +55,26 @@ is_set([], _) -> true. %% size(OrdSet) -> int(). %% Return the number of elements in OrdSet. --spec size(ordset(_)) -> non_neg_integer(). +-spec size(Ordset) -> non_neg_integer() when + Ordset :: ordset(_). size(S) -> length(S). %% to_list(OrdSet) -> [Elem]. %% Return the elements in OrdSet as a list. --spec to_list(ordset(T)) -> [T]. +-spec to_list(Ordset) -> List when + Ordset :: ordset(T), + List :: [T]. to_list(S) -> S. %% from_list([Elem]) -> Set. %% Build an ordered set from the elements in List. --spec from_list([T]) -> ordset(T). +-spec from_list(List) -> Ordset when + List :: [T], + Ordset :: ordset(T). from_list(L) -> lists:usort(L). @@ -76,7 +82,9 @@ from_list(L) -> %% is_element(Element, OrdSet) -> boolean(). %% Return 'true' if Element is an element of OrdSet, else 'false'. --spec is_element(term(), ordset(_)) -> boolean(). +-spec is_element(Element, Ordset) -> boolean() when + Element :: term(), + Ordset :: ordset(_). is_element(E, [H|Es]) when E > H -> is_element(E, Es); is_element(E, [H|_]) when E < H -> false; @@ -86,7 +94,12 @@ is_element(_, []) -> false. %% add_element(Element, OrdSet) -> OrdSet. %% Return OrdSet with Element inserted in it. --spec add_element(E, ordset(T)) -> [T | E,...]. +-spec add_element(Element, Ordset1) -> Ordset2 when + Element :: E, + Ordset1 :: ordset(T), + Ordset2 :: ordset(T | E). + +%-spec add_element(E, ordset(T)) -> [T | E,...]. add_element(E, [H|Es]) when E > H -> [H|add_element(E, Es)]; add_element(E, [H|_]=Set) when E < H -> [E|Set]; @@ -96,7 +109,10 @@ add_element(E, []) -> [E]. %% del_element(Element, OrdSet) -> OrdSet. %% Return OrdSet but with Element removed. --spec del_element(term(), ordset(T)) -> ordset(T). +-spec del_element(Element, Ordset1) -> Ordset2 when + Element :: term(), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). del_element(E, [H|Es]) when E > H -> [H|del_element(E, Es)]; del_element(E, [H|_]=Set) when E < H -> Set; @@ -106,7 +122,10 @@ del_element(_, []) -> []. %% union(OrdSet1, OrdSet2) -> OrdSet %% Return the union of OrdSet1 and OrdSet2. --spec union(ordset(T1), ordset(T2)) -> ordset(T1 | T2). +-spec union(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(T1), + Ordset2 :: ordset(T2), + Ordset3 :: ordset(T1 | T2). union([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|union(Es1, Set2)]; @@ -120,7 +139,9 @@ union(Es1, []) -> Es1. %% union([OrdSet]) -> OrdSet %% Return the union of the list of ordered sets. --spec union([ordset(T)]) -> ordset(T). +-spec union(OrdsetList) -> Ordset when + OrdsetList :: [ordset(T)], + Ordset :: ordset(T). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); @@ -133,7 +154,10 @@ union1(S1, []) -> S1. %% intersection(OrdSet1, OrdSet2) -> OrdSet. %% Return the intersection of OrdSet1 and OrdSet2. --spec intersection(ordset(_), ordset(_)) -> ordset(_). +-spec intersection(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). intersection([E1|Es1], [E2|_]=Set2) when E1 < E2 -> intersection(Es1, Set2); @@ -149,7 +173,9 @@ intersection(_, []) -> %% intersection([OrdSet]) -> OrdSet. %% Return the intersection of the list of ordered sets. --spec intersection([ordset(_),...]) -> ordset(_). +-spec intersection(OrdsetList) -> Ordset when + OrdsetList :: [ordset(_),...], + Ordset :: ordset(_). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); @@ -162,7 +188,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(OrdSet1, OrdSet2) -> boolean(). %% Check whether OrdSet1 and OrdSet2 are disjoint. --spec is_disjoint(ordset(_), ordset(_)) -> boolean(). +-spec is_disjoint(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_disjoint([E1|Es1], [E2|_]=Set2) when E1 < E2 -> is_disjoint(Es1, Set2); @@ -179,7 +207,10 @@ is_disjoint(_, []) -> %% Return all and only the elements of OrdSet1 which are not also in %% OrdSet2. --spec subtract(ordset(_), ordset(_)) -> ordset(_). +-spec subtract(Ordset1, Ordset2) -> Ordset3 when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_), + Ordset3 :: ordset(_). subtract([E1|Es1], [E2|_]=Set2) when E1 < E2 -> [E1|subtract(Es1, Set2)]; @@ -194,7 +225,9 @@ subtract(Es1, []) -> Es1. %% Return 'true' when every element of OrdSet1 is also a member of %% OrdSet2, else 'false'. --spec is_subset(ordset(_), ordset(_)) -> boolean(). +-spec is_subset(Ordset1, Ordset2) -> boolean() when + Ordset1 :: ordset(_), + Ordset2 :: ordset(_). is_subset([E1|_], [E2|_]) when E1 < E2 -> %E1 not in Set2 false; @@ -208,7 +241,11 @@ is_subset(_, []) -> false. %% fold(Fun, Accumulator, OrdSet) -> Accumulator. %% Fold function Fun over all elements in OrdSet and return Accumulator. --spec fold(fun((T, term()) -> term()), term(), ordset(T)) -> term(). +-spec fold(Function, Acc0, Ordset) -> Acc1 when + Function :: fun((Element :: T, AccIn :: term()) -> AccOut :: term()), + Ordset :: ordset(T), + Acc0 :: term(), + Acc1 :: term(). fold(F, Acc, Set) -> lists:foldl(F, Acc, Set). @@ -216,7 +253,10 @@ fold(F, Acc, Set) -> %% filter(Fun, OrdSet) -> OrdSet. %% Filter OrdSet with Fun. --spec filter(fun((T) -> boolean()), ordset(T)) -> ordset(T). +-spec filter(Pred, Ordset1) -> Ordset2 when + Pred :: fun((Element :: T) -> boolean()), + Ordset1 :: ordset(T), + Ordset2 :: ordset(T). filter(F, Set) -> lists:filter(F, Set). diff --git a/lib/stdlib/src/pg.erl b/lib/stdlib/src/pg.erl index 503654e706..ee177e4e0b 100644 --- a/lib/stdlib/src/pg.erl +++ b/lib/stdlib/src/pg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -35,7 +35,9 @@ %% Create a brand new empty process group with the master residing %% at the local node --spec create(term()) -> 'ok' | {'error', term()}. +-spec create(PgName) -> 'ok' | {'error', Reason} when + PgName :: term(), + Reason :: 'already_created' | term(). create(PgName) -> catch begin check(PgName), @@ -46,7 +48,10 @@ create(PgName) -> %% Create a brand new empty process group with the master %% residing at Node --spec create(term(), node()) -> 'ok' | {'error', term()}. +-spec create(PgName, Node) -> 'ok' | {'error', Reason} when + PgName :: term(), + Node :: node(), + Reason :: 'already_created' | term(). create(PgName, Node) -> catch begin check(PgName), @@ -66,7 +71,10 @@ standby(_PgName, _Node) -> %% Tell process group PgName that Pid is a new member of the group %% synchronously return a list of all old members in the group --spec join(atom(), pid()) -> [pid()]. +-spec join(PgName, Pid) -> Members when + PgName :: term(), + Pid :: pid(), + Members :: [pid()]. join(PgName, Pid) when is_atom(PgName) -> global:send(PgName, {join,self(),Pid}), @@ -77,7 +85,9 @@ join(PgName, Pid) when is_atom(PgName) -> %% Multi cast Mess to all members in the group --spec send(atom() | pid(), term()) -> 'ok'. +-spec send(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). send(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {send, self(), Mess}), @@ -89,7 +99,9 @@ send(Pg, Mess) when is_pid(Pg) -> %% multi cast a message to all members in the group but ourselves %% If we are a member --spec esend(atom() | pid(), term()) -> 'ok'. +-spec esend(PgName, Msg) -> 'ok' when + PgName :: term(), + Msg :: term(). esend(PgName, Mess) when is_atom(PgName) -> global:send(PgName, {esend,self(),Mess}), @@ -100,7 +112,9 @@ esend(Pg, Mess) when is_pid(Pg) -> %% Return the members of the group --spec members(atom() | pid()) -> [pid()]. +-spec members(PgName) -> Members when + PgName :: term(), + Members :: [pid()]. members(PgName) when is_atom(PgName) -> global:send(PgName, {self() ,members}), diff --git a/lib/stdlib/src/pool.erl b/lib/stdlib/src/pool.erl index a3c9927ee9..a5eb191ab2 100644 --- a/lib/stdlib/src/pool.erl +++ b/lib/stdlib/src/pool.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -52,11 +52,16 @@ %% Start up using the .hosts.erlang file --spec start(atom()) -> [node()]. +-spec start(Name) -> Nodes when + Name :: atom(), + Nodes :: [node()]. start(Name) -> start(Name,[]). --spec start(atom(), string()) -> [node()]. +-spec start(Name, Args) -> Nodes when + Name :: atom(), + Args :: string(), + Nodes :: [node()]. start(Name, Args) when is_atom(Name) -> gen_server:start({global, pool_master}, pool, [], []), Hosts = net_adm:host_file(), @@ -71,7 +76,8 @@ start(Name, Args) when is_atom(Name) -> get_nodes() -> get_elements(2, get_nodes_and_load()). --spec attach(node()) -> 'already_attached' | 'attached'. +-spec attach(Node) -> 'already_attached' | 'attached' when + Node :: node(). attach(Node) -> gen_server:call({global, pool_master}, {attach, Node}). @@ -82,11 +88,17 @@ get_nodes_and_load() -> get_node() -> gen_server:call({global, pool_master}, get_node). --spec pspawn(module(), atom(), [term()]) -> pid(). +-spec pspawn(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn(M, F, A) -> gen_server:call({global, pool_master}, {spawn, group_leader(), M, F, A}). --spec pspawn_link(module(), atom(), [term()]) -> pid(). +-spec pspawn_link(Mod, Fun, Args) -> pid() when + Mod :: module(), + Fun :: atom(), + Args :: [term()]. pspawn_link(M, F, A) -> P = pspawn(M, F, A), link(P), diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 4fb64a3353..02bcbb5a60 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.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 @@ -40,77 +40,108 @@ -type priority_level() :: 'high' | 'low' | 'max' | 'normal'. -type spawn_option() :: 'link' + | 'monitor' | {'priority', priority_level()} | {'min_heap_size', non_neg_integer()} + | {'min_bin_vheap_size', non_neg_integer()} | {'fullsweep_after', non_neg_integer()}. --type dict_or_pid() :: pid() | [_] | {integer(), integer(), integer()}. +-type dict_or_pid() :: pid() + | (ProcInfo :: [_]) + | {X :: integer(), Y :: integer(), Z :: integer()}. %%----------------------------------------------------------------------------- --spec spawn(function()) -> pid(). +-spec spawn(Fun) -> pid() when + Fun :: function(). spawn(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(atom(), atom(), [term()]) -> pid(). +-spec spawn(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(function()) -> pid(). +-spec spawn_link(Fun) -> pid() when + Fun :: function(). spawn_link(F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Module, Function, Args) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(M,F,A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn(node(), function()) -> pid(). +-spec spawn(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. + spawn(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_link(node(), function()) -> pid(). +-spec spawn_link(Node, Fun) -> pid() when + Node :: node(), + Fun :: function(). spawn_link(Node, F) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,F]). --spec spawn_link(node(), atom(), atom(), [term()]) -> pid(). +-spec spawn_link(Node, Module, Function, Args) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()]. spawn_link(Node, M, F, A) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), Ancestors = get_ancestors(), erlang:spawn_link(Node, ?MODULE, init_p, [Parent,Ancestors,M,F,A]). --spec spawn_opt(function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Fun, SpawnOpts) -> pid() when + Fun :: function(), + SpawnOpts :: [spawn_option()]. + spawn_opt(F, Opts) when is_function(F) -> Parent = get_my_name(), Ancestors = get_ancestors(), check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,F],Opts). --spec spawn_opt(node(), function(), [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Function, SpawnOpts) -> pid() when + Node :: node(), + Function :: function(), + SpawnOpts :: [spawn_option()]. spawn_opt(Node, F, Opts) when is_function(F) -> Parent = get_my_name(), @@ -118,7 +149,11 @@ spawn_opt(Node, F, Opts) when is_function(F) -> check_for_monitor(Opts), erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts). --spec spawn_opt(atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Module, Function, Args, SpawnOpts) -> pid() when + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -126,7 +161,12 @@ spawn_opt(M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> check_for_monitor(Opts), erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts). --spec spawn_opt(node(), atom(), atom(), [term()], [spawn_option()]) -> pid(). +-spec spawn_opt(Node, Module, Function, Args, SpawnOpts) -> pid() when + Node :: node(), + Module :: module(), + Function :: atom(), + Args :: [term()], + SpawnOpts :: [spawn_option()]. spawn_opt(Node, M, F, A, Opts) when is_atom(M), is_atom(F), is_list(A) -> Parent = get_my_name(), @@ -144,7 +184,10 @@ check_for_monitor(SpawnOpts) -> false end. --spec hibernate(module(), atom(), [term()]) -> no_return(). +-spec hibernate(Module, Function, Args) -> no_return() when + Module :: module(), + Function :: atom(), + Args :: [term()]. hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> erlang:hibernate(?MODULE, wake_up, [M, F, A]). @@ -210,35 +253,65 @@ exit_p(Class, Reason) -> exit(Reason) end. --spec start(atom(), atom(), [term()]) -> term(). +-spec start(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start(M, F, A, infinity). --spec start(atom(), atom(), [term()], timeout()) -> term(). +-spec start(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn(M, F, A), sync_wait(Pid, Timeout). --spec start(atom(), atom(), [term()], timeout(), [spawn_option()]) -> term(). +-spec start(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + Ret :: term() | {error, Reason :: term()}. start(M, F, A, Timeout, SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_opt(M, F, A, SpawnOpts), sync_wait(Pid, Timeout). --spec start_link(atom(), atom(), [term()]) -> term(). +-spec start_link(Module, Function, Args) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> start_link(M, F, A, infinity). --spec start_link(atom(), atom(), [term()], timeout()) -> term(). +-spec start_link(Module, Function, Args, Time) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + Ret :: term() | {error, Reason :: term()}. start_link(M, F, A, Timeout) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_link(M, F, A), sync_wait(Pid, Timeout). --spec start_link(atom(),atom(),[term()],timeout(),[spawn_option()]) -> term(). +-spec start_link(Module, Function, Args, Time, SpawnOpts) -> Ret when + Module :: module(), + Function :: atom(), + Args :: [term()], + Time :: timeout(), + SpawnOpts :: [spawn_option()], + Ret :: term() | {error, Reason :: term()}. start_link(M,F,A,Timeout,SpawnOpts) when is_atom(M), is_atom(F), is_list(A) -> Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)), @@ -267,13 +340,17 @@ flush(Pid) -> true end. --spec init_ack(pid(), term()) -> 'ok'. +-spec init_ack(Parent, Ret) -> 'ok' when + Parent :: pid(), + Ret :: term(). init_ack(Parent, Return) -> Parent ! {ack, self(), Return}, ok. --spec init_ack(term()) -> 'ok'. +-spec init_ack(Ret) -> 'ok' when + Ret :: term(). + init_ack(Return) -> [Parent|_] = get('$ancestors'), init_ack(Parent, Return). @@ -282,7 +359,11 @@ init_ack(Return) -> %% Fetch the initial call of a proc_lib spawned process. %% ----------------------------------------------------- --spec initial_call(dict_or_pid()) -> {atom(), atom(), [atom()]} | 'false'. +-spec initial_call(Process) -> {Module, Function, Args} | 'false' when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Args :: [atom()]. initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -305,7 +386,11 @@ make_dummy_args(N, Acc) -> %% This function is typically called from c:i() and c:regs(). %% ----------------------------------------------------- --spec translate_initial_call(dict_or_pid()) -> mfa(). +-spec translate_initial_call(Process) -> {Module, Function, Arity} when + Process :: dict_or_pid(), + Module :: module(), + Function :: atom(), + Arity :: byte(). translate_initial_call(DictOrPid) -> case raw_initial_call(DictOrPid) of @@ -577,7 +662,8 @@ check(Res) -> Res. %%% Format (and write) a generated crash info structure. %%% ----------------------------------------------------------- --spec format([term()]) -> string(). +-spec format(CrashReport) -> string() when + CrashReport :: [term()]. format([OwnReport,LinkReport]) -> OwnFormat = format_report(OwnReport), diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl index 6a45e0f868..68697d0da2 100644 --- a/lib/stdlib/src/proplists.erl +++ b/lib/stdlib/src/proplists.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -37,7 +37,7 @@ %% overriding the default settings, object properties, annotations, %% etc.

%% -%% @type property() = atom() | tuple() +%% % @type property() = atom() | tuple() -module(proplists). @@ -53,14 +53,8 @@ -type property() :: atom() | tuple(). --type aliases() :: [{any(), any()}]. --type negations() :: [{any(), any()}]. --type expansions() :: [{property(), [any()]}]. - %% --------------------------------------------------------------------- -%% @spec property(P::property()) -> property() -%% %% @doc Creates a normal form (minimal) representation of a property. If %% P is {Key, true} where Key is %% an atom, this returns Key, otherwise the whole term @@ -68,7 +62,8 @@ %% %% @see property/2 --spec property(property()) -> property(). +-spec property(Property) -> Property when + Property :: property(). property({Key, true}) when is_atom(Key) -> Key; @@ -76,8 +71,6 @@ property(Property) -> Property. -%% @spec property(Key::term(), Value::term()) -> property() -%% %% @doc Creates a normal form (minimal) representation of a simple %% key/value property. Returns Key if Value is %% true and Key is an atom, otherwise a tuple @@ -85,7 +78,10 @@ property(Property) -> %% %% @see property/1 --spec property(Key::term(), Value::term()) -> atom() | {term(), term()}. +-spec property(Key, Value) -> Property when + Key :: term(), + Value :: term(), + Property :: atom() | {term(), term()}. property(Key, true) when is_atom(Key) -> Key; @@ -95,14 +91,13 @@ property(Key, Value) -> %% --------------------------------------------------------------------- -%% @spec unfold(List::[term()]) -> [term()] -%% %% @doc Unfolds all occurences of atoms in List to tuples %% {Atom, true}. %% %% @see compact/1 --spec unfold(List::[term()]) -> [term()]. +-spec unfold(List) -> List when + List :: [term()]. unfold([P | Ps]) -> if is_atom(P) -> @@ -113,15 +108,14 @@ unfold([P | Ps]) -> unfold([]) -> []. -%% @spec compact(List::[term()]) -> [term()] -%% %% @doc Minimizes the representation of all entries in the list. This is %% equivalent to [property(P) || P <- List]. %% %% @see unfold/1 %% @see property/1 --spec compact(List::[property()]) -> [property()]. +-spec compact(List) -> List when + List :: [property()]. compact(List) -> [property(P) || P <- List]. @@ -129,8 +123,6 @@ compact(List) -> %% --------------------------------------------------------------------- -%% @spec lookup(Key::term(), List::[term()]) -> none | tuple() -%% %% @doc Returns the first entry associated with Key in %% List, if one exists, otherwise returns %% none. For an atom A in the list, the tuple @@ -140,7 +132,9 @@ compact(List) -> %% @see get_value/2 %% @see get_bool/2 --spec lookup(Key::term(), List::[term()]) -> 'none' | tuple(). +-spec lookup(Key, List) -> 'none' | tuple() when + Key :: term(), + List :: [term()]. lookup(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -154,15 +148,15 @@ lookup(Key, [P | Ps]) -> lookup(_Key, []) -> none. -%% @spec lookup_all(Key::term(), List::[term()]) -> [tuple()] -%% %% @doc Returns the list of all entries associated with Key %% in List. If no such entry exists, the result is the %% empty list. %% %% @see lookup/2 --spec lookup_all(Key::term(), List::[term()]) -> [tuple()]. +-spec lookup_all(Key, List) -> [tuple()] when + Key :: term(), + List :: [term()]. lookup_all(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -178,13 +172,13 @@ lookup_all(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec is_defined(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns true if List contains at least %% one entry associated with Key, otherwise %% false is returned. --spec is_defined(Key::term(), List::[term()]) -> boolean(). +-spec is_defined(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. is_defined(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -200,17 +194,15 @@ is_defined(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_value(Key::term(), List::[term()]) -> term() %% @equiv get_value(Key, List, undefined) --spec get_value(Key::term(), List::[term()]) -> term(). +-spec get_value(Key, List) -> term() when + Key :: term(), + List :: List::[term()]. get_value(Key, List) -> get_value(Key, List, undefined). -%% @spec get_value(Key::term(), List::[term()], Default::term()) -> -%% term() -%% %% @doc Returns the value of a simple key/value property in %% List. If lookup(Key, List) would yield %% {Key, Value}, this function returns the corresponding @@ -221,7 +213,10 @@ get_value(Key, List) -> %% @see get_all_values/2 %% @see get_bool/2 --spec get_value(Key::term(), List::[term()], Default::term()) -> term(). +-spec get_value(Key, List, Default) -> term() when + Key :: term(), + List :: [term()], + Default :: term(). get_value(Key, [P | Ps], Default) -> if is_atom(P), P =:= Key -> @@ -240,8 +235,6 @@ get_value(Key, [P | Ps], Default) -> get_value(_Key, [], Default) -> Default. -%% @spec get_all_values(Key, List) -> [term()] -%% %% @doc Similar to get_value/2, but returns the list of %% values for all entries {Key, Value} in %% List. If no such entry exists, the result is the empty @@ -249,7 +242,9 @@ get_value(_Key, [], Default) -> %% %% @see get_value/2 --spec get_all_values(Key::term(), List::[term()]) -> [term()]. +-spec get_all_values(Key, List) -> [term()] when + Key :: term(), + List :: [term()]. get_all_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -267,8 +262,6 @@ get_all_values(Key, [P | Ps]) -> get_all_values(_Key, []) -> []. -%% @spec append_values(Key::term(), List::[term()]) -> [term()] -%% %% @doc Similar to get_all_values/2, but each value is %% wrapped in a list unless it is already itself a list, and the %% resulting list of lists is concatenated. This is often useful for @@ -278,7 +271,9 @@ get_all_values(_Key, []) -> %% %% @see get_all_values/2 --spec append_values(Key::term(), List::[term()]) -> [term()]. +-spec append_values(Key, List) -> List when + Key :: term(), + List :: [term()]. append_values(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -301,8 +296,6 @@ append_values(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_bool(Key::term(), List::[term()]) -> boolean() -%% %% @doc Returns the value of a boolean key/value option. If %% lookup(Key, List) would yield {Key, true}, %% this function returns true; otherwise false @@ -311,7 +304,9 @@ append_values(_Key, []) -> %% @see lookup/2 %% @see get_value/2 --spec get_bool(Key::term(), List::[term()]) -> boolean(). +-spec get_bool(Key, List) -> boolean() when + Key :: term(), + List :: [term()]. get_bool(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -333,12 +328,11 @@ get_bool(_Key, []) -> %% --------------------------------------------------------------------- -%% @spec get_keys(List::[term()]) -> [term()] -%% %% @doc Returns an unordered list of the keys used in List, %% not containing duplicates. --spec get_keys(List::[term()]) -> [term()]. +-spec get_keys(List) -> [term()] when + List :: [term()]. get_keys(Ps) -> sets:to_list(get_keys(Ps, sets:new())). @@ -357,12 +351,12 @@ get_keys([], Keys) -> %% --------------------------------------------------------------------- -%% @spec delete(Key::term(), List::[term()]) -> [term()] -%% %% @doc Deletes all entries associated with Key from %% List. --spec delete(Key::term(), List::[term()]) -> [term()]. +-spec delete(Key, List) -> List when + Key :: term(), + List::[term()]. delete(Key, [P | Ps]) -> if is_atom(P), P =:= Key -> @@ -378,11 +372,6 @@ delete(_, []) -> %% --------------------------------------------------------------------- -%% @spec substitute_aliases(Aliases, List::[term()]) -> [term()] -%% -%% Aliases = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of properties. For each entry in %% List, if it is associated with some key K1 %% such that {K1, K2} occurs in Aliases, the @@ -398,7 +387,10 @@ delete(_, []) -> %% @see substitute_negations/2 %% @see normalize/2 --spec substitute_aliases(aliases(), List::[term()]) -> [term()]. +-spec substitute_aliases(Aliases, List) -> List when + Aliases :: [{Key, Key}], + Key :: term(), + List::[term()]. substitute_aliases(As, Props) -> [substitute_aliases_1(As, P) || P <- Props]. @@ -417,11 +409,6 @@ substitute_aliases_1([], P) -> %% --------------------------------------------------------------------- -%% @spec substitute_negations(Negations, List::[term()]) -> [term()] -%% -%% Negations = [{Key, Key}] -%% Key = term() -%% %% @doc Substitutes keys of boolean-valued properties and simultaneously %% negates their values. For each entry in List, if it is %% associated with some key K1 such that {K1, @@ -443,7 +430,10 @@ substitute_aliases_1([], P) -> %% @see substitute_aliases/2 %% @see normalize/2 --spec substitute_negations(negations(), List::[term()]) -> [term()]. +-spec substitute_negations(Negations, List) -> List when + Negations :: [{Key, Key}], + Key :: term(), + List :: [term()]. substitute_negations(As, Props) -> [substitute_negations_1(As, P) || P <- Props]. @@ -472,10 +462,6 @@ substitute_negations_1([], P) -> %% --------------------------------------------------------------------- -%% @spec expand(Expansions, List::[term()]) -> [term()] -%% -%% Expansions = [{property(), [term()]}] -%% %% @doc Expands particular properties to corresponding sets of %% properties (or other terms). For each pair {Property, %% Expansion} in Expansions, if E is @@ -510,7 +496,9 @@ substitute_negations_1([], P) -> %% %% @see normalize/2 --spec expand(Expansions::expansions(), [term()]) -> [term()]. +-spec expand(Expansions, List) -> List when + Expansions :: [{Property :: property(), Expansion :: [term()]}], + List :: [term()]. expand(Es, Ps) when is_list(Ps) -> Es1 = [{property(P), V} || {P, V} <- Es], @@ -589,15 +577,6 @@ flatten([]) -> %% --------------------------------------------------------------------- -%% @spec normalize(List::[term()], Stages::[Operation]) -> [term()] -%% -%% Operation = {aliases, Aliases} | {negations, Negations} -%% | {expand, Expansions} -%% Aliases = [{Key, Key}] -%% Negations = [{Key, Key}] -%% Key = term() -%% Expansions = [{property(), [term()]}] -%% %% @doc Passes List through a sequence of %% substitution/expansion stages. For an aliases operation, %% the function substitute_aliases/2 is applied using the @@ -619,11 +598,15 @@ flatten([]) -> %% @see expand/2 %% @see compact/1 --type operation() :: {'aliases', aliases()} - | {'negations', negations()} - | {'expand', expansions()}. - --spec normalize(List::[term()], Stages::[operation()]) -> [term()]. +-spec normalize(List, Stages) -> List when + List :: [term()], + Stages :: [Operation], + Operation :: {'aliases', Aliases} + | {'negations', Negations} + | {'expand', Expansions}, + Aliases :: [{Key, Key}], + Negations :: [{Key, Key}], + Expansions :: [{Property :: property(), Expansion :: [term()]}]. normalize(L, [{aliases, As} | Xs]) -> normalize(substitute_aliases(As, L), Xs); @@ -636,10 +619,6 @@ normalize(L, []) -> %% --------------------------------------------------------------------- -%% @spec split(List::[term()], Keys::[term()]) -> {Lists, Rest} -%% Lists = [[term()]] -%% Rest = [term()] -%% %% @doc Partitions List into a list of sublists and a %% remainder. Lists contains one sublist for each key in %% Keys, in the corresponding order. The relative order of @@ -654,7 +633,11 @@ normalize(L, []) -> %% {[[a], [{b, 5}, b],[{c, 2}, {c, 3, 4}]], [{e, 1}, d]} %%

--spec split(List::[term()], Keys::[term()]) -> {[[term()]], [term()]}. +-spec split(List, Keys) -> {Lists, Rest} when + List :: [term()], + Keys :: [term()], + Lists :: [[term()]], + Rest :: [term()]. split(List, Keys) -> {Store, Rest} = split(List, dict:from_list([{K, []} || K <- Keys]), []), diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl index bc6944e520..5ca04ff023 100644 --- a/lib/stdlib/src/qlc.erl +++ b/lib/stdlib/src/qlc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -60,7 +60,7 @@ -record(qlc_table, % qlc:table/2 {trav_fun, % traverse fun - trav_MS, % bool(); true iff traverse fun takes a match spec + trav_MS, % boolean(); true iff traverse fun takes a match spec pre_fun, post_fun, info_fun, @@ -110,12 +110,12 @@ -record(qlc_cursor, {c}). -record(qlc_opt, - {unique = false, % bool() - cache = false, % bool() | list (true~ets, false~no) + {unique = false, % boolean() + cache = false, % boolean() | list (true~ets, false~no) max_lookup = -1, % int() >= 0 | -1 (represents infinity) join = any, % any | nested_loop | merge | lookup tmpdir = "", % global tmpdir - lookup = any, % any | bool() + lookup = any, % any | boolean() max_list = ?MAX_LIST_SIZE, % int() >= 0 tmpdir_usage = allowed % allowed | not_allowed % | warning_msg | error_msg | info_msg @@ -125,6 +125,8 @@ -define(THROWN_ERROR, {?MODULE, throw_error, _}). +-export_type([query_handle/0]). + %%% A query handle is a tuple {qlc_handle, Handle} where Handle is one %%% of #qlc_append, #qlc_table, #qlc_sort, and #qlc_lc. @@ -144,6 +146,35 @@ get_handle(_) -> %%% Exported functions %%% +-type(query_list_comprehension() :: term()). +-opaque(query_cursor() :: {qlc_cursor, term()}). +-opaque(query_handle() :: {qlc_handle, term()}). +-type(query_handle_or_list() :: query_handle() | list()). +-type(answers() :: [answer()]). +-type(answer() :: term()). +-type(abstract_expr() :: erl_parse:abstract_expr()). +-type(match_expression() :: ets:match_spec()). +-type(spawn_options() :: default | [proc_lib:spawn_option()]). +-type(sort_options() :: [sort_option()] | sort_option()). +-type(sort_option() :: {compressed, boolean()} + | {no_files, no_files()} + | {order, order()} + | {size, pos_integer()} + | {tmpdir, tmp_directory()} + | {unique, boolean()}). +-type(order() :: ascending | descending | order_fun()). +-type(order_fun() :: fun((term(), term()) -> boolean())). +-type(tmp_directory() :: [] | file:name()). +-type(no_files() :: pos_integer()). % > 1 +-type(key_pos() :: pos_integer() | [pos_integer()]). +-type(max_list_size() :: non_neg_integer()). +-type(cache() :: ets | list | no). +-type(tmp_file_usage() :: allowed | not_allowed | info_msg + | warning_msg | error_msg). + +-spec(append(QHL) -> QH when + QHL :: [query_handle_or_list()], + QH :: query_handle()). append(QHs) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QHs]); @@ -151,6 +182,10 @@ append(QHs) -> end || QH <- QHs], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(append(QH1, QH2) -> QH3 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle_or_list(), + QH3 :: query_handle()). append(QH1, QH2) -> Hs = [case get_handle(QH) of badarg -> erlang:error(badarg, [QH1, QH2]); @@ -158,9 +193,22 @@ append(QH1, QH2) -> end || QH <- [QH1, QH2]], #qlc_handle{h = #qlc_append{hl = Hs}}. +-spec(cursor(QH) -> Cursor when + QH :: query_handle_or_list(), + Cursor :: query_cursor()). cursor(QH) -> cursor(QH, []). +-spec(cursor(QH, Options) -> Cursor when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {spawn_options, spawn_options()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Cursor :: query_cursor()). cursor(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, spawn_options, max_list_size, @@ -179,6 +227,8 @@ cursor(QH, Options) -> end end. +-spec(delete_cursor(QueryCursor) -> ok when + QueryCursor :: query_cursor()). delete_cursor(#qlc_cursor{c = {_, Owner}}=C) when Owner =/= self() -> erlang:error(not_cursor_owner, [C]); delete_cursor(#qlc_cursor{c = {Pid, _}}) -> @@ -186,15 +236,47 @@ delete_cursor(#qlc_cursor{c = {Pid, _}}) -> delete_cursor(T) -> erlang:error(badarg, [T]). +-spec(e(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH) -> eval(QH, []). +-spec(e(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). e(QH, Options) -> eval(QH, Options). +-spec(eval(QH) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH) -> eval(QH, []). +-spec(eval(QH, Options) -> Answers | Error when + QH :: query_handle_or_list(), + Answers :: answers(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). eval(QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -226,9 +308,35 @@ eval(QH, Options) -> end end. +-spec(fold(Function, Acc0, QH) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH) -> fold(Fun, Acc0, QH, []). +-spec(fold(Function, Acc0, QH, Options) -> + Acc1 | Error when + QH :: query_handle_or_list(), + Function :: fun((answer(), AccIn) -> AccOut), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Options :: [Option] | Option, + Option :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). fold(Fun, Acc0, QH, Options) -> case {options(Options, [unique_all, cache_all, tmpdir, max_list_size, tmpdir_usage]), @@ -258,6 +366,9 @@ fold(Fun, Acc0, QH, Options) -> end end. +-spec(format_error(Error) -> Chars when + Error :: {error, module(), term()}, + Chars :: io_lib:chars()). format_error(not_a_query_list_comprehension) -> io_lib:format("argument is not a query list comprehension", []); format_error({used_generator_variable, V}) -> @@ -295,9 +406,29 @@ format_error({error, Module, Reason}) -> format_error(E) -> io_lib:format("~p~n", [E]). +-spec(info(QH) -> Info when + QH :: query_handle_or_list(), + Info :: abstract_expr() | string()). info(QH) -> info(QH, []). +-spec(info(QH, Options) -> Info when + QH :: query_handle_or_list(), + Options :: [Option] | Option, + Option :: EvalOption | ReturnOption, + EvalOption :: {cache_all, cache()} | cache_all + | {max_list_size, max_list_size()} + | {tmpdir_usage, tmp_file_usage()} + | {tmpdir, tmp_directory()} + | {unique_all, boolean()} | unique_all, + ReturnOption :: {depth, Depth} + | {flat, boolean()} + | {format, Format} + | {n_elements, NElements}, + Depth :: infinity | non_neg_integer(), + Format :: abstract_code | string, + NElements :: infinity | pos_integer(), + Info :: abstract_expr() | string()). info(QH, Options) -> case {options(Options, [unique_all, cache_all, flat, format, n_elements, depth, tmpdir, max_list_size, tmpdir_usage]), @@ -333,9 +464,18 @@ info(QH, Options) -> end end. +-spec(keysort(KeyPos, QH1) -> QH2 when + KeyPos :: key_pos(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH) -> keysort(KeyPos, QH, []). +-spec(keysort(KeyPos, QH1, SortOptions) -> QH2 when + KeyPos :: key_pos(), + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). keysort(KeyPos, QH, Options) -> case {is_keypos(KeyPos), options(Options, [tmpdir, order, unique, compressed, @@ -354,9 +494,22 @@ keysort(KeyPos, QH, Options) -> -define(DEFAULT_NUM_OF_ANSWERS, 10). +-spec(next_answers(QueryCursor) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(C) -> next_answers(C, ?DEFAULT_NUM_OF_ANSWERS). +-spec(next_answers(QueryCursor, NumberOfAnswers) -> + Answers | Error when + QueryCursor :: query_cursor(), + Answers :: answers(), + NumberOfAnswers :: all_remaining | pos_integer(), + Error :: {error, module(), Reason}, + Reason :: file_sorter:reason()). next_answers(#qlc_cursor{c = {_, Owner}}=C, NumOfAnswers) when Owner =/= self() -> erlang:error(not_cursor_owner, [C, NumOfAnswers]); @@ -370,14 +523,35 @@ next_answers(#qlc_cursor{c = {Pid, _}}=C, NumOfAnswers) -> next_answers(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> qlc_pt:parse_transform(Forms, Options). %% The funcspecs qlc:q/1 and qlc:q/2 are known by erl_eval.erl and %% erl_lint.erl. +-spec(q(QLC) -> QH when + QLC :: query_list_comprehension(), + QH :: query_handle()). q(QLC_lc) -> q(QLC_lc, []). +-spec(q(QLC, Options) -> QH when + QH :: query_handle(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QLC :: query_list_comprehension()). q(#qlc_lc{}=QLC_lc, Options) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of [Unique, Cache, Max, Join, Lookup] -> @@ -390,9 +564,16 @@ q(#qlc_lc{}=QLC_lc, Options) -> q(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(sort(QH1) -> QH2 when + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH) -> sort(QH, []). +-spec(sort(QH1, SortOptions) -> QH2 when + SortOptions :: sort_options(), + QH1 :: query_handle_or_list(), + QH2 :: query_handle()). sort(QH, Options) -> case {options(Options, [tmpdir, order, unique, compressed, size, no_files]), get_handle(QH)} of @@ -406,14 +587,47 @@ sort(QH, Options) -> end. %% Note that the generated code is evaluated by (the slow) erl_eval. +-spec(string_to_handle(QueryString) -> QH | Error when + QueryString :: string(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str) -> string_to_handle(Str, []). +-spec(string_to_handle(QueryString, Options) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). string_to_handle(Str, Options) -> - string_to_handle(Str, Options, []). - -string_to_handle(Str, Options, Bindings) when is_list(Str), - is_list(Bindings) -> + string_to_handle(Str, Options, erl_eval:new_bindings()). + +-spec(string_to_handle(QueryString, Options, Bindings) -> QH | Error when + QueryString :: string(), + Options :: [Option] | Option, + Option :: {max_lookup, MaxLookup} + | {cache, cache()} | cache + | {join, Join} + | {lookup, Lookup} + | {unique, boolean()} | unique, + MaxLookup :: non_neg_integer() | infinity, + Join :: any | lookup | merge | nested_loop, + Lookup :: boolean() | any, + Bindings :: erl_eval:binding_struct(), + QH :: query_handle(), + Error :: {error, module(), Reason}, + Reason :: erl_parse:error_info() | erl_scan:error_info()). +string_to_handle(Str, Options, Bindings) when is_list(Str) -> case options(Options, [unique, cache, max_lookup, join, lookup]) of badarg -> erlang:error(badarg, [Str, Options, Bindings]); @@ -447,6 +661,51 @@ string_to_handle(Str, Options, Bindings) when is_list(Str), string_to_handle(T1, T2, T3) -> erlang:error(badarg, [T1, T2, T3]). +-spec(table(TraverseFun, Options) -> QH when + TraverseFun :: TraverseFun0 | TraverseFun1, + TraverseFun0 :: fun(() -> TraverseResult), + TraverseFun1 :: fun((match_expression()) -> TraverseResult), + TraverseResult :: Objects | term(), + Objects :: [] | [term() | ObjectList], + ObjectList :: TraverseFun0 | Objects, + Options :: [Option] | Option, + Option :: {format_fun, FormatFun} + | {info_fun, InfoFun} + | {lookup_fun, LookupFun} + | {parent_fun, ParentFun} + | {post_fun, PostFun} + | {pre_fun, PreFun} + | {key_equality, KeyComparison}, + FormatFun :: undefined | fun((SelectedObjects) -> FormatedTable), + SelectedObjects :: all + | {all, NElements, DepthFun} + | {match_spec, match_expression()} + | {lookup, Position, Keys} + | {lookup, Position, Keys, NElements, DepthFun}, + NElements :: infinity | pos_integer(), + DepthFun :: fun((term()) -> term()), + FormatedTable :: {Mod, Fun, Args} + | abstract_expr() + | string(), + InfoFun :: undefined | fun((InfoTag) -> InfoValue), + InfoTag :: indices | is_unique_objects | keypos | num_of_objects, + InfoValue :: undefined | term(), + LookupFun :: undefined | fun((Position, Keys) -> LookupResult), + LookupResult :: [term()] | term(), + ParentFun :: undefined | fun(() -> ParentFunValue), + PostFun :: undefined | fun(() -> term()), + PreFun :: undefined | fun((PreArgs) -> term()), + PreArgs :: [PreArg], + PreArg :: {parent_value, ParentFunValue} | {stop_fun, StopFun}, + ParentFunValue :: undefined | term(), + StopFun :: undefined | fun(() -> term()), + KeyComparison :: '=:=' | '==', + Position :: pos_integer(), + Keys :: [term()], + Mod :: atom(), + Fun :: atom(), + Args :: [term()], + QH :: query_handle()). table(TraverseFun, Options) when is_function(TraverseFun) -> case {is_function(TraverseFun, 0), IsFun1 = is_function(TraverseFun, 1)} of @@ -472,6 +731,11 @@ table(TraverseFun, Options) when is_function(TraverseFun) -> table(T1, T2) -> erlang:error(badarg, [T1, T2]). +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: abstract_expr(), + Expr :: abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bs0) -> qlc_pt:transform_from_evaluator(LC, Bs0). @@ -722,8 +986,8 @@ listify(T) -> %% Optimizations to be carried out. -record(optz, - {unique = false, % bool() - cache = false, % bool() | list + {unique = false, % boolean() + cache = false, % boolean() | list join_option = any, % constraint set by the 'join' option fast_join = no, % no | #qlc_join. 'no' means nested loop. opt % #qlc_opt @@ -756,8 +1020,8 @@ listify(T) -> lu_skip_quals = [], % qualifiers to skip due to lookup join = {[],[]}, % {Lookup, Merge} n_objs = undefined, % for join (not used yet) - is_unique_objects = false, % bool() - is_cached = false % bool() (true means 'ets' or 'list') + is_unique_objects = false, % boolean() + is_cached = false % boolean() (true means 'ets' or 'list') }). %%% Cursor process functions. @@ -1143,20 +1407,20 @@ monitor_request(Pid, Req) -> %% QueryDesc = {qlc, TemplateDesc, [QualDesc], [QueryOpt]} %% | {table, TableDesc} %% | {append, [QueryDesc]} -%% | {sort, QueryDesc, [SortOption]} -%% | {keysort, KeyPos, QueryDesc, [SortOption]} +%% | {sort, QueryDesc, [sort_option()]} +%% | {keysort, key_pos(), QueryDesc, [sort_option()]} %% | {list, list()} -%% | {list, QueryDesc, MatchExpression} +%% | {list, QueryDesc, match_expression()} %% TableDesc = {Mod, Fun, Args} -%% | AbstractExpression -%% | character_list() +%% | erl_parse:abstract_expr() +%% | string() %% Mod = module() %% Fun = atom() %% Args = [term()] %% QualDesc = FilterDesc %% | {generate, PatternDesc, QueryDesc} -%% QueryOpt = {cache, bool()} | cache -%% | {unique, bool()} | unique +%% QueryOpt = {cache, boolean()} | cache +%% | {unique, boolean()} | unique %% FilterDesc = PatternDesc = TemplateDesc = binary() le_info(#prepared{qh = #simple_qlc{le = LE, p = P, line = L, optz = Optz}}, diff --git a/lib/stdlib/src/qlc_pt.erl b/lib/stdlib/src/qlc_pt.erl index 24378a0698..21504d707b 100644 --- a/lib/stdlib/src/qlc_pt.erl +++ b/lib/stdlib/src/qlc_pt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -63,6 +63,12 @@ %%% Exported functions %%% +-spec(parse_transform(Forms, Options) -> Forms2 when + Forms :: [erl_parse:abstract_form()], + Forms2 :: [erl_parse:abstract_form()], + Options :: [Option], + Option :: type_checker | compile:option()). + parse_transform(Forms, Options) -> ?DEBUG("qlc Parse Transform~n", []), State = #state{imp = is_qlc_q_imported(Forms), @@ -96,10 +102,20 @@ parse_transform(Forms, Options) -> end end. +-spec(transform_from_evaluator(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_from_evaluator(LC, Bindings) -> ?DEBUG("qlc Parse Transform (Evaluator Version)~n", []), transform_expression(LC, Bindings, false). +-spec(transform_expression(LC, Bs) -> Expr when + LC :: erl_parse:abstract_expr(), + Expr :: erl_parse:abstract_expr(), + Bs :: erl_eval:binding_struct()). + transform_expression(LC, Bindings) -> transform_expression(LC, Bindings, true). diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl index c09079e8d2..4c6b4d710b 100644 --- a/lib/stdlib/src/queue.erl +++ b/lib/stdlib/src/queue.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -56,14 +56,16 @@ new() -> {[],[]}. %{RearList,FrontList} %% O(1) --spec is_queue(term()) -> boolean(). +-spec is_queue(Term) -> boolean() when + Term :: term(). is_queue({R,F}) when is_list(R), is_list(F) -> true; is_queue(_) -> false. %% O(1) --spec is_empty(queue()) -> boolean(). +-spec is_empty(Q) -> boolean() when + Q :: queue(). is_empty({[],[]}) -> true; is_empty({In,Out}) when is_list(In), is_list(Out) -> @@ -72,14 +74,16 @@ is_empty(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec len(queue()) -> non_neg_integer(). +-spec len(Q) -> non_neg_integer() when + Q :: queue(). len({R,F}) when is_list(R), is_list(F) -> length(R)+length(F); len(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec to_list(queue()) -> list(). +-spec to_list(Q) -> list() when + Q :: queue(). to_list({In,Out}) when is_list(In), is_list(Out) -> Out++lists:reverse(In, []); to_list(Q) -> @@ -88,7 +92,8 @@ to_list(Q) -> %% Create queue from list %% %% O(length(L)) --spec from_list(list()) -> queue(). +-spec from_list(L) -> queue() when + L :: list(). from_list(L) when is_list(L) -> f2r(L); from_list(L) -> @@ -97,7 +102,9 @@ from_list(L) -> %% Return true or false depending on if element is in queue %% %% O(length(Q)) worst case --spec member(term(), queue()) -> boolean(). +-spec member(Item, Q) -> boolean() when + Item :: term(), + Q :: queue(). member(X, {R,F}) when is_list(R), is_list(F) -> lists:member(X, R) orelse lists:member(X, F); member(X, Q) -> @@ -110,7 +117,10 @@ member(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in(term(), queue()) -> queue(). +-spec in(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). in(X, {[_]=In,[]}) -> {[X], In}; in(X, {In,Out}) when is_list(In), is_list(Out) -> @@ -122,7 +132,10 @@ in(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in_r(term(), queue()) -> queue(). +-spec in_r(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). in_r(X, {[],[_]=F}) -> {F,[X]}; in_r(X, {R,F}) when is_list(R), is_list(F) -> @@ -133,7 +146,10 @@ in_r(X, Q) -> %% Take from head/front %% %% O(1) amortized, O(len(Q)) worst case --spec out(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out(Q1) -> Result when + Q1 :: queue(), + Q2 :: queue(), + Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. out({[],[]}=Q) -> {empty,Q}; out({[V],[]}) -> @@ -151,7 +167,10 @@ out(Q) -> %% Take from tail/rear %% %% O(1) amortized, O(len(Q)) worst case --spec out_r(queue()) -> {'empty' | {'value',term()}, queue()}. +-spec out_r(Q1) -> Result when + Q1 :: queue(), + Q2 :: queue(), + Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. out_r({[],[]}=Q) -> {empty,Q}; out_r({[],[V]}) -> @@ -172,7 +191,9 @@ out_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get(queue()) -> term(). +-spec get(Q) -> Item when + Q :: queue(), + Item :: term(). get({[],[]}=Q) -> erlang:error(empty, [Q]); get({R,F}) when is_list(R), is_list(F) -> @@ -191,7 +212,9 @@ get([_|R], []) -> % malformed queue -> O(len(Q)) %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get_r(queue()) -> term(). +-spec get_r(Q) -> Item when + Q :: queue(), + Item :: term(). get_r({[],[]}=Q) -> erlang:error(empty, [Q]); get_r({[H|_],F}) when is_list(F) -> @@ -206,7 +229,9 @@ get_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek(queue()) -> 'empty' | {'value',term()}. +-spec peek(Q) -> 'empty' | {'value',Item} when + Q :: queue(), + Item :: term(). peek({[],[]}) -> empty; peek({R,[H|_]}) when is_list(R) -> @@ -221,7 +246,9 @@ peek(Q) -> %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek_r(queue()) -> 'empty' | {'value',term()}. +-spec peek_r(Q) -> 'empty' | {'value',Item} when + Q :: queue(), + Item :: term(). peek_r({[],[]}) -> empty; peek_r({[H|_],F}) when is_list(F) -> @@ -236,7 +263,9 @@ peek_r(Q) -> %% Remove the first element and return resulting queue %% %% O(1) amortized --spec drop(queue()) -> queue(). +-spec drop(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). drop({[],[]}=Q) -> erlang:error(empty, [Q]); drop({[_],[]}) -> @@ -254,7 +283,9 @@ drop(Q) -> %% Remove the last element and return resulting queue %% %% O(1) amortized --spec drop_r(queue()) -> queue(). +-spec drop_r(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). drop_r({[],[]}=Q) -> erlang:error(empty, [Q]); drop_r({[],[_]}) -> @@ -275,7 +306,9 @@ drop_r(Q) -> %% Return reversed queue %% %% O(1) --spec reverse(queue()) -> queue(). +-spec reverse(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). reverse({R,F}) when is_list(R), is_list(F) -> {F,R}; reverse(Q) -> @@ -285,7 +318,10 @@ reverse(Q) -> %% %% Q2 empty: O(1) %% else: O(len(Q1)) --spec join(queue(), queue()) -> queue(). +-spec join(Q1, Q2) -> Q3 when + Q1 :: queue(), + Q2 :: queue(), + Q3 :: queue(). join({R,F}=Q, {[],[]}) when is_list(R), is_list(F) -> Q; join({[],[]}, {R,F}=Q) when is_list(R), is_list(F) -> @@ -299,7 +335,11 @@ join(Q1, Q2) -> %% %% N = 0..len(Q) %% O(max(N, len(Q))) --spec split(non_neg_integer(), queue()) -> {queue(),queue()}. +-spec split(N, Q1) -> {Q2,Q3} when + N :: non_neg_integer(), + Q1 :: queue(), + Q2 :: queue(), + Q3 :: queue(). split(0, {R,F}=Q) when is_list(R), is_list(F) -> {{[],[]},Q}; split(N, {R,F}=Q) when is_integer(N), N >= 1, is_list(R), is_list(F) -> @@ -340,7 +380,10 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) -> %% %% Fun(_) -> List: O(length(List) * len(Q)) %% else: O(len(Q) --spec filter(fun((term()) -> boolean() | list()), queue()) -> queue(). +-spec filter(Fun, Q1) -> Q2 when + Fun :: fun((Item :: term()) -> boolean() | list()), + Q1 :: queue(), + Q2 :: queue(). filter(Fun, {R0,F0}) when is_function(Fun, 1), is_list(R0), is_list(F0) -> F = filter_f(Fun, F0), R = filter_r(Fun, R0), @@ -416,7 +459,10 @@ filter_r(Fun, [X|R0]) -> %% Cons to head %% --spec cons(term(), queue()) -> queue(). +-spec cons(Item, Q1) -> Q2 when + Item :: term(), + Q1 :: queue(), + Q2 :: queue(). cons(X, Q) -> in_r(X, Q). @@ -425,7 +471,9 @@ cons(X, Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec head(queue()) -> term(). +-spec head(Q) -> Item when + Q :: queue(), + Item :: term(). head({[],[]}=Q) -> erlang:error(empty, [Q]); head({R,F}) when is_list(R), is_list(F) -> @@ -435,7 +483,9 @@ head(Q) -> %% Remove head element and return resulting queue %% --spec tail(queue()) -> queue(). +-spec tail(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). tail(Q) -> drop(Q). @@ -443,22 +493,35 @@ tail(Q) -> %% Cons to tail %% --spec snoc(queue(), term()) -> queue(). +-spec snoc(Q1, Item) -> Q2 when + Q1 :: queue(), + Q2 :: queue(), + Item :: term(). snoc(Q, X) -> in(X, Q). %% Return last element --spec daeh(queue()) -> term(). +-spec daeh(Q) -> Item when + Q :: queue(), + Item :: term(). daeh(Q) -> get_r(Q). --spec last(queue()) -> term(). +-spec last(Q) -> Item when + Q :: queue(), + Item :: term(). last(Q) -> get_r(Q). %% Remove last element and return resulting queue --spec liat(queue()) -> queue(). +-spec liat(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). liat(Q) -> drop_r(Q). --spec lait(queue()) -> queue(). +-spec lait(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one. --spec init(queue()) -> queue(). +-spec init(Q1) -> Q2 when + Q1 :: queue(), + Q2 :: queue(). init(Q) -> drop_r(Q). %%-------------------------------------------------------------------------- diff --git a/lib/stdlib/src/random.erl b/lib/stdlib/src/random.erl index 01227c29b4..dbb524cc74 100644 --- a/lib/stdlib/src/random.erl +++ b/lib/stdlib/src/random.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -49,7 +49,10 @@ seed() -> %% seed({A1, A2, A3}) %% Seed random number generation --spec seed({integer(), integer(), integer()}) -> 'undefined' | ran(). +-spec seed({A1, A2, A3}) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed({A1, A2, A3}) -> seed(A1, A2, A3). @@ -57,7 +60,10 @@ seed({A1, A2, A3}) -> %% seed(A1, A2, A3) %% Seed random number generation --spec seed(integer(), integer(), integer()) -> 'undefined' | ran(). +-spec seed(A1, A2, A3) -> 'undefined' | ran() when + A1 :: integer(), + A2 :: integer(), + A3 :: integer(). seed(A1, A2, A3) -> put(random_seed, @@ -93,7 +99,8 @@ uniform() -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform(pos_integer()) -> pos_integer(). +-spec uniform(N) -> pos_integer() when + N :: pos_integer(). uniform(N) when is_integer(N), N >= 1 -> trunc(uniform() * N) + 1. @@ -104,7 +111,9 @@ uniform(N) when is_integer(N), N >= 1 -> %% uniform_s(State) -> {F, NewState} %% Returns a random float between 0 and 1. --spec uniform_s(ran()) -> {float(), ran()}. +-spec uniform_s(State0) -> {float(), State1} when + State0 :: ran(), + State1 :: ran(). uniform_s({A1, A2, A3}) -> B1 = (A1*171) rem 30269, @@ -117,7 +126,10 @@ uniform_s({A1, A2, A3}) -> %% Given an integer N >= 1, uniform(N) returns a random integer %% between 1 and N. --spec uniform_s(pos_integer(), ran()) -> {integer(), ran()}. +-spec uniform_s(N, State0) -> {integer(), State1} when + N :: pos_integer(), + State0 :: ran(), + State1 :: ran(). uniform_s(N, State0) when is_integer(N), N >= 1 -> {F, State1} = uniform_s(State0), diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index e2cc9f57ce..02dbe60741 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -19,15 +19,46 @@ -module(re). -export([grun/3,urun/3,ucompile/2,replace/3,replace/4,split/2,split/3]). +%-opaque mp() :: {re_pattern, _, _, _}. +-type mp() :: {re_pattern, _, _, _}. + +-type nl_spec() :: cr | crlf | lf | anycrlf | any. + +-type compile_option() :: unicode | anchored | caseless | dollar_endonly + | dotall | extended | firstline | multiline + | no_auto_capture | dupnames | ungreedy + | {newline, nl_spec()}| bsr_anycrlf + | bsr_unicode. + %% Emulator builtins in this module: %% re:compile/1 %% re:compile/2 %% re:run/2 %% re:run/3 +-spec split(Subject, RE) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + SplitList :: [iodata() | unicode:charlist()]. + split(Subject,RE) -> split(Subject,RE,[]). +-spec split(Subject, RE, Options) -> SplitList when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Options :: [ Option ], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, nl_spec()} + | bsr_anycrlf | bsr_unicode | {return, ReturnType} + | {parts, NumParts} | group | trim | CompileOpt, + NumParts :: non_neg_integer() | infinity, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + SplitList :: [RetData] | [GroupedRetData], + GroupedRetData :: [RetData], + RetData :: iodata() | unicode:charlist() | binary() | list(). + split(Subject,RE,Options) -> try {NewOpt,Convert,Unicode,Limit,Strip,Group} = @@ -197,10 +228,26 @@ compile_split(Pat,Options0) when not is_tuple(Pat) -> compile_split(_,_) -> throw(badre). +-spec replace(Subject, RE, Replacement) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Replacement :: iodata() | unicode:charlist(). replace(Subject,RE,Replacement) -> replace(Subject,RE,Replacement,[]). +-spec replace(Subject, RE, Replacement, Options) -> iodata() | unicode:charlist() when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata(), + Replacement :: iodata() | unicode:charlist(), + Options :: [Option], + Option :: anchored | global | notbol | noteol | notempty + | {offset, non_neg_integer()} | {newline, NLSpec} | bsr_anycrlf + | bsr_unicode | {return, ReturnType} | CompileOpt, + ReturnType :: iodata | list | binary, + CompileOpt :: compile_option(), + NLSpec :: cr | crlf | lf | anycrlf | any. + replace(Subject,RE,Replacement,Options) -> try {NewOpt,Convert,Unicode} = diff --git a/lib/stdlib/src/regexp.erl b/lib/stdlib/src/regexp.erl index 8f5994bbee..65f9ca247d 100644 --- a/lib/stdlib/src/regexp.erl +++ b/lib/stdlib/src/regexp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -37,6 +37,9 @@ -import(string, [substr/2,substr/3]). -import(lists, [reverse/1]). +-type errordesc() :: term(). +-opaque regexp() :: term(). + %% -type matchres() = {match,Start,Length} | nomatch | {error,E}. %% -type subres() = {ok,RepString,RepCount} | {error,E}. %% -type splitres() = {ok,[SubString]} | {error,E}. @@ -287,6 +290,10 @@ re_apply_or(R1, nomatch) -> R1. %% Convert a sh style regexp into a full AWK one. The main difficulty is %% getting character sets right as the conventions are different. +-spec sh_to_awk(ShRegExp) -> AwkRegExp when + ShRegExp :: string(), + AwkRegExp :: string(). + sh_to_awk(Sh) -> "^(" ++ sh_to_awk_1(Sh). %Fix the beginning sh_to_awk_1([$*|Sh]) -> %This matches any string @@ -336,6 +343,12 @@ special_char(_C) -> false. %% parse(RegExp) -> {ok,RE} | {error,E}. %% Parse the regexp described in the string RegExp. +-spec parse(RegExp) -> ParseRes when + RegExp :: string(), + ParseRes :: {ok, RE} | {error, Error}, + RE :: regexp(), + Error :: errordesc(). + parse(S) -> case catch reg(S) of {R,[]} -> {ok,R}; @@ -345,6 +358,10 @@ parse(S) -> %% format_error(Error) -> String. +-spec format_error(ErrorDescriptor) -> Chars when + ErrorDescriptor :: errordesc(), + Chars :: io_lib:chars(). + format_error({illegal,What}) -> ["illegal character `",What,"'"]; format_error({unterminated,What}) -> ["unterminated `",What,"'"]; format_error({char_class,What}) -> @@ -353,6 +370,14 @@ format_error({char_class,What}) -> %% -type match(String, RegExp) -> matchres(). %% Find the longest match of RegExp in String. +-spec match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> match(S, RE); @@ -378,6 +403,14 @@ match(RE, S, St, Pos, L) -> %% -type first_match(String, RegExp) -> matchres(). %% Find the first match of RegExp in String. +-spec first_match(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Start, Length} | nomatch | {error, Error}, + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + first_match(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> first_match(S, RE); @@ -400,6 +433,15 @@ first_match(_RE, [], _St) -> nomatch. %% -type matches(String, RegExp) -> {match,[{Start,Length}]} | {error,E}. %% Return the all the non-overlapping matches of RegExp in String. +-spec matches(String, RegExp) -> MatchRes when + String :: string(), + RegExp :: string() | regexp(), + MatchRes :: {match, Matches} | {error, Error}, + Matches :: [{Start, Length}], + Start :: pos_integer(), + Length :: pos_integer(), + Error :: errordesc(). + matches(S, RegExp) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> matches(S, RE); @@ -420,6 +462,15 @@ matches(S, RE, St) -> %% the string Replace in String. Accept pre-parsed regular %% expressions. +-spec sub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: 0 | 1, + Error :: errordesc(). + sub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> sub(String, RE, Rep); @@ -449,6 +500,15 @@ sub_repl([], _M, Rest) -> Rest. %% Substitute every match of the regular expression RegExp with the %% string New in String. Accept pre-parsed regular expressions. +-spec gsub(String, RegExp, New) -> SubRes when + String :: string(), + RegExp :: string() | regexp(), + New :: string(), + NewString :: string(), + SubRes :: {ok, NewString, RepCount} | {error, Error}, + RepCount :: non_neg_integer(), + Error :: errordesc(). + gsub(String, RegExp, Rep) when is_list(RegExp) -> case parse(RegExp) of {ok,RE} -> gsub(String, RE, Rep); @@ -462,6 +522,13 @@ gsub(String, RE, Rep) -> %% Split a string into substrings where the RegExp describes the %% field seperator. The RegExp " " is specially treated. +-spec split(String, RegExp) -> SplitRes when + String :: string(), + RegExp :: string() | regexp(), + SplitRes :: {ok, FieldList} | {error, Error}, + FieldList :: [string()], + Error :: errordesc(). + split(String, " ") -> %This is really special {ok,RE} = parse("[ \t]+"), case split_apply(String, RE, true) of diff --git a/lib/stdlib/src/sets.erl b/lib/stdlib/src/sets.erl index bcddca2567..3fd6c81e5f 100644 --- a/lib/stdlib/src/sets.erl +++ b/lib/stdlib/src/sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-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 @@ -84,30 +84,38 @@ new() -> %% is_set(Set) -> boolean(). %% Return 'true' if Set is a set of elements, else 'false'. --spec is_set(term()) -> boolean(). +-spec is_set(Set) -> boolean() when + Set :: term(). is_set(#set{}) -> true; is_set(_) -> false. %% size(Set) -> int(). %% Return the number of elements in Set. --spec size(set()) -> non_neg_integer(). +-spec size(Set) -> non_neg_integer() when + Set :: set(). size(S) -> S#set.size. %% to_list(Set) -> [Elem]. %% Return the elements in Set as a list. --spec to_list(set()) -> [term()]. +-spec to_list(Set) -> List when + Set :: set(), + List :: [term()]. to_list(S) -> fold(fun (Elem, List) -> [Elem|List] end, [], S). %% from_list([Elem]) -> Set. %% Build a set from the elements in List. --spec from_list([term()]) -> set(). +-spec from_list(List) -> Set when + List :: [term()], + Set :: set(). from_list(L) -> lists:foldl(fun (E, S) -> add_element(E, S) end, new(), L). %% is_element(Element, Set) -> boolean(). %% Return 'true' if Element is an element of Set, else 'false'. --spec is_element(term(), set()) -> boolean(). +-spec is_element(Element, Set) -> boolean() when + Element :: term(), + Set :: set(). is_element(E, S) -> Slot = get_slot(S, E), Bkt = get_bucket(S, Slot), @@ -115,7 +123,10 @@ is_element(E, S) -> %% add_element(Element, Set) -> Set. %% Return Set with Element inserted in it. --spec add_element(term(), set()) -> set(). +-spec add_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). add_element(E, S0) -> Slot = get_slot(S0, E), {S1,Ic} = on_bucket(fun (B0) -> add_bkt_el(E, B0, B0) end, S0, Slot), @@ -129,7 +140,10 @@ add_bkt_el(E, [], Bkt) -> {[E|Bkt],1}. %% del_element(Element, Set) -> Set. %% Return Set but with Element removed. --spec del_element(term(), set()) -> set(). +-spec del_element(Element, Set1) -> Set2 when + Element :: term(), + Set1 :: set(), + Set2 :: set(). del_element(E, S0) -> Slot = get_slot(S0, E), {S1,Dc} = on_bucket(fun (B0) -> del_bkt_el(E, B0) end, S0, Slot), @@ -144,7 +158,10 @@ del_bkt_el(_, []) -> {[],0}. %% union(Set1, Set2) -> Set %% Return the union of Set1 and Set2. --spec union(set(), set()) -> set(). +-spec union(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). union(S1, S2) when S1#set.size < S2#set.size -> fold(fun (E, S) -> add_element(E, S) end, S2, S1); union(S1, S2) -> @@ -152,7 +169,9 @@ union(S1, S2) -> %% union([Set]) -> Set %% Return the union of the list of sets. --spec union([set()]) -> set(). +-spec union(SetList) -> Set when + SetList :: [set()], + Set :: set(). union([S1,S2|Ss]) -> union1(union(S1, S2), Ss); union([S]) -> S; @@ -165,7 +184,10 @@ union1(S1, []) -> S1. %% intersection(Set1, Set2) -> Set. %% Return the intersection of Set1 and Set2. --spec intersection(set(), set()) -> set(). +-spec intersection(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). intersection(S1, S2) when S1#set.size < S2#set.size -> filter(fun (E) -> is_element(E, S2) end, S1); intersection(S1, S2) -> @@ -173,7 +195,9 @@ intersection(S1, S2) -> %% intersection([Set]) -> Set. %% Return the intersection of the list of sets. --spec intersection([set(),...]) -> set(). +-spec intersection(SetList) -> Set when + SetList :: [set(),...], + Set :: set(). intersection([S1,S2|Ss]) -> intersection1(intersection(S1, S2), Ss); intersection([S]) -> S. @@ -185,7 +209,9 @@ intersection1(S1, []) -> S1. %% is_disjoint(Set1, Set2) -> boolean(). %% Check whether Set1 and Set2 are disjoint. --spec is_disjoint(set(), set()) -> boolean(). +-spec is_disjoint(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). is_disjoint(S1, S2) when S1#set.size < S2#set.size -> fold(fun (_, false) -> false; (E, true) -> not is_element(E, S2) @@ -198,25 +224,39 @@ is_disjoint(S1, S2) -> %% subtract(Set1, Set2) -> Set. %% Return all and only the elements of Set1 which are not also in %% Set2. --spec subtract(set(), set()) -> set(). +-spec subtract(Set1, Set2) -> Set3 when + Set1 :: set(), + Set2 :: set(), + Set3 :: set(). subtract(S1, S2) -> filter(fun (E) -> not is_element(E, S2) end, S1). %% is_subset(Set1, Set2) -> boolean(). %% Return 'true' when every element of Set1 is also a member of %% Set2, else 'false'. --spec is_subset(set(), set()) -> boolean(). +-spec is_subset(Set1, Set2) -> boolean() when + Set1 :: set(), + Set2 :: set(). is_subset(S1, S2) -> fold(fun (E, Sub) -> Sub andalso is_element(E, S2) end, true, S1). %% fold(Fun, Accumulator, Set) -> Accumulator. %% Fold function Fun over all elements in Set and return Accumulator. --spec fold(fun((_,_) -> _), T, set()) -> T. +-spec fold(Function, Acc0, Set) -> Acc1 when + Function :: fun((E :: term(),AccIn) -> AccOut), + Set :: set(), + Acc0 :: T, + Acc1 :: T, + AccIn :: T, + AccOut :: T. fold(F, Acc, D) -> fold_set(F, Acc, D). %% filter(Fun, Set) -> Set. %% Filter Set with Fun. --spec filter(fun((_) -> boolean()), set()) -> set(). +-spec filter(Pred, Set1) -> Set2 when + Pred :: fun((E :: term()) -> boolean()), + Set1 :: set(), + Set2 :: set(). filter(F, D) -> filter_set(F, D). %% get_slot(Hashdb, Key) -> Slot. diff --git a/lib/stdlib/src/shell.erl b/lib/stdlib/src/shell.erl index ebb221c151..e3e23e09bc 100644 --- a/lib/stdlib/src/shell.erl +++ b/lib/stdlib/src/shell.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 @@ -115,7 +115,9 @@ whereis_evaluator(Shell) -> %% Call this function to start a user restricted shell %% from a normal shell session. --spec start_restricted(module()) -> {'error', code:load_error_rsn()}. +-spec start_restricted(Module) -> {'error', Reason} when + Module :: module(), + Reason :: code:load_error_rsn(). start_restricted(RShMod) when is_atom(RShMod) -> case code:ensure_loaded(RShMod) of @@ -465,7 +467,7 @@ getc(N) -> {get({command,N}), get({result,N}), N}. get_cmd(Num, C) -> - case catch erl_eval:expr(Num, []) of + case catch erl_eval:expr(Num, erl_eval:new_bindings()) of {value,N,_} when N < 0 -> getc(C+N); {value,N,_} -> getc(N); _Other -> {undefined,undefined,undefined} @@ -1184,6 +1186,8 @@ expr(E, Bs, Lf, Ef) -> expr_list(Es, Bs, Lf, Ef) -> erl_eval:expr_list(Es, strip_bindings(Bs), Lf, Ef). +-spec strip_bindings(erl_eval:binding_struct()) -> erl_eval:binding_struct(). + strip_bindings(Bs) -> Bs -- [B || {{module,_},_}=B <- Bs]. @@ -1468,23 +1472,26 @@ set_env(App, Name, Val, Default) -> application_controller:set_env(App, Name, Val), Prev. --spec history(non_neg_integer()) -> non_neg_integer(). +-spec history(N) -> non_neg_integer() when + N :: non_neg_integer(). history(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_history_length, L, ?DEF_HISTORY). --spec results(non_neg_integer()) -> non_neg_integer(). +-spec results(N) -> non_neg_integer() when + N :: non_neg_integer(). results(L) when is_integer(L), L >= 0 -> set_env(stdlib, shell_saved_results, L, ?DEF_RESULTS). --spec catch_exception(boolean()) -> boolean(). +-spec catch_exception(Bool) -> Bool when + Bool :: boolean(). catch_exception(Bool) -> set_env(stdlib, shell_catch_exception, Bool, ?DEF_CATCH_EXCEPTION). --type prompt_func() :: 'default' | {module(),atom()}. --spec prompt_func(prompt_func()) -> prompt_func(). +-spec prompt_func(PromptFunc) -> PromptFunc when + PromptFunc :: 'default' | {module(),atom()}. prompt_func(String) -> set_env(stdlib, shell_prompt_func, String, ?DEF_PROMPT_FUNC). diff --git a/lib/stdlib/src/slave.erl b/lib/stdlib/src/slave.erl index 196b659938..d79ee676d9 100644 --- a/lib/stdlib/src/slave.erl +++ b/lib/stdlib/src/slave.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -54,6 +54,10 @@ pseudo([Master | ServerList]) -> pseudo(_) -> error_msg("No master node given to slave:pseudo/1~n",[]). +-spec pseudo(Master, ServerList) -> ok when + Master :: node(), + ServerList :: [atom()]. + pseudo(_, []) -> ok; pseudo(Master, [S|Tail]) -> start_pseudo(S, whereis(S), Master), @@ -68,6 +72,9 @@ start_pseudo(_,_,_) -> ok. %% It's already there %% This relay can be used to relay all messages directed to a process. +-spec relay(Pid) -> none() when + Pid :: pid(). + relay({badrpc,Reason}) -> error_msg(" ** exiting relay server ~w :~w **~n", [self(),Reason]), exit(Reason); @@ -120,25 +127,61 @@ relay1(Pid) -> %% {error, no_rsh} | %% {error, {already_running, Name@Host}} +-spec start(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start(Host, Name). + start(Host, Name, [], no_link). + +-spec start(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start(Host, Name) -> start(Host, Name, []). +-spec start(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start(Host, Name, Args) -> start(Host, Name, Args, no_link). +-spec start_link(Host) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host) -> L = atom_to_list(node()), Name = upto($@, L), - start_link(Host, Name). + start(Host, Name, [], self()). + +-spec start_link(Host, Name) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. start_link(Host, Name) -> start_link(Host, Name, []). +-spec start_link(Host, Name, Args) -> {ok, Node} | {error, Reason} when + Host :: atom(), + Name :: atom(), + Args :: string(), + Node :: node(), + Reason :: timeout | no_rsh | {already_running, Node}. + start_link(Host, Name, Args) -> start(Host, Name, Args, self()). @@ -163,6 +206,9 @@ start(Host0, Name, Args, LinkTo, Prog) -> %% Stops a running node. +-spec stop(Node) -> ok when + Node :: node(). + stop(Node) -> % io:format("stop(~p)~n", [Node]), rpc:call(Node, erlang, halt, []), diff --git a/lib/stdlib/src/sofs.erl b/lib/stdlib/src/sofs.erl index a83f803330..d38b8ab37a 100644 --- a/lib/stdlib/src/sofs.erl +++ b/lib/stdlib/src/sofs.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2001-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 2001-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(sofs). @@ -40,11 +40,11 @@ substitution/2, projection/2, partition/1, partition/2, partition/3, multiple_relative_product/2, join/4]). --export([family_to_relation/1, family_specification/2, +-export([family_to_relation/1, family_specification/2, union_of_family/1, intersection_of_family/1, family_union/1, family_intersection/1, family_domain/1, family_range/1, family_field/1, - family_union/2, family_intersection/2, family_difference/2, + family_union/2, family_intersection/2, family_difference/2, partition_family/2, family_projection/2]). -export([family_to_digraph/1, family_to_digraph/2, @@ -64,9 +64,9 @@ -compile({inline, [{external_fun,1},{element_type,1}]}). --compile({inline, +-compile({inline, [{unify_types,2}, {match_types,2}, - {test_rel,3}, {symdiff,3}, + {test_rel,3}, {symdiff,3}, {subst,3}]}). -compile({inline, [{fam_binop,3}]}). @@ -80,13 +80,13 @@ -define(TAG, 'Set'). -define(ORDTAG, 'OrdSet'). --record(?TAG, {data = [], type = type}). --record(?ORDTAG, {orddata = {}, ordtype = type}). +-record(?TAG, {data = [] :: list(), type = type :: term()}). +-record(?ORDTAG, {orddata = {} :: tuple(), ordtype = type :: term()}). -define(LIST(S), (S)#?TAG.data). -define(TYPE(S), (S)#?TAG.type). -%%-define(SET(L, T), -%% case is_type(T) of +%%-define(SET(L, T), +%% case is_type(T) of %% true -> #?TAG{data = L, type = T}; %% false -> erlang:error(badtype, [T]) %% end @@ -113,14 +113,40 @@ -define(IS_SET_OF(X), is_list(X)). -define(FAMILY(X, Y), ?BINREL(X, ?SET_OF(Y))). +-export_type([anyset/0, binary_relation/0, external_set/0, a_function/0, + family/0, relation/0, set_of_sets/0, set_fun/0, spec_fun/0, + type/0]). +-export_type([ordset/0, a_set/0]). + +-type(anyset() :: ordset() | a_set()). +-type(binary_relation() :: relation()). +-type(external_set() :: term()). +-type(a_function() :: relation()). +-type(family() :: a_function()). +-opaque(ordset() :: #?ORDTAG{}). +-type(relation() :: a_set()). +-opaque(a_set() :: #?TAG{}). +-type(set_of_sets() :: a_set()). +-type(set_fun() :: pos_integer() + | {external, fun((external_set()) -> external_set())} + | fun((anyset()) -> anyset())). +-type(spec_fun() :: {external, fun((external_set()) -> boolean())} + | fun((anyset()) -> boolean())). +-type(type() :: term()). + +-type(tuple_of(_T) :: tuple()). + %% %% Exported functions %% -%%% +%%% %%% Create sets -%%% +%%% +-spec(from_term(Term) -> AnySet when + AnySet :: anyset(), + Term :: term()). from_term(T) -> Type = case T of _ when is_list(T) -> [?ANYTYPE]; @@ -133,6 +159,10 @@ from_term(T) -> Set end. +-spec(from_term(Term, Type) -> AnySet when + AnySet :: anyset(), + Term :: term(), + Type :: type()). from_term(L, T) -> case is_type(T) of true -> @@ -146,14 +176,23 @@ from_term(L, T) -> erlang:error(badarg, [L, T]) end. +-spec(from_external(ExternalSet, Type) -> AnySet when + ExternalSet :: external_set(), + AnySet :: anyset(), + Type :: type()). from_external(L, ?SET_OF(Type)) -> ?SET(L, Type); from_external(T, Type) -> ?ORDSET(T, Type). +-spec(empty_set() -> Set when + Set :: a_set()). empty_set() -> ?SET([], ?ANYTYPE). +-spec(is_type(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_type(Atom) when ?IS_ATOM_TYPE(Atom), Atom =/= ?ANYTYPE -> true; is_type(?SET_OF(T)) -> @@ -163,19 +202,26 @@ is_type(T) when tuple_size(T) > 0 -> is_type(_T) -> false. +-spec(set(Terms) -> Set when + Set :: a_set(), + Terms :: [term()]). set(L) -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L]); - SL -> + SL -> ?SET(SL, ?ATOM_TYPE) end. +-spec(set(Terms, Type) -> Set when + Set :: a_set(), + Terms :: [term()], + Type :: type()). set(L, ?SET_OF(Type) = T) when ?IS_ATOM_TYPE(Type), Type =/= ?ANYTYPE -> case catch usort(L) of {'EXIT', _} -> erlang:error(badarg, [L, T]); - SL -> + SL -> ?SET(SL, Type) end; set(L, ?SET_OF(_) = T) -> @@ -188,6 +234,12 @@ set(L, ?SET_OF(_) = T) -> set(L, T) -> erlang:error(badarg, [L, T]). +-spec(from_sets(ListOfSets) -> Set when + Set :: a_set(), + ListOfSets :: [anyset()]; + (TupleOfSets) -> Ordset when + Ordset :: ordset(), + TupleOfSets :: tuple_of(anyset())). from_sets(Ss) when is_list(Ss) -> case set_of_sets(Ss, [], ?ANYTYPE) of {error, Error} -> @@ -205,6 +257,9 @@ from_sets(Tuple) when is_tuple(Tuple) -> from_sets(T) -> erlang:error(badarg, [T]). +-spec(relation(Tuples) -> Relation when + Relation :: relation(), + Tuples :: [tuple()]). relation([]) -> ?SET([], ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)); relation(Ts = [T | _]) when is_tuple(T) -> @@ -217,6 +272,11 @@ relation(Ts = [T | _]) when is_tuple(T) -> relation(E) -> erlang:error(badarg, [E]). +-spec(relation(Tuples, Type) -> Relation when + N :: integer(), + Type :: N | type(), + Relation :: relation(), + Tuples :: [tuple()]). relation(Ts, TS) -> case catch rel(Ts, TS) of {'EXIT', _} -> @@ -225,6 +285,9 @@ relation(Ts, TS) -> Set end. +-spec(a_function(Tuples) -> Function when + Function :: a_function(), + Tuples :: [tuple()]). a_function(Ts) -> case catch func(Ts, ?BINREL(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -235,6 +298,10 @@ a_function(Ts) -> Set end. +-spec(a_function(Tuples, Type) -> Function when + Function :: a_function(), + Tuples :: [tuple()], + Type :: type()). a_function(Ts, T) -> case catch a_func(Ts, T) of {'EXIT', _} -> @@ -245,6 +312,9 @@ a_function(Ts, T) -> Set end. +-spec(family(Tuples) -> Family when + Family :: family(), + Tuples :: [tuple()]). family(Ts) -> case catch fam2(Ts, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) of {'EXIT', _} -> @@ -255,6 +325,10 @@ family(Ts) -> Set end. +-spec(family(Tuples, Type) -> Family when + Family :: family(), + Tuples :: [tuple()], + Type :: type()). family(Ts, T) -> case catch fam(Ts, T) of {'EXIT', _} -> @@ -265,20 +339,30 @@ family(Ts, T) -> Set end. -%%% +%%% %%% Functions on sets. -%%% +%%% +-spec(to_external(AnySet) -> ExternalSet when + ExternalSet :: external_set(), + AnySet :: anyset()). to_external(S) when ?IS_SET(S) -> ?LIST(S); to_external(S) when ?IS_ORDSET(S) -> ?ORDDATA(S). +-spec(type(AnySet) -> Type when + AnySet :: anyset(), + Type :: type()). type(S) when ?IS_SET(S) -> ?SET_OF(?TYPE(S)); type(S) when ?IS_ORDSET(S) -> ?ORDTYPE(S). +-spec(to_sets(ASet) -> Sets when + ASet :: a_set() | ordset(), + Sets :: tuple_of(AnySet) | [AnySet], + AnySet :: anyset()). to_sets(S) when ?IS_SET(S) -> case ?TYPE(S) of ?SET_OF(Type) -> list_of_sets(?LIST(S), Type, []); @@ -289,6 +373,9 @@ to_sets(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> to_sets(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(no_elements(ASet) -> NoElements when + ASet :: a_set() | ordset(), + NoElements :: pos_integer()). no_elements(S) when ?IS_SET(S) -> length(?LIST(S)); no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> @@ -296,6 +383,10 @@ no_elements(S) when ?IS_ORDSET(S), is_tuple(?ORDTYPE(S)) -> no_elements(S) when ?IS_ORDSET(S) -> erlang:error(badarg, [S]). +-spec(specification(Fun, Set1) -> Set2 when + Fun :: spec_fun(), + Set1 :: a_set(), + Set2 :: a_set()). specification(Fun, S) when ?IS_SET(S) -> Type = ?TYPE(S), R = case external_fun(Fun) of @@ -311,36 +402,62 @@ specification(Fun, S) when ?IS_SET(S) -> erlang:error(Bad, [Fun, S]) end. +-spec(union(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). union(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(umerge(?LIST(S1), ?LIST(S2)), Type) end. +-spec(intersection(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). intersection(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(intersection(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(difference(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). difference(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(difference(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symdiff(Set1, Set2) -> Set3 when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). symdiff(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> ?SET(symdiff(?LIST(S1), ?LIST(S2), []), Type) end. +-spec(symmetric_partition(Set1, Set2) -> {Set3, Set4, Set5} when + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set(), + Set5 :: a_set()). symmetric_partition(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case unify_types(?TYPE(S1), ?TYPE(S2)) of [] -> erlang:error(type_mismatch, [S1, S2]); Type -> sympart(?LIST(S1), ?LIST(S2), [], [], [], Type) end. +-spec(product(Set1, Set2) -> BinRel when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> if ?TYPE(S1) =:= ?ANYTYPE -> S1; @@ -351,6 +468,9 @@ product(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> ?SET(relprod(map(F, ?LIST(S1)), map(F, ?LIST(S2))), T) end. +-spec(product(TupleOfSets) -> Relation when + Relation :: relation(), + TupleOfSets :: tuple_of(a_set())). product({S1, S2}) -> product(S1, S2); product(T) when is_tuple(T) -> @@ -365,11 +485,15 @@ product(T) when is_tuple(T) -> case member([], L) of true -> empty_set(); - false -> + false -> ?SET(reverse(prod(L, [], [])), Type) end end. +-spec(constant_function(Set, AnySet) -> Function when + AnySet :: anyset(), + Function :: a_function(), + Set :: a_set()). constant_function(S, E) when ?IS_SET(S) -> case {?TYPE(S), is_sofs_set(E)} of {?ANYTYPE, true} -> S; @@ -381,6 +505,10 @@ constant_function(S, E) when ?IS_SET(S) -> constant_function(S, E) when ?IS_ORDSET(S) -> erlang:error(badarg, [S, E]). +-spec(is_equal(AnySet1, AnySet2) -> Bool when + AnySet1 :: anyset(), + AnySet2 :: anyset(), + Bool :: boolean()). is_equal(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> ?LIST(S1) == ?LIST(S2); @@ -396,12 +524,19 @@ is_equal(S1, S2) when ?IS_SET(S1), ?IS_ORDSET(S2) -> is_equal(S1, S2) when ?IS_ORDSET(S1), ?IS_SET(S2) -> erlang:error(type_mismatch, [S1, S2]). +-spec(is_subset(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_subset(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> subset(?LIST(S1), ?LIST(S2)); false -> erlang:error(type_mismatch, [S1, S2]) end. +-spec(is_sofs_set(Term) -> Bool when + Bool :: boolean(), + Term :: term()). is_sofs_set(S) when ?IS_SET(S) -> true; is_sofs_set(S) when ?IS_ORDSET(S) -> @@ -409,16 +544,26 @@ is_sofs_set(S) when ?IS_ORDSET(S) -> is_sofs_set(_S) -> false. +-spec(is_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). is_set(S) when ?IS_SET(S) -> true; is_set(S) when ?IS_ORDSET(S) -> false. -is_empty_set(S) when ?IS_SET(S) -> +-spec(is_empty_set(AnySet) -> Bool when + AnySet :: anyset(), + Bool :: boolean()). +is_empty_set(S) when ?IS_SET(S) -> ?LIST(S) =:= []; is_empty_set(S) when ?IS_ORDSET(S) -> false. +-spec(is_disjoint(Set1, Set2) -> Bool when + Bool :: boolean(), + Set1 :: a_set(), + Set2 :: a_set()). is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> case match_types(?TYPE(S1), ?TYPE(S2)) of true -> @@ -433,6 +578,9 @@ is_disjoint(S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> %%% Functions on set-of-sets. %%% +-spec(union(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). union(Sets) when ?IS_SET(Sets) -> case ?TYPE(Sets) of ?SET_OF(Type) -> ?SET(lunion(?LIST(Sets)), Type); @@ -440,6 +588,9 @@ union(Sets) when ?IS_SET(Sets) -> _ -> erlang:error(badarg, [Sets]) end. +-spec(intersection(SetOfSets) -> Set when + Set :: a_set(), + SetOfSets :: set_of_sets()). intersection(Sets) when ?IS_SET(Sets) -> case ?LIST(Sets) of [] -> erlang:error(badarg, [Sets]); @@ -451,32 +602,41 @@ intersection(Sets) when ?IS_SET(Sets) -> end end. +-spec(canonical_relation(SetOfSets) -> BinRel when + BinRel :: binary_relation(), + SetOfSets :: set_of_sets()). canonical_relation(Sets) when ?IS_SET(Sets) -> ST = ?TYPE(Sets), case ST of ?SET_OF(?ANYTYPE) -> empty_set(); - ?SET_OF(Type) -> + ?SET_OF(Type) -> ?SET(can_rel(?LIST(Sets), []), ?BINREL(Type, ST)); ?ANYTYPE -> Sets; _ -> erlang:error(badarg, [Sets]) end. -%%% +%%% %%% Functions on binary relations only. -%%% +%%% rel2fam(R) -> relation_to_family(R). +-spec(relation_to_family(BinRel) -> Family when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. relation_to_family(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> ?SET(rel2family(?LIST(R)), ?FAMILY(DT, RT)); ?ANYTYPE -> R; _Else -> erlang:error(badarg, [R]) end. +-spec(domain(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). domain(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, _) -> ?SET(dom(?LIST(R)), DT); @@ -484,6 +644,9 @@ domain(R) when ?IS_SET(R) -> _Else -> erlang:error(badarg, [R]) end. +-spec(range(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). range(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(_, RT) -> ?SET(ran(?LIST(R), []), RT); @@ -491,35 +654,63 @@ range(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(field(BinRel) -> Set when + BinRel :: binary_relation(), + Set :: a_set()). %% In "Introduction to LOGIC", Suppes defines the field of a binary %% relation to be the union of the domain and the range (or %% counterdomain). field(R) -> union(domain(R), range(R)). +-spec(relative_product(ListOfBinRels) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel2 :: binary_relation()). +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT) when is_tuple(RT) -> - case relprod_n(RT, foo, false, false) of - {error, Reason} -> - erlang:error(Reason, [RT]); + relative_product(tuple_to_list(RT)); +relative_product(RL) when is_list(RL) -> + case relprod_n(RL, foo, false, false) of + {error, Reason} -> + erlang:error(Reason, [RL]); Reply -> Reply end. +-spec(relative_product(ListOfBinRels, BinRel1) -> BinRel2 when + ListOfBinRels :: [BinRel, ...], + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(); + (BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> relative_product1(converse(R1), R2); +%% The following clause is kept for backward compatibility. +%% The list is due to Dialyzer's specs. relative_product(RT, R) when is_tuple(RT), ?IS_SET(R) -> + relative_product(tuple_to_list(RT), R); +relative_product(RL, R) when is_list(RL), ?IS_SET(R) -> EmptyR = case ?TYPE(R) of ?BINREL(_, _) -> ?LIST(R) =:= []; ?ANYTYPE -> true; - _ -> erlang:error(badarg, [RT, R]) + _ -> erlang:error(badarg, [RL, R]) end, - case relprod_n(RT, R, EmptyR, true) of - {error, Reason} -> - erlang:error(Reason, [RT, R]); + case relprod_n(RL, R, EmptyR, true) of + {error, Reason} -> + erlang:error(Reason, [RL, R]); Reply -> Reply end. +-spec(relative_product1(BinRel1, BinRel2) -> BinRel3 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + BinRel3 :: binary_relation()). relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> {DTR1, RTR1} = case ?TYPE(R1) of ?BINREL(_, _) = R1T -> R1T; @@ -538,16 +729,23 @@ relative_product1(R1, R2) when ?IS_SET(R1), ?IS_SET(R2) -> false -> erlang:error(type_mismatch, [R1, R2]) end. +-spec(converse(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). converse(R) when ?IS_SET(R) -> case ?TYPE(R) of ?BINREL(DT, RT) -> ?SET(converse(?LIST(R), []), ?BINREL(RT, DT)); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(DT, ?TYPE(S)) of true -> ?SET(usort(restrict(?LIST(S), ?LIST(R))), RT); @@ -558,9 +756,13 @@ image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(inverse_image(BinRel, Set1) -> Set2 when + BinRel :: binary_relation(), + Set1 :: a_set(), + Set2 :: a_set()). inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case match_types(RT, ?TYPE(S)) of true -> NL = restrict(?LIST(S), converse(?LIST(R), [])), @@ -572,17 +774,23 @@ inverse_image(R, S) when ?IS_SET(R), ?IS_SET(S) -> _ -> erlang:error(badarg, [R, S]) end. +-spec(strict_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). strict_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - Type = ?BINREL(_, _) -> + Type = ?BINREL(_, _) -> ?SET(strict(?LIST(R), []), Type); ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(weak_relation(BinRel1) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). weak_relation(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case unify_types(DT, RT) of [] -> erlang:error(badarg, [R]); @@ -592,7 +800,12 @@ weak_relation(R) when ?IS_SET(R) -> ?ANYTYPE -> R; _ -> erlang:error(badarg, [R]) end. - + +-spec(extension(BinRel1, Set, AnySet) -> BinRel2 when + AnySet :: anyset(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> case {?TYPE(R), ?TYPE(S), is_sofs_set(E)} of {T=?BINREL(DT, RT), ST, true} -> @@ -621,9 +834,12 @@ extension(R, S, E) when ?IS_SET(R), ?IS_SET(S) -> erlang:error(badarg, [R, S, E]) end. +-spec(is_a_function(BinRel) -> Bool when + Bool :: boolean(), + BinRel :: binary_relation()). is_a_function(R) when ?IS_SET(R) -> case ?TYPE(R) of - ?BINREL(_, _) -> + ?BINREL(_, _) -> case ?LIST(R) of [] -> true; [{V,_} | Es] -> is_a_func(Es, V) @@ -632,16 +848,28 @@ is_a_function(R) when ?IS_SET(R) -> _ -> erlang:error(badarg, [R]) end. +-spec(restriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). restriction(Relation, Set) -> restriction(1, Relation, Set). +-spec(drestriction(BinRel1, Set) -> BinRel2 when + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation(), + Set :: a_set()). drestriction(Relation, Set) -> drestriction(1, Relation, Set). -%%% +%%% %%% Functions on functions only. -%%% +%%% +-spec(composite(Function1, Function2) -> Function3 when + Function1 :: a_function(), + Function2 :: a_function(), + Function3 :: a_function()). composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> ?BINREL(DTF1, RTF1) = case ?TYPE(Fn1)of ?BINREL(_, _) = F1T -> F1T; @@ -656,7 +884,7 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> case match_types(RTF1, DTF2) of true when DTF1 =:= ?ANYTYPE -> Fn1; true when DTF2 =:= ?ANYTYPE -> Fn2; - true -> + true -> case comp(?LIST(Fn1), ?LIST(Fn2)) of SL when is_list(SL) -> ?SET(sort(SL), ?BINREL(DTF1, RTF2)); @@ -666,9 +894,12 @@ composite(Fn1, Fn2) when ?IS_SET(Fn1), ?IS_SET(Fn2) -> false -> erlang:error(type_mismatch, [Fn1, Fn2]) end. +-spec(inverse(Function1) -> Function2 when + Function1 :: a_function(), + Function2 :: a_function()). inverse(Fn) when ?IS_SET(Fn) -> case ?TYPE(Fn) of - ?BINREL(DT, RT) -> + ?BINREL(DT, RT) -> case inverse1(?LIST(Fn)) of SL when is_list(SL) -> ?SET(SL, ?BINREL(RT, DT)); @@ -678,11 +909,16 @@ inverse(Fn) when ?IS_SET(Fn) -> ?ANYTYPE -> Fn; _ -> erlang:error(badarg, [Fn]) end. - -%%% + +%%% %%% Functions on relations (binary or other). -%%% +%%% +-spec(restriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). %% Equivalent to range(restriction(inverse(substitution(Fun, S1)), S2)). restriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), @@ -747,6 +983,11 @@ restriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(drestriction(SetFun, Set1, Set2) -> Set3 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set()). drestriction(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -812,6 +1053,10 @@ drestriction(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(projection(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). projection(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -827,6 +1072,10 @@ projection(I, Set) when is_integer(I), ?IS_SET(Set) -> projection(Fun, Set) -> range(substitution(Fun, Set)). +-spec(substitution(SetFun, Set1) -> Set2 when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set()). substitution(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -867,11 +1116,18 @@ substitution(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(partition(SetOfSets) -> Partition when + SetOfSets :: set_of_sets(), + Partition :: a_set()). partition(Sets) -> F1 = relation_to_family(canonical_relation(Sets)), F2 = relation_to_family(converse(F1)), range(F2). +-spec(partition(SetFun, Set) -> Partition when + SetFun :: set_fun(), + Partition :: a_set(), + Set :: a_set()). partition(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -887,6 +1143,12 @@ partition(I, Set) when is_integer(I), ?IS_SET(Set) -> partition(Fun, Set) -> range(partition_family(Fun, Set)). +-spec(partition(SetFun, Set1, Set2) -> {Set3, Set4} when + SetFun :: set_fun(), + Set1 :: a_set(), + Set2 :: a_set(), + Set3 :: a_set(), + Set4 :: a_set()). partition(I, R, S) when is_integer(I), ?IS_SET(R), ?IS_SET(S) -> RT = ?TYPE(R), ST = ?TYPE(S), @@ -954,21 +1216,32 @@ partition(SetFun, S1, S2) when ?IS_SET(S1), ?IS_SET(S2) -> end end. +-spec(multiple_relative_product(TupleOfBinRels, BinRel1) -> BinRel2 when + TupleOfBinRels :: tuple_of(BinRel), + BinRel :: binary_relation(), + BinRel1 :: binary_relation(), + BinRel2 :: binary_relation()). multiple_relative_product(T, R) when is_tuple(T), ?IS_SET(R) -> case test_rel(R, tuple_size(T), eq) of true when ?TYPE(R) =:= ?ANYTYPE -> empty_set(); - true -> + true -> MProd = mul_relprod(tuple_to_list(T), 1, R), - relative_product(list_to_tuple(MProd)); - false -> + relative_product(MProd); + false -> erlang:error(badarg, [T, R]) end. -join(R1, I1, R2, I2) +-spec(join(Relation1, I, Relation2, J) -> Relation3 when + Relation1 :: relation(), + Relation2 :: relation(), + Relation3 :: relation(), + I :: pos_integer(), + J :: pos_integer()). +join(R1, I1, R2, I2) when ?IS_SET(R1), ?IS_SET(R2), is_integer(I1), is_integer(I2) -> case test_rel(R1, I1, lte) and test_rel(R2, I2, lte) of - false -> + false -> erlang:error(badarg, [R1, I1, R2, I2]); true when ?TYPE(R1) =:= ?ANYTYPE -> R1; true when ?TYPE(R2) =:= ?ANYTYPE -> R2; @@ -980,8 +1253,8 @@ join(R1, I1, R2, I2) true -> fun({X,Y}) -> join_element(X, Y) end; false -> - fun({X,Y}) -> - list_to_tuple(join_element(X, Y, I2)) + fun({X,Y}) -> + list_to_tuple(join_element(X, Y, I2)) end end, ?SET(replace(T, F, []), F({?TYPE(R1), ?TYPE(R2)})) @@ -1001,9 +1274,15 @@ test_rel(R, I, C) -> %%% Family functions %%% +-spec(fam2rel(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). fam2rel(F) -> family_to_relation(F). +-spec(family_to_relation(Family) -> BinRel when + Family :: family(), + BinRel :: binary_relation()). %% Inlined. family_to_relation(F) when ?IS_SET(F) -> case ?TYPE(F) of @@ -1013,6 +1292,10 @@ family_to_relation(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_specification(Fun, Family1) -> Family2 when + Fun :: spec_fun(), + Family1 :: family(), + Family2 :: family()). family_specification(Fun, F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) = FType -> @@ -1032,6 +1315,9 @@ family_specification(Fun, F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [Fun, F]) end. +-spec(union_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). union_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1040,6 +1326,9 @@ union_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(intersection_of_family(Family) -> Set when + Family :: family(), + Set :: a_set()). intersection_of_family(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_DT, Type) -> @@ -1052,6 +1341,9 @@ intersection_of_family(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_union(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_union(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1060,6 +1352,9 @@ family_union(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_intersection(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_intersection(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?SET_OF(Type)) -> @@ -1073,6 +1368,9 @@ family_intersection(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_domain(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_domain(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(FDT, ?BINREL(DT, _)) -> @@ -1082,6 +1380,9 @@ family_domain(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_range(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_range(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(DT, ?BINREL(_, RT)) -> @@ -1091,15 +1392,30 @@ family_range(F) when ?IS_SET(F) -> _ -> erlang:error(badarg, [F]) end. +-spec(family_field(Family1) -> Family2 when + Family1 :: family(), + Family2 :: family()). family_field(F) -> family_union(family_domain(F), family_range(F)). +-spec(family_union(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_union(F1, F2) -> fam_binop(F1, F2, fun fam_union/3). +-spec(family_intersection(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_intersection(F1, F2) -> fam_binop(F1, F2, fun fam_intersect/3). +-spec(family_difference(Family1, Family2) -> Family3 when + Family1 :: family(), + Family2 :: family(), + Family3 :: family()). family_difference(F1, F2) -> fam_binop(F1, F2, fun fam_difference/3). @@ -1108,13 +1424,17 @@ fam_binop(F1, F2, FF) when ?IS_SET(F1), ?IS_SET(F2) -> case unify_types(?TYPE(F1), ?TYPE(F2)) of [] -> erlang:error(type_mismatch, [F1, F2]); - ?ANYTYPE -> + ?ANYTYPE -> F1; - Type = ?FAMILY(_, _) -> + Type = ?FAMILY(_, _) -> ?SET(FF(?LIST(F1), ?LIST(F2), []), Type); _ -> erlang:error(badarg, [F1, F2]) end. +-spec(partition_family(SetFun, Set) -> Family when + Family :: family(), + SetFun :: set_fun(), + Set :: a_set()). partition_family(I, Set) when is_integer(I), ?IS_SET(Set) -> Type = ?TYPE(Set), case check_for_sort(Type, I) of @@ -1159,8 +1479,12 @@ partition_family(SetFun, Set) when ?IS_SET(Set) -> end end. +-spec(family_projection(SetFun, Family1) -> Family2 when + SetFun :: set_fun(), + Family1 :: family(), + Family2 :: family()). family_projection(SetFun, F) when ?IS_SET(F) -> - case ?TYPE(F) of + case ?TYPE(F) of ?FAMILY(_, _) when [] =:= ?LIST(F) -> empty_set(); ?FAMILY(DT, Type) -> @@ -1172,7 +1496,7 @@ family_projection(SetFun, F) when ?IS_SET(F) -> Bad -> erlang:error(Bad, [SetFun, F]) end; - _ -> + _ -> erlang:error(badarg, [SetFun, F]) end; ?ANYTYPE -> F; @@ -1183,6 +1507,9 @@ family_projection(SetFun, F) when ?IS_SET(F) -> %%% Digraph functions %%% +-spec(family_to_digraph(Family) -> Graph when + Graph :: digraph(), + Family :: family()). family_to_digraph(F) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> fam2digraph(F, digraph:new()); @@ -1190,6 +1517,10 @@ family_to_digraph(F) when ?IS_SET(F) -> _Else -> erlang:error(badarg, [F]) end. +-spec(family_to_digraph(Family, GraphType) -> Graph when + Graph :: digraph(), + Family :: family(), + GraphType :: [digraph:d_type()]). family_to_digraph(F, Type) when ?IS_SET(F) -> case ?TYPE(F) of ?FAMILY(_, _) -> ok; @@ -1208,12 +1539,19 @@ family_to_digraph(F, Type) when ?IS_SET(F) -> error:badarg -> erlang:error(badarg, [F, Type]) end. +-spec(digraph_to_family(Graph) -> Family when + Graph :: digraph(), + Family :: family()). digraph_to_family(G) -> case catch digraph_family(G) of {'EXIT', _} -> erlang:error(badarg, [G]); L -> ?SET(L, ?FAMILY(?ATOM_TYPE, ?ATOM_TYPE)) end. +-spec(digraph_to_family(Graph, Type) -> Family when + Graph :: digraph(), + Family :: family(), + Type :: type()). digraph_to_family(G, T) -> case {is_type(T), T} of {true, ?SET_OF(?FAMILY(_,_) = Type)} -> @@ -1284,7 +1622,7 @@ rel(Ts, [Type]) -> end; rel(Ts, Sz) -> rel(Ts, Sz, erlang:make_tuple(Sz, ?ATOM_TYPE)). - + atoms_only(Type, I) when ?IS_ATOM_TYPE(?REL_TYPE(I, Type)) -> atoms_only(Type, I+1); atoms_only(Type, I) when I > tuple_size(Type), ?IS_RELATION(Type) -> @@ -1312,7 +1650,7 @@ rel_type([], SL, Type) when ?IS_RELATION(Type) -> %% Inlined. a_func(Ts, T) -> case {T, is_type(T)} of - {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?BINREL(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> func(Ts, Type); {[Type], true} -> @@ -1333,16 +1671,16 @@ func([], _X0, L, Type) -> %% Inlined. fam(Ts, T) -> case {T, is_type(T)} of - {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), + {[?FAMILY(DT, RT) = Type], true} when ?IS_ATOM_TYPE(DT), ?IS_ATOM_TYPE(RT) -> fam2(Ts, Type); {[Type], true} -> func_type(Ts, [], Type, fun(?FAMILY(_,_)) -> true end) end. -fam2([], Type) -> +fam2([], Type) -> ?SET([], Type); -fam2(Ts, Type) -> +fam2(Ts, Type) -> fam2(sort(Ts), Ts, [], Type). fam2([{I,L} | T], I0, SL, Type) when I /= I0 -> @@ -1383,7 +1721,7 @@ setify(E, Type0) -> {Type, OrdSet} = make_element(E, Type0, Type0), ?ORDSET(OrdSet, Type). -is_no_lists(T) when is_tuple(T) -> +is_no_lists(T) when is_tuple(T) -> Sz = tuple_size(T), is_no_lists(T, Sz, Sz, []). @@ -1404,7 +1742,7 @@ create([], T, _T0, L) -> make_element(C, ?ANYTYPE, _T0) -> make_element(C); -make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), +make_element(C, Atom, ?ANYTYPE) when ?IS_ATOM_TYPE(Atom), not is_list(C), not is_tuple(C) -> {Atom, C}; make_element(C, Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> @@ -1585,12 +1923,12 @@ sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) when H1 == H2 -> sympart([H1 | T1], [H2 | T2], L1, L12, L2, T) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart(S1, [], L1, L12, L2, T) -> - {?SET(reverse(L1, S1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, S1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}; sympart(_, S2, L1, L12, L2, T) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, S2), T)}. sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 < H2 -> @@ -1600,8 +1938,8 @@ sympart1([H1 | T1], T2, L1, L12, L2, T, H2) when H1 == H2 -> sympart1([H1 | T1], T2, L1, L12, L2, T, H2) -> sympart2(T1, T2, L1, L12, [H2 | L2], T, H1); sympart1(_, T2, L1, L12, L2, T, H2) -> - {?SET(reverse(L1), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1), T), + ?SET(reverse(L12), T), ?SET(reverse(L2, [H2 | T2]), T)}. sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 > H2 -> @@ -1611,8 +1949,8 @@ sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) when H1 == H2 -> sympart2(T1, [H2 | T2], L1, L12, L2, T, H1) -> sympart1(T1, T2, [H1 | L1], L12, L2, T, H2); sympart2(T1, _, L1, L12, L2, T, H1) -> - {?SET(reverse(L1, [H1 | T1]), T), - ?SET(reverse(L12), T), + {?SET(reverse(L1, [H1 | T1]), T), + ?SET(reverse(L12), T), ?SET(reverse(L2), T)}. prod([[E | Es] | Xs], T, L) -> @@ -1660,7 +1998,7 @@ lunion([[] | Ls]) -> lunion(Ls); lunion([S | Ss]) -> umerge(lunion(Ss, last(S), [S], [])); -lunion([]) -> +lunion([]) -> []. lunion([[E] = S | Ss], Last, SL, Ls) when E > Last -> % optimization @@ -1669,7 +2007,7 @@ lunion([S | Ss], Last, SL, Ls) when hd(S) > Last -> lunion(Ss, last(S), [S | SL], Ls); lunion([S | Ss], _Last, SL, Ls) -> lunion(Ss, last(S), [S], [append(reverse(SL)) | Ls]); -lunion([], _Last, SL, Ls) -> +lunion([], _Last, SL, Ls) -> [append(reverse(SL)) | Ls]. %% The empty list is always the first list, if present. @@ -1752,18 +2090,17 @@ relprod(B0, Bx0, By0, A0, L, Ax, [{Bx,By} | B], Ay) when Ay == Bx -> relprod(B0, Bx0, By0, A0, L, _Ax, _B, _Ay) -> relprod2(B0, Bx0, By0, A0, L). -relprod_n({}, _R, _EmptyG, _IsR) -> +relprod_n([], _R, _EmptyG, _IsR) -> {error, badarg}; -relprod_n(RT, R, EmptyR, IsR) -> - RL = tuple_to_list(RT), +relprod_n(RL, R, EmptyR, IsR) -> case domain_type(RL, ?ANYTYPE) of - Error = {error, _Reason} -> + Error = {error, _Reason} -> Error; DType -> Empty = any(fun is_empty_set/1, RL) or EmptyR, RType = range_type(RL, []), Type = ?BINREL(DType, RType), - Prod = + Prod = case Empty of true when DType =:= ?ANYTYPE; RType =:= ?ANYTYPE -> empty_set(); @@ -1771,7 +2108,7 @@ relprod_n(RT, R, EmptyR, IsR) -> ?SET([], Type); false -> TL = ?LIST((relprod_n(RL))), - Sz = tuple_size(RT), + Sz = length(RL), Fun = fun({X,A}) -> {X, flat(Sz, A, [])} end, ?SET(map(Fun, TL), Type) end, @@ -1799,12 +2136,12 @@ flat(N, {T,A}, L) -> domain_type([T | Ts], T0) when ?IS_SET(T) -> case ?TYPE(T) of - ?BINREL(DT, _RT) -> + ?BINREL(DT, _RT) -> case unify_types(DT, T0) of [] -> {error, type_mismatch}; T1 -> domain_type(Ts, T1) end; - ?ANYTYPE -> + ?ANYTYPE -> domain_type(Ts, T0); _ -> {error, badarg} end; @@ -1813,12 +2150,12 @@ domain_type([], T0) -> range_type([T | Ts], L) -> case ?TYPE(T) of - ?BINREL(_DT, RT) -> + ?BINREL(_DT, RT) -> range_type(Ts, [RT | L]); - ?ANYTYPE -> + ?ANYTYPE -> ?ANYTYPE end; -range_type([], L) -> +range_type([], L) -> list_to_tuple(reverse(L)). converse([{A,B} | X], L) -> @@ -1861,7 +2198,7 @@ weak1([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < Y weak1(Es, Ys, L, X) -> weak(Es, Ys, [{X,X} | L]). -weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y +weak2([E={X,_Y} | Es], Ys, L, X0) when X == X0 -> % when X < _Y weak2(Es, Ys, [E | L], X); weak2(Es, Ys, L, _X) -> weak(Es, Ys, L). @@ -1910,7 +2247,7 @@ restrict_n(I, [T | Ts], Key, Keys, L) -> end; restrict_n(_I, _Ts, _Key, _Keys, L) -> L. - + restrict_n(I, K, Ts, [Key | Keys], L, E) when K > Key -> restrict_n(I, K, Ts, Keys, L, E); restrict_n(I, K, Ts, [Key | Keys], L, E) when K == Key -> @@ -1933,7 +2270,7 @@ restrict([{K,E} | Ts], _Key, Keys, L) -> restrict(Ts, K, Keys, L, E); restrict(_Ts, _Key, _Keys, L) -> L. - + restrict(Ts, K, [Key | Keys], L, E) when K > Key -> restrict(Ts, K, Keys, L, E); restrict(Ts, K, [Key | Keys], L, E) when K == Key -> @@ -1956,7 +2293,7 @@ diff_restrict_n(I, _Ts, _Key, _Keys, L) when I =:= 1 -> reverse(L); diff_restrict_n(_I, _Ts, _Key, _Keys, L) -> sort(L). - + diff_restrict_n(I, K, Ts, [Key | Keys], L, T) when K > Key -> diff_restrict_n(I, K, Ts, Keys, L, T); diff_restrict_n(I, K, Ts, [Key | Keys], L, _T) when K == Key -> @@ -1981,7 +2318,7 @@ diff_restrict([{K,E} | Ts], _Key, Keys, L) -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(_Ts, _Key, _Keys, L) -> L. - + diff_restrict(Ts, K, [Key | Keys], L, E) when K > Key -> diff_restrict(Ts, K, Keys, L, E); diff_restrict(Ts, K, [Key | Keys], L, _E) when K == Key -> @@ -2041,7 +2378,7 @@ external_fun({external, Function}) when is_atom(Function) -> false; external_fun({external, Fun}) -> Fun; -external_fun(_) -> +external_fun(_) -> false. %% Inlined. @@ -2121,7 +2458,7 @@ partition3_n(I, _Ts, _Key, _Keys, L1, L2) when I =:= 1 -> [reverse(L1) | reverse(L2)]; partition3_n(_I, _Ts, _Key, _Keys, L1, L2) -> [sort(L1) | sort(L2)]. - + partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K > Key -> partition3_n(I, K, Ts, Keys, L1, L2, T); partition3_n(I, K, Ts, [Key | Keys], L1, L2, T) when K == Key -> @@ -2146,7 +2483,7 @@ partition3([{K,E} | Ts], _Key, Keys, L1, L2) -> partition3(Ts, K, Keys, L1, L2, E); partition3(_Ts, _Key, _Keys, L1, L2) -> [L1 | L2]. - + partition3(Ts, K, [Key | Keys], L1, L2, E) when K > Key -> partition3(Ts, K, Keys, L1, L2, E); partition3(Ts, K, [Key | Keys], L1, L2, E) when K == Key -> @@ -2192,7 +2529,7 @@ join_element(E1, E2, I2) -> join_element2([B | Bs], C, I2) when C =/= I2 -> [B | join_element2(Bs, C+1, I2)]; -join_element2([_ | Bs], _C, _I2) -> +join_element2([_ | Bs], _C, _I2) -> Bs. family2rel([{X,S} | F], L) -> @@ -2297,7 +2634,7 @@ check_function([{X,_} | XL], R) -> check_function(X, XL, R); check_function([], R) -> R. - + check_function(X0, [{X,_} | XL], R) when X0 /= X -> check_function(X, XL, R); check_function(X0, [{X,_} | _XL], _R) when X0 == X -> @@ -2371,14 +2708,14 @@ term2set(T, Type) -> ?ORDSET(T, Type). fam2digraph(F, G) -> - Fun = fun({From, ToL}) -> + Fun = fun({From, ToL}) -> digraph:add_vertex(G, From), Fun2 = fun(To) -> digraph:add_vertex(G, To), case digraph:add_edge(G, From, To) of - {error, {bad_edge, _}} -> + {error, {bad_edge, _}} -> throw({error, cyclic}); - _ -> + _ -> true end end, @@ -2397,7 +2734,7 @@ digraph_fam([V | Vs], V0, G, L) when V /= V0 -> digraph_fam([], _V0, _G, L) -> reverse(L). -%% -> bool() +%% -> boolean() check_fun(T, F, FunT) -> true = is_type(FunT), {NT, _MaxI} = number_tuples(T, 1), @@ -2424,7 +2761,7 @@ check_for_sort(T, _I) when T =:= ?ANYTYPE -> check_for_sort(T, I) when ?IS_RELATION(T), I =< ?REL_ARITY(T), I >= 1 -> I > 1; check_for_sort(_T, _I) -> - error. + error. inverse_substitution(L, Fun, Sort) -> %% One easily sees that the inverse of the tuples created by @@ -2477,11 +2814,11 @@ match_types(Type1, Type2) -> match_types1(Type1, Type2). match_types1(Atom, Atom) when ?IS_ATOM_TYPE(Atom) -> true; -match_types1(?ANYTYPE, _) -> +match_types1(?ANYTYPE, _) -> true; -match_types1(_, ?ANYTYPE) -> +match_types1(_, ?ANYTYPE) -> true; -match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> +match_types1(?SET_OF(Type1), ?SET_OF(Type2)) -> match_types1(Type1, Type2); match_types1(T1, T2) when tuple_size(T1) =:= tuple_size(T2) -> match_typesl(tuple_size(T1), T1, T2); diff --git a/lib/stdlib/src/string.erl b/lib/stdlib/src/string.erl index 264348180f..30eac4f07d 100644 --- a/lib/stdlib/src/string.erl +++ b/lib/stdlib/src/string.erl @@ -29,23 +29,23 @@ %%--------------------------------------------------------------------------- --type direction() :: 'left' | 'right' | 'both'. - -%%--------------------------------------------------------------------------- - %% Robert's bit %% len(String) %% Return the length of a string. --spec len(string()) -> non_neg_integer(). +-spec len(String) -> Length when + String :: string(), + Length :: non_neg_integer(). len(S) -> length(S). %% equal(String1, String2) %% Test if 2 strings are equal. --spec equal(string(), string()) -> boolean(). +-spec equal(String1, String2) -> boolean() when + String1 :: string(), + String2 :: string(). equal(S, S) -> true; equal(_, _) -> false. @@ -53,7 +53,10 @@ equal(_, _) -> false. %% concat(String1, String2) %% Concatenate 2 strings. --spec concat(string(), string()) -> string(). +-spec concat(String1, String2) -> String3 when + String1 :: string(), + String2 :: string(), + String3 :: string(). concat(S1, S2) -> S1 ++ S2. @@ -61,7 +64,10 @@ concat(S1, S2) -> S1 ++ S2. %% rchr(String, Char) %% Return the first/last index of the character in a string. --spec chr(string(), char()) -> non_neg_integer(). +-spec chr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). chr(S, C) when is_integer(C) -> chr(S, C, 1). @@ -69,7 +75,10 @@ chr([C|_Cs], C, I) -> I; chr([_|Cs], C, I) -> chr(Cs, C, I+1); chr([], _C, _I) -> 0. --spec rchr(string(), char()) -> non_neg_integer(). +-spec rchr(String, Character) -> Index when + String :: string(), + Character :: char(), + Index :: non_neg_integer(). rchr(S, C) when is_integer(C) -> rchr(S, C, 1, 0). @@ -85,7 +94,10 @@ rchr([], _C, _I, L) -> L. %% Return the first/last index of the sub-string in a string. %% index/2 is kept for backwards compatibility. --spec str(string(), string()) -> non_neg_integer(). +-spec str(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). str(S, Sub) when is_list(Sub) -> str(S, Sub, 1). @@ -97,7 +109,10 @@ str([C|S], [C|Sub], I) -> str([_|S], Sub, I) -> str(S, Sub, I+1); str([], _Sub, _I) -> 0. --spec rstr(string(), string()) -> non_neg_integer(). +-spec rstr(String, SubString) -> Index when + String :: string(), + SubString :: string(), + Index :: non_neg_integer(). rstr(S, Sub) when is_list(Sub) -> rstr(S, Sub, 1, 0). @@ -116,7 +131,10 @@ prefix(Pre, String) when is_list(Pre), is_list(String) -> false. %% span(String, Chars) -> Length. %% cspan(String, Chars) -> Length. --spec span(string(), string()) -> non_neg_integer(). +-spec span(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). span(S, Cs) when is_list(Cs) -> span(S, Cs, 0). @@ -127,7 +145,10 @@ span([C|S], Cs, I) -> end; span([], _Cs, I) -> I. --spec cspan(string(), string()) -> non_neg_integer(). +-spec cspan(String, Chars) -> Length when + String :: string(), + Chars :: string(), + Length :: non_neg_integer(). cspan(S, Cs) when is_list(Cs) -> cspan(S, Cs, 0). @@ -142,14 +163,21 @@ cspan([], _Cs, I) -> I. %% substr(String, Start, Length) %% Extract a sub-string from String. --spec substr(string(), pos_integer()) -> string(). +-spec substr(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). substr(String, 1) when is_list(String) -> String; substr(String, S) when is_integer(S), S > 1 -> substr2(String, S). --spec substr(string(), pos_integer(), non_neg_integer()) -> string(). +-spec substr(String, Start, Length) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Length :: non_neg_integer(). substr(String, S, L) when is_integer(S), S >= 1, is_integer(L), L >= 0 -> substr1(substr2(String, S), L). @@ -163,7 +191,10 @@ substr2([_|String], S) -> substr2(String, S-1). %% tokens(String, Seperators). %% Return a list of tokens seperated by characters in Seperators. --spec tokens(string(), string()) -> [[char(),...]]. +-spec tokens(String, SeparatorList) -> Tokens when + String :: string(), + SeparatorList :: string(), + Tokens :: [Token :: nonempty_string()]. tokens(S, Seps) -> tokens1(S, Seps, []). @@ -184,11 +215,18 @@ tokens2([C|S], Seps, Toks, Cs) -> tokens2([], _Seps, Toks, Cs) -> reverse([reverse(Cs)|Toks]). --spec chars(char(), non_neg_integer()) -> string(). +-spec chars(Character, Number) -> String when + Character :: char(), + Number :: non_neg_integer(), + String :: string(). chars(C, N) -> chars(C, N, []). --spec chars(char(), non_neg_integer(), string()) -> string(). +-spec chars(Character, Number, Tail) -> String when + Character :: char(), + Number :: non_neg_integer(), + Tail :: string(), + String :: string(). chars(C, N, Tail) when N > 0 -> chars(C, N-1, [C|Tail]); @@ -199,7 +237,10 @@ chars(C, 0, Tail) when is_integer(C) -> %%% COPIES %%% --spec copies(string(), non_neg_integer()) -> string(). +-spec copies(String, Number) -> Copies when + String :: string(), + Copies :: string(), + Number :: non_neg_integer(). copies(CharList, Num) when is_list(CharList), is_integer(Num), Num >= 0 -> copies(CharList, Num, []). @@ -211,11 +252,16 @@ copies(CharList, Num, R) -> %%% WORDS %%% --spec words(string()) -> pos_integer(). +-spec words(String) -> Count when + String :: string(), + Count :: pos_integer(). words(String) -> words(String, $\s). --spec words(string(), char()) -> pos_integer(). +-spec words(String, Character) -> Count when + String :: string(), + Character :: char(), + Count :: pos_integer(). words(String, Char) when is_integer(Char) -> w_count(strip(String, both, Char), Char, 0). @@ -226,11 +272,18 @@ w_count([_H|T], Char, Num) -> w_count(T, Char, Num). %%% SUB_WORDS %%% --spec sub_word(string(), integer()) -> string(). +-spec sub_word(String, Number) -> Word when + String :: string(), + Word :: string(), + Number :: integer(). sub_word(String, Index) -> sub_word(String, Index, $\s). --spec sub_word(string(), integer(), char()) -> string(). +-spec sub_word(String, Number, Character) -> Word when + String :: string(), + Word :: string(), + Number :: integer(), + Character :: char(). sub_word(String, Index, Char) when is_integer(Index), is_integer(Char) -> case words(String, Char) of @@ -254,14 +307,21 @@ s_word([_|T],Stop,Char,Index,Res) when Index < Stop -> strip(String) -> strip(String, both). --spec strip(string(), direction()) -> string(). +-spec strip(String, Direction) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both. strip(String, left) -> strip_left(String, $\s); strip(String, right) -> strip_right(String, $\s); strip(String, both) -> strip_right(strip_left(String, $\s), $\s). --spec strip(string(), direction(), char()) -> string(). +-spec strip(String, Direction, Character) -> Stripped when + String :: string(), + Stripped :: string(), + Direction :: left | right | both, + Character :: char(). strip(String, right, Char) -> strip_right(String, Char); strip(String, left, Char) -> strip_left(String, Char); @@ -285,11 +345,18 @@ strip_right([], Sc) when is_integer(Sc) -> %%% LEFT %%% --spec left(string(), non_neg_integer()) -> string(). +-spec left(String, Number) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(). left(String, Len) when is_integer(Len) -> left(String, Len, $\s). --spec left(string(), non_neg_integer(), char()) -> string(). +-spec left(String, Number, Character) -> Left when + String :: string(), + Left :: string(), + Number :: non_neg_integer(), + Character :: char(). left(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -303,11 +370,18 @@ l_pad(String, Num, Char) -> String ++ chars(Char, Num). %%% RIGHT %%% --spec right(string(), non_neg_integer()) -> string(). +-spec right(String, Number) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(). right(String, Len) when is_integer(Len) -> right(String, Len, $\s). --spec right(string(), non_neg_integer(), char()) -> string(). +-spec right(String, Number, Character) -> Right when + String :: string(), + Right :: string(), + Number :: non_neg_integer(), + Character :: char(). right(String, Len, Char) when is_integer(Char) -> Slen = length(String), @@ -321,11 +395,18 @@ r_pad(String, Num, Char) -> chars(Char, Num, String). %%% CENTRE %%% --spec centre(string(), non_neg_integer()) -> string(). +-spec centre(String, Number) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(). centre(String, Len) when is_integer(Len) -> centre(String, Len, $\s). --spec centre(string(), non_neg_integer(), char()) -> string(). +-spec centre(String, Number, Character) -> Centered when + String :: string(), + Centered :: string(), + Number :: non_neg_integer(), + Character :: char(). centre(String, 0, Char) when is_list(String), is_integer(Char) -> []; % Strange cases to centre string @@ -341,11 +422,18 @@ centre(String, Len, Char) when is_integer(Char) -> %%% SUB_STRING %%% --spec sub_string(string(), pos_integer()) -> string(). +-spec sub_string(String, Start) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(). sub_string(String, Start) -> substr(String, Start). --spec sub_string(string(), pos_integer(), pos_integer()) -> string(). +-spec sub_string(String, Start, Stop) -> SubString when + String :: string(), + SubString :: string(), + Start :: pos_integer(), + Stop :: pos_integer(). sub_string(String, Start, Stop) -> substr(String, Start, Stop - Start + 1). @@ -370,23 +458,34 @@ to_upper_char(C) when is_integer(C), 16#F8 =< C, C =< 16#FE -> to_upper_char(C) -> C. --spec to_lower(string()) -> string() - ; (char()) -> char(). +-spec to_lower(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_lower(S) when is_list(S) -> [to_lower_char(C) || C <- S]; to_lower(C) when is_integer(C) -> to_lower_char(C). --spec to_upper(string()) -> string() - ; (char()) -> char(). +-spec to_upper(String) -> Result when + String :: string(), + Result :: string() + ; (Char) -> CharResult when + Char :: char(), + CharResult :: char(). to_upper(S) when is_list(S) -> [to_upper_char(C) || C <- S]; to_upper(C) when is_integer(C) -> to_upper_char(C). --spec join([string()], string()) -> string(). +-spec join(StringList, Separator) -> String when + StringList :: [string()], + Separator :: string(), + String :: string(). join([], Sep) when is_list(Sep) -> []; diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index 09a01a9aea..e60706ed05 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -35,19 +35,28 @@ %%-------------------------------------------------------------------------- --export_type([child_spec/0, del_err/0, startchild_ret/0, strategy/0]). +-export_type([child_spec/0, startchild_ret/0, strategy/0]). %%-------------------------------------------------------------------------- --type child_id() :: pid() | 'undefined'. --type mfargs() :: {module(), atom(), [term()] | undefined}. +-type child() :: pid() | 'undefined'. +-type child_id() :: term(). +-type mfargs() :: {M :: module(), F :: atom(), A :: [term()] | undefined}. -type modules() :: [module()] | 'dynamic'. -type restart() :: 'permanent' | 'transient' | 'temporary'. -type shutdown() :: 'brutal_kill' | timeout(). -type worker() :: 'worker' | 'supervisor'. --type sup_name() :: {'local', atom()} | {'global', atom()}. --type sup_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). --type child_spec() :: {term(),mfargs(),restart(),shutdown(),worker(),modules()}. +-type sup_name() :: {'local', Name :: atom()} | {'global', Name :: atom()}. +-type sup_ref() :: (Name :: atom()) + | {Name :: atom(), Node :: node()} + | {'global', Name :: atom()} + | pid(). +-type child_spec() :: {Id :: child_id(), + StartFunc :: mfargs(), + Restart :: restart(), + Shutdown :: shutdown(), + Type :: worker(), + Modules :: modules()}. -type strategy() :: 'one_for_all' | 'one_for_one' | 'rest_for_one' | 'simple_one_for_one'. @@ -55,14 +64,14 @@ %%-------------------------------------------------------------------------- -record(child, {% pid is undefined when child is not running - pid = undefined :: child_id(), + pid = undefined :: child(), name, mfargs :: mfargs(), restart_type :: restart(), shutdown :: shutdown(), child_type :: worker(), modules = [] :: modules()}). --type child() :: #child{}. +-type child_rec() :: #child{}. -define(DICT, dict). -define(SETS, sets). @@ -70,7 +79,7 @@ -record(state, {name, strategy :: strategy(), - children = [] :: [child()], + children = [] :: [child_rec()], dynamics :: ?DICT() | ?SET(), intensity :: non_neg_integer(), period :: pos_integer(), @@ -99,11 +108,16 @@ behaviour_info(_Other) -> -type startlink_err() :: {'already_started', pid()} | 'shutdown' | term(). -type startlink_ret() :: {'ok', pid()} | 'ignore' | {'error', startlink_err()}. --spec start_link(module(), term()) -> startlink_ret(). +-spec start_link(Module, Args) -> startlink_ret() when + Module :: module(), + Args :: term(). start_link(Mod, Args) -> gen_server:start_link(supervisor, {self, Mod, Args}, []). --spec start_link(sup_name(), module(), term()) -> startlink_ret(). +-spec start_link(SupName, Module, Args) -> startlink_ret() when + SupName :: sup_name(), + Module :: module(), + Args :: term(). start_link(SupName, Mod, Args) -> gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []). @@ -111,24 +125,33 @@ start_link(SupName, Mod, Args) -> %%% Interface functions. %%% --------------------------------------------------- --type info() :: term(). -type startchild_err() :: 'already_present' - | {'already_started', child_id()} | term(). --type startchild_ret() :: {'ok', child_id()} | {'ok', child_id(), info()} + | {'already_started', Child :: child()} | term(). +-type startchild_ret() :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} | {'error', startchild_err()}. --spec start_child(sup_ref(), child_spec() | [term()]) -> startchild_ret(). +-spec start_child(SupRef, ChildSpec) -> startchild_ret() when + SupRef :: sup_ref(), + ChildSpec :: child_spec() | (List :: [term()]). start_child(Supervisor, ChildSpec) -> call(Supervisor, {start_child, ChildSpec}). --type restart_err() :: 'running' | 'not_found' | 'simple_one_for_one' | term(). --spec restart_child(sup_ref(), term()) -> - {'ok', child_id()} | {'ok', child_id(), info()} | {'error', restart_err()}. +-spec restart_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: {'ok', Child :: child()} + | {'ok', Child :: child(), Info :: term()} + | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one' | term(). restart_child(Supervisor, Name) -> call(Supervisor, {restart_child, Name}). --type del_err() :: 'running' | 'not_found' | 'simple_one_for_one'. --spec delete_child(sup_ref(), term()) -> 'ok' | {'error', del_err()}. +-spec delete_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'running' | 'not_found' | 'simple_one_for_one'. delete_child(Supervisor, Name) -> call(Supervisor, {delete_child, Name}). @@ -139,22 +162,39 @@ delete_child(Supervisor, Name) -> %% way (maybe killed). %%----------------------------------------------------------------- --type term_err() :: 'not_found' | 'simple_one_for_one'. --spec terminate_child(sup_ref(), pid() | term()) -> 'ok' | {'error', term_err()}. +-spec terminate_child(SupRef, Id) -> Result when + SupRef :: sup_ref(), + Id :: pid() | child_id(), + Result :: 'ok' | {'error', Error}, + Error :: 'not_found' | 'simple_one_for_one'. terminate_child(Supervisor, Name) -> call(Supervisor, {terminate_child, Name}). --spec which_children(sup_ref()) -> [{term(), child_id(), worker(), modules()}]. +-spec which_children(SupRef) -> [{Id,Child,Type,Modules}] when + SupRef :: sup_ref(), + Id :: child_id() | undefined, + Child :: child(), + Type :: worker(), + Modules :: modules(). which_children(Supervisor) -> call(Supervisor, which_children). +-spec count_children(SupRef) -> PropListOfCounts when + SupRef :: sup_ref(), + PropListOfCounts :: [Count], + Count :: {specs, ChildSpecCount :: non_neg_integer()} + | {active, ActiveProcessCount :: non_neg_integer()} + | {supervisors, ChildSupervisorCount :: non_neg_integer()} + |{workers, ChildWorkerCount :: non_neg_integer()}. count_children(Supervisor) -> call(Supervisor, count_children). call(Supervisor, Req) -> gen_server:call(Supervisor, Req, infinity). --spec check_childspecs([child_spec()]) -> 'ok' | {'error', term()}. +-spec check_childspecs(ChildSpecs) -> Result when + ChildSpecs :: [child_spec()], + Result :: 'ok' | {'error', Error :: term()}. check_childspecs(ChildSpecs) when is_list(ChildSpecs) -> case check_startspec(ChildSpecs) of {ok, _} -> ok; @@ -222,12 +262,12 @@ init_dynamic(_State, StartSpec) -> %%----------------------------------------------------------------- %% Func: start_children/2 -%% Args: Children = [child()] in start order +%% Args: Children = [child_rec()] in start order %% SupName = {local, atom()} | {global, atom()} | {pid(), Mod} %% Purpose: Start all children. The new list contains #child's %% with pids. %% Returns: {ok, NChildren} | {error, NChildren} -%% NChildren = [child()] in termination order (reversed +%% NChildren = [child_rec()] in termination order (reversed %% start order) %%----------------------------------------------------------------- start_children(Children, SupName) -> start_children(Children, [], SupName). @@ -687,9 +727,9 @@ restart(one_for_all, Child, State) -> %%----------------------------------------------------------------- %% Func: terminate_children/2 -%% Args: Children = [child()] in termination order +%% Args: Children = [child_rec()] in termination order %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} -%% Returns: NChildren = [child()] in +%% Returns: NChildren = [child_rec()] in %% startup order (reversed termination order) %%----------------------------------------------------------------- terminate_children(Children, SupName) -> @@ -958,7 +998,7 @@ supname(N, _) -> N. %%% Shutdown = integer() | infinity | brutal_kill %%% ChildType = supervisor | worker %%% Modules = [atom()] | dynamic -%%% Returns: {ok, [child()]} | Error +%%% Returns: {ok, [child_rec()]} | Error %%% ------------------------------------------------------ check_startspec(Children) -> check_startspec(Children, []). diff --git a/lib/stdlib/src/supervisor_bridge.erl b/lib/stdlib/src/supervisor_bridge.erl index 3d2bd2c9a5..555cb5a66f 100644 --- a/lib/stdlib/src/supervisor_bridge.erl +++ b/lib/stdlib/src/supervisor_bridge.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -49,9 +49,25 @@ behaviour_info(_Other) -> %%%----------------------------------------------------------------- -record(state, {mod, pid, child_state, name}). +-spec start_link(Module, Args) -> Result when + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Mod, StartArgs) -> gen_server:start_link(supervisor_bridge, [Mod, StartArgs, self], []). +-spec start_link(SupBridgeName, Module, Args) -> Result when + SupBridgeName :: {local, Name} | {global, Name}, + Name :: atom(), + Module :: module(), + Args :: term(), + Result :: {ok, Pid} | ignore | {error, Error}, + Error :: {already_started, Pid} | term(), + Pid :: pid(). + start_link(Name, Mod, StartArgs) -> gen_server:start_link(Name, supervisor_bridge, [Mod, StartArgs, Name], []). diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index 12209c16d7..8ab72c9b50 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. 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 @@ -33,22 +33,74 @@ %%----------------------------------------------------------------- -type name() :: pid() | atom() | {'global', atom()}. --type system_event() :: {'in', _Msg} | {'in', _Msg, _From} | {'out', _Msg, _To}. +-type system_event() :: {'in', Msg :: _} + | {'in', Msg :: _, From :: _} + | {'out', Msg :: _, To :: _} + | term(). +-opaque dbg_opt() :: list(). +-type dbg_fun() :: fun((FuncState :: _, + Event :: system_event(), + ProcState :: _) -> 'done' | (NewFuncState :: _)). %%----------------------------------------------------------------- %% System messages %%----------------------------------------------------------------- +-spec suspend(Name) -> Void when + Name :: name(), + Void :: term(). suspend(Name) -> send_system_msg(Name, suspend). +-spec suspend(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). suspend(Name, Timeout) -> send_system_msg(Name, suspend, Timeout). +-spec resume(Name) -> Void when + Name :: name(), + Void :: term(). resume(Name) -> send_system_msg(Name, resume). +-spec resume(Name, Timeout) -> Void when + Name :: name(), + Timeout :: timeout(), + Void :: term(). resume(Name, Timeout) -> send_system_msg(Name, resume, Timeout). +-spec get_status(Name) -> Status when + Name :: name(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (Dbg :: dbg_opt()) + | (Misc :: term()). get_status(Name) -> send_system_msg(Name, get_status). +-spec get_status(Name, Timeout) -> Status when + Name :: name(), + Timeout :: timeout(), + Status :: {status, Pid :: pid(), {module, Module :: module()}, [SItem]}, + SItem :: (PDict :: [{Key :: term(), Value :: term()}]) + | (SysState :: 'running' | 'suspended') + | (Parent :: pid()) + | (Dbg :: dbg_opt()) + | (Misc :: term()). get_status(Name, Timeout) -> send_system_msg(Name, get_status, Timeout). +-spec change_code(Name, Module, OldVsn, Extra) -> 'ok' | {error, Reason} when + Name :: name(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + Reason :: term(). change_code(Name, Mod, Vsn, Extra) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}). +-spec change_code(Name, Module, OldVsn, Extra, Timeout) -> + 'ok' | {error, Reason} when + Name :: name(), + Module :: module(), + OldVsn :: 'undefined' | term(), + Extra :: term(), + Timeout :: timeout(), + Reason :: term(). change_code(Name, Mod, Vsn, Extra, Timeout) -> send_system_msg(Name, {change_code, Mod, Vsn, Extra}, Timeout). @@ -56,52 +108,116 @@ change_code(Name, Mod, Vsn, Extra, Timeout) -> %% Debug commands %%----------------------------------------------------------------- --type log_flag() :: 'true' | {'true',pos_integer()} | 'false' | 'get' | 'print'. - --spec log(name(), log_flag()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print'. log(Name, Flag) -> send_system_msg(Name, {debug, {log, Flag}}). --spec log(name(), log_flag(), timeout()) -> 'ok' | {'ok', [system_event()]}. +-spec log(Name, Flag, Timeout) -> 'ok' | {'ok', [system_event()]} when + Name :: name(), + Flag :: 'true' | + {'true', N :: pos_integer()} + | 'false' | 'get' | 'print', + Timeout :: timeout(). log(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {log, Flag}}, Timeout). --spec trace(name(), boolean()) -> 'ok'. +-spec trace(Name, Flag) -> 'ok' when + Name :: name(), + Flag :: boolean(). trace(Name, Flag) -> send_system_msg(Name, {debug, {trace, Flag}}). --spec trace(name(), boolean(), timeout()) -> 'ok'. +-spec trace(Name, Flag, Timeout) -> 'ok' when + Name :: name(), + Flag :: boolean(), + Timeout :: timeout(). trace(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {trace, Flag}}, Timeout). --type l2f_fname() :: string() | 'false'. - --spec log_to_file(name(), l2f_fname()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false'. log_to_file(Name, FileName) -> send_system_msg(Name, {debug, {log_to_file, FileName}}). --spec log_to_file(name(), l2f_fname(), timeout()) -> 'ok' | {'error','open_file'}. +-spec log_to_file(Name, Flag, Timeout) -> 'ok' | {'error','open_file'} when + Name :: name(), + Flag :: (FileName :: string()) | 'false', + Timeout :: timeout(). log_to_file(Name, FileName, Timeout) -> send_system_msg(Name, {debug, {log_to_file, FileName}}, Timeout). +-spec statistics(Name, Flag) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple], + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(). statistics(Name, Flag) -> send_system_msg(Name, {debug, {statistics, Flag}}). + +-spec statistics(Name, Flag, Timeout) -> 'ok' | {'ok', Statistics} when + Name :: name(), + Flag :: 'true' | 'false' | 'get', + Statistics :: [StatisticsTuple], + StatisticsTuple :: {'start_time', DateTime1} + | {'current_time', DateTime2} + | {'reductions', non_neg_integer()} + | {'messages_in', non_neg_integer()} + | {'messages_out', non_neg_integer()}, + DateTime1 :: file:date_time(), + DateTime2 :: file:date_time(), + Timeout :: timeout(). statistics(Name, Flag, Timeout) -> send_system_msg(Name, {debug, {statistics, Flag}}, Timeout). --spec no_debug(name()) -> 'ok'. +-spec no_debug(Name) -> 'ok' when + Name :: name(). no_debug(Name) -> send_system_msg(Name, {debug, no_debug}). --spec no_debug(name(), timeout()) -> 'ok'. +-spec no_debug(Name, Timeout) -> 'ok' when + Name :: name(), + Timeout :: timeout(). no_debug(Name, Timeout) -> send_system_msg(Name, {debug, no_debug}, Timeout). +-spec install(Name, FuncSpec) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Void :: term(). install(Name, {Func, FuncState}) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}). +-spec install(Name, FuncSpec, Timeout) -> Void when + Name :: name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(), + Timeout :: timeout(), + Void :: term(). install(Name, {Func, FuncState}, Timeout) -> send_system_msg(Name, {debug, {install, {Func, FuncState}}}, Timeout). +-spec remove(Name, Func) -> Void when + Name :: name(), + Func :: dbg_fun(), + Void :: term(). remove(Name, Func) -> send_system_msg(Name, {debug, {remove, Func}}). +-spec remove(Name, Func, Timeout) -> Void when + Name :: name(), + Func :: dbg_fun(), + Timeout :: timeout(), + Void :: term(). remove(Name, Func, Timeout) -> send_system_msg(Name, {debug, {remove, Func}}, Timeout). @@ -150,6 +266,14 @@ mfa(Name, Req, Timeout) -> %% The Module must export system_continue/3, system_terminate/4 %% and format_status/2 for status information. %%----------------------------------------------------------------- +-spec handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> Void when + Msg :: term(), + From :: {pid(), Tag :: _}, + Parent :: pid(), + Module :: module(), + Debug :: [dbg_opt()], + Misc :: term(), + Void :: term(). handle_system_msg(Msg, From, Parent, Module, Debug, Misc) -> handle_system_msg(running, Msg, From, Parent, Module, Debug, Misc, false). @@ -176,6 +300,11 @@ handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib) -> %% Func is a formatting function, called as Func(Device, Event). %% Returns: [debug_opts()] %%----------------------------------------------------------------- +-spec handle_debug(Debug, FormFunc, Extra, Event) -> [dbg_opt()] when + Debug :: [dbg_opt()], + FormFunc :: dbg_fun(), + Extra :: term(), + Event :: system_event(). handle_debug([{trace, true} | T], FormFunc, State, Event) -> print_event({Event, State, FormFunc}), [{trace, true} | handle_debug(T, FormFunc, State, Event)]; @@ -341,24 +470,36 @@ trim(N, LogData) -> %% Debug structure manipulating functions %%----------------------------------------------------------------- install_debug(Item, Data, Debug) -> - case get_debug(Item, Debug, undefined) of + case get_debug2(Item, Debug, undefined) of undefined -> [{Item, Data} | Debug]; _ -> Debug end. remove_debug(Item, Debug) -> lists:keydelete(Item, 1, Debug). + +-spec get_debug(Item, Debug, Default) -> term() when + Item :: 'log' | 'statistics', + Debug :: [dbg_opt()], + Default :: term(). get_debug(Item, Debug, Default) -> + get_debug2(Item, Debug, Default). + +%% Workaround: accepts more Item types than get_debug/3. +get_debug2(Item, Debug, Default) -> case lists:keysearch(Item, 1, Debug) of {value, {Item, Data}} -> Data; _ -> Default end. +-spec print_log(Debug) -> Void when + Debug :: [dbg_opt()], + Void :: term(). print_log(Debug) -> {_N, Logs} = get_debug(log, Debug, {0, []}), lists:foreach(fun print_event/1, lists:reverse(Logs)). close_log_file(Debug) -> - case get_debug(log_to_file, Debug, []) of + case get_debug2(log_to_file, Debug, []) of [] -> Debug; Fd -> @@ -375,6 +516,15 @@ close_log_file(Debug) -> %% system messages. %% Returns: [debug_opts()] %%----------------------------------------------------------------- + +-spec debug_options(Options) -> [dbg_opt()] when + Options :: [Opt], + Opt :: 'trace' | 'log' | 'statistics' | {'log_to_file', FileName} + | {'install', FuncSpec}, + FileName :: file:name(), + FuncSpec :: {Func, FuncState}, + Func :: dbg_fun(), + FuncState :: term(). debug_options(Options) -> debug_options(Options, []). debug_options([trace | T], Debug) -> diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index b456c5d6c1..89fae05e4f 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.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 @@ -22,7 +22,7 @@ send_after/3, send_after/2, exit_after/3, exit_after/2, kill_after/2, kill_after/1, apply_interval/4, send_interval/3, send_interval/2, - cancel/1, sleep/1, tc/2, tc/3, now_diff/2, + cancel/1, sleep/1, tc/1, tc/2, tc/3, now_diff/2, seconds/1, minutes/1, hours/1, hms/3]). -export([start_link/0, start/0, @@ -46,103 +46,189 @@ %% -opaque tref() :: {integer(), reference()}. -type time() :: non_neg_integer(). --type timestamp() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. %% %% Interface functions %% --spec apply_after(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}. +-spec apply_after(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). + apply_after(Time, M, F, A) -> req(apply_after, {Time, {M, F, A}}). --spec send_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_after(Time, Pid, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Pid, Message) -> req(apply_after, {Time, {?MODULE, send, [Pid, Message]}}). --spec send_after(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_after(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_after(Time, Message) -> send_after(Time, self(), Message). --spec exit_after(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec exit_after(Time, Pid, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Pid, Reason) -> req(apply_after, {Time, {erlang, exit, [Pid, Reason]}}). --spec exit_after(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec exit_after(Time, Reason1) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason1 :: term(), + Reason2 :: term(). exit_after(Time, Reason) -> exit_after(Time, self(), Reason). --spec kill_after(time(), pid() | atom()) -> {'ok', tref()} | {'error', term()}. +-spec kill_after(Time, Pid) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + TRef :: tref(), + Reason2 :: term(). kill_after(Time, Pid) -> exit_after(Time, Pid, kill). --spec kill_after(time()) -> {'ok', tref()} | {'error', term()}. +-spec kill_after(Time) -> {'ok', TRef} | {'error', Reason2} when + Time :: time(), + TRef :: tref(), + Reason2 :: term(). kill_after(Time) -> exit_after(Time, self(), kill). --spec apply_interval(time(), atom(), atom(), [term()]) -> {'ok', tref()} | {'error', term()}. +-spec apply_interval(Time, Module, Function, Arguments) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Module :: module(), + Function :: atom(), + Arguments :: [term()], + TRef :: tref(), + Reason :: term(). apply_interval(Time, M, F, A) -> req(apply_interval, {Time, self(), {M, F, A}}). --spec send_interval(time(), pid() | atom(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_interval(Time, Pid, Message) -> + {'ok', TRef} | {'error', Reason} when + Time :: time(), + Pid :: pid() | (RegName :: atom()), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Pid, Message) -> req(apply_interval, {Time, Pid, {?MODULE, send, [Pid, Message]}}). --spec send_interval(time(), term()) -> {'ok', tref()} | {'error', term()}. +-spec send_interval(Time, Message) -> {'ok', TRef} | {'error', Reason} when + Time :: time(), + Message :: term(), + TRef :: tref(), + Reason :: term(). send_interval(Time, Message) -> send_interval(Time, self(), Message). --spec cancel(tref()) -> {'ok', 'cancel'} | {'error', term()}. +-spec cancel(TRef) -> {'ok', 'cancel'} | {'error', Reason} when + TRef :: tref(), + Reason :: term(). cancel(BRef) -> req(cancel, BRef). --spec sleep(timeout()) -> 'ok'. +-spec sleep(Time) -> 'ok' when + Time :: timeout(). sleep(T) -> receive after T -> ok end. +%% +%% Measure the execution time (in microseconds) for Fun(). +%% +-spec tc(Fun) -> {Time, Value} when + Fun :: function(), + Time :: integer(), + Value :: term(). +tc(F) -> + Before = os:timestamp(), + Val = F(), + After = os:timestamp(), + {now_diff(After, Before), Val}. %% %% Measure the execution time (in microseconds) for Fun(Args). %% --spec tc(function(), [_]) -> {time(), term()}. +-spec tc(Fun, Arguments) -> {Time, Value} when + Fun :: function(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). tc(F, A) -> - Before = erlang:now(), - Val = (catch apply(F, A)), - After = erlang:now(), + Before = os:timestamp(), + Val = apply(F, A), + After = os:timestamp(), {now_diff(After, Before), Val}. %% %% Measure the execution time (in microseconds) for an MFA. %% --spec tc(atom(), atom(), [term()]) -> {time(), term()}. +-spec tc(Module, Function, Arguments) -> {Time, Value} when + Module :: module(), + Function :: atom(), + Arguments :: [term()], + Time :: integer(), + Value :: term(). tc(M, F, A) -> - Before = erlang:now(), - Val = (catch apply(M, F, A)), - After = erlang:now(), + Before = os:timestamp(), + Val = apply(M, F, A), + After = os:timestamp(), {now_diff(After, Before), Val}. %% %% Calculate the time difference (in microseconds) of two %% erlang:now() timestamps, T2-T1. %% --spec now_diff(timestamp(), timestamp()) -> integer(). +-spec now_diff(T1, T2) -> Tdiff when + T1 :: calendar:t_now(), + T2 :: calendar:t_now(), + Tdiff :: integer(). now_diff({A2, B2, C2}, {A1, B1, C1}) -> ((A2-A1)*1000000 + B2-B1)*1000000 + C2-C1. %% %% Convert seconds, minutes etc. to milliseconds. %% --spec seconds(non_neg_integer()) -> non_neg_integer(). +-spec seconds(Seconds) -> MilliSeconds when + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). seconds(Seconds) -> 1000*Seconds. --spec minutes(non_neg_integer()) -> non_neg_integer(). +-spec minutes(Minutes) -> MilliSeconds when + Minutes :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). minutes(Minutes) -> 1000*60*Minutes. --spec hours(non_neg_integer()) -> non_neg_integer(). +-spec hours(Hours) -> MilliSeconds when + Hours :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hours(Hours) -> 1000*60*60*Hours. --spec hms(non_neg_integer(), non_neg_integer(), non_neg_integer()) -> non_neg_integer(). +-spec hms(Hours, Minutes, Seconds) -> MilliSeconds when + Hours :: non_neg_integer(), + Minutes :: non_neg_integer(), + Seconds :: non_neg_integer(), + MilliSeconds :: non_neg_integer(). hms(H, M, S) -> hours(H) + minutes(M) + seconds(S). diff --git a/lib/stdlib/src/unicode.erl b/lib/stdlib/src/unicode.erl index 12bc60623d..a5d9965ca2 100644 --- a/lib/stdlib/src/unicode.erl +++ b/lib/stdlib/src/unicode.erl @@ -30,12 +30,34 @@ characters_to_binary/3, bom_to_encoding/1, encoding_to_bom/1]). --export_type([encoding/0]). +-export_type([chardata/0, charlist/0, encoding/0, external_chardata/0, + external_charlist/0, latin1_chardata/0, + latin1_charlist/0, unicode_binary/0, unicode_char/0]). -type encoding() :: 'latin1' | 'unicode' | 'utf8' | 'utf16' | {'utf16', endian()} | 'utf32' | {'utf32', endian()}. -type endian() :: 'big' | 'little'. +-type unicode_binary() :: binary(). +-type unicode_char() :: non_neg_integer(). +-type charlist() :: [unicode_char() | unicode_binary() | charlist()]. +-type chardata() :: charlist() | unicode_binary(). +-type external_unicode_binary() :: binary(). +-type external_chardata() :: external_charlist() | external_unicode_binary(). +-type external_charlist() :: [unicode_char() | external_unicode_binary() + | external_charlist()]. +-type latin1_binary() :: binary(). +-type latin1_char() :: byte(). +-type latin1_chardata() :: latin1_charlist() | latin1_binary(). +-type latin1_charlist() :: [latin1_char() | latin1_binary() + | latin1_charlist()]. + +-spec characters_to_list(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: list() + | {error, list(), RestData} + | {incomplete, list(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). characters_to_list(ML) -> unicode:characters_to_list(ML,unicode). @@ -69,6 +91,13 @@ do_characters_to_list(ML, Encoding) -> end. +-spec characters_to_binary(Data) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML) -> try unicode:characters_to_binary(ML,unicode) @@ -104,6 +133,15 @@ characters_to_binary_int(ML,InEncoding) -> erlang:raise(error,TheError,[{Mod,characters_to_binary,L}|Rest]) end. +-spec characters_to_binary(Data, InEncoding, OutEncoding) -> Result when + Data :: latin1_chardata() | chardata() | external_chardata(), + InEncoding :: encoding(), + OutEncoding :: encoding(), + Result :: binary() + | {error, binary(), RestData} + | {incomplete, binary(), binary()}, + RestData :: latin1_chardata() | chardata() | external_chardata(). + characters_to_binary(ML, latin1, latin1) when is_binary(ML) -> ML; characters_to_binary(ML, latin1, Uni) when is_binary(ML) and ((Uni =:= utf8) or (Uni =:= unicode)) -> @@ -215,6 +253,13 @@ characters_to_binary_int(ML, InEncoding, OutEncoding) -> Res end. +-spec bom_to_encoding(Bin) -> {Encoding, Length} when + Bin :: binary(), + Encoding :: 'latin1' | 'utf8' + | {'utf16', endian()} + | {'utf32', endian()}, + Length :: non_neg_integer(). + bom_to_encoding(<<239,187,191,_/binary>>) -> {utf8,3}; bom_to_encoding(<<0,0,254,255,_/binary>>) -> @@ -228,6 +273,10 @@ bom_to_encoding(<<255,254,_/binary>>) -> bom_to_encoding(Bin) when is_binary(Bin) -> {latin1,0}. +-spec encoding_to_bom(InEncoding) -> Bin when + Bin :: binary(), + InEncoding :: encoding(). + encoding_to_bom(unicode) -> <<239,187,191>>; encoding_to_bom(utf8) -> diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl index ee0d17bc94..598e77ffdc 100644 --- a/lib/stdlib/src/win32reg.erl +++ b/lib/stdlib/src/win32reg.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 @@ -52,14 +52,17 @@ -define(reg_dword, 4). %% Basic types internal to this file. --type open_mode() :: 'read' | 'write'. --type reg_handle() :: {'win32reg',port()}. +-opaque reg_handle() :: {'win32reg',port()}. -type name() :: string() | 'default'. -type value() :: string() | integer() | binary(). %%% Exported functions. --spec open([open_mode()]) -> {'ok', reg_handle()} | {'error', 'enotsup'}. +-spec open(OpenModeList) -> ReturnValue when + OpenModeList :: [OpenMode], + OpenMode :: 'read' | 'write', + ReturnValue :: {'ok', RegHandle} | {'error', ErrorId :: 'enotsup'}, + RegHandle :: reg_handle(). open(Modes) -> case os:type() of @@ -75,14 +78,17 @@ open(Modes) -> {error, enotsup} end. --spec close(reg_handle()) -> 'ok'. +-spec close(RegHandle) -> 'ok' when + RegHandle :: reg_handle(). close({win32reg, Reg}) when is_port(Reg) -> unlink(Reg), exit(Reg, die), ok. --spec current_key(reg_handle()) -> {'ok', string()}. +-spec current_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', string()}. current_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_current], @@ -94,12 +100,18 @@ current_key({win32reg, Reg}) when is_port(Reg) -> _ -> Root ++ [$\\|Name] end}. --spec change_key(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_open_key, Key). --spec change_key_create(reg_handle(), string()) -> 'ok' | {'error', atom()}. +-spec change_key_create(RegHandle, Key) -> ReturnValue when + RegHandle :: reg_handle(), + Key :: string(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. change_key_create({win32reg, Reg}, Key) when is_port(Reg) -> change_key(Reg, ?cmd_create_key, Key). @@ -113,21 +125,30 @@ change_key(Reg, Cmd, Key) -> {error, Reason} end. --spec sub_keys(reg_handle()) -> {'ok', [string()]} | {'error', atom()}. +-spec sub_keys(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [SubKey]} | {'error', ErrorId :: atom()}, + SubKey :: string(). sub_keys({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_subkeys], Reg ! {self(), {command, Cmd}}, collect_keys(Reg, []). --spec delete_key(reg_handle()) -> 'ok' | {'error', atom()}. +-spec delete_key(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_key({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_delete_key], Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec set_value(reg_handle(), name(), value()) -> 'ok' | {'error', atom()}. +-spec set_value(RegHandle, Name, Value) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + Value :: value(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Name = @@ -140,7 +161,10 @@ set_value({win32reg, Reg}, Name0, Value) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec value(reg_handle(), name()) -> {'ok', value()} | {'error', atom()}. +-spec value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: {'ok', Value :: value()} | {'error', ErrorId :: atom()}. value({win32reg, Reg}, Name) when is_port(Reg) -> Cmd = [?cmd_get_value, Name, 0], @@ -152,14 +176,20 @@ value({win32reg, Reg}, Name) when is_port(Reg) -> {error, Reason} end. --spec values(reg_handle()) -> {'ok', [{name(), value()}]} | {'error', atom()}. +-spec values(RegHandle) -> ReturnValue when + RegHandle :: reg_handle(), + ReturnValue :: {'ok', [ValuePair]} | {'error', ErrorId :: atom()}, + ValuePair :: {Name :: name(), Value :: value()}. values({win32reg, Reg}) when is_port(Reg) -> Cmd = [?cmd_get_all_values], Reg ! {self(), {command, Cmd}}, collect_values(Reg, []). --spec delete_value(reg_handle(), name()) -> 'ok' | {'error', atom()}. +-spec delete_value(RegHandle, Name) -> ReturnValue when + RegHandle :: reg_handle(), + Name :: name(), + ReturnValue :: 'ok' | {'error', ErrorId :: atom()}. delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Name = @@ -171,7 +201,9 @@ delete_value({win32reg, Reg}, Name0) when is_port(Reg) -> Reg ! {self(), {command, Cmd}}, get_result(Reg). --spec expand(string()) -> string(). +-spec expand(String) -> ExpandedString when + String :: string(), + ExpandedString :: string(). expand(Value) -> expand(Value, [], []). @@ -195,7 +227,9 @@ expand([C|Rest], Env, Result) -> expand([], [], Result) -> lists:reverse(Result). --spec format_error(atom()) -> string(). +-spec format_error(ErrorId) -> ErrorString when + ErrorId :: atom(), + ErrorString :: string(). format_error(ErrorId) -> erl_posix_msg:message(ErrorId). @@ -203,7 +237,7 @@ format_error(ErrorId) -> %%% Implementation. -spec collect_values(port(), [{name(), value()}]) -> - {'ok', [{name(), value()}]} | {'error', atom()}. + {'ok', [{name(), value()}]} | {'error', ErrorId :: atom()}. collect_values(P, Result) -> case get_result(P) of @@ -215,7 +249,7 @@ collect_values(P, Result) -> {error, Reason} end. --spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', atom()}. +-spec collect_keys(port(), string()) -> {'ok', [string()]} | {'error', ErrorId :: atom()}. collect_keys(P, Result) -> case get_result(P) of diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index d41aeefa59..524d709431 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.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 @@ -203,6 +203,9 @@ zip_comment_length}). +-type zip_file() :: #zip_file{}. +-type zip_comment() :: #zip_comment{}. + %% Open a zip archive with options %% @@ -323,8 +326,33 @@ openzip_close(_) -> %% Accepted options: %% verbose, cooked, file_list, keep_old_files, file_filter, memory +-spec(unzip(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + unzip(F) -> unzip(F, []). +-spec(unzip(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + unzip(F, Options) -> case ?CATCH do_unzip(F, Options) of {ok, R} -> {ok, R}; @@ -345,6 +373,18 @@ do_unzip(F, Options) -> {ok, Files}. %% Iterate over all files in a zip archive +-spec(foldl(Fun, Acc0, Archive) -> {ok, Acc1} | {error, Reason} when + Fun :: fun((FileInArchive, GetInfo, GetBin, AccIn) -> AccOut), + FileInArchive :: file:name(), + GetInfo :: fun(() -> file:file_info()), + GetBin :: fun(() -> binary()), + Acc0 :: term(), + Acc1 :: term(), + AccIn :: term(), + AccOut :: term(), + Archive :: file:name() | {file:name(), binary()}, + Reason :: term()). + foldl(Fun, Acc0, Archive) when is_function(Fun, 4) -> ZipFun = fun({Name, GetInfo, GetBin}, A) -> @@ -368,8 +408,33 @@ foldl(_,_, _) -> %% Accepted options: %% verbose, cooked, memory, comment +-spec(zip(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files) -> zip(F, Files, []). +-spec(zip(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + zip(F, Files, Options) -> case ?CATCH do_zip(F, Files, Options) of {ok, R} -> {ok, R}; @@ -392,8 +457,20 @@ do_zip(F, Files, Options) -> %% Accepted options: %% cooked, file_filter, file_output (latter 2 undocumented) +-spec(list_dir(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + list_dir(F) -> list_dir(F, []). +-spec(list_dir(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + Options :: [Option], + Option :: cooked). + list_dir(F, Options) -> case ?CATCH do_list_dir(F, Options) of {ok, R} -> {ok, R}; @@ -411,6 +488,10 @@ do_list_dir(F, Options) -> %% Print zip directory in short form +-spec(t(Archive) -> ok when + Archive :: file:name() | binary | ZipHandle, + ZipHandle :: pid()). + t(F) when is_pid(F) -> zip_t(F); t(F) when is_record(F, openzip) -> openzip_t(F); t(F) -> t(F, fun raw_short_print_info_etc/5). @@ -431,6 +512,10 @@ do_t(F, RawPrint) -> %% Print zip directory in long form (like ls -l) +-spec(tt(Archive) -> ok when + Archive :: file:name() | binary | ZipHandle, + ZipHandle :: pid()). + tt(F) when is_pid(F) -> zip_tt(F); tt(F) when is_record(F, openzip) -> openzip_tt(F); tt(F) -> t(F, fun raw_long_print_info_etc/5). @@ -605,11 +690,78 @@ get_list_dir_options(F, Options) -> get_list_dir_opt(Options, Opts). %% aliases for erl_tar compatibility +-spec(table(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()]). + table(F) -> list_dir(F). + +-spec(table(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, CommentAndFiles} | {error, Reason :: term()}, + CommentAndFiles :: [zip_comment() | zip_file()], + + Options :: [Option], + Option :: cooked). + table(F, O) -> list_dir(F, O). + +-spec(create(Name, FileList) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). + create(F, Fs) -> zip(F, Fs). + +-spec(create(Name, FileList, Options) -> RetValue when + Name :: file:name(), + FileList :: [FileSpec], + FileSpec :: file:name() | {file:name(), binary()} + | {file:name(), binary(), file:file_info()}, + Options :: [Option], + Option :: memory | cooked | verbose | {comment, Comment} + | {cwd, CWD} | {compress, What} | {uncompress, What}, + What :: all | [Extension] | {add, [Extension]} | {del, [Extension]}, + Extension :: string(), + Comment :: string(), + CWD :: string(), + RetValue :: {ok, FileName :: file:name()} + | {ok, {FileName :: file:name(), binary()}} + | {error, Reason :: term()}). create(F, Fs, O) -> zip(F, Fs, O). + +-spec(extract(Archive) -> RetValue when + Archive :: file:name() | binary(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}]). + extract(F) -> unzip(F). + +-spec(extract(Archive, Options) -> RetValue when + Archive :: file:name() | binary(), + Options :: [Option], + Option :: {file_list, FileList} + | keep_old_files | verbose | memory | + {file_filter, FileFilter} | {cwd, CWD}, + FileList :: [file:name()], + FileBinList :: [{file:name(),binary()}], + FileFilter :: fun((ZipFile) -> boolean()), + CWD :: string(), + ZipFile :: zip_file(), + RetValue :: {ok, FileList} + | {ok, FileBinList} + | {error, Reason :: term()} + | {error, {Name :: file:name(), Reason :: term()}}). + extract(F, O) -> unzip(F, O). @@ -990,21 +1142,52 @@ server_loop(OpenZip) -> {error, bad_msg} end. +-spec(zip_open(Archive) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Reason :: term()). + zip_open(Archive) -> zip_open(Archive, []). +-spec(zip_open(Archive, Options) -> {ok, ZipHandle} | {error, Reason} when + Archive :: file:name() | binary(), + ZipHandle :: pid(), + Options :: [Option], + Option :: cooked | memory | {cwd, CWD :: string()}, + Reason :: term()). + zip_open(Archive, Options) -> Pid = spawn(fun() -> server_loop(not_open) end), request(self(), Pid, {open, Archive, Options}). +-spec(zip_get(ZipHandle) -> {ok, [Result]} | {error, Reason} when + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(Pid) when is_pid(Pid) -> request(self(), Pid, get). +-spec(zip_close(ZipHandle) -> ok | {error, einval} when + ZipHandle :: pid()). + zip_close(Pid) when is_pid(Pid) -> request(self(), Pid, close). +-spec(zip_get(FileName, ZipHandle) -> {ok, [Result]} | {error, Reason} when + FileName :: file:name(), + ZipHandle :: pid(), + Result :: file:name() | {file:name(), binary()}, + Reason :: term()). + zip_get(FileName, Pid) when is_pid(Pid) -> request(self(), Pid, {get, FileName}). +-spec(zip_list_dir(ZipHandle) -> Result | {error, Reason} when + Result :: [zip_comment() | zip_file()], + ZipHandle :: pid(), + Reason :: term()). + zip_list_dir(Pid) when is_pid(Pid) -> request(self(), Pid, list_dir). diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index 4b59cee99e..0bcf3c5b71 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1199,7 +1199,7 @@ local_func(F, As0, Bs0) when is_atom(F) -> lfh_value_extra() -> %% Not documented. - {value, fun(F, As) -> local_func_value(F, As) end, []}. + {value, fun(F, As, a1, a2) -> local_func_value(F, As) end, [a1, a2]}. lfh_value() -> {value, fun(F, As) -> local_func_value(F, As) end}. diff --git a/lib/stdlib/test/erl_pp_SUITE.erl b/lib/stdlib/test/erl_pp_SUITE.erl index bc811355ab..280c95b1aa 100644 --- a/lib/stdlib/test/erl_pp_SUITE.erl +++ b/lib/stdlib/test/erl_pp_SUITE.erl @@ -1161,7 +1161,7 @@ parse_forms2(String, Cont0, Line, Forms) -> {done, {ok, Tokens, EndLine}, Chars} -> {ok, Form} = erl_parse:parse_form(Tokens), parse_forms2(Chars, [], EndLine, [Form | Forms]); - {more, Cont} when element(3, Cont) =:= [] -> + {more, Cont} when element(4, Cont) =:= [] -> %% extra spaces after forms... parse_forms2([], Cont, Line, Forms); {more, Cont} -> diff --git a/lib/stdlib/test/sofs_SUITE.erl b/lib/stdlib/test/sofs_SUITE.erl index 01de1f0600..d6f88a655e 100644 --- a/lib/stdlib/test/sofs_SUITE.erl +++ b/lib/stdlib/test/sofs_SUITE.erl @@ -1602,19 +1602,15 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a},b}])}, ER)), ?line {'EXIT', {badarg, _}} = (catch relative_product({}, ER)), - ?line eval(relative_product({relation([{a,b}])}, - from_term([],[{{atom},atom}])), - ER), - ?line eval(relative_product({relation([{a,b}]),relation([{a,1}])}, - from_term([{{b,1},{tjo,hej,sa}}])), - from_term([{a,{tjo,hej,sa}}])), - ?line eval(relative_product({relation([{a,b}]), ER}, - from_term([{{a,b},b}])), - ER), - ?line eval(relative_product({relation([{a,b},{c,a}]), - relation([{a,1},{a,2}])}, - from_term([{{b,1},b1},{{b,2},b2}])), - relation([{a,b1},{a,b2}])), + ?line relprod2({relation([{a,b}])}, from_term([],[{{atom},atom}]), ER), + ?line relprod2({relation([{a,b}]),relation([{a,1}])}, + from_term([{{b,1},{tjo,hej,sa}}]), + from_term([{a,{tjo,hej,sa}}])), + ?line relprod2({relation([{a,b}]), ER}, from_term([{{a,b},b}]), ER), + ?line relprod2({relation([{a,b},{c,a}]), + relation([{a,1},{a,2}])}, + from_term([{{b,1},b1},{{b,2},b2}]), + relation([{a,b1},{a,b2}])), ?line eval(relative_product({relation([{a,b}]), ER}), from_term([],[{atom,{atom,atom}}])), ?line eval(relative_product({from_term([{{a,[a,b]},[a]}]), @@ -1622,6 +1618,11 @@ relative_product_2(Conf) when is_list(Conf) -> from_term([{{a,[a,b]},{[a],[[a,b]]}}])), ok. +relprod2(A1T, A2, R) -> + %% A tuple as first argument is the old interface: + eval(relative_product(A1T, A2), R), + eval(relative_product(tuple_to_list(A1T), A2), R). + product_1(suite) -> []; product_1(doc) -> [""]; product_1(Conf) when is_list(Conf) -> -- cgit v1.2.3 From 99033bc070be6325ccbbb0a3e7e69396c8a30ec9 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 23 Mar 2011 16:07:03 +0100 Subject: Never fail when stopping rb, and fix file descriptor leak rb:stop did sometimes return {error,running}. This came from supervisor:delete_child and happened when the rb_server has not yet terminated when this function was called. Instead of having a separate gen_server call to rb_server for stopping the process, supervisor:terminate_child is now called. This is a synchronous function - i.e. it waits for the process to actually terminate before it returns. A file descriptor leak in rb:scan_files is corrected. The index file was never closed after reading. --- lib/sasl/src/rb.erl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl index 13753565d8..500c795721 100644 --- a/lib/sasl/src/rb.erl +++ b/lib/sasl/src/rb.erl @@ -53,7 +53,7 @@ start_link(Options) -> gen_server:start_link({local, rb_server}, rb, Options, []). stop() -> - call(stop), + supervisor:terminate_child(sasl_sup, rb_server), supervisor:delete_child(sasl_sup, rb_server). rescan() -> rescan([]). @@ -205,8 +205,6 @@ handle_call({rescan, Options}, _From, State) -> NewState = State#state{data = Data, max = Max, type = Type, device = Device, abort = Abort, log = Log1}, {reply, ok, NewState}; -handle_call(stop, _From, State) -> - {stop, normal, stopped, State}; handle_call(_, _From, #state{data = undefined}) -> {reply, {error, no_data}, #state{}}; handle_call({list, Type}, _From, State) -> @@ -312,11 +310,14 @@ scan_files(RptDir, Max, Type) -> {ok, Fd} -> case catch file:read(Fd, 1) of {ok, [LastWritten]} -> + file:close(Fd), Files = make_file_list(RptDir, LastWritten), scan_files(RptDir, Files, Max, Type); - _ -> exit("cannot read the index file") + _X -> + file:close(Fd), + exit("cannot read the index file") end; - _ -> exit("cannot read the index file") + _X -> exit("cannot read the index file") end. make_file_list(Dir, FirstFileNo) -> -- cgit v1.2.3 From de5377beb78d2fc0f0bea964fb9244722492a448 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 1 Apr 2011 16:36:08 +0200 Subject: Don't attempt to do supervisor:delete_child for temporary child After a bug fix supervisor does no longer save childspecs for temporary children. Due to this, all calls to supervisor:delete_child will fail for temporary children. rb:stop is therefore now rewritten to only do supervisor:terminate_child. --- lib/sasl/src/rb.erl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl index 500c795721..5fdb0b1695 100644 --- a/lib/sasl/src/rb.erl +++ b/lib/sasl/src/rb.erl @@ -53,8 +53,7 @@ start_link(Options) -> gen_server:start_link({local, rb_server}, rb, Options, []). stop() -> - supervisor:terminate_child(sasl_sup, rb_server), - supervisor:delete_child(sasl_sup, rb_server). + supervisor:terminate_child(sasl_sup, rb_server). rescan() -> rescan([]). rescan(Options) -> -- cgit v1.2.3 From b1dd3337729aa9340dba45c194c1896944262376 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Fri, 13 May 2011 10:13:29 +0200 Subject: Bugfix in rb:filter when using 're' (regexp) and 'no' There was an inconsistency in the filter function, as filter([{Key,Regexp,re,no}]) did not work in the same way as filter([{Key,Value,no}]) The first filter only returned 'proplist' reports, while the second returned *all* reports that didn't match the Value. This has been corrected so both filters now return all reports that don't match. --- lib/sasl/src/rb.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sasl/src/rb.erl b/lib/sasl/src/rb.erl index 5fdb0b1695..8004ef2c5a 100644 --- a/lib/sasl/src/rb.erl +++ b/lib/sasl/src/rb.erl @@ -789,7 +789,7 @@ filter_report([{Key, RegExp, re}|T], Msg) -> filter_report([{Key, RegExp, re, no}|T], Msg) -> case proplists:get_value(Key, Msg) of undefined -> - false; + true; Value -> Subject = lists:flatten(io_lib:format("~p",[Value])), case run_re(Subject, RegExp) of -- cgit v1.2.3 From e03ed7ed525f04352d7b52cde54cf7f9b42595ce Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Fri, 13 May 2011 11:23:15 +0200 Subject: Ssl sometimes fails when reusing a session Invalidation of a session for reusing should first flag that the session may no longer be reused and then later when all possible pending reuses have been handled delete the session from the database. This could otherwise cause the client to terminate due to {badarg,[{erlang,byte_size,[undefined]}, and the server to terminate due to {{badmatch,{resumed,undefined}}. --- lib/ssl/src/ssl_manager.erl | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index f845b1ecc0..5a2d0c9496 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.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 @@ -57,6 +57,7 @@ -define('24H_in_sec', 8640). -define(SESSION_VALIDATION_INTERVAL, 60000). -define(CERTIFICATE_CACHE_CLEANUP, 30000). +-define(CLEAN_SESSION_DB, 60000). %%==================================================================== %% API @@ -70,7 +71,8 @@ start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []). %%-------------------------------------------------------------------- --spec connection_init(string()| {der, list()}, client | server) -> {ok, reference(), cache_ref()}. +-spec connection_init(string()| {der, list()}, client | server) -> + {ok, reference(), cache_ref()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- @@ -101,7 +103,9 @@ lookup_trusted_cert(Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- -spec issuer_candidate(cert_key() | no_candidate) -> - {cert_key(), {der_cert(), #'OTPCertificate'{}}} | no_more_candidates. + {cert_key(), + {der_cert(), + #'OTPCertificate'{}}} | no_more_candidates. %% %% Description: Return next issuer candidate. %%-------------------------------------------------------------------- @@ -117,7 +121,8 @@ client_session_id(Host, Port, SslOpts, OwnCert) -> call({client_session_id, Host, Port, SslOpts, OwnCert}). %%-------------------------------------------------------------------- --spec server_session_id(host(), port_num(), #ssl_options{}, der_cert()) -> session_id(). +-spec server_session_id(host(), port_num(), #ssl_options{}, + der_cert()) -> session_id(). %% %% Description: Select a session id for the server. %%-------------------------------------------------------------------- @@ -139,7 +144,9 @@ register_session(Port, Session) -> -spec invalidate_session(port_num(), #session{}) -> ok. -spec invalidate_session(host(), port_num(), #session{}) -> ok. %% -%% Description: Make the session unavilable for reuse. +%% Description: Make the session unavailable for reuse. After +%% a the session has been marked "is_resumable = false" for some while +%% it will be safe to remove the data from the session database. %%-------------------------------------------------------------------- invalidate_session(Host, Port, Session) -> cast({invalidate_session, Host, Port, Session}). @@ -259,23 +266,26 @@ handle_cast({register_session, Port, Session}, {noreply, State}; handle_cast({invalidate_session, Host, Port, - #session{session_id = ID}}, + #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - CacheCb:delete(Cache, {{Host, Port}, ID}), + CacheCb:update(Cache, {{Host, Port}, ID}, Session#session{is_resumable = false}), + timer:apply_after(?CLEAN_SESSION_DB, CacheCb, delete, {{Host, Port}, ID}), {noreply, State}; -handle_cast({invalidate_session, Port, #session{session_id = ID}}, +handle_cast({invalidate_session, Port, #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - CacheCb:delete(Cache, {Port, ID}), + CacheCb:update(Cache, {Port, ID}, Session#session{is_resumable = false}), + timer:apply_after(?CLEAN_SESSION_DB, CacheCb, delete, {Port, ID}), {noreply, State}; handle_cast({recache_pem, File, LastWrite, Pid, From}, #state{certificate_db = [_, FileToRefDb, _]} = State0) -> case ssl_certificate_db:lookup(File, FileToRefDb) of undefined -> - {reply, Msg, State} = handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), + {reply, Msg, State} = + handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), gen_server:reply(From, Msg), {noreply, State}; _ -> %% Send message to self letting cleanup messages be handled -- cgit v1.2.3 From 33eba903b4bd0b18e8f8d9afacd3118c00557b39 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 13 May 2011 12:11:20 +0200 Subject: Mend --with-ssl= in erts/configure --- erts/configure.in | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/erts/configure.in b/erts/configure.in index 3cd6f0b6bf..13bc86b14e 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3882,8 +3882,32 @@ dnl so it is - be adoptable AC_MSG_ERROR(Invalid path to option --with-ssl=PATH) fi SSL_ROOT="$with_ssl" + SSL_CRYPTO_LIBNAME=crypto + SSL_SSL_LIBNAME=ssl if test "x$MIXED_CYGWIN" = "xyes" && test -d "$with_ssl/lib/VC"; then - SSL_LIBDIR="$with_ssl/lib/VC" + if test -f "$with_ssl/lib/VC/libeay32.lib"; then + SSL_LIBDIR="$with_ssl/lib/VC" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_SSL_LIBNAME=ssleay32 + elif test -f "$with_ssl/lib/VC/openssl.lib"; then + SSL_LIBDIR="$with_ssl/lib/VC" + elif test -f $with_ssl/lib/VC/libeay32MD.lib; then + SSL_CRYPTO_LIBNAME=libeay32MD + SSL_SSL_LIBNAME=ssleay32MD + if test "x$enable_dynamic_ssl" = "xno" && \ + test -f $with_ssl/lib/VC/static/libeay32MD.lib; then + SSL_LIBDIR="$with_ssl/lib/VC/static" + else + SSL_LIBDIR="$with_ssl/lib/VC" + fi + elif test -f "$with_ssl/lib/libeay32.lib"; then + SSL_LIBDIR="$with_ssl/lib" + SSL_CRYPTO_LIBNAME=libeay32 + SSL_CRYPTO_LIBNAME=ssleay32 + else + # This probably wont work, but that's what the user said, so... + SSL_LIBDIR="$with_ssl/lib" + fi elif test "x$ac_cv_sizeof_void_p" = "x8"; then if test -f "$with_ssl/lib64/libcrypto.a"; then SSL_LIBDIR="$with_ssl/lib64" -- cgit v1.2.3 From 498a1d56be241458c85231b5f9da43f4eac0b033 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 14:26:44 +0200 Subject: Add needed barriers for write_concurrency tables Ets tables using the write_concurrency option could potentially get into an internally inconsistent state. --- erts/emulator/beam/erl_db_hash.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 9ef990cc4f..9092fa8785 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -105,7 +105,7 @@ #define NSEG_2 256 /* Size of second segment table */ #define NSEG_INC 128 /* Number of segments to grow after that */ -#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read(&(tb)->segtab)) +#define SEGTAB(tb) ((struct segment**)erts_smp_atomic_read_acqb(&(tb)->segtab)) #define NACTIVE(tb) ((int)erts_smp_atomic_read(&(tb)->nactive)) #define NITEMS(tb) ((int)erts_smp_atomic_read(&(tb)->common.nitems)) @@ -122,8 +122,8 @@ */ static ERTS_INLINE Uint hash_to_ix(DbTableHash* tb, HashValue hval) { - Uint mask = erts_smp_atomic_read(&tb->szm); - Uint ix = hval & mask; + Uint mask = erts_smp_atomic_read_acqb(&tb->szm); + Uint ix = hval & mask; if (ix >= erts_smp_atomic_read(&tb->nactive)) { ix &= mask>>1; ASSERT(ix < erts_smp_atomic_read(&tb->nactive)); @@ -668,6 +668,7 @@ int db_create_hash(Process *p, DbTable *tbl) else { /* coarse locking */ tb->locks = NULL; } + ERTS_THR_MEMORY_BARRIER; #endif /* ERST_SMP */ return DB_ERROR_NONE; } @@ -2342,7 +2343,7 @@ static int alloc_seg(DbTableHash *tb) struct ext_segment* eseg; eseg = (struct ext_segment*) SEGTAB(tb)[seg_ix-1]; MY_ASSERT(eseg!=NULL && eseg->s.is_ext_segment); - erts_smp_atomic_set(&tb->segtab, (erts_aint_t) eseg->segtab); + erts_smp_atomic_set_relb(&tb->segtab, (erts_aint_t) eseg->segtab); tb->nsegs = eseg->nsegs; } ASSERT(seg_ix < tb->nsegs); @@ -2414,7 +2415,7 @@ static int free_seg(DbTableHash *tb, int free_records) MY_ASSERT(newtop->s.is_ext_segment); if (newtop->prev_segtab != NULL) { /* Time to use a smaller segtab */ - erts_smp_atomic_set(&tb->segtab, (erts_aint_t)newtop->prev_segtab); + erts_smp_atomic_set_relb(&tb->segtab, (erts_aint_t)newtop->prev_segtab); tb->nsegs = seg_ix; ASSERT(tb->nsegs == EXTSEG(SEGTAB(tb))->nsegs); } @@ -2431,7 +2432,7 @@ static int free_seg(DbTableHash *tb, int free_records) if (seg_ix > 0) { if (seg_ix < tb->nsegs) SEGTAB(tb)[seg_ix] = NULL; } else { - erts_smp_atomic_set(&tb->segtab, (erts_aint_t)NULL); + erts_smp_atomic_set_relb(&tb->segtab, (erts_aint_t)NULL); } #endif tb->nslots -= SEGSZ; @@ -2526,9 +2527,9 @@ static void grow(DbTableHash* tb, int nactive) } erts_smp_atomic_inc(&tb->nactive); if (from_ix == 0) { - erts_smp_atomic_set(&tb->szm, szm); + erts_smp_atomic_set_relb(&tb->szm, szm); } - erts_smp_atomic_set(&tb->is_resizing, 0); + erts_smp_atomic_set_relb(&tb->is_resizing, 0); /* Finally, let's split the bucket. We try to do it in a smart way to keep link order and avoid unnecessary updates of next-pointers */ @@ -2560,7 +2561,7 @@ static void grow(DbTableHash* tb, int nactive) return; abort: - erts_smp_atomic_set(&tb->is_resizing, 0); + erts_smp_atomic_set_relb(&tb->is_resizing, 0); } @@ -2604,7 +2605,7 @@ static void shrink(DbTableHash* tb, int nactive) erts_smp_atomic_set(&tb->nactive, src_ix); if (dst_ix == 0) { - erts_smp_atomic_set(&tb->szm, low_szm); + erts_smp_atomic_set_relb(&tb->szm, low_szm); } WUNLOCK_HASH(lck); @@ -2618,7 +2619,7 @@ static void shrink(DbTableHash* tb, int nactive) } /*else already done */ - erts_smp_atomic_set(&tb->is_resizing, 0); + erts_smp_atomic_set_relb(&tb->is_resizing, 0); } -- cgit v1.2.3 From 434cab885e50dd72f3c4f87f30d9ee21085cc657 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 20:12:02 +0200 Subject: Ensure that stack updates are seen when stack is released Ets tables using ordered_set could potentially get into an internally inconsistent state. --- erts/emulator/beam/erl_db_tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c index a59c0c258d..d5b02584a5 100644 --- a/erts/emulator/beam/erl_db_tree.c +++ b/erts/emulator/beam/erl_db_tree.c @@ -111,7 +111,7 @@ static void release_stack(DbTableTree* tb, DbTreeStack* stack) { if (stack == &tb->static_stack) { ASSERT(erts_smp_atomic_read(&tb->is_stack_busy) == 1); - erts_smp_atomic_set(&tb->is_stack_busy, 0); + erts_smp_atomic_set_relb(&tb->is_stack_busy, 0); } else { erts_db_free(ERTS_ALC_T_DB_STK, (DbTable *) tb, -- cgit v1.2.3 From 673f5d1bf684f15ac5526d8a21552eca9a0c9052 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 20:25:53 +0200 Subject: Ensure that all rehashing information are seen when done This is not a bugfix. The change is done in order to avoid a future bug. --- erts/emulator/beam/safe_hash.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erts/emulator/beam/safe_hash.c b/erts/emulator/beam/safe_hash.c index 21d6ce9304..3e9243c77d 100644 --- a/erts/emulator/beam/safe_hash.c +++ b/erts/emulator/beam/safe_hash.c @@ -99,7 +99,7 @@ static void rehash(SafeHash* h, int grow_limit) erts_free(h->type, (void *) old_tab); } /*else already done */ - erts_smp_atomic_set(&h->is_rehashing, 0); + erts_smp_atomic_set_relb(&h->is_rehashing, 0); } -- cgit v1.2.3 From 425e282be62f8205b1ba262b112f38688b421c49 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 20:43:11 +0200 Subject: Ensure quick break Make sure that we don't have to wait in poll before break handling is done. --- erts/emulator/sys/common/erl_check_io.c | 5 +++++ erts/emulator/sys/win32/erl_poll.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index 218bd79584..71b374527e 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1137,6 +1137,11 @@ ERTS_CIO_EXPORT(erts_check_io)(int do_wait) restart: +#ifdef ERTS_BREAK_REQUESTED + if (ERTS_BREAK_REQUESTED) + erts_do_break_handling(); +#endif + /* Figure out timeout value */ if (do_wait) { erts_time_remaining(&wait_time); diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 7662f190ef..074e2e247f 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -693,6 +693,7 @@ static void *break_waiter(void *param) ResetEvent(harr[0]); erts_mtx_lock(&break_waiter_lock); erts_atomic32_set(&break_waiter_state,BREAK_WAITER_GOT_BREAK); + ERTS_THR_MEMORY_BARRIER; SetEvent(break_happened_event); erts_mtx_unlock(&break_waiter_lock); break; @@ -700,6 +701,7 @@ static void *break_waiter(void *param) ResetEvent(harr[1]); erts_mtx_lock(&break_waiter_lock); erts_atomic32_set(&break_waiter_state,BREAK_WAITER_GOT_HALT); + ERTS_THR_MEMORY_BARRIER; SetEvent(break_happened_event); erts_mtx_unlock(&break_waiter_lock); break; -- cgit v1.2.3 From 3da283a85f73132e73ab911154c2c0ff8797d61d Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 20:59:36 +0200 Subject: Remove pointless erts_ports_alive variable --- erts/emulator/beam/bif.c | 22 ++++++++++------------ erts/emulator/beam/erl_port_task.c | 3 --- erts/emulator/beam/global.h | 1 - erts/emulator/beam/io.c | 3 --- 4 files changed, 10 insertions(+), 19 deletions(-) diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index b3325d635b..fe8dcc4d8b 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3270,6 +3270,7 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) Eterm* dead_ports; int alive, dead; Uint32 next_ss; + int i; /* To get a consistent snapshot... * We add alive ports from start of the buffer @@ -3283,19 +3284,16 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) next_ss = erts_smp_atomic_inctest(&erts_ports_snapshot); - if (erts_smp_atomic_read(&erts_ports_alive) > 0) { - erts_aint_t i; - for (i = erts_max_ports-1; i >= 0; i--) { - Port* prt = &erts_port[i]; - erts_smp_port_state_lock(prt); - if (!(prt->status & ERTS_PORT_SFLGS_DEAD) - && prt->snapshot != next_ss) { - ASSERT(prt->snapshot == next_ss - 1); - *pp++ = prt->id; - prt->snapshot = next_ss; /* Consumed by this snapshot */ - } - erts_smp_port_state_unlock(prt); + for (i = erts_max_ports-1; i >= 0; i--) { + Port* prt = &erts_port[i]; + erts_smp_port_state_lock(prt); + if (!(prt->status & ERTS_PORT_SFLGS_DEAD) + && prt->snapshot != next_ss) { + ASSERT(prt->snapshot == next_ss - 1); + *pp++ = prt->id; + prt->snapshot = next_ss; /* Consumed by this snapshot */ } + erts_smp_port_state_unlock(prt); } dead_ports = (Eterm*)erts_smp_atomic_xchg(&erts_dead_ports_ptr, diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 1b07024ca1..326021643f 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -658,8 +658,6 @@ erts_port_task_free_port(Port *pp) when scheduled out... */ ErtsPortTask *ptp = port_task_alloc(); erts_smp_port_state_lock(pp); - ASSERT(erts_smp_atomic_read(&erts_ports_alive) > 0); - erts_smp_atomic_dec(&erts_ports_alive); pp->status &= ~ERTS_PORT_SFLG_CLOSING; pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED; erts_may_save_closed_port(pp); @@ -681,7 +679,6 @@ erts_port_task_free_port(Port *pp) port_is_dequeued = 1; } erts_smp_port_state_lock(pp); - erts_smp_atomic_dec(&erts_ports_alive); pp->status &= ~ERTS_PORT_SFLG_CLOSING; pp->status |= ERTS_PORT_SFLG_FREE_SCHEDULED; erts_may_save_closed_port(pp); diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 96da894d90..18dc9f75d5 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -527,7 +527,6 @@ union erl_off_heap_ptr { /* arrays that get malloced at startup */ extern Port* erts_port; -extern erts_smp_atomic_t erts_ports_alive; extern Uint erts_max_ports; extern Uint erts_port_tab_index_mask; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index f619c6f88b..86f550310c 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -56,7 +56,6 @@ static erts_smp_tsd_key_t driver_list_last_error_key; /* Save last DDLL error o per thread basis (for BC interfaces) */ Port* erts_port; /* The port table */ -erts_smp_atomic_t erts_ports_alive; erts_smp_atomic_t erts_bytes_out; /* No bytes sent out of the system */ erts_smp_atomic_t erts_bytes_in; /* No bytes gotten into the system */ @@ -421,7 +420,6 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, new_name = (char*) erts_alloc(ERTS_ALC_T_PORT_NAME, sys_strlen(name)+1); sys_strcpy(new_name, name); erts_smp_runq_lock(runq); - erts_smp_atomic_inc(&erts_ports_alive); erts_smp_port_state_lock(prt); prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus; prt->snapshot = (Uint32) erts_smp_atomic_read(&erts_ports_snapshot); @@ -1274,7 +1272,6 @@ void init_io(void) erts_smp_atomic_init(&erts_bytes_out, 0); erts_smp_atomic_init(&erts_bytes_in, 0); - erts_smp_atomic_init(&erts_ports_alive, 0); for (i = 0; i < erts_max_ports; i++) { erts_port_task_init_sched(&erts_port[i].sched); -- cgit v1.2.3 From 78ebe8aa3754fc8837ab3a6b0bc11d1e78275eef Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 21:12:05 +0200 Subject: Use 32-bit atomic for port snapshot --- erts/emulator/beam/bif.c | 2 +- erts/emulator/beam/global.h | 8 ++++---- erts/emulator/beam/io.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index fe8dcc4d8b..fe21e30d82 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -3282,7 +3282,7 @@ BIF_RETTYPE ports_0(BIF_ALIST_0) erts_smp_atomic_set(&erts_dead_ports_ptr, (erts_aint_t) (port_buf + erts_max_ports)); - next_ss = erts_smp_atomic_inctest(&erts_ports_snapshot); + next_ss = erts_smp_atomic32_inctest(&erts_ports_snapshot); for (i = erts_max_ports-1; i >= 0; i--) { Port* prt = &erts_port[i]; diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 18dc9f75d5..7dc5aa85e9 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -183,7 +183,7 @@ struct port { process to get (line oriented I/O)*/ Uint32 status; /* Status and type flags */ int control_flags; /* Flags for port_control() */ - Uint32 snapshot; /* Next snapshot that port should be part of */ + erts_aint32_t snapshot; /* Next snapshot that port should be part of */ struct reg_proc *reg; ErlDrvPDL port_data_lock; @@ -530,7 +530,7 @@ extern Port* erts_port; extern Uint erts_max_ports; extern Uint erts_port_tab_index_mask; -extern erts_smp_atomic_t erts_ports_snapshot; +extern erts_smp_atomic32_t erts_ports_snapshot; extern erts_smp_atomic_t erts_dead_ports_ptr; ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt); @@ -540,12 +540,12 @@ ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt); ERTS_GLB_INLINE void erts_may_save_closed_port(Port *prt) { ERTS_SMP_LC_ASSERT(erts_smp_lc_spinlock_is_locked(&prt->state_lck)); - if (prt->snapshot != erts_smp_atomic_read(&erts_ports_snapshot)) { + if (prt->snapshot != erts_smp_atomic32_read_acqb(&erts_ports_snapshot)) { /* Dead ports are added from the end of the snapshot buffer */ Eterm* tombstone = (Eterm*) erts_smp_atomic_addtest(&erts_dead_ports_ptr, -(erts_aint_t)sizeof(Eterm)); ASSERT(tombstone+1 != NULL); - ASSERT(prt->snapshot == (Uint32) erts_smp_atomic_read(&erts_ports_snapshot) - 1); + ASSERT(prt->snapshot == erts_smp_atomic32_read(&erts_ports_snapshot) - 1); *tombstone = prt->id; } /*else no ongoing snapshot or port was already included or created after snapshot */ diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 86f550310c..8c1126aa6e 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -189,7 +189,7 @@ typedef struct line_buf_context { static erts_smp_spinlock_t get_free_port_lck; static Uint last_port_num; static Uint port_num_mask; -erts_smp_atomic_t erts_ports_snapshot; /* Identifies the _next_ snapshot (not the ongoing) */ +erts_smp_atomic32_t erts_ports_snapshot; /* Identifies the _next_ snapshot (not the ongoing) */ static ERTS_INLINE void @@ -422,7 +422,7 @@ setup_port(Port* prt, Eterm pid, erts_driver_t *driver, erts_smp_runq_lock(runq); erts_smp_port_state_lock(prt); prt->status = ERTS_PORT_SFLG_CONNECTED | xstatus; - prt->snapshot = (Uint32) erts_smp_atomic_read(&erts_ports_snapshot); + prt->snapshot = erts_smp_atomic32_read(&erts_ports_snapshot); old_name = prt->name; prt->name = new_name; #ifdef ERTS_SMP @@ -1293,7 +1293,7 @@ void init_io(void) erts_port[i].port_data_lock = NULL; } - erts_smp_atomic_init(&erts_ports_snapshot, (erts_aint_t) 0); + erts_smp_atomic32_init(&erts_ports_snapshot, (erts_aint32_t) 0); last_port_num = 0; erts_smp_spinlock_init(&get_free_port_lck, "get_free_port"); -- cgit v1.2.3 From 150e88657f617d652d0ab5e4b45409edeb529c50 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 21:35:30 +0200 Subject: Reduce number of atomic ops Counters for active, and used schedulers have been coalesced in order to reduce the amount of atomic operations needed. Some currently not strictly necessary barriers have also been added in order to be future proof. --- erts/emulator/beam/erl_process.c | 135 +++++++++++++++++++++++++++++---------- 1 file changed, 100 insertions(+), 35 deletions(-) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 31f23d3978..559224be58 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -166,9 +166,8 @@ static struct { static struct { erts_smp_mtx_t update_mtx; - erts_smp_atomic32_t active_runqs; + erts_smp_atomic32_t no_runqs; int last_active_runqs; - erts_smp_atomic32_t used_runqs; int forced_check_balance; erts_smp_atomic32_t checking_balance; int halftime; @@ -965,7 +964,7 @@ sched_spin_wait(ErtsSchedulerSleepInfo *ssi, int spincount) erts_aint32_t flgs; do { - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if ((flgs & (ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING)) != (ERTS_SSI_FLG_SLEEPING|ERTS_SSI_FLG_WAITING)) { break; @@ -1114,7 +1113,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) else { erts_aint_t dt; - erts_smp_atomic32_set(&function_calls, 0); + erts_smp_atomic32_set_relb(&function_calls, 0); *fcalls = 0; sched_waiting_sys(esdp->no, rq); @@ -1147,7 +1146,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) nonblockable_aux_work(esdp, ssi, aux_work); #endif - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if (!(flgs & ERTS_SSI_FLG_WAITING)) { ASSERT(!(flgs & ERTS_SSI_FLG_SLEEPING)); goto sys_woken; @@ -1333,6 +1332,76 @@ wake_all_schedulers(void) } } +#define ERTS_NO_USED_RUNQS_SHIFT 16 +#define ERTS_NO_RUNQS_MASK 0xffff + +#if ERTS_MAX_NO_OF_SCHEDULERS > ERTS_NO_RUNQS_MASK +# error "Too large amount of schedulers allowed" +#endif + +static ERTS_INLINE void +init_no_runqs(int active, int used) +{ + erts_aint32_t no_runqs = (erts_aint32_t) (active & ERTS_NO_RUNQS_MASK); + no_runqs |= (erts_aint32_t) ((used & ERTS_NO_RUNQS_MASK) << ERTS_NO_USED_RUNQS_SHIFT); + erts_smp_atomic32_init(&balance_info.no_runqs, no_runqs); +} + +static ERTS_INLINE void +get_no_runqs(int *active, int *used) +{ + erts_aint32_t no_runqs = erts_smp_atomic32_read(&balance_info.no_runqs); + if (active) + *active = (int) (no_runqs & ERTS_NO_RUNQS_MASK); + if (used) + *used = (int) ((no_runqs >> ERTS_NO_USED_RUNQS_SHIFT) & ERTS_NO_RUNQS_MASK); +} + +static ERTS_INLINE void +set_no_used_runqs(int used) +{ + erts_aint32_t exp = erts_smp_atomic32_read(&balance_info.no_runqs); + while (1) { + erts_aint32_t act, new; + new = (used << ERTS_NO_USED_RUNQS_SHIFT) | (exp & ERTS_NO_RUNQS_MASK); + act = erts_smp_atomic32_cmpxchg(&balance_info.no_runqs, new, exp); + if (act == exp) + break; + exp = act; + } +} + +static ERTS_INLINE void +set_no_active_runqs(int active) +{ + erts_aint32_t exp = erts_smp_atomic32_read(&balance_info.no_runqs); + while (1) { + erts_aint32_t act, new; + new = (exp & (ERTS_NO_RUNQS_MASK << ERTS_NO_USED_RUNQS_SHIFT)) | active; + act = erts_smp_atomic32_cmpxchg(&balance_info.no_runqs, new, exp); + if (act == exp) + break; + exp = act; + } +} + +static ERTS_INLINE int +try_inc_no_active_runqs(int active) +{ + erts_aint32_t exp = erts_smp_atomic32_read(&balance_info.no_runqs); + if (((exp >> ERTS_NO_USED_RUNQS_SHIFT) & ERTS_NO_RUNQS_MASK) < active) + return 0; + if ((exp & ERTS_NO_RUNQS_MASK) + 1 == active) { + erts_aint32_t new, act; + new = (exp & ~ERTS_NO_RUNQS_MASK) | active; + act = erts_smp_atomic32_cmpxchg(&balance_info.no_runqs, new, exp); + if (act == exp) + return 1; + } + return 0; +} + + static ERTS_INLINE int chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) { @@ -1344,9 +1413,7 @@ chk_wake_sched(ErtsRunQueue *crq, int ix, int activate) iflgs = erts_smp_atomic32_read(&wrq->info_flags); if (!(iflgs & (ERTS_RUNQ_IFLG_SUSPENDED|ERTS_RUNQ_IFLG_NONEMPTY))) { if (activate) { - if (ix == erts_smp_atomic32_cmpxchg(&balance_info.active_runqs, - ix+1, - ix)) { + if (try_inc_no_active_runqs(ix+1)) { erts_smp_xrunq_lock(crq, wrq); wrq->flags &= ~ERTS_RUNQ_FLG_INACTIVE; erts_smp_xrunq_unlock(crq, wrq); @@ -1363,8 +1430,9 @@ wake_scheduler_on_empty_runq(ErtsRunQueue *crq) { int ix = crq->ix; int stop_ix = ix; - int active_ix = erts_smp_atomic32_read(&balance_info.active_runqs); - int balance_ix = erts_smp_atomic32_read(&balance_info.used_runqs); + int active_ix, balance_ix; + + get_no_runqs(&active_ix, &balance_ix); if (active_ix > balance_ix) active_ix = balance_ix; @@ -1416,7 +1484,7 @@ erts_sched_notify_check_cpu_bind(void) int ix; if (erts_common_run_queue) { for (ix = 0; ix < erts_no_schedulers; ix++) - erts_smp_atomic32_set(&ERTS_SCHEDULER_IX(ix)->chk_cpu_bind, 1); + erts_smp_atomic32_set_relb(&ERTS_SCHEDULER_IX(ix)->chk_cpu_bind, 1); wake_all_schedulers(); } else { @@ -1871,8 +1939,7 @@ try_steal_task(ErtsRunQueue *rq) ERTS_SMP_LC_CHK_RUNQ_LOCK(rq, rq_locked); - active_rqs = erts_smp_atomic32_read(&balance_info.active_runqs); - blnc_rqs = erts_smp_atomic32_read(&balance_info.used_runqs); + get_no_runqs(&active_rqs, &blnc_rqs); if (active_rqs > blnc_rqs) active_rqs = blnc_rqs; @@ -1883,7 +1950,7 @@ try_steal_task(ErtsRunQueue *rq) if (active_rqs < blnc_rqs) { int no = blnc_rqs - active_rqs; int stop_ix = vix = active_rqs + rq->ix % no; - while (erts_smp_atomic32_read(&no_empty_run_queues) < blnc_rqs) { + while (erts_smp_atomic32_read_acqb(&no_empty_run_queues) < blnc_rqs) { res = check_possible_steal_victim(rq, &rq_locked, vix); if (res) goto done; @@ -1898,7 +1965,7 @@ try_steal_task(ErtsRunQueue *rq) vix = rq->ix; /* ... then try to steal a job from another active queue... */ - while (erts_smp_atomic32_read(&no_empty_run_queues) < blnc_rqs) { + while (erts_smp_atomic32_read_acqb(&no_empty_run_queues) < blnc_rqs) { vix++; if (vix >= active_rqs) vix = 0; @@ -1999,7 +2066,7 @@ check_balance(ErtsRunQueue *c_rq) return; } - blnc_no_rqs = (int) erts_smp_atomic32_read(&balance_info.used_runqs); + get_no_runqs(NULL, &blnc_no_rqs); if (blnc_no_rqs == 1) { c_rq->check_balance_reds = INT_MAX; erts_smp_atomic32_set(&balance_info.checking_balance, 0); @@ -2038,7 +2105,8 @@ check_balance(ErtsRunQueue *c_rq) forced = balance_info.forced_check_balance; balance_info.forced_check_balance = 0; - blnc_no_rqs = (int) erts_smp_atomic32_read(&balance_info.used_runqs); + get_no_runqs(¤t_active, &blnc_no_rqs); + if (blnc_no_rqs == 1) { erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_runq_lock(c_rq); @@ -2052,8 +2120,6 @@ check_balance(ErtsRunQueue *c_rq) if (balance_info.full_reds_history_index >= ERTS_FULL_REDS_HISTORY_SIZE) balance_info.full_reds_history_index = 0; - current_active = erts_smp_atomic32_read(&balance_info.active_runqs); - /* Read balance information for all run queues */ for (qix = 0; qix < blnc_no_rqs; qix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(qix); @@ -2387,7 +2453,7 @@ erts_fprintf(stderr, "--------------------------------\n"); } balance_info.last_active_runqs = active; - erts_smp_atomic32_set(&balance_info.active_runqs, active); + set_no_active_runqs(active); balance_info.halftime = 1; erts_smp_atomic32_set(&balance_info.checking_balance, 0); @@ -2695,9 +2761,8 @@ erts_init_scheduling(int mrq, int no_schedulers, int no_schedulers_online) erts_smp_atomic32_init(&schdlr_sspnd.msb.ongoing, 0); erts_smp_atomic32_init(&schdlr_sspnd.active, no_schedulers); schdlr_sspnd.msb.procs = NULL; - erts_smp_atomic32_set(&balance_info.used_runqs, - erts_common_run_queue ? 1 : no_schedulers_online); - erts_smp_atomic32_init(&balance_info.active_runqs, no_schedulers); + init_no_runqs(no_schedulers, + erts_common_run_queue ? 1 : no_schedulers_online); balance_info.last_active_runqs = no_schedulers; erts_smp_mtx_init(&balance_info.update_mtx, "migration_info_update"); balance_info.forced_check_balance = 0; @@ -2939,7 +3004,7 @@ sched_spin_suspended(ErtsSchedulerSleepInfo *ssi, int spincount) erts_aint32_t flgs; do { - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if ((flgs & (ERTS_SSI_FLG_SLEEPING | ERTS_SSI_FLG_WAITING | ERTS_SSI_FLG_SUSPENDED)) @@ -3068,7 +3133,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) wake = 0; } - flgs = erts_smp_atomic32_read(&ssi->flags); + flgs = erts_smp_atomic32_read_acqb(&ssi->flags); if (!(flgs & ERTS_SSI_FLG_SUSPENDED)) break; erts_smp_mtx_unlock(&schdlr_sspnd.mtx); @@ -3292,7 +3357,7 @@ erts_set_schedulers_online(Process *p, ErtsRunQueue *to_rq = ERTS_RUNQ_IX(ix % no); evacuate_run_queue(from_rq, to_rq); } - erts_smp_atomic32_set(&balance_info.used_runqs, no); + set_no_used_runqs(no); erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_mtx_lock(&schdlr_sspnd.mtx); } @@ -3346,7 +3411,7 @@ erts_set_schedulers_online(Process *p, for (ix = erts_no_run_queues-1; ix >= no; ix--) evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(ix % no)); - erts_smp_atomic32_set(&balance_info.used_runqs, no); + set_no_used_runqs(no); erts_smp_mtx_unlock(&balance_info.update_mtx); erts_smp_mtx_lock(&schdlr_sspnd.mtx); for (ix = no; ix < online; ix++) { @@ -3443,7 +3508,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) else { erts_smp_mtx_unlock(&schdlr_sspnd.mtx); erts_smp_mtx_lock(&balance_info.update_mtx); - erts_smp_atomic32_set(&balance_info.used_runqs, 1); + set_no_used_runqs(1); for (ix = 0; ix < online; ix++) { ErtsRunQueue *rq = ERTS_RUNQ_IX(ix); erts_smp_runq_lock(rq); @@ -3580,7 +3645,7 @@ erts_block_multi_scheduling(Process *p, ErtsProcLocks plocks, int on, int all) evacuate_run_queue(ERTS_RUNQ_IX(ix), ERTS_RUNQ_IX(ix % online)); - erts_smp_atomic32_set(&balance_info.used_runqs, online); + set_no_used_runqs(online); /* Make sure that we balance soon... */ balance_info.forced_check_balance = 1; erts_smp_runq_lock(ERTS_RUNQ_IX(0)); @@ -5097,7 +5162,7 @@ Process *schedule(Process *p, int calls) esdp = erts_get_scheduler_data(); rq = erts_get_runq_current(esdp); ASSERT(esdp); - fcalls = (int) erts_smp_atomic32_read(&function_calls); + fcalls = (int) erts_smp_atomic32_read_acqb(&function_calls); actual_reds = reds = 0; erts_smp_runq_lock(rq); } else { @@ -5248,14 +5313,14 @@ Process *schedule(Process *p, int calls) | ERTS_RUNQ_FLG_CHK_CPU_BIND | ERTS_RUNQ_FLG_SUSPENDED)) { if ((rq->flags & ERTS_RUNQ_FLG_SUSPENDED) - || (erts_smp_atomic32_read(&esdp->ssi->flags) + || (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED)) { ASSERT(erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED); suspend_scheduler(esdp); } if ((rq->flags & ERTS_RUNQ_FLG_CHK_CPU_BIND) - || erts_smp_atomic32_read(&esdp->chk_cpu_bind)) { + || erts_smp_atomic32_read_acqb(&esdp->chk_cpu_bind)) { erts_sched_check_cpu_bind(esdp); } } @@ -5306,7 +5371,7 @@ Process *schedule(Process *p, int calls) if (rq->flags & (ERTS_RUNQ_FLG_SHARED_RUNQ | ERTS_RUNQ_FLG_SUSPENDED)) { if ((rq->flags & ERTS_RUNQ_FLG_SUSPENDED) - || (erts_smp_atomic32_read(&esdp->ssi->flags) + || (erts_smp_atomic32_read_acqb(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED)) { ASSERT(erts_smp_atomic32_read(&esdp->ssi->flags) & ERTS_SSI_FLG_SUSPENDED); @@ -5350,7 +5415,7 @@ Process *schedule(Process *p, int calls) * Schedule system-level activities. */ - erts_smp_atomic32_set(&function_calls, 0); + erts_smp_atomic32_set_relb(&function_calls, 0); fcalls = 0; ASSERT(!erts_port_task_have_outstanding_io_tasks()); @@ -5392,7 +5457,7 @@ Process *schedule(Process *p, int calls) if (erts_common_run_queue->waiting) wake_scheduler(erts_common_run_queue, 0, 1); } - else if (erts_smp_atomic32_read(&no_empty_run_queues) != 0) { + else if (erts_smp_atomic32_read_acqb(&no_empty_run_queues) != 0) { wake_scheduler_on_empty_runq(rq); rq->wakeup_other = 0; } -- cgit v1.2.3 From 6af0f286a3a46a7e2faf722306e7be57bf3de687 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 21:11:41 +0200 Subject: Fix build with hipe on amd64 --- erts/emulator/beam/atom.names | 1 + 1 file changed, 1 insertion(+) diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 327620772f..de76cb9680 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -76,6 +76,7 @@ atom allocator_sizes atom alloc_util_allocators atom allow_passive_connect atom already_loaded +atom amd64 atom anchored atom and atom andalso -- cgit v1.2.3 From 139fa05489a6ba3e4384e6f20ea3f943741449d5 Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Tue, 10 May 2011 14:37:49 +0200 Subject: Silence warnings --- erts/emulator/beam/beam_emu.c | 2 +- erts/emulator/beam/erl_drv_thread.c | 7 +++++++ erts/emulator/sys/common/erl_poll.c | 7 ++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 32ea8588d2..99bf7e0439 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5322,7 +5322,7 @@ void process_main(void) ep->code[3] = (BeamInstr) OpCode(apply_bif); ep->code[4] = (BeamInstr) bif_table[i].f; /* XXX: set func info for bifs */ - ((BeamInstr*)ep->code + 3)[-5] = (BeamInstr) BeamOp(op_i_func_info_IaaI); + ep->fake_op_func_info_for_hipe[0] = (BeamInstr) BeamOp(op_i_func_info_IaaI); } return; diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 39bbe9633b..dc578f6d2a 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -700,6 +700,13 @@ erl_drv_thread_join(ErlDrvTid tid, void **respp) extern int erts_darwin_main_thread_pipe[2]; extern int erts_darwin_main_thread_result_pipe[2]; +int erl_drv_stolen_main_thread_join(ErlDrvTid tid, void **respp); +int erl_drv_steal_main_thread(char *name, + ErlDrvTid *dtid, + void* (*func)(void*), + void* arg, + ErlDrvThreadOpts *opts); + int erl_drv_stolen_main_thread_join(ErlDrvTid tid, void **respp) diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index 3ae5b8d747..f5c785d683 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -766,7 +766,7 @@ write_batch_buf(ErtsPollSet ps, ErtsPollBatchBuf *bbp) short filter; int fd = (int) ebuf[i].ident; - switch ((int) ebuf[i].udata) { + switch ((int) (long) ebuf[i].udata) { /* * Since we use a lazy update approach EV_DELETE will @@ -805,7 +805,7 @@ write_batch_buf(ErtsPollSet ps, ErtsPollBatchBuf *bbp) if (fd == (int) ebuf[j].ident) { ebuf[j].udata = (void *) ERTS_POLL_KQ_OP_HANDLED; if (!(ebuf[j].flags & EV_ERROR)) { - switch ((int) ebuf[j].udata) { + switch ((int) (long) ebuf[j].udata) { case ERTS_POLL_KQ_OP_ADD2_W: filter = EVFILT_WRITE; goto rm_add_fb; @@ -823,7 +823,8 @@ write_batch_buf(ErtsPollSet ps, ErtsPollBatchBuf *bbp) } } /* The other add succeded... */ - filter = (((int) ebuf[i].udata == ERTS_POLL_KQ_OP_ADD2_W) + filter = ((((int) (long) ebuf[i].udata) + == ERTS_POLL_KQ_OP_ADD2_W) ? EVFILT_READ : EVFILT_WRITE); rm_add_fb: -- cgit v1.2.3 From 8c9edd9c00142d0622beb74ef852c79871a631a6 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Fri, 13 May 2011 15:52:03 +0200 Subject: Fixit init per tescase for testcase for initial_server_connect. For this case to work, we need crypto! --- lib/inets/test/httpc_SUITE.erl | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 7607bc9eb6..1998bd3950 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -217,12 +217,17 @@ init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); init_per_testcase(initial_server_connect, Config) -> - inets:start(), - application:start(crypto), - application:start(public_key), - application:start(ssl), - application:start(inets), - 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), + inets:start(), + Config; + _ -> + {skip,"Could not start crypto"} + end; init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). -- cgit v1.2.3 From 2f53cae005b91e09969a715978e4001fc04cb668 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 16 May 2011 10:53:51 +0200 Subject: Moved entry from "Improvements..." to "Fixed...". --- lib/snmp/doc/src/notes.xml | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index c1debd42b1..6a20d8ee3a 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -43,15 +43,6 @@

-

--> - -

Fixed endode/decode of values of type Counter32.

-

This type (Counter32) is an unsigned integer 32, - but is actually encoded as an signed integer 32. - The encode/decode functions however, treated it as if it was - encodeded as an unsigned integer 32.

-

Own Id: OTP-9022

-
-

[agent] Added support for sending traps to IPv6 targets.

See the @@ -125,19 +116,21 @@

Fixed Bugs and Malfunctions -

-

+ -

[agent] For the table vacmAccessTable, - when performing the is_set_ok and set operation(s), - all values of the vacmAccessSecurityModel column was - incorrectly translated to any.

-

Own Id: OTP-8980

-
+

Fixed endode/decode of values of type Counter32.

+

This type (Counter32) is an unsigned integer 32, + but is actually encoded as an signed integer 32. + The encode/decode functions however, treated it as if it was + encodeded as an unsigned integer 32.

+

Own Id: OTP-9022

+
--->
-- cgit v1.2.3 From 6d446fc5d08d56174a79ab546d5aa2e79277af06 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Thu, 9 Dec 2010 15:08:52 +0100 Subject: Add {majority, boolean()} per-table option. With {majority, true} set for a table, write transactions will abort if they cannot commit to a majority of the nodes that have a copy of the table. Currently, the implementation hooks into the prepare_commit, and forces an asymmetric transaction if the commit set affects any table with the majority flag set. In the commit itself, the transaction will abort if it cannot satisfy the majority requirement for all tables involved in the thransaction. A future optimization might be to abort already when a write lock is attempted on such a table (/-object) and the lock cannot be set on enough nodes. This functionality introduces the possibility to automatically "fence off" a table in the presence of failures. This is a first implementation. Only basic tests have been performed. --- lib/mnesia/src/mnesia.hrl | 1 + lib/mnesia/src/mnesia_schema.erl | 15 +++++++- lib/mnesia/src/mnesia_tm.erl | 76 ++++++++++++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 11 deletions(-) diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl index d488d9364a..26537815a3 100644 --- a/lib/mnesia/src/mnesia.hrl +++ b/lib/mnesia/src/mnesia.hrl @@ -62,6 +62,7 @@ disc_only_copies = [], % [Node] load_order = 0, % Integer access_mode = read_write, % read_write | read_only + majority = false, % true | false index = [], % [Integer] snmp = [], % Snmp Ustruct local_content = false, % true | false diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index d1d892a387..a6c8ffec01 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -178,6 +178,7 @@ do_set_schema(Tab, Cs) -> set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies), set({Tab, load_order}, Cs#cstruct.load_order), set({Tab, access_mode}, Cs#cstruct.access_mode), + set({Tab, majority}, Cs#cstruct.majority), set({Tab, snmp}, Cs#cstruct.snmp), set({Tab, user_properties}, Cs#cstruct.user_properties), [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties], @@ -651,6 +652,7 @@ list2cs(List) when is_list(List) -> Snmp = pick(Name, snmp, List, []), LoadOrder = pick(Name, load_order, List, 0), AccessMode = pick(Name, access_mode, List, read_write), + Majority = pick(Name, majority, List, false), UserProps = pick(Name, user_properties, List, []), verify({alt, [nil, list]}, mnesia_lib:etype(UserProps), {bad_type, Name, {user_properties, UserProps}}), @@ -676,6 +678,7 @@ list2cs(List) when is_list(List) -> snmp = Snmp, load_order = LoadOrder, access_mode = AccessMode, + majority = Majority, local_content = LC, record_name = RecName, attributes = Attrs, @@ -809,7 +812,16 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> Access = Cs#cstruct.access_mode, verify({alt, [read_write, read_only]}, Access, {bad_type, Tab, {access_mode, Access}}), - + Majority = Cs#cstruct.majority, + verify({alt, [true, false]}, Majority, + {bad_type, Tab, {majority, Majority}}), + case Majority of + true -> + verify(false, LC, + {combine_error, Tab, [{local_content,true},{majority,true}]}); + false -> + ok + end, Snmp = Cs#cstruct.snmp, verify(true, mnesia_snmp_hook:check_ustruct(Snmp), {badarg, Tab, {snmp, Snmp}}), @@ -2971,6 +2983,7 @@ merge_versions(AnythingNew, Cs, RemoteCs, Force) -> Cs#cstruct.index == RemoteCs#cstruct.index, Cs#cstruct.snmp == RemoteCs#cstruct.snmp, Cs#cstruct.access_mode == RemoteCs#cstruct.access_mode, + Cs#cstruct.majority == RemoteCs#cstruct.majority, Cs#cstruct.load_order == RemoteCs#cstruct.load_order, Cs#cstruct.user_properties == RemoteCs#cstruct.user_properties -> do_merge_versions(AnythingNew, Cs, RemoteCs); diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index bb8e788b40..c7a0c28589 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -64,7 +64,8 @@ prev_tab = [], % initiate to a non valid table name prev_types, prev_snmp, - types + types, + majority = [] }). -record(participant, {tid, pid, commit, disc_nodes = [], @@ -1100,9 +1101,12 @@ t_commit(Type) -> case arrange(Tid, Store, Type) of {N, Prep} when N > 0 -> multi_commit(Prep#prep.protocol, + majority_attr(Prep), Tid, Prep#prep.records, Store); {0, Prep} -> - multi_commit(read_only, Tid, Prep#prep.records, Store) + multi_commit(read_only, + majority_attr(Prep), + Tid, Prep#prep.records, Store) end; true -> %% nested commit @@ -1117,6 +1121,12 @@ t_commit(Type) -> do_commit_nested end. +majority_attr(#prep{majority = M}) -> + M; +majority_attr(_) -> + []. + + %% This function arranges for all objects we shall write in S to be %% in a list of {Node, CommitRecord} %% Important function for the performance of mnesia. @@ -1222,11 +1232,13 @@ prepare_items(Tid, Tab, Key, Items, Prep) -> {blocked, _} -> unblocked = req({unblock_me, Tab}), prepare_items(Tid, Tab, Key, Items, Prep); - _ -> + _ -> + Majority = needs_majority(Tab, Prep), Snmp = val({Tab, snmp}), Recs2 = do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Prep#prep.records), Prep2 = Prep#prep{records = Recs2, prev_tab = Tab, + majority = Majority, prev_types = Types, prev_snmp = Snmp}, check_prep(Prep2, Types) end. @@ -1235,6 +1247,41 @@ do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Recs) -> Recs2 = prepare_snmp(Tid, Tab, Key, Types, Snmp, Items, Recs), % May exit prepare_nodes(Tid, Types, Items, Recs2, normal). + +needs_majority(Tab, #prep{majority = M}) -> + case lists:keymember(Tab, 1, M) of + true -> + M; + false -> + case ?catch_val({Tab, majority}) of + {'EXIT', _} -> + M; + false -> + [{Tab, []} | M]; + true -> + CopyHolders = all_copy_holders(Tab), + [{Tab, CopyHolders} | M] + end + end. + +all_copy_holders(Tab) -> + DC = val({Tab, disc_copies}), + DO = val({Tab, disc_only_copies}), + RC = val({Tab, ram_copies}), + DC ++ DO ++ RC. + +have_majority([], _) -> + ok; +have_majority([{Tab, AllNodes} | Rest], Nodes) -> + Missing = AllNodes -- Nodes, + Present = AllNodes -- Missing, + case length(Present) > length(Missing) of + true -> + have_majority(Rest, Nodes); + false -> + {error, Tab} + end. + prepare_snmp(Tab, Key, Items) -> case val({Tab, snmp}) of [] -> @@ -1261,10 +1308,15 @@ prepare_snmp(Tid, Tab, Key, Types, Us, Items, Recs) -> prepare_nodes(Tid, Types, [{clear_table, Tab}], Recs, snmp) end. -check_prep(Prep, Types) when Prep#prep.types == Types -> +check_prep(#prep{majority = [], types = Types} = Prep, Types) -> Prep; -check_prep(Prep, Types) when Prep#prep.types == undefined -> - Prep#prep{types = Types}; +check_prep(#prep{majority = M, types = undefined} = Prep, Types) -> + Protocol = if M == [] -> + Prep#prep.protocol; + true -> + asym_trans + end, + Prep#prep{protocol = Protocol, types = Types}; check_prep(Prep, _Types) -> Prep#prep{protocol = asym_trans}. @@ -1311,7 +1363,7 @@ prepare_node(_Node, _Storage, [], Rec, _Kind) -> %% multi_commit((Protocol, Tid, CommitRecords, Store) %% Local work is always performed in users process -multi_commit(read_only, Tid, CR, _Store) -> +multi_commit(read_only, _Maj = [], Tid, CR, _Store) -> %% This featherweight commit protocol is used when no %% updates has been performed in the transaction. @@ -1324,7 +1376,7 @@ multi_commit(read_only, Tid, CR, _Store) -> ?MODULE ! {delete_transaction, Tid}, do_commit; -multi_commit(sym_trans, Tid, CR, Store) -> +multi_commit(sym_trans, _Maj = [], Tid, CR, Store) -> %% This lightweight commit protocol is used when all %% the involved tables are replicated symetrically. %% Their storage types must match on each node. @@ -1376,7 +1428,7 @@ multi_commit(sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(sync_sym_trans, Tid, CR, Store) -> +multi_commit(sync_sym_trans, _Maj = [], Tid, CR, Store) -> %% This protocol is the same as sym_trans except that it %% uses syncronized calls to disk_log and syncronized commits %% when several nodes are involved. @@ -1408,7 +1460,7 @@ multi_commit(sync_sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(asym_trans, Tid, CR, Store) -> +multi_commit(asym_trans, Majority, Tid, CR, Store) -> %% This more expensive commit protocol is used when %% table definitions are changed (schema transactions). %% It is also used when the involved tables are @@ -1469,6 +1521,10 @@ multi_commit(asym_trans, Tid, CR, Store) -> {D2, CR2} = commit_decision(D, CR, [], []), DiscNs = D2#decision.disc_nodes, RamNs = D2#decision.ram_nodes, + case have_majority(Majority, DiscNs ++ RamNs) of + ok -> ok; + {error, Tab} -> mnesia:abort({no_majority, Tab}) + end, Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs), ?ets_insert(Store, Pending), {WaitFor, Local} = ask_commit(asym_trans, Tid, CR2, DiscNs, RamNs), -- cgit v1.2.3 From 38eef7af0f8bc1d2d152cefb8df1e1303ddcac45 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Thu, 9 Dec 2010 18:14:15 +0100 Subject: Write locks now check majority when needed. Since the table loader also sets (table) write locks, a special lock type, 'load', was needed. Unfortunately, this affects mnesia activity callbacks that redefine the lock operation. --- lib/mnesia/src/mnesia.erl | 6 ++++++ lib/mnesia/src/mnesia_lib.erl | 10 ++++++++++ lib/mnesia/src/mnesia_loader.erl | 2 +- lib/mnesia/src/mnesia_locker.erl | 33 ++++++++++++++++++++++++++++++++- lib/mnesia/src/mnesia_schema.erl | 1 + lib/mnesia/src/mnesia_tm.erl | 12 ++---------- 6 files changed, 52 insertions(+), 12 deletions(-) diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 025b32f506..43953f9ad4 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -39,6 +39,7 @@ %% Access within an activity - Lock acquisition lock/2, lock/4, + lock_table/2, read_lock_table/1, write_lock_table/1, @@ -415,6 +416,9 @@ lock(LockItem, LockKind) -> abort(no_transaction) end. +lock_table(Tab, LockKind) -> + lock({table, Tab}, LockKind). + lock(Tid, Ts, LockItem, LockKind) -> case element(1, Tid) of tid -> @@ -467,6 +471,8 @@ lock_table(Tid, Ts, Tab, LockKind) when is_atom(Tab) -> mnesia_locker:rlock_table(Tid, Store, Tab); write -> mnesia_locker:wlock_table(Tid, Store, Tab); + load -> + mnesia_locker:load_lock_table(Tid, Store, Tab); sticky_write -> mnesia_locker:sticky_wlock_table(Tid, Store, Tab); none -> diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl index 36bcfe8de9..7e926a6258 100644 --- a/lib/mnesia/src/mnesia_lib.erl +++ b/lib/mnesia/src/mnesia_lib.erl @@ -96,6 +96,8 @@ exists/1, fatal/2, get_node_number/0, + have_majority/2, + have_majority/3, fix_error/1, important/2, incr_counter/1, @@ -660,6 +662,14 @@ proc_info(_) -> false. get_node_number() -> {node(), self()}. +have_majority(Tab, HaveNodes) -> + have_majority(Tab, val({Tab, all_nodes}), HaveNodes). + +have_majority(_Tab, AllNodes, HaveNodes) -> + Missing = AllNodes -- HaveNodes, + Present = AllNodes -- Missing, + length(Present) > length(Missing). + read_log_files() -> [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()]. diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index 3de329503e..e785b795d1 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -702,7 +702,7 @@ send_table(Pid, Tab, RemoteS) -> prepare_copy(Pid, Tab, Storage) -> Trans = fun() -> - mnesia:write_lock_table(Tab), + mnesia:lock_table(Tab, load), mnesia_subscr:subscribe(Pid, {table, Tab}), update_where_to_write(Tab, node(Pid)), mnesia_lib:db_fixtable(Storage, Tab, true), diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index ca0cc79c45..9822dfd116 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -40,7 +40,8 @@ sticky_wlock_table/3, wlock/3, wlock_no_exist/4, - wlock_table/3 + wlock_table/3, + load_lock_table/3 ]). %% sys callback functions @@ -657,6 +658,7 @@ rwlock(Tid, Store, Oid) -> case need_lock(Store, Tab, Key, Lock) of yes -> Ns = w_nodes(Tab), + check_majority(Tab, Ns), Res = get_rwlocks_on_nodes(Ns, rwlock, Node, Store, Tid, Oid), ?ets_insert(Store, {{locks, Tab, Key}, Lock}), Res; @@ -683,6 +685,28 @@ w_nodes(Tab) -> _ -> mnesia:abort({no_exists, Tab}) end. +%% If the table has the 'majority' flag set, we can +%% only take a write lock if we see a majority of the +%% nodes. + +check_majority(true, Tab, HaveNs) -> + check_majority(Tab, HaveNs); +check_majority(false, _, _) -> + ok. + +check_majority(Tab, HaveNs) -> + case ?catch_val({Tab, majority}) of + true -> + case mnesia_lib:have_majority(Tab, HaveNs) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; + _ -> + ok + end. + %% aquire a sticky wlock, a sticky lock is a lock %% which remains at this node after the termination of the %% transaction. @@ -773,10 +797,14 @@ sticky_wlock_table(Tid, Store, Tab) -> %% local store when we have aquired the lock. %% wlock(Tid, Store, Oid) -> + wlock(Tid, Store, Oid, _CheckMajority = true). + +wlock(Tid, Store, Oid, CheckMajority) -> {Tab, Key} = Oid, case need_lock(Store, Tab, Key, write) of yes -> Ns = w_nodes(Tab), + check_majority(CheckMajority, Tab, Ns), Op = {self(), {write, Tid, Oid}}, ?ets_insert(Store, {{locks, Tab, Key}, write}), get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid); @@ -789,6 +817,9 @@ wlock(Tid, Store, Oid) -> wlock_table(Tid, Store, Tab) -> wlock(Tid, Store, {Tab, ?ALL}). +load_lock_table(Tid, Store, Tab) -> + wlock(Tid, Store, {Tab, ?ALL}, _CheckMajority = false). + %% Write lock even if the table does not exist wlock_no_exist(Tid, Store, Tab, Ns) -> diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index a6c8ffec01..b3f06322d9 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -179,6 +179,7 @@ do_set_schema(Tab, Cs) -> set({Tab, load_order}, Cs#cstruct.load_order), set({Tab, access_mode}, Cs#cstruct.access_mode), set({Tab, majority}, Cs#cstruct.majority), + set({Tab, all_nodes}, mnesia_lib:cs_to_nodes(Cs)), set({Tab, snmp}, Cs#cstruct.snmp), set({Tab, user_properties}, Cs#cstruct.user_properties), [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties], diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index c7a0c28589..0b87c40add 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -1259,23 +1259,15 @@ needs_majority(Tab, #prep{majority = M}) -> false -> [{Tab, []} | M]; true -> - CopyHolders = all_copy_holders(Tab), + CopyHolders = val({Tab, all_nodes}), [{Tab, CopyHolders} | M] end end. -all_copy_holders(Tab) -> - DC = val({Tab, disc_copies}), - DO = val({Tab, disc_only_copies}), - RC = val({Tab, ram_copies}), - DC ++ DO ++ RC. - have_majority([], _) -> ok; have_majority([{Tab, AllNodes} | Rest], Nodes) -> - Missing = AllNodes -- Nodes, - Present = AllNodes -- Missing, - case length(Present) > length(Missing) of + case mnesia_lib:have_majority(Tab, AllNodes, Nodes) of true -> have_majority(Rest, Nodes); false -> -- cgit v1.2.3 From 5309d521c394aa7ca1feb679dbda6333a3cb4873 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Mon, 13 Dec 2010 08:08:46 +0100 Subject: check majority for sticky locks --- lib/mnesia/src/mnesia_locker.erl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index 9822dfd116..2025a2ab37 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -689,6 +689,7 @@ w_nodes(Tab) -> %% only take a write lock if we see a majority of the %% nodes. + check_majority(true, Tab, HaveNs) -> check_majority(Tab, HaveNs); check_majority(false, _, _) -> @@ -732,6 +733,7 @@ sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> end. do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> + sticky_check_majority(Lock, Tab), ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}}, N = node(), receive @@ -761,6 +763,22 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> dirty_sticky_lock(Tab, Key, [N], Lock) end. +sticky_check_majority(read, _) -> + ok; +sticky_check_majority(write, Tab) -> + case ?catch_val({Tab, majority}) of + true -> + HaveNodes = val({Tab, where_to_write}), + case mnesia_lib:have_majority(Tab, HaveNodes) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; + _ -> + ok + end. + not_stuck(Tid, Store, Tab, _Key, Oid, _Lock, N) -> rlock(Tid, Store, {Tab, ?ALL}), %% needed? wlock(Tid, Store, Oid), %% perfect sync -- cgit v1.2.3 From b10094bc2f120ceda0e927d31067670e26a0a4d7 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Tue, 14 Dec 2010 19:27:26 +0100 Subject: optimize sticky_lock maj. check --- lib/mnesia/src/mnesia_locker.erl | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index 2025a2ab37..635668ff59 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -733,13 +733,14 @@ sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> end. do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> - sticky_check_majority(Lock, Tab), + WNodes = w_nodes(Tab), + sticky_check_majority(Lock, Tab, WNodes), ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}}, N = node(), receive {?MODULE, N, granted} -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], granted; {?MODULE, N, {granted, Val}} -> %% for rwlocks case opt_lookup_in_client(Val, Oid, write) of @@ -747,7 +748,7 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> exit({aborted, C}); Val2 -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], Val2 end; {?MODULE, N, {not_granted, Reason}} -> @@ -763,13 +764,12 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> dirty_sticky_lock(Tab, Key, [N], Lock) end. -sticky_check_majority(read, _) -> +sticky_check_majority(read, _, _) -> ok; -sticky_check_majority(write, Tab) -> +sticky_check_majority(write, Tab, WNodes) -> case ?catch_val({Tab, majority}) of true -> - HaveNodes = val({Tab, where_to_write}), - case mnesia_lib:have_majority(Tab, HaveNodes) of + case mnesia_lib:have_majority(Tab, WNodes) of true -> ok; false -> -- cgit v1.2.3 From ca1412a4f614942fcfe6601c3e8b5d6d2df153f7 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Thu, 27 Jan 2011 19:33:34 +0100 Subject: bug in mnesia_tm:needs_majority/2 --- lib/mnesia/src/mnesia_tm.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index 0b87c40add..01abc14a81 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -1257,7 +1257,7 @@ needs_majority(Tab, #prep{majority = M}) -> {'EXIT', _} -> M; false -> - [{Tab, []} | M]; + M; true -> CopyHolders = val({Tab, all_nodes}), [{Tab, CopyHolders} | M] -- cgit v1.2.3 From 54235590360d5f6d9a390ec3f0ac7e3f4e603c42 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Sun, 30 Jan 2011 19:11:21 +0100 Subject: where_to_wlock optimization + change_table_majority/2 --- lib/mnesia/src/mnesia.erl | 5 +++- lib/mnesia/src/mnesia_controller.erl | 26 ++++++++++++++++--- lib/mnesia/src/mnesia_dumper.erl | 8 ++++++ lib/mnesia/src/mnesia_locker.erl | 48 +++++++++++++++++------------------- lib/mnesia/src/mnesia_schema.erl | 38 ++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+), 29 deletions(-) diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 43953f9ad4..980a9c6213 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -93,7 +93,7 @@ add_table_copy/3, del_table_copy/2, move_table_copy/3, add_table_index/2, del_table_index/2, transform_table/3, transform_table/4, - change_table_copy_type/3, + change_table_copy_type/3, change_table_majority/2, read_table_property/2, write_table_property/2, delete_table_property/2, change_table_frag/2, clear_table/1, clear_table/4, @@ -2461,6 +2461,9 @@ change_table_access_mode(T, Access) -> change_table_load_order(T, O) -> mnesia_schema:change_table_load_order(T, O). +change_table_majority(T, M) -> + mnesia_schema:change_table_majority(T, M). + set_master_nodes(Nodes) when is_list(Nodes) -> UseDir = system_info(use_dir), IsRunning = system_info(is_running), diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index 0254769758..d4b2c7b5cc 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -72,6 +72,7 @@ add_active_replica/4, update/1, change_table_access_mode/1, + change_table_majority/1, del_active_replica/2, wait_for_tables/2, get_network_copy/2, @@ -690,7 +691,8 @@ handle_call({update_where_to_write, [add, Tab, AddNode], _From}, _Dummy, State) case lists:member(AddNode, Current) and (State#state.schema_is_merged == true) of true -> - mnesia_lib:add_lsort({Tab, where_to_write}, AddNode); + mnesia_lib:add_lsort({Tab, where_to_write}, AddNode), + update_where_to_wlock(Tab); false -> ignore end, @@ -1690,6 +1692,8 @@ add_active_replica(Tab, Node, Storage, AccessMode) -> set(Var, mark_blocked_tab(Blocked, Del)), mnesia_lib:del({Tab, where_to_write}, Node) end, + + update_where_to_wlock(Tab), add({Tab, active_replicas}, Node). del_active_replica(Tab, Node) -> @@ -1699,7 +1703,8 @@ del_active_replica(Tab, Node) -> New = lists:sort(Del), set(Var, mark_blocked_tab(Blocked, New)), % where_to_commit mnesia_lib:del({Tab, active_replicas}, Node), - mnesia_lib:del({Tab, where_to_write}, Node). + mnesia_lib:del({Tab, where_to_write}, Node), + update_where_to_wlock(Tab). change_table_access_mode(Cs) -> W = fun() -> @@ -1708,7 +1713,22 @@ change_table_access_mode(Cs) -> val({Tab, active_replicas})) end, update(W). - + +change_table_majority(Cs) -> + W = fun() -> + Tab = Cs#cstruct.name, + set({Tab, majority}, Cs#cstruct.majority), + update_where_to_wlock(Tab) + end, + update(W). + +update_where_to_wlock(Tab) -> + WNodes = val({Tab, where_to_write}), + Majority = case catch val({Tab, majority}) of + true -> true; + _ -> false + end, + set({Tab, where_to_wlock}, {WNodes, Majority}). %% node To now has tab loaded, but this must be undone %% This code is rpc:call'ed from the tab_copier process diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index 644133cf5d..9a0a2c4dcc 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -896,6 +896,14 @@ insert_op(Tid, _, {op, change_table_access_mode,TabDef, _OldAccess, _Access}, In end, insert_cstruct(Tid, Cs, true, InPlace, InitBy); +insert_op(Tid, _, {op, change_table_majority,TabDef, _OldAccess, _Access}, InPlace, InitBy) -> + Cs = mnesia_schema:list2cs(TabDef), + case InitBy of + startup -> ignore; + _ -> mnesia_controller:change_table_majority(Cs) + end, + insert_cstruct(Tid, Cs, true, InPlace, InitBy); + insert_op(Tid, _, {op, change_table_load_order, TabDef, _OldLevel, _Level}, InPlace, InitBy) -> Cs = mnesia_schema:list2cs(TabDef), insert_cstruct(Tid, Cs, true, InPlace, InitBy); diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index 635668ff59..0492d794f3 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -657,17 +657,17 @@ rwlock(Tid, Store, Oid) -> Lock = write, case need_lock(Store, Tab, Key, Lock) of yes -> - Ns = w_nodes(Tab), - check_majority(Tab, Ns), + {Ns, Majority} = w_nodes(Tab), + check_majority(Majority, Tab, Ns), Res = get_rwlocks_on_nodes(Ns, rwlock, Node, Store, Tid, Oid), ?ets_insert(Store, {{locks, Tab, Key}, Lock}), Res; no -> if Key == ?ALL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); Tab == ?GLOBAL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); true -> dirty_rpc(Node, Tab, Key, Lock) end @@ -679,9 +679,8 @@ rwlock(Tid, Store, Oid) -> %% in the local store under the key == nodes w_nodes(Tab) -> - Nodes = ?catch_val({Tab, where_to_write}), - case Nodes of - [_ | _] -> Nodes; + case ?catch_val({Tab, where_to_wlock}) of + {[_ | _], _} = Where -> Where; _ -> mnesia:abort({no_exists, Tab}) end. @@ -733,8 +732,8 @@ sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> end. do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> - WNodes = w_nodes(Tab), - sticky_check_majority(Lock, Tab, WNodes), + {WNodes, Majority} = w_nodes(Tab), + sticky_check_majority(Lock, Tab, Majority, WNodes), ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}}, N = node(), receive @@ -764,20 +763,15 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> dirty_sticky_lock(Tab, Key, [N], Lock) end. -sticky_check_majority(read, _, _) -> - ok; -sticky_check_majority(write, Tab, WNodes) -> - case ?catch_val({Tab, majority}) of +sticky_check_majority(W, Tab, true, WNodes) when W==write; W==read_write -> + case mnesia_lib:have_majority(Tab, WNodes) of true -> - case mnesia_lib:have_majority(Tab, WNodes) of - true -> - ok; - false -> - mnesia:abort({no_majority, Tab}) - end; - _ -> - ok - end. + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; +sticky_check_majority(_, _, _, _) -> + ok. not_stuck(Tid, Store, Tab, _Key, Oid, _Lock, N) -> rlock(Tid, Store, {Tab, ?ALL}), %% needed? @@ -821,15 +815,19 @@ wlock(Tid, Store, Oid, CheckMajority) -> {Tab, Key} = Oid, case need_lock(Store, Tab, Key, write) of yes -> - Ns = w_nodes(Tab), - check_majority(CheckMajority, Tab, Ns), + {Ns, Majority} = w_nodes(Tab), + if CheckMajority -> + check_majority(Majority, Tab, Ns); + true -> + ignore + end, Op = {self(), {write, Tid, Oid}}, ?ets_insert(Store, {{locks, Tab, Key}, write}), get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid); no when Key /= ?ALL, Tab /= ?GLOBAL -> []; no -> - w_nodes(Tab) + element(2, w_nodes(Tab)) end. wlock_table(Tid, Store, Tab) -> diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index b3f06322d9..360da8a17d 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -37,6 +37,7 @@ change_table_copy_type/3, change_table_access_mode/2, change_table_load_order/2, + change_table_majority/2, change_table_frag/2, clear_table/1, create_table/1, @@ -1508,6 +1509,43 @@ make_change_table_load_order(Tab, LoadOrder) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +change_table_majority(Tab, Majority) when is_boolean(Majority) -> + schema_transaction(fun() -> do_change_table_majority(Tab, Majority) end). + +do_change_table_majority(schema, _Majority) -> + mnesia:abort({bad_type, schema}); +do_change_table_majority(Tab, Majority) -> + TidTs = get_tid_ts_and_lock(schema, write), + get_tid_ts_and_lock(Tab, none), + insert_schema_ops(TidTs, make_change_table_majority(Tab, Majority)). + +make_change_table_majority(Tab, Majority) -> + ensure_writable(schema), + Cs = incr_version(val({Tab, cstruct})), + ensure_active(Cs), + OldMajority = Cs#cstruct.majority, + Cs2 = Cs#cstruct{majority = Majority}, + FragOps = case lists:keyfind(base_table, 1, Cs#cstruct.frag_properties) of + {_, Tab} -> + FragNames = mnesia_frag:frag_names(Tab) -- [Tab], + lists:map( + fun(T) -> + get_tid_ts_and_lock(Tab, none), + CsT = incr_version(val({T, cstruct})), + ensure_active(CsT), + CsT2 = CsT#cstruct{majority = Majority}, + verify_cstruct(CsT2), + {op, change_table_majority, cs2list(CsT2), + OldMajority, Majority} + end, FragNames); + false -> []; + {_, _} -> mnesia:abort({bad_type, Tab}) + end, + verify_cstruct(Cs2), + [{op, change_table_majority, cs2list(Cs2), OldMajority, Majority} | FragOps]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + write_table_property(Tab, Prop) when is_tuple(Prop), size(Prop) >= 1 -> schema_transaction(fun() -> do_write_table_property(Tab, Prop) end); write_table_property(Tab, Prop) -> -- cgit v1.2.3 From 4eacd6dcc0ffd28c4d76507212b87b75b249daca Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Sun, 30 Jan 2011 20:18:35 +0100 Subject: add mnesia_majority_test suite --- lib/mnesia/test/Makefile | 1 + lib/mnesia/test/mnesia_SUITE.erl | 99 ++++++++++++++++ lib/mnesia/test/mnesia_majority_test.erl | 188 +++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 lib/mnesia/test/mnesia_majority_test.erl diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile index b165924ef2..ae4c9626c7 100644 --- a/lib/mnesia/test/Makefile +++ b/lib/mnesia/test/Makefile @@ -42,6 +42,7 @@ MODULES= \ mnesia_dirty_access_test \ mnesia_atomicity_test \ mnesia_consistency_test \ + mnesia_majority_test \ mnesia_isolation_test \ mnesia_durability_test \ mnesia_recovery_test \ diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl index 8ba8427213..dc8f216c1c 100644 --- a/lib/mnesia/test/mnesia_SUITE.erl +++ b/lib/mnesia/test/mnesia_SUITE.erl @@ -142,6 +142,105 @@ silly() -> mnesia_install_test:silly(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +light(doc) -> + ["The 'light' test suite runs a selected set of test suites and is", + "intended to be the smallest test suite that is meaningful", + "to run. It starts with an installation test (which in essence is the", + "'silly' test case) and then it covers all functions in the API in", + "various depths. All configuration parameters and examples are also", + "covered."]; +light(suite) -> + [ + install, + nice, + evil, + {mnesia_frag_test, light}, + qlc, + registry, + config, + examples + ]. + +install(suite) -> + [{mnesia_install_test, all}]. + +nice(suite) -> + [{mnesia_nice_coverage_test, all}]. + +evil(suite) -> + [{mnesia_evil_coverage_test, all}]. + +qlc(suite) -> + [{mnesia_qlc_test, all}]. + +registry(suite) -> + [{mnesia_registry_test, all}]. + +config(suite) -> + [{mnesia_config_test, all}]. + +examples(suite) -> + [{mnesia_examples_test, all}]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +medium(doc) -> + ["The 'medium' test suite verfies the ACID (atomicity, consistency", + "isolation and durability) properties and various recovery scenarios", + "These tests may take quite while to run."]; +medium(suite) -> + [ + install, + atomicity, + isolation, + durability, + recovery, + consistency, + majority, + {mnesia_frag_test, medium} + ]. + +atomicity(suite) -> + [{mnesia_atomicity_test, all}]. + +isolation(suite) -> + [{mnesia_isolation_test, all}]. + +durability(suite) -> + [{mnesia_durability_test, all}]. + +recovery(suite) -> + [{mnesia_recovery_test, all}]. + +consistency(suite) -> + [{mnesia_consistency_test, all}]. + +majority(suite) -> + [{mnesia_majority_test, all}]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +heavy(doc) -> + ["The 'heavy' test suite runs some resource consuming tests and", + "benchmarks"]; +heavy(suite) -> + [measure]. + +measure(suite) -> + [{mnesia_measure_test, all}]. + +prediction(suite) -> + [{mnesia_measure_test, prediction}]. + +fairness(suite) -> + [{mnesia_measure_test, fairness}]. + +benchmarks(suite) -> + [{mnesia_measure_test, benchmarks}]. + +consumption(suite) -> + [{mnesia_measure_test, consumption}]. + +scalability(suite) -> + [{mnesia_measure_test, scalability}]. clean_up_suite(doc) -> ["Not a test case only kills mnesia and nodes, that where" "started during the tests"]; diff --git a/lib/mnesia/test/mnesia_majority_test.erl b/lib/mnesia/test/mnesia_majority_test.erl new file mode 100644 index 0000000000..17d1d8bcdd --- /dev/null +++ b/lib/mnesia/test/mnesia_majority_test.erl @@ -0,0 +1,188 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2009. 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(mnesia_majority_test). +-author('ulf.wiger@erlang-solutions.com'). +-compile(export_all). +-include("mnesia_test_lib.hrl"). + +init_per_testcase(Func, Conf) -> + mnesia_test_lib:init_per_testcase(Func, Conf). + +fin_per_testcase(Func, Conf) -> + mnesia_test_lib:fin_per_testcase(Func, Conf). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all(doc) -> + ["Verify that majority checking works"]; +all(suite) -> + [ + write + , wread + , delete + , clear_table + , frag + , change_majority + , frag_change_majority + ]. + +write(suite) -> []; +write(Config) when is_list(Config) -> + [N1, N2, N3] = Nodes = ?acquire_nodes(3, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2,N3]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok,ok],[]}, + rpc:multicall([N1,N2,N3], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N3]), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)). + +wread(suite) -> []; +wread(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)). + +delete(suite) -> []; +delete(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)). + +clear_table(suite) -> []; +clear_table(Config) when is_list(Config) -> + [N1, N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, mnesia:clear_table(t)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, mnesia:clear_table(t)). + +frag(suite) -> []; +frag(Config) when is_list(Config) -> + [N1] = Nodes = ?acquire_nodes(1, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1]}, + {majority,true}, + {frag_properties, [{n_fragments, 2}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)). + +change_majority(suite) -> []; +change_majority(Config) when is_list(Config) -> + [N1,N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)). + +frag_change_majority(suite) -> []; +frag_change_majority(Config) when is_list(Config) -> + [N1,N2] = Nodes = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false}, + {frag_properties, + [{n_fragments, 2}, + {n_ram_copies, 2}, + {node_pool, [N1,N2]}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match(false, mnesia:table_info(t_frag2, majority)), + ?match({aborted,{bad_type,t_frag2}}, + mnesia:change_table_majority(t_frag2, true)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)). -- cgit v1.2.3 From 497f4b2215e2fc1b6a10c3f96c71b90dbb529eae Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Sun, 30 Jan 2011 20:19:48 +0100 Subject: Add documentation text about majority checking --- lib/mnesia/doc/src/Mnesia_chap7.xmlsrc | 7 +++++++ lib/mnesia/doc/src/mnesia.xml | 32 +++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc index 7078499fbf..21174340d1 100644 --- a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc @@ -473,6 +473,13 @@ dets:close(N). mnesia:table_info(Tab, master_nodes) may be used to obtain information about the potential master nodes.

+

Determining which data to keep after communication failure is outside + the scope of Mnesia. One approach would be to determine which "island" + contains a majority of the nodes. Using the {majority,true} option + for critical tables can be a way of ensuring that nodes that are not part + of a "majority island" are not able to update those tables. Note that this + constitutes a reduction in service on the minority nodes. This would be + a tradeoff in favour of higher consistency guarantees.

The function mnesia:force_load_table(Tab) may be used to force load the table regardless of which table load mechanism is activated. diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index 16e78ea0af..2a2c7d3a9f 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -160,6 +160,14 @@ If a new item is inserted with the same key as behavior. The default is false.

+ +

majority This attribute can be either true or + false (default is false). When true, a majority + of the table replicas must be available for an update to succeed. + Majority checking can be enabled on tables with mission-critical data, + where it is vital to avoid inconsistencies due to network splits. +

+

snmp Each (set based) Mnesia table can be automatically turned into an SNMP ordered table as well. @@ -649,6 +657,17 @@ mnesia:change_table_copy_type(person, node(), disc_copies) LoadOrder priority will be loaded first at startup.

+ + change_table_majority(Tab, Majority) -> {aborted, R} | {atomic, ok} + Change the majority check setting for the table. + +

Majority must be a boolean; the default is false. + When true, a majority of the table's replicas must be available + for an update to succeed. When used on fragmented tables, Tab + must be the name base table. Directly changing the majority setting on + individual fragments is not allowed.

+
+
clear_table(Tab) -> {aborted, R} | {atomic, ok} Deletes all entries in a table. @@ -753,6 +772,14 @@ mnesia:change_table_copy_type(person, node(), disc_copies) priority will be loaded first at startup.

+ +

{majority, Flag}, where Flag must be a boolean. + If true, any (non-dirty) update to the table will abort unless + a majority of the table's replicas are available for the commit. + When used on a fragmented table, all fragments will be given + the same majority setting. +

+

{ram_copies, Nodelist}, where Nodelist is a list of the nodes where this table @@ -1737,7 +1764,10 @@ mnesia:create_table(person, write and sticky_write are supported.

If the user wants to update the record it is more efficient to - use write/sticky_write as the LockKind. + use write/sticky_write as the LockKind. If majority checking + is active on the table, it will be checked as soon as a write lock is + attempted. This can be used to quickly abort if the majority condition + isn't met.

-- cgit v1.2.3 From 8d7963d76fb651f6cb250033a0a5a643abd01389 Mon Sep 17 00:00:00 2001 From: Ulf Wiger Date: Fri, 13 May 2011 18:39:41 +0200 Subject: dialyzer warning on mnesia_tm --- lib/mnesia/src/mnesia_tm.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index 01abc14a81..f62f7cb7c8 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -1122,9 +1122,7 @@ t_commit(Type) -> end. majority_attr(#prep{majority = M}) -> - M; -majority_attr(_) -> - []. + M. %% This function arranges for all objects we shall write in S to be -- cgit v1.2.3 From 38b2f44764e79a34048f686d98b6d741d18738d2 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 5 May 2011 14:22:18 +0200 Subject: Improve erl_docgen's support for Dialyzer specs and types The support for using Dialyzer specifications and types has been improved. --- lib/erl_docgen/priv/bin/specs_gen.escript | 101 ++++-- lib/erl_docgen/priv/xsl/db_html.xsl | 556 +++++++++++++++++++++--------- lib/erl_docgen/priv/xsl/db_man.xsl | 424 +++++++++++++++-------- lib/erl_docgen/priv/xsl/db_pdf.xsl | 451 ++++++++++++++++-------- lib/erl_docgen/src/otp_specs.erl | 14 +- make/otp.mk.in | 5 +- 6 files changed, 1069 insertions(+), 482 deletions(-) diff --git a/lib/erl_docgen/priv/bin/specs_gen.escript b/lib/erl_docgen/priv/bin/specs_gen.escript index 840fed6dd5..982afece7f 100644 --- a/lib/erl_docgen/priv/bin/specs_gen.escript +++ b/lib/erl_docgen/priv/bin/specs_gen.escript @@ -2,7 +2,7 @@ %% -*- erlang -*- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 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 @@ -29,7 +29,7 @@ %%% "-I" Directory to be searched when including a file. %%% "-module Module" %%% Module name to use when there is no File argument. -%%% A temporary file will be created. +%%% A empty specifications file will be created. %%% Exactly one of -module Module and File must be given. %%% %%% The name of the generated file is "specs_.xml". Its exact @@ -67,34 +67,73 @@ usage() -> halt(1). call_edoc(FileSpec, InclFs, Dir) -> - Incl = [{includes, InclFs}], - Pre = [{preprocess, true}], - Choice = [{dialyzer_specs, all}], - DirOpt = [{dir, Dir}], - Pretty = [{pretty_print, erl_pp}], - Layout = [{layout, otp_specs}, - {file_suffix, ".specs"}, - {stylesheet, ""}], - Warn = [{report_missing_type, false}, - {report_type_mismatch, false}], - OptionList = (DirOpt ++ Choice ++ Pre ++ Warn ++ Pretty ++ Layout ++ Incl), - {File, TmpFile} = case FileSpec of - {file, File0} -> - {File0, false}; - {module, Module} -> - {create_tmp_file(Dir, Module), true} - end, - try edoc:files([File], OptionList) of - ok -> - clean_up(Dir, File, TmpFile), - rename(Dir, File) + ReadOpts = [{includes, InclFs}, {preprocess, true}], + ExtractOpts = [{report_missing_type, false}], + LayoutOpts = [{pretty_printer, erl_pp}, {layout, otp_specs}], + File = case FileSpec of + {file, File0} -> File0; + {module, Module0} -> Module0 + end, + try + Fs = case FileSpec of + {file, _} -> + Fs0 = read_file(File, ReadOpts), + clauses(Fs0); + {module, Module} -> + [{attribute,0,module,list_to_atom(Module)}] + end, + Doc = extract(File, Fs, ExtractOpts), + Text = edoc:layout(Doc, LayoutOpts), + ok = write_text(Text, File, Dir), + rename(Dir, File) catch _:_ -> io:format("EDoc could not process file '~s'\n", [File]), - clean_up(Dir, File, TmpFile), + clean_up(Dir), halt(3) end. +read_file(File, Opts) -> + edoc:read_source(File, Opts). + +extract(File, Forms, Opts) -> + Env = edoc_lib:get_doc_env([], [], [], _Opts=[]), + {_Module, Doc} = edoc_extract:source(Forms, File, Env, Opts), + Doc. + +clauses(Fs) -> + clauses(Fs, no). + +clauses([], no) -> + []; +clauses([F | Fs], Spec) -> + case F of + {attribute,_,spec,_} -> + clauses(Fs, F); + {function,_,_N,_A,_Cls} when Spec =/= no-> + {attribute,_,spec,{Name,FunTypes}} = Spec, + %% [throw({no,Name,{_N,_A}}) || Name =/= {_N,_A}], + %% EDoc doesn't care if a function appears more than once; + %% this is how overloaded specs are handled: + (lists:append([[setelement(4, Spec, {Name,[T]}),F] || + T <- FunTypes]) + ++ clauses(Fs, no)); + _ -> + [F | clauses(Fs, Spec)] + end. + +write_text(Text, File, Dir) -> + Base = filename:basename(File, ".erl"), + OutFile = filename:join(Dir, Base) ++ ".specs", + case file:write_file(OutFile, Text) of + ok -> + ok; + {error, R} -> + R1 = file:format_error(R), + io:format("could not write file '~s': ~s\n", [File, R1]), + halt(2) + end. + rename(Dir, F) -> Mod = filename:basename(F, ".erl"), Old = filename:join(Dir, Mod ++ ".specs"), @@ -108,22 +147,10 @@ rename(Dir, F) -> halt(2) end. -clean_up(Dir, File, TmpFile) -> - [file:delete(File) || TmpFile], +clean_up(Dir) -> _ = [file:delete(filename:join(Dir, F)) || F <- ["packages-frame.html", "overview-summary.html", "modules-frame.html", "index.html", "erlang.png", "edoc-info"]], ok. - -create_tmp_file(Dir, Module) -> - TmpFile = filename:join(Dir, Module++".erl"), - case file:write_file(TmpFile, "-module(" ++ Module ++ ").\n") of - ok -> - TmpFile; - {error, R} -> - R1 = file:format_error(R), - io:format("could not write file '~s': ~s\n", [TmpFile, R1]), - halt(2) - end. diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index c6375ea621..982572aeef 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -22,12 +22,15 @@ @@ -38,49 +41,58 @@ + - Error : - /: + Error in : + : + + /: - + - + - - + + + + + + ambiguous spec + unknown spec + + + - unknown spec + + + - - - - / - - - - - + + + + + + @@ -89,72 +101,189 @@ - - - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - + + - + + + +
- - + + + + + + + + + + + + + + + + + + + + +

Types:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + + + -

Types:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + unknown type variable + + + + - - + + + + -
+
+ + + -
- + + + +
- + +
+
+ + + + + + + +
+ + + + + +
+ + +
@@ -193,130 +322,175 @@
- - - -
-
- - - -
- - -
-
- - -
- - - -
-
+ + + + - - - - - - - - - - + + + + ambiguous type + unknown type + + + - unknown type + - + + + + + + + + + + + - + - + + + + +
+
+ + + + + + + + + +
+
+ + + + +
+ + +
+
+ + + +
+ + + +
+
+ + select="ancestor::type_desc/preceding-sibling::name + | ancestor::desc/preceding-sibling::name"/> - cannot find 'name' () + + cannot find tag 'name' (anno ) - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + unknown annotation + + + + + + + + + + + + + + + + "$spec0[string-length($clause_i) = 0 + or position() = $clause_i]/anno[.=$anno]"/> - - - - - - - - - - - - - - - - - unknown annotation - - + + - @@ -324,6 +498,50 @@  
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1178,14 +1396,7 @@ - - - / - - - - - + @@ -1221,11 +1432,19 @@ -
  • - - / - -
  • + + + + + +
  • + + / + +
  • +
    +
    @@ -1470,7 +1689,10 @@ -

    +

    +

    @@ -1478,7 +1700,6 @@
    - @@ -1488,6 +1709,11 @@ + + + Error : arity is mandatory when referring to specifications! + + @@ -1556,12 +1782,17 @@ -

    Types:

    + + - - - -
    +

    Types:

    + + + + +
    + +
    @@ -1612,7 +1843,10 @@
    + + + @@ -1633,16 +1867,16 @@ --> - + - + - + diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl index 2a8fb9fe3e..25b62f68c5 100644 --- a/lib/erl_docgen/priv/xsl/db_man.xsl +++ b/lib/erl_docgen/priv/xsl/db_man.xsl @@ -3,7 +3,7 @@ # # %CopyrightBegin% # - # Copyright Ericsson AB 2009-2010. All Rights Reserved. + # Copyright Ericsson AB 2009-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 @@ -21,13 +21,16 @@ --> + xmlns:xsl="http://www.w3.org/1999/XSL/Transform" + xmlns:exsl="http://exslt.org/common" + extension-element-prefixes="exsl"> - @@ -35,38 +38,58 @@ + - Error : - /: + Error in : + : + + /: - + - + - - + + + + + + ambiguous spec + unknown spec + + + - unknown spec + + + + + + + + + + + @@ -75,42 +98,12 @@ - - - - - - - - - - + .br - - - - - - - - - - - - - - - - - - - - - .nf .B @@ -119,29 +112,141 @@ .fi - - + + + + + + + + + + + + + + + + + + + + .RS + .TP + Types + + + + + + + + + + + + + + + + + + + + + + + + + + .RE + + + + + + + - .RS - .TP - Types - - - - - .RE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + unknown type variable + + + + + + + + + + - - - - .br - + + + .br + + + + + + + + + + + + + + .br + + + @@ -164,59 +269,46 @@ - - .nf - .B - - .br - .fi - - - - .RS - - .RE - - - - - - .br - - + - - - - - - - - - - + + + + ambiguous type + unknown type + + + - unknown type + - + + + + + + + + - + .nf .B @@ -226,68 +318,109 @@ + + .nf + .B + + .br + .fi + + + + .nf + + .fi + + + + + .RS + + .RE + + + + + + .br + + + select="ancestor::type_desc/preceding-sibling::name + | ancestor::desc/preceding-sibling::name"/> - cannot find 'name' () + + cannot find tag 'name' (anno ) - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + unknown annotation + + + + + + + + + + + + + + + + "$spec0[string-length($clause_i) = 0 + or position() = $clause_i]/anno[.=$anno]"/> - - - - - - - - - - - - - - - - - unknown annotation - - + + - @@ -542,6 +675,9 @@ .LP + @@ -554,6 +690,11 @@ + + + Error : arity is mandatory when referring to specifications! + + @@ -569,11 +710,14 @@ - .RS - .TP - Types - - .RE + + + .RS + .TP + Types + + .RE + @@ -622,7 +766,7 @@ > - + diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl index f500cd3fee..5119e3e36a 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl @@ -21,6 +21,8 @@ --> @@ -28,45 +30,66 @@ + - Error : - /: + Error in : + : + + /: - + - + - - + + + + + + ambiguous spec + unknown spec + + + - unknown spec + + + + + + + + + + + @@ -76,78 +99,175 @@ - - - - - - - - - - + - - - - - + + + + - - + + + + + + + + + + - - - - - + + + + + + + + + + Types: + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + unknown type variable + + + - - - Types: - - - - + + - + + + - - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + @@ -182,6 +302,53 @@ + + + + + + + + + + + + + + + ambiguous type + unknown type + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -189,6 +356,20 @@ + + + + + + + + + @@ -211,108 +392,82 @@ - - - - - - - - - - - - - - - - - - - - - - - - - unknown type - - - - - - - - - - - - + select="ancestor::type_desc/preceding-sibling::name + | ancestor::desc/preceding-sibling::name"/> - cannot find 'name' () + + cannot find tag 'name' (anno ) - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + unknown annotation + + + + + + + + + + + + + + + + "$spec0[string-length($clause_i) = 0 + or position() = $clause_i]/anno[.=$anno]"/> - - - - - - - - - - - - - - - - - unknown annotation - - + + - @@ -1227,6 +1382,9 @@ + @@ -1245,6 +1403,11 @@ + + + Error : arity is mandatory when referring to specifications! + + @@ -1276,15 +1439,20 @@ - - Types: - + + - - - - - + + Types: + + + + + + + + + @@ -1431,6 +1599,7 @@ + diff --git a/lib/erl_docgen/src/otp_specs.erl b/lib/erl_docgen/src/otp_specs.erl index 728ddb2e6e..edb437a942 100644 --- a/lib/erl_docgen/src/otp_specs.erl +++ b/lib/erl_docgen/src/otp_specs.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 @@ -380,6 +380,8 @@ t_type([E=#xmlElement{name = float}]) -> t_float(E); t_type([#xmlElement{name = nil}]) -> t_nil(); +t_type([#xmlElement{name = paren, content = Es}]) -> + t_paren(Es); t_type([#xmlElement{name = list, content = Es}]) -> t_list(Es); t_type([#xmlElement{name = nonempty_list, content = Es}]) -> @@ -416,6 +418,9 @@ t_float(E) -> t_nil() -> ["[]"]. +t_paren(Es) -> + ["("] ++ t_utype(get_elem(type, Es)) ++ [")"]. + t_list(Es) -> ["["] ++ t_utype(get_elem(type, Es)) ++ ["]"]. @@ -532,6 +537,8 @@ ot_type([E=#xmlElement{name = float}]) -> ot_float(E); ot_type([#xmlElement{name = nil}]) -> ot_nil(); +ot_type([#xmlElement{name = paren, content = Es}]) -> + ot_paren(Es); ot_type([#xmlElement{name = list, content = Es}]) -> ot_list(Es); ot_type([#xmlElement{name = nonempty_list, content = Es}]) -> @@ -582,6 +589,9 @@ ot_float(E) -> ot_nil() -> {nil,0}. +ot_paren(Es) -> + {paren_type,0,[ot_utype(get_elem(type, Es))]}. + ot_list(Es) -> {type,0,list,[ot_utype(get_elem(type, Es))]}. @@ -697,5 +707,7 @@ annos_type([#xmlElement{name = union, content = Es}]) -> lists:flatmap(fun annos_elem/1, Es); annos_type([E=#xmlElement{name = typevar}]) -> annos_elem(E); +annos_type([#xmlElement{name = paren, content = Es}]) -> + annos(get_elem(type, Es)); annos_type(_) -> []. diff --git a/make/otp.mk.in b/make/otp.mk.in index bf287be416..a4e9dad46f 100644 --- a/make/otp.mk.in +++ b/make/otp.mk.in @@ -4,7 +4,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 @@ -240,9 +240,10 @@ FOP = @FOP@ DOCGEN=$(ERL_TOP)/lib/erl_docgen SPECS_ESRC = ../../src +SPECS_EXTRACTOR=$(DOCGEN)/priv/bin/specs_gen.escript # Extract specifications and types from Erlang source files (-spec, -type) $(SPECDIR)/specs_%.xml: $(SPECS_ESRC)/%.erl - escript $(DOCGEN)/priv/bin/specs_gen.escript $(SPECS_FLAGS) -o$(dir $@) $< + escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) -o$(dir $@) $< $(MAN1DIR)/%.1: %.xml -- cgit v1.2.3 From 1f065e1df462e75464fa0a04badd02a6905bec30 Mon Sep 17 00:00:00 2001 From: Micael Karlberg Date: Mon, 16 May 2011 18:22:09 +0200 Subject: Spelling in (backward *compatibility*) comment. --- lib/snmp/src/manager/snmpm.erl | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 49f7d66b9a..e457d3b47a 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -92,7 +92,7 @@ -export([format_reason/1, format_reason/2]). -%% Backward compatibillity exports (API version "2") +%% Backward compatibility exports (API version "2") -export([ sync_get/3, sync_get/4, sync_get/5, sync_get/6, async_get/3, async_get/4, async_get/5, async_get/6, @@ -104,7 +104,7 @@ async_get_bulk/5, async_get_bulk/6, async_get_bulk/7, async_get_bulk/8 ]). -%% Backward compatibillity exports (API version "1") +%% Backward compatibility exports (API version "1") -deprecated({agent_info, 3}). -deprecated({update_agent_info, 5}). -deprecated({g, 3}). @@ -387,23 +387,23 @@ register_agent(UserId, TargetName, Config) is_list(Config)) -> do_register_agent(UserId, TargetName, [{reg_type, target_name} | Config]); -%% Backward compatibillity +%% Backward compatibility %% Note that the agent engine id is a mandatory config option, %% so this function *will* fail! register_agent(UserId, Addr, Port) when is_integer(Port) -> register_agent(UserId, Addr, Port, []); -%% Backward compatibillity +%% Backward compatibility register_agent(UserId, Addr, Config) when is_list(Config) -> register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, Config). -%% Backward compatibillity +%% Backward compatibility %% Note that the agent engine id is a mandatory config option, %% so this function *will* fail! register_agent(UserId, Addr) -> register_agent(UserId, Addr, ?DEFAULT_AGENT_PORT, []). -%% Backward compatibillity +%% Backward compatibility register_agent(UserId, Addr, Port, Config0) -> case lists:keymember(target_name, 1, Config0) of false -> @@ -423,7 +423,7 @@ register_agent(UserId, Addr, Port, Config0) -> unregister_agent(UserId, TargetName) when is_list(TargetName) -> snmpm_config:unregister_agent(UserId, TargetName); -%% Backward compatibillity functions +%% Backward compatibility functions unregister_agent(UserId, Addr) -> unregister_agent(UserId, Addr, ?DEFAULT_AGENT_PORT). @@ -438,7 +438,7 @@ unregister_agent(UserId, Addr, Port) -> agent_info(TargetName, Item) -> snmpm_config:agent_info(TargetName, Item). -%% Backward compatibillity +%% Backward compatibility agent_info(Addr, Port, Item) -> case target_name(Addr, Port) of {ok, TargetName} -> @@ -450,7 +450,7 @@ agent_info(Addr, Port, Item) -> update_agent_info(UserId, TargetName, Item, Val) -> snmpm_config:update_agent_info(UserId, TargetName, Item, Val). -%% Backward compatibillity functions +%% Backward compatibility functions update_agent_info(UserId, Addr, Port, Item, Val) -> case target_name(Addr, Port) of {ok, TargetName} -> @@ -1365,7 +1365,7 @@ cancel_async_request(UserId, ReqId) -> %%%----------------------------------------------------------------- -%%% Audit Trail Log functions (for backward compatibillity) +%%% Audit Trail Log functions (for backward compatibility) %%%----------------------------------------------------------------- log_to_txt(LogDir, Mibs) -> OutFile = "snmpm_log.txt", -- cgit v1.2.3 From 657cf4b1363e468c5839628a1e6f9db88dbf45dc Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Mon, 16 May 2011 21:42:27 +0200 Subject: re: remove gratuitous "it " in manpage --- lib/stdlib/doc/src/re.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/stdlib/doc/src/re.xml b/lib/stdlib/doc/src/re.xml index b8db55fc26..8c9e08ac3a 100644 --- a/lib/stdlib/doc/src/re.xml +++ b/lib/stdlib/doc/src/re.xml @@ -133,7 +133,7 @@ This option makes it possible to include comments inside complicated patterns. N multiline

    By default, PCRE treats the subject string as consisting of a single line of characters (even if it actually contains newlines). The "start of line" metacharacter (^) matches only at the start of the string, while the "end of line" metacharacter ($) matches only at the end of the string, or before a terminating newline (unless dollar_endonly is given). This is the same as Perl.

    -

    When multiline it is given, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting multiline has no effect.

    +

    When multiline is given, the "start of line" and "end of line" constructs match immediately following or immediately before internal newlines in the subject string, respectively, as well as at the very start and end. This is equivalent to Perl's /m option, and it can be changed within a pattern by a (?m) option setting. If there are no newlines in a subject string, or no occurrences of ^ or $ in a pattern, setting multiline has no effect.

    no_auto_capture Disables the use of numbered capturing parentheses in the pattern. Any opening parenthesis that is not followed by ? behaves as if it were followed by ?: but named parentheses can still be used for capturing (and they acquire numbers in the usual way). There is no equivalent of this option in Perl. -- cgit v1.2.3 From 797126e753f922bfee56c9e1c34de5297c33f1e4 Mon Sep 17 00:00:00 2001 From: Lars Thorsen Date: Thu, 12 May 2011 08:09:50 +0200 Subject: Fix default encoding in SAX parser. --- lib/xmerl/src/xmerl_sax_parser.erl | 4 ++-- lib/xmerl/vsn.mk | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index c1acf51b8a..571feabee0 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -263,11 +263,11 @@ detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>> = Xml, State {Xml, State#xmerl_sax_parser_state{encoding=Enc}} end; _ -> - {Xml, State#xmerl_sax_parser_state{encoding=utf8}} + {Xml, State} end end; detect_charset_1(Xml, State) -> - {Xml, State#xmerl_sax_parser_state{encoding=utf8}}. + {Xml, State}. %%---------------------------------------------------------------------- %% Function: convert_encoding(Enc) diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index 280ff10efa..965a0ae7b4 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.2.8 +XMERL_VSN = 1.2.9 -- cgit v1.2.3 From 766e09ac2d1abfa65f65377945ce8f06905e786e Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Tue, 17 May 2011 09:52:27 +0200 Subject: Fix bugs concerning the option report_missing_types Bugs concerning the option report_missing_types that was added in EDoc-0.7.7 have been corrected: the option was misspelled in the source, and local definitions as well as the function tags @private and @hidden were not handled correctly. (Thanks to Manolis Papadakis.) --- lib/edoc/src/edoc.hrl | 2 +- lib/edoc/src/edoc_specs.erl | 6 +-- lib/edoc/src/edoc_tags.erl | 116 +++++++++++++++++++++++--------------------- 3 files changed, 66 insertions(+), 58 deletions(-) diff --git a/lib/edoc/src/edoc.hrl b/lib/edoc/src/edoc.hrl index 43657b3b8f..31cf45ade9 100644 --- a/lib/edoc/src/edoc.hrl +++ b/lib/edoc/src/edoc.hrl @@ -37,7 +37,7 @@ -define(SOURCE_DIR, "src"). -define(EBIN_DIR, "ebin"). -define(EDOC_DIR, "doc"). --define(REPORT_MISSING_TYPE, false). +-define(REPORT_MISSING_TYPES, false). -include("edoc_doclet.hrl"). diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 45016ef85a..519ade726f 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -1,4 +1,4 @@ -% +%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1996-2011. All Rights Reserved. @@ -428,8 +428,8 @@ get_typevars(Ts) -> expand_records(Entries, TypeDefs, DT, Opts, File, Module) -> TypeList = [{type_name(T), T, not_seen} || T <- TypeDefs], true = ets:insert(DT, TypeList), - Warn = proplists:get_value(report_missing_type, Opts, - ?REPORT_MISSING_TYPE) =:= true, + Warn = proplists:get_value(report_missing_types, Opts, + ?REPORT_MISSING_TYPES) =:= true, P = #parms{tab = DT, warn = Warn, file = File, line = 0}, ExportedTypes = [Name || {export_type,Ts} <- Module#module.attributes, diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl index def39ee34c..8ee8f87b5f 100644 --- a/lib/edoc/src/edoc_tags.erl +++ b/lib/edoc/src/edoc_tags.erl @@ -331,8 +331,8 @@ parse_typedef(Data, Line, _Env, Where) -> NAs = length(As), case edoc_types:is_predefined(T, NAs) of true -> - case - edoc_types:is_new_predefined(T, NAs) + case + edoc_types:is_new_predefined(T, NAs) orelse edoc_types:is_predefined_otp_type(T, NAs) of false -> @@ -406,17 +406,20 @@ throw_error(L, D) -> -record(parms, {tab, warn, file, line}). -check_types(Entries0, Opts, File) -> - Entries = edoc_data:hidden_filter(Entries0, Opts), +check_types(Entries, Opts, File) -> Tags = edoc_data:get_all_tags(Entries), + TypeTags = [Tag || #tag{data = {#t_typedef{},_}}=Tag <- Tags], + Entries2 = edoc_data:hidden_filter(Entries, Opts), + Tags2 = edoc_data:get_all_tags(Entries2), + SpecTags = [Tag || #tag{data = #t_spec{}}=Tag <- Tags2], DT = ets:new(types, [bag]), _ = [add_type(DT, Name, As, File, Line) || #tag{line = Line, - data = {#t_typedef{name = Name, args = As},_}} <- Tags], - Warn = proplists:get_value(report_missing_type, Opts, - ?REPORT_MISSING_TYPE) =:= true, + data = {#t_typedef{name = Name, args = As},_}} <- TypeTags], + Warn = proplists:get_value(report_missing_types, Opts, + ?REPORT_MISSING_TYPES) =:= true, P = #parms{tab = DT, warn = Warn, file = File, line = 0}, - try check_types(Tags, P) + try check_types3(TypeTags++SpecTags, P, []) after true = ets:delete(DT) end. @@ -431,60 +434,64 @@ add_type(DT, Name, Args, File, Line) -> ets:insert(DT, {Name, NArgs}) end. -check_types([], _P)-> +check_types3([], _P, _Ls)-> ok; -check_types([Tag | Tags], P) -> - check_type(Tag, P, Tags). +check_types3([Tag | Tags], P, Ls) -> + check_type(Tag, P, Ls, Tags). -check_type(#tag{line = L, data = Data}, P0, Ts) -> +check_type(#tag{line = L, data = Data}, P0, Ls, Ts) -> P = P0#parms{line = L}, case Data of {#t_typedef{type = Type, defs = Defs},_} -> - check_type(Type, P, Defs++Ts); + check_type(Type, P, Ls, Defs++Ts); #t_spec{type = Type, defs = Defs} -> - check_type(Type, P, Defs++Ts); + LocalTypes = + [{N,length(Args)} || + #t_def{name = #t_type{name = N, args = Args}} <- Defs], + check_type(Type, P, LocalTypes, Defs), + check_types3(Ts, P, Ls); _-> - check_types(Ts, P0) + check_types3(Ts, P0, Ls) end; -check_type(#t_def{type = Type}, P, Ts) -> - check_type(Type, P, Ts); -check_type(#t_type{name = Name, args = Args}, P, Ts) -> - check_used_type(Name, Args, P), - check_types(Args++Ts, P); -check_type(#t_var{}, P, Ts) -> - check_types(Ts, P); -check_type(#t_fun{args = Args, range = Range}, P, Ts) -> - check_type(Range, P, Args++Ts); -check_type(#t_tuple{types = Types}, P, Ts) -> - check_types(Types ++Ts, P); -check_type(#t_list{type = Type}, P, Ts) -> - check_type(Type, P, Ts); -check_type(#t_nil{}, P, Ts) -> - check_types(Ts, P); -check_type(#t_paren{type = Type}, P, Ts) -> - check_type(Type, P, Ts); -check_type(#t_nonempty_list{type = Type}, P, Ts) -> - check_type(Type, P, Ts); -check_type(#t_atom{}, P, Ts) -> - check_types(Ts, P); -check_type(#t_integer{}, P, Ts) -> - check_types(Ts, P); -check_type(#t_integer_range{}, P, Ts) -> - check_types(Ts, P); -check_type(#t_binary{}, P, Ts) -> - check_types(Ts, P); -check_type(#t_float{}, P, Ts) -> - check_types(Ts, P); -check_type(#t_union{types = Types}, P, Ts) -> - check_types(Types++Ts, P); -check_type(#t_record{fields = Fields}, P, Ts) -> - check_types(Fields++Ts, P); -check_type(#t_field{type = Type}, P, Ts) -> - check_type(Type, P, Ts); -check_type(undefined, P, Ts) -> - check_types(Ts, P). - -check_used_type(#t_name{name = N, module = Mod}=Name, Args, P) -> +check_type(#t_def{type = Type}, P, Ls, Ts) -> + check_type(Type, P, Ls, Ts); +check_type(#t_type{name = Name, args = Args}, P, Ls, Ts) -> + check_used_type(Name, Args, P, Ls), + check_types3(Args++Ts, P, Ls); +check_type(#t_var{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); +check_type(#t_fun{args = Args, range = Range}, P, Ls, Ts) -> + check_type(Range, P, Ls, Args++Ts); +check_type(#t_tuple{types = Types}, P, Ls, Ts) -> + check_types3(Types ++Ts, P, Ls); +check_type(#t_list{type = Type}, P, Ls, Ts) -> + check_type(Type, P, Ls, Ts); +check_type(#t_nil{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); +check_type(#t_paren{type = Type}, P, Ls, Ts) -> + check_type(Type, P, Ls, Ts); +check_type(#t_nonempty_list{type = Type}, P, Ls, Ts) -> + check_type(Type, P, Ls, Ts); +check_type(#t_atom{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); +check_type(#t_integer{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); +check_type(#t_integer_range{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); +check_type(#t_binary{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); +check_type(#t_float{}, P, Ls, Ts) -> + check_types3(Ts, P, Ls); +check_type(#t_union{types = Types}, P, Ls, Ts) -> + check_types3(Types++Ts, P, Ls); +check_type(#t_record{fields = Fields}, P, Ls, Ts) -> + check_types3(Fields++Ts, P, Ls); +check_type(#t_field{type = Type}, P, Ls, Ts) -> + check_type(Type, P, Ls, Ts); +check_type(undefined, P, Ls, Ts) -> + check_types3(Ts, P, Ls). + +check_used_type(#t_name{name = N, module = Mod}=Name, Args, P, LocalTypes) -> NArgs = length(Args), TypeName = {Name, NArgs}, DT = P#parms.tab, @@ -493,6 +500,7 @@ check_used_type(#t_name{name = N, module = Mod}=Name, Args, P) -> orelse lists:member(TypeName, ets:lookup(DT, Name)) orelse edoc_types:is_predefined(N, NArgs) orelse edoc_types:is_predefined_otp_type(N, NArgs) + orelse lists:member(TypeName, LocalTypes) of true -> ok; -- cgit v1.2.3 From 06f99b287fbb79e89831bedf1b2e64e7185f66c8 Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Wed, 11 May 2011 15:57:51 +0200 Subject: Allow same module name in multiple applications if explicitely excluded Earlier, reltool expected all module names detected under the lib directories to have unique names. If this was not the case, the result was undefined - i.e. the beam file of the duplicated module might be included in multiple applications in the target area, or it might even be excluded from all applications. This commit adds awareness in reltool that a module might occur in multiple applications, and it is allowed as long as the module or it's application is explicitely excluded in all but one of the containing applications. --- lib/reltool/src/reltool_server.erl | 189 ++++++++++++++------- lib/reltool/src/reltool_sys_win.erl | 26 +-- lib/reltool/test/Makefile | 4 +- lib/reltool/test/reltool_server_SUITE.erl | 125 +++++++++++++- .../test/reltool_server_SUITE_data/Makefile.src | 19 +++ .../otp_9229/x-1.0/ebin/x.app | 7 + .../otp_9229/x-1.0/src/mylib.erl | 4 + .../otp_9229/x-1.0/src/x.erl | 4 + .../otp_9229/y-1.0/ebin/y.app | 7 + .../otp_9229/y-1.0/src/mylib.erl | 4 + .../otp_9229/y-1.0/src/y.erl | 4 + 11 files changed, 321 insertions(+), 72 deletions(-) create mode 100644 lib/reltool/test/reltool_server_SUITE_data/Makefile.src create mode 100644 lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app create mode 100644 lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl create mode 100644 lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 9743289ca6..692baea0a4 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -465,16 +465,16 @@ analyse(#state{common = C, [MissingApp2 | Apps2] end, app_propagate_is_used_by(C, Apps3), - Apps4 = read_apps(C, Sys, Apps3, []), + {Apps4,Status4} = app_recap_dependencies(C, Sys, Apps3, [], Status3), %% io:format("Missing app: ~p\n", %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), Sys2 = Sys#sys{apps = Apps4}, - case verify_config(RelApps2, Sys2, Status3) of - {ok, _Warnings} = Status4 -> - {S#state{sys = Sys2}, Status4}; - {error, _} = Status4 -> - {S, Status4} + case verify_config(RelApps2, Sys2, Status4) of + {ok, _Warnings} = Status5 -> + {S#state{sys = Sys2}, Status5}; + {error, _} = Status5 -> + {S, Status5} end. apps_in_rels(Rels, Apps, Status) -> @@ -548,21 +548,24 @@ app_init_is_included(C, {derived, [_ | _]} -> % App is included in at least one rel {true, undefined, true, Status} end, - A2 = A#app{is_pre_included = IsPreIncl, + {Mods2,Status3} = lists:mapfoldl(fun(Mod,Acc) -> + mod_init_is_included(C, + Mod, + ModCond, + AppCond, + Default, + Acc) + end, + Status2, + Mods), + A2 = A#app{mods = Mods2, + is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, ets:insert(C#common.app_tab, A2), - lists:foreach(fun(Mod) -> - mod_init_is_included(C, - Mod, - ModCond, - AppCond, - Default) - end, - Mods), - {A2, Status2}. - -mod_init_is_included(C, M, ModCond, AppCond, Default) -> + {A2, Status3}. + +mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), IsIncl = case AppCond of @@ -595,9 +598,52 @@ mod_init_is_included(C, M, ModCond, AppCond, Default) -> Default end end, + M2 = M#mod{is_pre_included = IsIncl, is_included = IsIncl}, + + Status2 = + case ets:lookup(C#common.mod_tab,M#mod.name) of + [Existing] -> + case {Existing#mod.is_included,IsIncl} of + {false,_} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + M#mod.app_name, "."]), + ets:insert(C#common.mod_tab, M2), + reltool_utils:add_warning(Status,Warning); + {_,false} -> + Warning = + lists:concat( + ["Module ",M#mod.name, + " exists in applications ", Existing#mod.app_name, + " and ", M#mod.app_name, + ". Using module from application ", + Existing#mod.app_name, "."]), + + %% Don't insert in mod_tab - using Existing + reltool_utils:add_warning(Status,Warning); + {_,_} -> + Error = + lists:concat( + ["Module ",M#mod.name, + " potentially included by ", + "two different applications: ", + Existing#mod.app_name, " and ", + M#mod.app_name, "."]), + %% Don't insert in mod_tab - using Existing + reltool_utils:return_first_error(Status,Error) + end; + [] -> + ets:insert(C#common.mod_tab, M2), + Status + end, + %% print(M#mod.name, hipe, "~p -> ~p\n", [M2, IsIncl]), - ets:insert(C#common.mod_tab, M2). + {M2,Status2}. false_to_undefined(Bool) -> case Bool of @@ -612,21 +658,27 @@ app_propagate_is_included(_C, _Sys, [], Acc) -> Acc. mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - %% print(ModName, file, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), Acc2 = - case M2#mod.is_included of - true -> - %% Propagate include mark - mod_mark_is_included(C, Sys, ModName, M2#mod.uses_mods, Acc); - false -> - Acc; - undefined -> - Acc - end, + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + %% print(ModName, file, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", + %% [M2, M2#mod.is_included]), + case M2#mod.is_included of + true -> + %% Propagate include mark + mod_mark_is_included(C,Sys,ModName,M2#mod.uses_mods,Acc); + false -> + Acc; + undefined -> + Acc + end; + [_] -> + %% This module is currently used from a different application + %% Ignore + Acc + end, mod_propagate_is_included(C, Sys, A, Mods, Acc2); mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> Acc. @@ -740,9 +792,10 @@ mod_propagate_is_used_by(C, [#mod{name = ModName} | Mods]) -> mod_propagate_is_used_by(_C, []) -> ok. -read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> - {Mods2, IsIncl2} = read_apps(C, Sys, A, Mods, [], IsIncl), - Status = +app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc, Status) -> + {Mods2, IsIncl2, Status2} = + mod_recap_dependencies(C, Sys, A, Mods, [], IsIncl, Status), + AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; false -> ok @@ -759,34 +812,52 @@ read_apps(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc) -> UsedByApps2 = lists:usort(UsedByApps), A2 = A#app{mods = Mods2, - status = Status, + status = AppStatus, uses_mods = UsesMods2, used_by_mods = UsedByMods2, uses_apps = UsesApps2, used_by_apps = UsedByApps2, is_included = IsIncl2}, - read_apps(C, Sys, Apps, [A2 | Acc]); -read_apps(_C, _Sys, [], Acc) -> - lists:reverse(Acc). - -read_apps(C, Sys, A, [#mod{name = ModName} | Mods], Acc, IsIncl) -> - [M2] = ets:lookup(C#common.mod_tab, ModName), - Status = do_get_status(M2), - %% print(M2#mod.name, hipe, "status -> ~p\n", [Status]), - {IsIncl2, M3} = - case M2#mod.is_included of - true -> - UsedByMods = - [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, - ModName)], - {true, M2#mod{status = Status, used_by_mods = UsedByMods}}; - _ -> - {IsIncl, M2#mod{status = Status, used_by_mods = []}} - end, - ets:insert(C#common.mod_tab, M3), - read_apps(C, Sys, A, Mods, [M3 | Acc], IsIncl2); -read_apps(_C, _Sys, _A, [], Acc, IsIncl) -> - {lists:reverse(Acc), IsIncl}. + ets:insert(C#common.app_tab,A2), + app_recap_dependencies(C, Sys, Apps, [A2 | Acc], Status2); +app_recap_dependencies(_C, _Sys, [], Acc, Status) -> + {lists:reverse(Acc), Status}. + +mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> + case ets:lookup(C#common.mod_tab, ModName) of + [M2] when M2#mod.app_name=:=A#app.name -> + ModStatus = do_get_status(M2), + %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]), + {IsIncl2, M3} = + case M2#mod.is_included of + true -> + UsedByMods = + [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + ModName)], + {true, M2#mod{status = ModStatus, used_by_mods = UsedByMods}}; + _ -> + {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} + end, + ets:insert(C#common.mod_tab, M3), + mod_recap_dependencies(C, Sys, A, Mods, [M3 | Acc], IsIncl2, Status); + [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> + %% App is explicitely excluded so it is ok that the module + %% record does not exist for this module in this + %% application. + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status); + [M2] -> + %% A module is potensially included by multiple + %% applications. This is not allowed! + Error = + lists:concat( + ["Module ",ModName, + " potentially included by two different applications: ", + A#app.name, " and ", M2#mod.app_name, "."]), + Status2 = reltool_utils:return_first_error(Status,Error), + mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status2) + end; +mod_recap_dependencies(_C, _Sys, _A, [], Acc, IsIncl, Status) -> + {lists:reverse(Acc), IsIncl, Status}. do_get_status(M) -> if diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index dbb8e32aa2..76c064f1e7 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-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 @@ -1092,17 +1092,23 @@ move_app(S, {_ItemNo, AppBase}, Action) -> OldApp#app{incl_cond = AppCond}. do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> - {ok, AnalysedApp, Warnings} = reltool_server:set_app(ServerPid, NewApp), + Result = reltool_server:set_app(ServerPid, NewApp), [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], S2 = redraw_apps(S), - case Warnings of - [] -> - ignore; - _ -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING) - end, - {ok, AnalysedApp, S2}. + ReturnApp = + case Result of + {ok, AnalysedApp, []} -> + AnalysedApp; + {ok, AnalysedApp, Warnings} -> + Msg = lists:flatten([[W, $\n] || W <- Warnings]), + display_message(Msg, ?wxICON_WARNING), + AnalysedApp; + {error, Reason} -> + display_message(Reason, ?wxICON_ERROR), + {ok,OldApp} = reltool_server:get_app(ServerPid, NewApp#app.name), + OldApp + end, + {ok, ReturnApp, S2}. redraw_apps(#state{server_pid = ServerPid, source = SourceCtrl, diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 62fe05238b..767454b66a 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -76,8 +76,8 @@ release_tests_spec: opt $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) $(RELSYSDIR) $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + chmod -R u+w $(RELSYSDIR) + @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index b77560db94..9ed79e8c95 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include("reltool_test_lib.hrl"). +-include_lib("common_test/include/ct.hrl"). -define(NODE_NAME, '__RELTOOL__TEMPORARY_TEST__NODE__'). -define(WORK_DIR, "reltool_work_dir"). @@ -53,7 +54,7 @@ all() -> [start_server, set_config, create_release, create_script, create_target, create_embedded, create_standalone, create_old_target, - otp_9135]. + otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod]. groups() -> []. @@ -360,6 +361,114 @@ create_old_target(_Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% OTP-9229 - handle duplicated module names, i.e. same module name +%% exists in two applications. + +%% Include on app, exclude the other +otp_9229_exclude_app(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclApp = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclApp}]), + ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(false, filelib:is_file(AbsYArchive)), + ?m(non_existing, mod_path(Node,y)), + + ?msym(ok, stop_node(Node)), + + ok. + +%% Include both apps, but exclude common module from one app +otp_9229_exclude_mod(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"otp_9229"), + + %% Configure the server + ExclMod = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include},{mod, mylib,[{incl_cond,exclude}]}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_excl_mod"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), + {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclMod}]), + ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + + AbsTargetDir = filename:absname(TargetDir), + XArchive = "x-1.0.ez", + AbsXArchive = filename:join([AbsTargetDir,lib,XArchive]), + XEbin = ["ebin","x-1.0",XArchive], + YArchive = "y-1.0.ez", + AbsYArchive = filename:join([AbsTargetDir,lib,YArchive]), + YEbin = ["ebin","y-1.0",YArchive], + + ?m(true, filelib:is_file(AbsXArchive)), + ?m(XEbin, mod_path(Node,x)), + ?m(XEbin, mod_path(Node,mylib)), + ?m(true, filelib:is_file(AbsYArchive)), + ?m(YEbin, mod_path(Node,y)), + + %% Remove path to XEbin and check that mylib is not located in YEbin + Mylib = rpc:call(Node,code,which,[mylib]), + rpc:call(Node,code,del_path,[filename:dirname(Mylib)]), + ?m(non_existing, mod_path(Node,mylib)), + + ?msym(ok, stop_node(Node)), + + ok. + + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Library functions @@ -407,6 +516,20 @@ os_cmd(Cmd) when is_list(Cmd) -> end end. +%% Returns the location (directory) of the given module. Split, +%% reverted and relative to the lib dir. +mod_path(Node,Mod) -> + case rpc:call(Node,code,which,[Mod]) of + Path when is_list(Path) -> + lists:takewhile( + fun("lib") -> false; + (_) -> true + end, + lists:reverse(filename:split(filename:dirname(Path)))); + Other -> + Other + end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Node handling diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src new file mode 100644 index 0000000000..049e8dd6cc --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -0,0 +1,19 @@ +EFLAGS=+debug_info + +OTP9229= \ + otp_9229/x-1.0/ebin/x.@EMULATOR@ \ + otp_9229/x-1.0/ebin/mylib.@EMULATOR@ \ + otp_9229/y-1.0/ebin/y.@EMULATOR@ \ + otp_9229/y-1.0/ebin/mylib.@EMULATOR@ + + +all: $(OTP9229) + +otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl +otp_9229/x-1.0/ebin/mylib.@EMULATOR@: otp_9229/x-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/mylib.erl +otp_9229/y-1.0/ebin/y.@EMULATOR@: otp_9229/y-1.0/src/y.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/y.erl +otp_9229/y-1.0/ebin/mylib.@EMULATOR@: otp_9229/y-1.0/src/mylib.erl + erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/mylib.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app new file mode 100644 index 0000000000..e597704b19 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, x, + [{description, "X CXC 138 11"}, + {vsn, "1.0"}, + {modules, [x, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl new file mode 100644 index 0000000000..17ff84f08f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/x-1.0/src/x.erl @@ -0,0 +1,4 @@ +-module(x). +-export([x/0]). + +x() ->mylib:foo(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app new file mode 100644 index 0000000000..5b327862e3 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, y, + [{description, "Y CXC 138 11"}, + {vsn, "1.0"}, + {modules, [y, mylib]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl new file mode 100644 index 0000000000..c8603d1a8e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/mylib.erl @@ -0,0 +1,4 @@ +-module(mylib). +-export([foo/0]). + +foo() -> erlang:time(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl new file mode 100644 index 0000000000..342e7da7d5 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/otp_9229/y-1.0/src/y.erl @@ -0,0 +1,4 @@ +-module(y). +-export([y/0]). + +y() ->mylib:foo(). -- cgit v1.2.3 From 87fce5499baa1824eebe822cc62608c5bb58d3cf Mon Sep 17 00:00:00 2001 From: Siri Hansen Date: Tue, 17 Aug 2010 13:34:35 +0200 Subject: Add SASL test suite --- lib/sasl/Makefile | 4 +- lib/sasl/examples/ebin/.gitignore | 0 lib/sasl/examples/src/Makefile | 78 + lib/sasl/examples/src/target_system.erl | 259 +++ lib/sasl/test/.gitignore | 5 + lib/sasl/test/Makefile | 91 + lib/sasl/test/alarm_handler_SUITE.erl | 179 ++ lib/sasl/test/installer.erl | 778 +++++++ lib/sasl/test/overload_SUITE.erl | 175 ++ lib/sasl/test/rb_SUITE.erl | 606 ++++++ lib/sasl/test/release_handler_SUITE.erl | 1651 +++++++++++++++ .../test/release_handler_SUITE_data/Makefile.src | 108 + .../P2B/a-2.0/ebin/a.app | 8 + .../release_handler_SUITE_data/P2B/a-2.0/src/a.erl | 47 + .../P2B/a-2.0/src/a_sup.erl | 37 + .../app1_app2/lib1/app1-1.0/ebin/app1.app | 9 + .../app1_app2/lib1/app1-1.0/src/app1.erl | 22 + .../app1_app2/lib1/app1-1.0/src/app1_server.erl | 32 + .../app1_app2/lib1/app1-1.0/src/app1_sup.erl | 17 + .../app1_app2/lib1/app2-1.0/ebin/app2.app | 9 + .../app1_app2/lib1/app2-1.0/src/app2.erl | 17 + .../app1_app2/lib1/app2-1.0/src/app2_server.erl | 32 + .../app1_app2/lib1/app2-1.0/src/app2_sup.erl | 17 + .../app1_app2/lib2/app1-2.0/ebin/app1.app | 9 + .../app1_app2/lib2/app1-2.0/ebin/app1.appup | 4 + .../app1_app2/lib2/app1-2.0/src/app1.erl | 22 + .../app1_app2/lib2/app1-2.0/src/app1_server.erl | 35 + .../app1_app2/lib2/app1-2.0/src/app1_sup.erl | 17 + .../app1_app2/lib2/app2-1.0/ebin/app2.app | 9 + .../app1_app2/lib2/app2-1.0/src/app2.erl | 17 + .../app1_app2/lib2/app2-1.0/src/app2_server.erl | 32 + .../app1_app2/lib2/app2-1.0/src/app2_sup.erl | 17 + lib/sasl/test/release_handler_SUITE_data/c/aa.erl | 41 + lib/sasl/test/release_handler_SUITE_data/c/b.erl | 38 + lib/sasl/test/release_handler_SUITE_data/c/c.app | 8 + .../test/release_handler_SUITE_data/c/c_sup.erl | 40 + .../release_handler_SUITE_data/clients/start_cli1 | 38 + .../release_handler_SUITE_data/clients/start_cli2 | 37 + .../lib/a-1.0/ebin/a.app | 8 + .../release_handler_SUITE_data/lib/a-1.0/src/a.app | 8 + .../release_handler_SUITE_data/lib/a-1.0/src/a.erl | 49 + .../lib/a-1.0/src/a_sup.erl | 37 + .../lib/a-1.1/ebin/a.app | 8 + .../lib/a-1.1/ebin/a.appup | 3 + .../release_handler_SUITE_data/lib/a-1.1/src/a.erl | 54 + .../lib/a-1.1/src/a_sup.erl | 37 + .../lib/installer-1.0/ebin/installer.app | 6 + .../lib/installer-1.0/src/installer.erl | 1 + .../otp_2740/vsn_atom.erl | 26 + .../otp_2740/vsn_list.erl | 26 + .../otp_2740/vsn_numeric.erl | 26 + .../otp_2740/vsn_string.erl | 26 + .../otp_2740/vsn_tuple.erl | 26 + lib/sasl/test/release_handler_SUITE_data/start | 29 + .../release_handler_SUITE_data/target_system.erl | 1 + lib/sasl/test/sasl.cover | 2 + lib/sasl/test/sasl.spec | 1 + lib/sasl/test/sasl_SUITE.erl | 98 + lib/sasl/test/systools_SUITE.erl | 2112 ++++++++++++++++++++ .../d_bad_app_vsn/lib/db-2.1/ebin/db.app | 8 + .../d_bad_app_vsn/lib/db-2.1/ebin/db.appup | 20 + .../d_bad_app_vsn/lib/db-2.1/src/db1.erl | 2 + .../d_bad_app_vsn/lib/db-2.1/src/db2.erl | 2 + .../d_bad_app_vsn/lib/fe-3.1/ebin/fe.app | 8 + .../d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup | 27 + .../d_bad_app_vsn/lib/fe-3.1/src/fe1.erl | 2 + .../d_bad_app_vsn/lib/fe-3.1/src/fe2.erl | 2 + .../d_bad_app_vsn/lib/fe-3.1/src/fe3.erl | 2 + .../d_bad_appup/lib/fe-3.1/ebin/fe.app | 7 + .../d_bad_appup/lib/fe-3.1/ebin/fe.appup | 27 + .../d_bad_mod+warn/lib/db-2.1/ebin/db.app | 8 + .../d_bad_mod+warn/lib/db-2.1/ebin/db.appup | 20 + .../d_bad_mod+warn/lib/db-2.1/src/db1.erl | 2 + .../d_bad_mod+warn/lib/db-2.1/src/db2.erl | 2 + .../d_bad_mod+warn/lib/fe-3.1/ebin/fe.app | 8 + .../d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup | 27 + .../d_bad_mod+warn/lib/fe-3.1/src/fe1.erl | 2 + .../d_bad_mod+warn/lib/fe-3.1/src/fe2.erl | 2 + .../d_bad_mod+warn/lib/fe-3.1/src/fe3.erl | 2 + .../d_links/lib/db-2.1/ebin/db.app | 8 + .../d_links/lib/db-2.1/ebin/db.appup | 20 + .../d_links/lib/db-2.1/src/db2.erl | 2 + .../d_links/lib/db-2.1/src/db3.erl | 2 + .../d_links/lib/fe-3.1/ebin/fe.appup | 27 + .../d_links/lib/fe-3.1/src/fe1.erl | 2 + .../d_links/lib/fe-3.1/src/fe2.erl | 2 + .../d_links/lib/fe-3.1/src/fe3.erl | 2 + .../d_missing_src/lib/db-2.1/ebin/db.app | 8 + .../d_missing_src/lib/db-2.1/ebin/db.appup | 20 + .../d_missing_src/lib/db-2.1/src/db1.erl | 2 + .../d_missing_src/lib/db-2.1/src/db2.erl | 2 + .../d_missing_src/lib/fe-3.1/ebin/fe.app | 8 + .../d_missing_src/lib/fe-3.1/ebin/fe.appup | 27 + .../d_missing_src/lib/fe-3.1/src/fe1.erl | 2 + .../d_missing_src/lib/fe-3.1/src/fe2.erl | 2 + .../d_missing_src/lib/fe-3.1/src/fe3.erl | 2 + .../d_no_appup/lib/fe-2.1/ebin/fe.app | 8 + .../d_no_appup/lib/fe-2.1/ebin/fe.appup | 27 + .../d_no_appup/lib/fe-3.1/ebin/fe.app | 7 + .../d_normal/lib/db-1.0/ebin/db.app | 8 + .../d_normal/lib/db-1.0/src/db1.erl | 2 + .../d_normal/lib/db-1.0/src/db2.erl | 2 + .../d_normal/lib/db-1.1/ebin/db.app | 8 + .../d_normal/lib/db-1.1/src/db1.erl | 2 + .../d_normal/lib/db-1.1/src/db2.erl | 2 + .../d_normal/lib/db-2.1/ebin/db.app | 7 + .../d_normal/lib/db-2.1/ebin/db.appup | 20 + .../d_normal/lib/db-2.1/src/db1.erl | 13 + .../d_normal/lib/db-2.1/src/db2.erl | 2 + .../d_normal/lib/db-3.1/ebin/db.app | 7 + .../d_normal/lib/db-3.1/ebin/db.appup | 20 + .../d_normal/lib/db-3.1/src/db1.erl | 2 + .../d_normal/lib/db-3.1/src/db2.erl | 2 + .../d_normal/lib/fe-2.1.1/ebin/fe.app | 8 + .../d_normal/lib/fe-2.1.1/src/fe1.erl | 2 + .../d_normal/lib/fe-2.1.1/src/fe2.erl | 2 + .../d_normal/lib/fe-2.1.1/src/fe3.erl | 2 + .../d_normal/lib/fe-2.1/ebin/fe.app | 8 + .../d_normal/lib/fe-2.1/src/fe1.erl | 2 + .../d_normal/lib/fe-2.1/src/fe2.erl | 2 + .../d_normal/lib/fe-2.1/src/fe3.erl | 2 + .../d_normal/lib/fe-3.1/ebin/fe.app | 7 + .../d_normal/lib/fe-3.1/ebin/fe.appup | 27 + .../d_normal/lib/fe-3.1/src/fe1.erl | 7 + .../d_normal/lib/fe-3.1/src/fe2.erl | 2 + .../d_normal/lib/fe-3.1/src/fe3.erl | 2 + .../systools_SUITE_data/lib/kernel/ebin/kernel.app | 6 + .../lib/kernel/ebin/kernel.appup | 12 + .../systools_SUITE_data/lib/stdlib/ebin/stdlib.app | 6 + .../lib/stdlib/ebin/stdlib.appup | 12 + lib/sasl/test/systools_rc_SUITE.erl | 488 +++++ 131 files changed, 8181 insertions(+), 2 deletions(-) create mode 100644 lib/sasl/examples/ebin/.gitignore create mode 100644 lib/sasl/examples/src/Makefile create mode 100644 lib/sasl/examples/src/target_system.erl create mode 100644 lib/sasl/test/.gitignore create mode 100644 lib/sasl/test/Makefile create mode 100644 lib/sasl/test/alarm_handler_SUITE.erl create mode 100644 lib/sasl/test/installer.erl create mode 100644 lib/sasl/test/overload_SUITE.erl create mode 100644 lib/sasl/test/rb_SUITE.erl create mode 100644 lib/sasl/test/release_handler_SUITE.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/Makefile.src create mode 100644 lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/c/aa.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/c/b.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/c/c.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl create mode 100755 lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 create mode 100755 lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app create mode 120000 lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl create mode 100644 lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl create mode 100755 lib/sasl/test/release_handler_SUITE_data/start create mode 120000 lib/sasl/test/release_handler_SUITE_data/target_system.erl create mode 100644 lib/sasl/test/sasl.cover create mode 100644 lib/sasl/test/sasl.spec create mode 100644 lib/sasl/test/sasl_SUITE.erl create mode 100644 lib/sasl/test/systools_SUITE.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl create mode 100644 lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl create mode 100644 lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app create mode 100644 lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup create mode 100644 lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app create mode 100644 lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup create mode 100644 lib/sasl/test/systools_rc_SUITE.erl diff --git a/lib/sasl/Makefile b/lib/sasl/Makefile index 2affcf1e40..4073e5af85 100644 --- a/lib/sasl/Makefile +++ b/lib/sasl/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-2010. 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 @@ -23,7 +23,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # Macros # -SUB_DIRECTORIES = src doc/src +SUB_DIRECTORIES = src doc/src examples/src include vsn.mk VSN = $(SASL_VSN) diff --git a/lib/sasl/examples/ebin/.gitignore b/lib/sasl/examples/ebin/.gitignore new file mode 100644 index 0000000000..e69de29bb2 diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile new file mode 100644 index 0000000000..4a4e04a536 --- /dev/null +++ b/lib/sasl/examples/src/Makefile @@ -0,0 +1,78 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2010. 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% +# + +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(SASL_VSN) + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/sasl-$(VSN) + +# ---------------------------------------------------- +# Common Macros +# ---------------------------------------------------- +EXTRA_ERLC_FLAGS = +warn_unused_vars +ERL_COMPILE_FLAGS += $(EXTRA_ERLC_FLAGS) + + +MODULES = target_system + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +debug opt: $(TARGET_FILES) + +clean: + rm -fr $(TARGET_FILES) *~ *.beam + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) $(RELSYSDIR)/examples/src + $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin + (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -)) + chmod -f -R ug+w $(RELSYSDIR)/examples + +release_docs_spec: + + + + + + + diff --git a/lib/sasl/examples/src/target_system.erl b/lib/sasl/examples/src/target_system.erl new file mode 100644 index 0000000000..0e1e0b2324 --- /dev/null +++ b/lib/sasl/examples/src/target_system.erl @@ -0,0 +1,259 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(target_system). +-export([create/1, create/2, install/2]). + +%% Note: RelFileName below is the *stem* without trailing .rel, +%% .script etc. +%% + +%% create(RelFileName) +%% +create(RelFileName) -> + create(RelFileName,[]). + +create(RelFileName,SystoolsOpts) -> + RelFile = RelFileName ++ ".rel", + Dir = filename:dirname(RelFileName), + PlainRelFileName = filename:join(Dir,"plain"), + PlainRelFile = PlainRelFileName ++ ".rel", + io:fwrite("Reading file: ~p ...~n", [RelFile]), + {ok, [RelSpec]} = file:consult(RelFile), + io:fwrite("Creating file: ~p from ~p ...~n", + [PlainRelFile, RelFile]), + {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + AppVsns} = RelSpec, + PlainRelSpec = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + lists:filter(fun({kernel, _}) -> + true; + ({stdlib, _}) -> + true; + (_) -> + false + end, AppVsns) + }, + {ok, Fd} = file:open(PlainRelFile, [write]), + io:fwrite(Fd, "~p.~n", [PlainRelSpec]), + file:close(Fd), + + io:fwrite("Making \"~s.script\" and \"~s.boot\" files ...~n", + [PlainRelFileName,PlainRelFileName]), + make_script(PlainRelFileName,SystoolsOpts), + + io:fwrite("Making \"~s.script\" and \"~s.boot\" files ...~n", + [RelFileName, RelFileName]), + make_script(RelFileName,SystoolsOpts), + + TarFileName = filename:join(Dir,RelFileName ++ ".tar.gz"), + io:fwrite("Creating tar file ~p ...~n", [TarFileName]), + make_tar(RelFileName,SystoolsOpts), + + TmpDir = filename:join(Dir,"tmp"), + io:fwrite("Creating directory ~p ...~n",[TmpDir]), + file:make_dir(TmpDir), + + io:fwrite("Extracting ~p into directory ~p ...~n", [TarFileName,TmpDir]), + extract_tar(TarFileName, TmpDir), + + TmpBinDir = filename:join([TmpDir, "bin"]), + ErtsBinDir = filename:join([TmpDir, "erts-" ++ ErtsVsn, "bin"]), + io:fwrite("Deleting \"erl\" and \"start\" in directory ~p ...~n", + [ErtsBinDir]), + file:delete(filename:join([ErtsBinDir, "erl"])), + file:delete(filename:join([ErtsBinDir, "start"])), + + io:fwrite("Creating temporary directory ~p ...~n", [TmpBinDir]), + file:make_dir(TmpBinDir), + + io:fwrite("Copying file \"~s.boot\" to ~p ...~n", + [PlainRelFileName, filename:join([TmpBinDir, "start.boot"])]), + copy_file(PlainRelFileName++".boot",filename:join([TmpBinDir, "start.boot"])), + + io:fwrite("Copying files \"epmd\", \"run_erl\" and \"to_erl\" from \n" + "~p to ~p ...~n", + [ErtsBinDir, TmpBinDir]), + copy_file(filename:join([ErtsBinDir, "epmd"]), + filename:join([TmpBinDir, "epmd"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "run_erl"]), + filename:join([TmpBinDir, "run_erl"]), [preserve]), + copy_file(filename:join([ErtsBinDir, "to_erl"]), + filename:join([TmpBinDir, "to_erl"]), [preserve]), + + StartErlDataFile = filename:join([TmpDir, "releases", "start_erl.data"]), + io:fwrite("Creating ~p ...~n", [StartErlDataFile]), + StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]), + write_file(StartErlDataFile, StartErlData), + + io:fwrite("Recreating tar file ~p from contents in directory ~p ...~n", + [TarFileName,TmpDir]), + {ok, Tar} = erl_tar:open(TarFileName, [write, compressed]), + %% {ok, Cwd} = file:get_cwd(), + %% file:set_cwd("tmp"), + ErtsDir = "erts-"++ErtsVsn, + erl_tar:add(Tar, filename:join(TmpDir,"bin"), "bin", []), + erl_tar:add(Tar, filename:join(TmpDir,ErtsDir), ErtsDir, []), + erl_tar:add(Tar, filename:join(TmpDir,"releases"), "releases", []), + erl_tar:add(Tar, filename:join(TmpDir,"lib"), "lib", []), + erl_tar:close(Tar), + %% file:set_cwd(Cwd), + io:fwrite("Removing directory ~p ...~n",[TmpDir]), + remove_dir_tree(TmpDir), + ok. + + +install(RelFileName, RootDir) -> + TarFile = RelFileName ++ ".tar.gz", + io:fwrite("Extracting ~p ...~n", [TarFile]), + extract_tar(TarFile, RootDir), + StartErlDataFile = filename:join([RootDir, "releases", "start_erl.data"]), + {ok, StartErlData} = read_txt_file(StartErlDataFile), + [ErlVsn, _RelVsn| _] = string:tokens(StartErlData, " \n"), + ErtsBinDir = filename:join([RootDir, "erts-" ++ ErlVsn, "bin"]), + BinDir = filename:join([RootDir, "bin"]), + io:fwrite("Substituting in erl.src, start.src and start_erl.src to\n" + "form erl, start and start_erl ...\n"), + subst_src_scripts(["erl", "start", "start_erl"], ErtsBinDir, BinDir, + [{"FINAL_ROOTDIR", RootDir}, {"EMU", "beam"}], + [preserve]), + io:fwrite("Creating the RELEASES file ...\n"), + create_RELEASES(RootDir, + filename:join([RootDir, "releases", RelFileName])). + +%% LOCALS + +%% make_script(RelFileName,Opts) +%% +make_script(RelFileName,Opts) -> + systools:make_script(RelFileName, [no_module_tests, + {outdir,filename:dirname(RelFileName)} + |Opts]). + +%% make_tar(RelFileName,Opts) +%% +make_tar(RelFileName,Opts) -> + RootDir = code:root_dir(), + systools:make_tar(RelFileName, [{erts, RootDir}, + {outdir,filename:dirname(RelFileName)} + |Opts]). + +%% extract_tar(TarFile, DestDir) +%% +extract_tar(TarFile, DestDir) -> + erl_tar:extract(TarFile, [{cwd, DestDir}, compressed]). + +create_RELEASES(DestDir, RelFileName) -> + release_handler:create_RELEASES(DestDir, RelFileName ++ ".rel"). + +subst_src_scripts(Scripts, SrcDir, DestDir, Vars, Opts) -> + lists:foreach(fun(Script) -> + subst_src_script(Script, SrcDir, DestDir, + Vars, Opts) + end, Scripts). + +subst_src_script(Script, SrcDir, DestDir, Vars, Opts) -> + subst_file(filename:join([SrcDir, Script ++ ".src"]), + filename:join([DestDir, Script]), + Vars, Opts). + +subst_file(Src, Dest, Vars, Opts) -> + {ok, Conts} = read_txt_file(Src), + NConts = subst(Conts, Vars), + write_file(Dest, NConts), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +%% subst(Str, Vars) +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Str, using the list +%% of variables in Vars. +%% +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). + +copy_file(Src, Dest, Opts) -> + {ok,_} = file:copy(Src, Dest), + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +write_file(FName, Conts) -> + {ok, Fd} = file:open(FName, [write]), + file:write(Fd, Conts), + file:close(Fd). + +read_txt_file(File) -> + {ok, Bin} = file:read_file(File), + {ok, binary_to_list(Bin)}. + +remove_dir_tree(Dir) -> + remove_all_files(".", [Dir]). + +remove_all_files(Dir, Files) -> + lists:foreach(fun(File) -> + FilePath = filename:join([Dir, File]), + case filelib:is_dir(FilePath) of + true -> + {ok, DirFiles} = file:list_dir(FilePath), + remove_all_files(FilePath, DirFiles), + file:del_dir(FilePath); + _ -> + file:delete(FilePath) + end + end, Files). diff --git a/lib/sasl/test/.gitignore b/lib/sasl/test/.gitignore new file mode 100644 index 0000000000..76e706b874 --- /dev/null +++ b/lib/sasl/test/.gitignore @@ -0,0 +1,5 @@ +# +# Don't ignore *.beam files in any sub-directory. +# + +!*.beam diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile new file mode 100644 index 0000000000..ad08c8136b --- /dev/null +++ b/lib/sasl/test/Makefile @@ -0,0 +1,91 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 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% +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + sasl_SUITE \ + alarm_handler_SUITE \ + installer \ + release_handler_SUITE \ + systools_SUITE \ + systools_rc_SUITE \ + overload_SUITE \ + rb_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INSTALL_PROGS= $(TARGET_FILES) + +EMAKEFILE=Emakefile + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/sasl_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_MAKE_FLAGS += +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/test_server/include \ + -I$(ERL_TOP)/lib/sasl/src + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +make_emakefile: + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make'\ + > $(EMAKEFILE) + $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES)\ + >> $(EMAKEFILE) + +tests debug opt: make_emakefile + erl $(ERL_MAKE_FLAGS) -make + +clean: + rm -f $(EMAKEFILE) + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: make_emakefile + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) + $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) $(RELSYSDIR) + chmod -f -R u+w $(RELSYSDIR) + @tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + +release_docs_spec: diff --git a/lib/sasl/test/alarm_handler_SUITE.erl b/lib/sasl/test/alarm_handler_SUITE.erl new file mode 100644 index 0000000000..a98e8c9c67 --- /dev/null +++ b/lib/sasl/test/alarm_handler_SUITE.erl @@ -0,0 +1,179 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(alarm_handler_SUITE). + +-include_lib("test_server/include/test_server.hrl"). + +%%----------------------------------------------------------------- +%% We will add an own alarm handler in order to verify that the +%% alarm_handler deliver the expected events. +%%----------------------------------------------------------------- + +-export([init_per_suite/1, end_per_suite/1, all/0,groups/0, + init_per_group/2,end_per_group/2, + set_alarm/1, clear_alarm/1, swap/1]). + +-export([init/1, handle_event/2, handle_call/2, handle_info/2, + terminate/2]). + + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + [set_alarm, clear_alarm, swap]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%----------------------------------------------------------------- + +set_alarm(suite) -> []; +set_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line ok = alarm_handler:set_alarm(Alarm1), + reported(set_alarm, Alarm1), + ?line ok = alarm_handler:set_alarm(Alarm2), + reported(set_alarm, Alarm2), + ?line ok = alarm_handler:set_alarm(Alarm3), + reported(set_alarm, Alarm3), + + ?line [Alarm3,Alarm2,Alarm1] = alarm_handler:get_alarms(), + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ?line [] = alarm_handler:get_alarms(), + + test_server:messages_get(), + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +clear_alarm(suite) -> []; +clear_alarm(Config) when is_list(Config) -> + ?line gen_event:add_handler(alarm_handler, ?MODULE, self()), + Alarm1 = {alarm1, "this is the alarm"}, + Alarm2 = {"alarm2", this_is_the_alarm}, + Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + alarm_handler:set_alarm(Alarm1), + alarm_handler:set_alarm(Alarm2), + alarm_handler:set_alarm(Alarm3), + test_server:messages_get(), + + ?line ok = alarm_handler:clear_alarm(alarm1), + reported(clear_alarm, alarm1), + ?line ok = alarm_handler:clear_alarm("alarm2"), + reported(clear_alarm, "alarm2"), + ?line ok = alarm_handler:clear_alarm({alarm3}), + reported(clear_alarm, {alarm3}), + ?line [] = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, ?MODULE, []), + ok. + +%%----------------------------------------------------------------- + +swap(suite) -> []; +swap(Config) when is_list(Config) -> + ?line Alarm1 = {alarm1, "this is the alarm"}, + ?line Alarm2 = {"alarm2", this_is_the_alarm}, + ?line Alarm3 = {{alarm3}, {this_is,"the_alarm"}}, + ?line alarm_handler:set_alarm(Alarm1), + ?line alarm_handler:set_alarm(Alarm2), + ?line alarm_handler:set_alarm(Alarm3), + + ?line foo, + case gen_event:which_handlers(alarm_handler) of + [alarm_handler] -> + ?line ok = gen_event:swap_handler(alarm_handler, + {alarm_handler, swap}, + {?MODULE, self()}), + ?line [?MODULE] = gen_event:which_handlers(alarm_handler), + Alarms = [Alarm3, Alarm2, Alarm1], + reported(swap_alarms, Alarms), + + %% get_alarms is only valid with the default handler installed. + ?line {error, _} = alarm_handler:get_alarms(), + + ?line my_yes = gen_event:delete_handler(alarm_handler, + ?MODULE, []), + ?line gen_event:add_handler(alarm_handler, alarm_handler, []), + ok; + _ -> + alarm_handler:clear_alarm(alarm1), + alarm_handler:clear_alarm("alarm2"), + alarm_handler:clear_alarm({alarm3}), + ok + end. + +%%----------------------------------------------------------------- +%% Check that the alarm has been received. +%%----------------------------------------------------------------- +reported(Tag, Data) -> + receive + {Tag, Data} -> + test_server:messages_get(), + ok + after 1000 -> + test_server:fail(no_alarm_received) + end. + +%%----------------------------------------------------------------- +%% The error_logger handler (gen_event behaviour). +%% Sends a notification to the Tester process about the events +%% generated by the Tester process. +%%----------------------------------------------------------------- +init(Tester) when is_pid(Tester) -> + {ok, Tester}; +init({Tester, {alarm_handler,Alarms}}) -> % Swap from default handler. + Tester ! {swap_alarms, Alarms}, + {ok, Tester}. + +handle_event({set_alarm, Alarm}, Tester) -> + Tester ! {set_alarm, Alarm}, + {ok, Tester}; +handle_event({clear_alarm, AlarmId}, Tester) -> + Tester ! {clear_alarm, AlarmId}, + {ok, Tester}; +handle_event(_Event, Tester) -> + {ok, Tester}. + +handle_info(_, Tester) -> + {ok, Tester}. + +handle_call(_Query, Tester) -> {ok, {error, bad_query}, Tester}. + +terminate(_Reason, _Tester) -> + my_yes. diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl new file mode 100644 index 0000000000..a114c4b5c9 --- /dev/null +++ b/lib/sasl/test/installer.erl @@ -0,0 +1,778 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(installer). + +%%-compile(export_all). +-export([install_1/2]). +-export([install_2/1]). +-export([install_3/2]). +-export([install_3a/1]). +-export([install_4/1]). +-export([install_5/1]). +-export([install_5a/1]). +-export([install_6/1]). +-export([install_7/1]). +-export([install_8/1]). +-export([install_9/1]). +-export([install_10/1]). +-export([install_11/1]). +-export([client1_1/4]). +-export([client2/3]). +-export([stop/1]). +-export([unpack_p1h/2]). +-export([permanent_p1h/1]). +-export([reg_proc/1]). +-export([registered_loop/1]). + +-define(print(List), {rh_print, TestNode} ! {print, {?MODULE, ?LINE}, List}). +-define(print_line(Line,List), {rh_print, TestNode} ! {print, {?MODULE, Line}, List}). +-define(fail(Term), exit({?MODULE, ?LINE, Term})). +-define(fail_line(Line,Term), exit({?MODULE, Line, Term})). + +-define(check_release(Vsn,Status,Apps), + check_release(TestNode,node(),Vsn,Status,Apps,?LINE)). +-define(check_release_client(Node,Vsn,Status,Apps), + check_release(TestNode,Node,Vsn,Status,Apps,?LINE)). + +-define(check_running_app(App,Vsn), + check_running_app(TestNode,node(),App,Vsn,?LINE)). +-define(check_running_app_client(Node,App,Vsn), + check_running_app(TestNode,Node,App,Vsn,?LINE)). + + +install_1(TestNode,PrivDir) -> + ?print([TestNode]), + ?print(["install_1 start"]), + + % Unpack and install P1H + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?print(["unpack_release P1H ok"]), + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_release P1H ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + X = a:a(), + ?print(["X", X]), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + ?print(["install_1 end OK"]), + ok. + % release_handler_SUITE will reboot this node now! + +install_2(TestNode) -> + ?print(["install_2 start"]), + + % Check that P1H is still unpacked, install it and make_permanent + ?check_release("P1H",unpacked,["a-1.0"]), + ?print(["install_2 P1H unpacked"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?print(["install_2 install_release ok"]), + ?check_release("P1H",current,["a-1.0"]), + ?check_running_app(a,"1.0"), + ok = release_handler:make_permanent("P1H"). + % release_handler_SUITE will reboot this node now! + +install_3(TestNode,PrivDir) -> + ?print(["install_3 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Unpack and install P1I + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + ?print(["install_3 unpack_release P1I ok"]), + ?check_release("P1I",unpacked,["a-1.1"]), + {ok,"P1H",[{extra, gott}]} = release_handler:check_install_release("P1I"), + {error,_} = release_handler:check_install_release("P1J"), + {ok,"P1H",[{extra, gott}]} = release_handler:install_release("P1I"), + ?print(["install_3 install_release P1I ok"]), + ?check_release("P1I",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X2 = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = a:b(), + + % Unpack and install P2A + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + ?print(["install_3 unpack_release P2A ok"]), + ?check_release("P2A",unpacked,["a-1.1"]), + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok = release_handler:make_permanent("P1I"), + ?print(["install_3 make_permanent P1I ok"]), + ?check_release("P1I",permanent,["a-1.1"]), + ok. + +install_3a(TestNode) -> + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_3 P2A installed"]), + ok. + + + +install_4(TestNode) -> + ?print(["install_4 start"]), + + % Check that P2A is in use. + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok. + % release_handler_SUITE will reboot this node now! + +install_5(TestNode) -> + ?print(["install_5 start"]), + + % Check that P1I is used + {ok, "P1I", [new_emu]} = release_handler:check_install_release("P2A"), + ok. + +install_5a(TestNode) -> + % Install P2A again + {ok, "P1I", [new_emu]} = release_handler:install_release("P2A"), + %% Node is rebooted by the release_handler:install_release + %% (init:reboot) because P2A includes a new erts vsn and the relup + %% file contains a 'restart_new_emulator' instruction. + ?print(["install_5 P2A installed"]), + ok. + +install_6(TestNode) -> + ?print(["install_6 start"]), + + % Check that P2A is used + ?check_release("P2A",current,["a-1.1"]), + ?check_running_app(a,"1.1"), + X = a:a(), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = a:b(), + ok = release_handler:make_permanent("P2A"). + % release_handler_SUITE will reboot this node now! + + +install_7(TestNode) -> + ?print(["install_7 start"]), + + % Check that P2A is used + ?check_release("P2A",permanent,["a-1.1"]), + + % Install old P1H + ok = release_handler:reboot_old_release("P1H"), + ok. + +install_8(TestNode) -> + ?print(["install_8 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2A and check that a-1.1 and erts- are removed + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + true = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + {ok, Dirs} = file:list_dir(code:root_dir()), + ["erts-4.4"] = lists:filter(fun(Dir) -> lists:prefix("erts-",Dir) end, Dirs), + ok. + % release_handler_SUITE will reboot this node now! + +install_9(TestNode) -> + ?print(["install_9 start"]), + + % Check that P1H is permanent + ?check_release("P1H",permanent,["a-1.0"]), + X = a:a(), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + % Install old P1G + ok = release_handler:reboot_old_release("P1G"), + ok. + +install_10(TestNode) -> + ?print(["install_10 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ?check_release("P1H",old,["a-1.0"]), + + %% Remove P1H and check that both versions of application a is removed + ok = release_handler:remove_release("P1H"), + {ok, Libs} = file:list_dir(code:lib_dir()), + {_,_,StdlibVsn} = lists:keyfind(stdlib,1,application:which_applications()), + true = lists:member("stdlib-"++StdlibVsn, Libs), + false = lists:member("a-1.0", Libs), + false = lists:member("a-1.1", Libs), + ok. + % release_handler_SUITE will reboot this node now! + +install_11(TestNode) -> + ?print(["install_11 start"]), + + % Check that P1G is permanent + ?check_release("P1G",permanent,[]), + ok. + + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node runs all tests as in installer/1 test case. +%% Thus, the client node will be rebooted several times. +%% The to_erl /tmp/NODENAME@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/NODENAME@HOSTNAME/log +%%----------------------------------------------------------------- + + +client1_1(TestNode,PrivDir,MasterDir,ClientSname) -> + TestHost = test_host(), + ?print(["client1_1 start"]), + + {ok,IP} = inet:getaddr(TestHost,inet), + erl_boot_server:start([IP]), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,ClientSname), + trace_disallowed_calls(Node), + + %% Check env var for SASL on client node + SaslEnv = rpc:call(Node, application, get_all_env, [sasl]), + {_,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"])), + true = (Master =:= node()), + + %% Unpack P1H on master + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + %% Unpack and install P1H on client + P1HDir = filename:join([Root, "releases", "P1H"]), + + %% The AppDirs argument (last arg to set_unpacked) below is really + %% not necessary, it could just be [] since the path is the same + %% as default. But it is given here in order to force hitting the + %% release_handler:check_path function so it can be checked that + %% it does not use file:read_file_info on the client node, see + %% trace_disallowed_calls/1 and check_disallowed_calls/0 below. + %% (OTP-9142) + {ok, "P1H"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1HDir, "rel1.rel"), + [{a,"1.0",filename:join(MasterDir,lib)}]]), + + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1H", filename:join(P1HDir, "relup")]), + ?print([{release_handler_state, Node}, + rpc:call(Node, sys, get_status, [release_handler])]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + + Apps = rpc:call(Node, application, which_applications, []), + {a,"A CXC 138 11","1.0"} = lists:keyfind(a, 1, Apps), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_2(TestNode,PrivDir,Node). + +client1_2(TestNode,PrivDir,Node) -> + ?print(["client1_2 start"]), + + %% Check that P1H is still unpacked, install it and make_permanent + ?check_release_client(Node,"P1H",unpacked,["a-1.0"]), + + {ok,"P1G",[new_appl]} = + rpc:call(Node, release_handler, install_release, ["P1H"]), + ?check_release_client(Node,"P1H",current,["a-1.0"]), + ?check_running_app_client(Node,a,"1.0"), + + ok = rpc:call(Node, release_handler, make_permanent, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_3(TestNode,PrivDir,Node). + +client1_3(TestNode,PrivDir,Node) -> + ?print(["client1_3 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Unpack P1I on master + {ok, "P1I"} = unpack_release(PrivDir,"rel2"), + + MasterRoot = code:root_dir(), + + %% Unpack and install P1I on client + P1IDir = filename:join([MasterRoot, "releases", "P1I"]), + {ok, "P1I"} = rpc:call(Node, release_handler, set_unpacked, + [filename:join(P1IDir, "rel2.rel"),[]]), + + ?check_release_client(Node,"P1I",unpacked,["a-1.1"]), + + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P1I", filename:join(P1IDir, "relup")]), + + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, check_install_release, ["P1I"]), + {error,_} = rpc:call(Node, release_handler, check_install_release, ["P1J"]), + {ok,"P1H",[{extra, gott}]} = + rpc:call(Node, release_handler, install_release, ["P1I"]), + + ?check_running_app_client(Node,a,"1.1"), + X2 = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X2), + {key1, val1} = lists:keyfind(key1, 1, X2), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Unpack P2A on master + {ok, "P2A"} = unpack_release(PrivDir,"rel3"), + + %% Unpack and install P2A on client + P2ADir = filename:join([MasterRoot, "releases", "P2A"]), + {ok, "P2A"} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join(P2ADir, "rel3.rel"),[]]), + + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "sys.config")]), + ok = rpc:call(Node, release_handler, install_file, + ["P2A", filename:join(P2ADir, "relup")]), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + ok = rpc:call(Node, release_handler, make_permanent, ["P1I"]), + ?check_release_client(Node,"P1I",permanent,["a-1.1"]), + + %% since the install_release below reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + %% Reboots the client ! + + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_4(TestNode,Node). + +client1_4(TestNode,Node) -> + ?print(["client1_4 start"]), + + %% Check that P2A is in use. + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Reboot from P1I + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_5(TestNode,Node). + +client1_5(TestNode,Node) -> + ?print(["client1_5 start"]), + + %% Check that P1I is used + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, check_install_release, ["P2A"]), + + %% since the install_release below will reboot the node... + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install P2A again + {ok, "P1I", [new_emu]} = + rpc:call(Node, release_handler, install_release, ["P2A"]), + + %% We are rebooted again. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_6(TestNode,Node). + +client1_6(TestNode,Node) -> + ?print(["client1_6 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",current,["a-1.1"]), + ?check_running_app_client(Node,a,"1.1"), + X = rpc:call(Node, a, a, []), + {key2, newval2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + {ok, bval} = rpc:call(Node, a, b, []), + + %% Make P2A permanent + ok = rpc:call(Node, release_handler, make_permanent, ["P2A"]), + + %% Reboot from P2A + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_7(TestNode,Node). + +client1_7(TestNode,Node) -> + ?print(["client1_7 start"]), + + %% Check that P2A is used + ?check_release_client(Node,"P2A",permanent,["a-1.1"]), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1H + rpc:call(Node, release_handler, reboot_old_release, ["P1H"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_8(TestNode,Node). + +client1_8(TestNode,Node) -> + ?print(["client1_8 start"]), + + %% Check that P1H is permanent + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% Remove P1I and P2I from client + ok = rpc:call(Node, release_handler, set_removed, ["P2A"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1I"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_9(TestNode,Node). + +client1_9(TestNode,Node) -> + ?print(["client1_9 start"]), + + %% Check that P2A and P1I does not exists and that PiH is permanent. + Rels = rpc:call(Node, release_handler, which_releases, []), + false = lists:keysearch("P2A", 2, Rels), + false = lists:keysearch("P1I", 2, Rels), + ?check_release_client(Node,"P1H",permanent,["a-1.0"]), + X = rpc:call(Node, a, a, []), + {key2, val2} = lists:keyfind(key2, 1, X), + {key1, val1} = lists:keyfind(key1, 1, X), + + %% since the reboot_old_release below will reboot the node + check_disallowed_calls(), + cover_client(TestNode,Node,stop_cover), + + %% Install old P1G + rpc:call(Node, release_handler, reboot_old_release, ["P1G"]), + %% We are rebooted. + check_reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_10(TestNode,Node). + +client1_10(TestNode,Node) -> + ?print(["client1_10 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + ?check_release_client(Node,"P1H",old,["a-1.0"]), + {error,client_node} = rpc:call(Node,release_handler,remove_release,["P1H"]), + ok = rpc:call(Node, release_handler, set_removed, ["P1H"]), + + check_disallowed_calls(), + reboot(TestNode,Node), + trace_disallowed_calls(Node), + + client1_11(TestNode,Node). + +client1_11(TestNode,Node) -> + ?print(["client1_11 start"]), + + %% Check that P1G is permanent + ?check_release_client(Node,"P1G",permanent,[]), + + check_disallowed_calls(), + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + ok = release_handler:remove_release("P2A"), + ok = release_handler:remove_release("P1I"), + ok = release_handler:remove_release("P1H"), + ok. + +%% Start tracing of the file module on the client node. This module +%% shall never be called, since +%% 1) the node is a client from the release_handler's point of view, +%% so all file access should be done via rpc calls to the master +%% 2) it is started with erl_prim_loader loader set to 'inet' so all +%% code loading should be done via the inet to the master +%% (OTP-9142) +%% This function is called each time the client node is started and to +%% check if a call has been made, call check_disallowed_node/0 +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,[]]). + +check_disallowed_calls() -> + receive + Trace when element(1,Trace)==trace -> + exit({disallowed_function_call,Trace}) + after 0 -> + 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]), + receive + {nodeup, Node} -> + wait_started(TestNode,Node) + after 30000 -> + ?print([{start_client,failed,Node},net_adm:ping(Node)]), + ?fail({"can not start", Node}) + end. + +do_start_client(Client, Host) -> + Node = list_to_atom(lists:concat([Client,"@",Host])), + Start = filename:join(["clients", "type1", Node, "bin", "start"]), + {Start, Node}. + +reboot(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, reboot, []), + check_reboot(TestNode,Node). + +%% This way of checking that the node is rebooted will only work if +%% the nodes are automatically re-connected after the reboot. This +%% happens for master/client (when sasl is started on the client). +check_reboot(TestNode,Node) -> + receive + {nodedown, Node} -> + receive + {nodeup, Node} -> wait_started(TestNode,Node) + after 30000 -> + ?fail({Node, "not rebooted",net_adm:ping(Node)}) + end + after 30000 -> + ?fail({Node, "not closing down",net_adm:ping(Node)}) + end. + +stop_client(TestNode,Node) -> + cover_client(TestNode,Node,stop_cover), + rpc:call(Node, init, stop, []), + receive + {nodedown, Node} -> ok + after 30000 -> + ?fail({Node, "not stopping"}) + end. + +wait_started(TestNode,Node) -> + case rpc:call(Node, init, get_status, []) of + {started, _} -> + cover_client(TestNode,Node,start_cover), + Node; + _ -> + timer:sleep(1000), + wait_started(TestNode,Node) + end. + +cover_client(TestNode,Node,Func) -> + R = rpc:call(TestNode,release_handler_SUITE,Func,[Node]), + ?print([{Func,Node,R}]). + + +%%----------------------------------------------------------------- +%% This test starts a client node which uses this node as master +%% for the release_handler. +%% The client node has the name cli2@HOSTNAME. +%% The client node is not allowed to do ANY release updates +%% as it also have another (non-existing) master node. +%% +%% The to_erl /tmp/cli2@HOSTNAME/ command can be used to connect +%% to the client node. +%% run_erl logs for the client can be found in the directory: +%% code:root_dir() ++ "/clients/type1/cli2@HOSTNAME/log +%%----------------------------------------------------------------- +client2(TestNode,PrivDir,ClientSname) -> + TestHost = test_host(), + ?print(["client2 start"]), + + %% Clean up if previous test case failed + release_handler:remove_release("P1H"), + + ok = net_kernel:monitor_nodes(true), + Node = start_client(TestNode,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]), + {_,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"])), + true = (Master =:= node()), + true = (Master2 =:= list_to_atom("master2@"++TestHost)), + + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + + {error,{bad_masters,[Master2]}} = + rpc:call(Node, release_handler, set_unpacked, + [filename:join([Root, "releases", "P1H", "rel1.rel"]),[]]), + + {error,{no_such_release,"P1H"}} = + rpc:call(Node, release_handler, check_install_release, ["P1H"]), + + stop_client(TestNode,Node), %% TEST IS OK !! + net_kernel:monitor_nodes(false), + + release_handler:remove_release("P1H"), + ok. + + +stop(Now) -> + %% The timestamp is only used for debugging. It is printed by + %% release_handler_SUITE also. + R = init:stop(), + erlang:display({init_stop,Now,R}), + R. + +unpack_p1h(TestNode,PrivDir) -> + {ok, "P1H"} = unpack_release(PrivDir,"rel1"), + ?check_release("P1H",unpacked,["a-1.0"]), + ok. + +permanent_p1h(TestNode) -> + ?check_release("P1H",unpacked,["a-1.0"]), + {ok,"P1G",[new_appl]} = release_handler:install_release("P1H"), + ?check_release("P1H",current,["a-1.0"]), + ok = release_handler:make_permanent("P1H"), + ?check_release("P1H",permanent,["a-1.0"]), + ok. + + +reg_proc(Name) -> + catch unregister(Name), + Pid = spawn_link(?MODULE, registered_loop, [Name]), + global:register_name(Name, Pid), + ok. + +registered_loop(_Name) -> + receive + kill -> + exit(killed) + end. + +check_release(TestNode,Node,Vsn,Status,Apps,Line) -> + case rpc:call(Node,release_handler,which_releases,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_release,Node,Vsn,Status,Error}); + Rels -> + ?print_line(Line,["check_release:", Rels]), + {"SASL-test", Vsn, Libs, Status} = lists:keyfind(Vsn, 2, Rels), + true = lists:all(fun(App) -> lists:member(App,Libs) end,Apps), + ok + end. + +check_running_app(TestNode,Node,App,Vsn,Line) -> + case rpc:call(Node,application,which_applications,[]) of + {badrpc,_}=Error -> + ?fail_line(Line,{check_running_app,Node,App,Vsn,Error}); + Apps -> + ?print_line(Line,["check_running_app:", Apps]), + {App, _, Vsn} = lists:keyfind(a, 1, Apps), + ok + end. + +test_host() -> + {ok,Host} = inet:gethostname(), + Host. + +unpack_release(PrivDir,Rel) -> + copy(filename:join([PrivDir,Rel,Rel++".tar.gz"]), + filename:join(code:root_dir(),releases)), + release_handler:unpack_release(Rel). + +copy(Src, DestDir) -> + Dest = filename:join(DestDir, filename:basename(Src)), + {ok,_} = file:copy(Src, Dest), + ok. + diff --git a/lib/sasl/test/overload_SUITE.erl b/lib/sasl/test/overload_SUITE.erl new file mode 100644 index 0000000000..92b1aaed6e --- /dev/null +++ b/lib/sasl/test/overload_SUITE.erl @@ -0,0 +1,175 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(overload_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +all() -> [info, set_config_data, set_env_vars, request, timeout]. +all(suite) -> all(). + +init_per_testcase(_Case,Config) -> + restart_sasl(), + Config. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + +%%%----------------------------------------------------------------- +info(suite) -> []; +info(_Config) -> + ?line Info = overload:get_overload_info(), + ?line [{total_intensity,0.0}, + {accept_intensity,0.0}, + {max_intensity,0.8}, + {weight,0.1}, + {total_requests,0}, + {accepted_requests,0}] = Info. + +%%%----------------------------------------------------------------- +set_config_data(suite) -> []; +set_config_data(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = overload:set_config_data(0.5,0.4), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. + +%%%----------------------------------------------------------------- +set_env_vars(suite) -> []; +set_env_vars(_Config) -> + ?line InfoDefault = overload:get_overload_info(), + ?line ok = check_info(0.8,0.1,InfoDefault), + ?line ok = application:set_env(sasl,overload_max_intensity,0.5), + ?line ok = application:set_env(sasl,overload_weight,0.4), + ?line ok = application:stop(sasl), + ?line ok = application:start(sasl), + ?line Info1 = overload:get_overload_info(), + ?line ok = check_info(0.5,0.4,Info1), + ok. +set_env_vars(cleanup,_Config) -> + application:unset_env(sasl,overload_max_intensity), + application:unset_env(sasl,overload_weight), + ok. + +%%%----------------------------------------------------------------- +request(suite) -> []; +request(_Config) -> + %% Find number of request that can be done with default settings + %% and no delay + ?line overload:set_config_data(0.8, 0.1), + ?line NDefault = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NDefault: ~p",[NDefault]), + + %% Check that the number of requests increases when max_intensity + %% increases + ?line overload:set_config_data(2, 0.1), + ?line NLargeMI = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeMI: ~p",[NLargeMI]), + ?line true = NLargeMI > NDefault, + + %% Check that the number of requests decreases when weight + %% increases + ?line overload:set_config_data(0.8, 1), + ?line NLargeWeight = do_many_requests(0), + ?line restart_sasl(), + ?line ?t:format("NLargeWeight: ~p",[NLargeWeight]), + ?line true = NLargeWeight < NDefault, + + %% Check that number of requests increases when delay between + %% requests increases. + %% (Keeping same config and comparing to large weight in order to + %% minimize the time needed for this case.) + ?line overload:set_config_data(0.8, 1), + ?line NLargeTime = do_many_requests(500), + ?line restart_sasl(), + ?line ?t:format("NLargeTime: ~p",[NLargeTime]), + ?line true = NLargeTime > NLargeWeight, + ok. + +%%%----------------------------------------------------------------- +timeout(suite) -> []; +timeout(_Config) -> + ?line overload:set_config_data(0.8, 1), + ?line _N = do_many_requests(0), + + %% Check that the overload alarm is raised + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% Fake a clear timeout in overload.erl and check that, since it + %% came very soon after the overload situation, the alarm is not + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [{overload,_}] = alarm_handler:get_alarms(), + + %% A bit later, try again and check that this time the alarm is + %% cleared + ?line overload ! timeout, + ?line timer:sleep(1000), + ?line [] = alarm_handler:get_alarms(), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +%%%----------------------------------------------------------------- +%%% Call overload:request/0 up to 30 times with the given time delay +%%% between. Stop when 'reject' is returned. +do_many_requests(T) -> + 30 - do_requests(30,T). + +do_requests(0,_) -> + ?t:fail(never_rejected); +do_requests(N,T) -> + case overload:request() of + accept -> + timer:sleep(T), + do_requests(N-1,T); + reject -> + N + end. + +%%%----------------------------------------------------------------- +%%% Restart the sasl application +restart_sasl() -> + application:stop(sasl), + application:start(sasl), + ok. + +%%%----------------------------------------------------------------- +%%% Check that max_intensity and weight is set as expected +check_info(MI,W,Info) -> + case {lists:keyfind(max_intensity,1,Info), lists:keyfind(weight,1,Info)} of + {{_,MI},{_,W}} -> ok; + _ -> ?t:fail({unexpected_info,MI,W,Info}) + end. + + diff --git a/lib/sasl/test/rb_SUITE.erl b/lib/sasl/test/rb_SUITE.erl new file mode 100644 index 0000000000..b53c382609 --- /dev/null +++ b/lib/sasl/test/rb_SUITE.erl @@ -0,0 +1,606 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(rb_SUITE). +-include("test_server.hrl"). + +-compile(export_all). + +-define(SUP,rb_SUITE_sup). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all() -> + no_group_cases() ++ [{group,running_error_logger}]. + +no_group_cases() -> + [help, + start_error_stop]. + +groups() -> + [{running_error_logger,[shuffle],[show, + list, + rescan, + start_stop_log, + grep, + filter_filter, + filter_date, + filter_filter_and_date, + filter_re_no + ]}]. + + +all(suite) -> + no_group_cases() ++ + [{conf, + install_mf_h, + element(3,lists:keyfind(running_error_logger,1,groups())), + remove_mf_h} + ]. + + +init_per_suite(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line RbDir = filename:join(PrivDir,rb), + ?line ok = file:make_dir(RbDir), + NewConfig = [{rb_dir,RbDir}|Config], + reset_sasl(NewConfig), + NewConfig. + +end_per_suite(_Config) -> + ok. + +init_per_group(running_error_logger,Config) -> + install_mf_h(Config). + +end_per_group(running_error_logger,Config) -> + remove_mf_h(Config). + +init_per_testcase(_Case,Config) -> + case whereis(?SUP) of + undefined -> ok; + Pid -> kill(Pid) + end, + empty_error_logs(Config), + Config. + +kill(Pid) -> + Ref = erlang:monitor(process,Pid), + exit(Pid,kill), + receive {'DOWN', Ref, process, Pid, _Info} -> ok end. + +end_per_testcase(Case,Config) -> + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ok. + + +%%%----------------------------------------------------------------- + +help() -> help(suite). +help(suite) -> []; +help(_Config) -> + ?line Help = capture(fun() -> rb:h() end), + ?line "Report Browser Tool - usage" = hd(Help), + ?line "rb:stop - stop the rb_server" = lists:last(Help), + ok. + + +start_error_stop() -> start_error_stop(suite). +start_error_stop(suite) -> []; +start_error_stop(Config) -> + ?line RbDir = ?config(rb_dir,Config), + + ?line {error,{"cannot locate report directory",_}} = rb:start(), + + + ?line ok = application:set_env(sasl,error_logger_mf_dir,"invaliddir"), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,1000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + ?line {error,{"cannot read the index file",_}} = rb:start(), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line restart_sasl(), + ?line {ok,_} = rb:start(), + + ?line ok = rb:stop(), + ok. + + +%% start_opts(suite) -> []; +%% start_opts(Config) -> +%% PrivDir = ?config(priv_dir,Config), +%% RbDir = filename:join(PrivDir,rb_opts), +%% ok = file:make_dir(RbDir), + + +install_mf_h(Config) -> + ?line RbDir = ?config(rb_dir,Config), + ?line ok = application:set_env(sasl,error_logger_mf_dir,RbDir), + ?line ok = application:set_env(sasl,error_logger_mf_maxbytes,5000), + ?line ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ?line restart_sasl(), + Config. + +remove_mf_h(_Config) -> + ok. + + + +show() -> show(suite). +show(suite) -> []; +show(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + %% Show all reports + ?line All = check_report(fun() -> rb:show() end,OutFile), + + %% Show by number + ?line [{_,First}] = check_report(fun() -> rb:show(1) end,OutFile), + ?line {1,First} = lists:keyfind(1,1,All), + + %% Show by type + ?line [{_,CR}] = check_report(fun() -> rb:show(crash_report) end,OutFile), + ?line true = contains(CR,"rb_test_crash"), + ?line [{_,EC},{_,EM}] = check_report(fun() -> rb:show(error) end,OutFile), + ?line true = contains(EC,"rb_test_crash"), + ?line true = contains(EM,"rb_test_error_msg"), + ?line [{_,ER}] = check_report(fun() -> rb:show(error_report) end,OutFile), + ?line true = contains(ER,"rb_test_error"), + ?line [{_,IR}] = check_report(fun() -> rb:show(info_report) end,OutFile), + ?line true = contains(IR,"rb_test_info"), + ?line [{_,IM}] = check_report(fun() -> rb:show(info_msg) end,OutFile), + ?line true = contains(IM,"rb_test_info_msg"), + ?line [_|_] = check_report(fun() -> rb:show(progress) end,OutFile), + ?line [{_,SR}] = check_report(fun() -> rb:show(supervisor_report) end, + OutFile), + ?line true = contains(SR,"child_terminated"), + ?line true = contains(SR,"{rb_SUITE,rb_test_crash}"), + + ok. + +list() -> list(suite). +list(suite) -> []; +list(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = capture(fun() -> rb:list() end), + ?line [{crash_report,[_]=CR}, + {error,[_,_]=EM}, + {error_report,[_]=ER}, + {info_msg,[_]=IM}, + {info_report,[_]=IR}, + {progress,[_|_]=P}, + {supervisor_report,[_]=SR}] = sort_list(All), + + ?line [{crash_report,CR}] = + sort_list(capture(fun() -> rb:list(crash_report) end)), + ?line [{error,EM}] = + sort_list(capture(fun() -> rb:list(error) end)), + ?line [{error_report,ER}] = + sort_list(capture(fun() -> rb:list(error_report) end)), + ?line [{info_msg,IM}] = + sort_list(capture(fun() -> rb:list(info_msg) end)), + ?line [{info_report,IR}] = + sort_list(capture(fun() -> rb:list(info_report) end)), + ?line [{progress,P}] = + sort_list(capture(fun() -> rb:list(progress) end)), + ?line [{supervisor_report,SR}] = + sort_list(capture(fun() -> rb:list(supervisor_report) end)), + + ok. + + +grep() -> grep(suite). +grep(suite) -> []; +grep(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line [{_,S}, + {_,CR}, + {_,EC}, + {_,IM}, + {_,IR}, + {_,EM}, + {_,ER}]= check_report(fun() -> rb:grep("rb_test_") end,OutFile), + ?line true = contains(S, "rb_test_crash"), + ?line true = contains(CR, "rb_test_crash"), + ?line true = contains(EC, "rb_test_crash"), + ?line true = contains(IM, "rb_test_info_msg"), + ?line true = contains(IR, "rb_test_info"), + ?line true = contains(EM, "rb_test_error_msg"), + ?line true = contains(ER, "rb_test_error"), + ok. + + +filter_filter() -> filter_filter(suite). +filter_filter(suite) -> []; +filter_filter(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line ER = [_] = rb_filter([{rb_SUITE,rb_test_error}],OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test}],OutFile), + ?line _E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButER = rb_filter([{rb_SUITE,rb_test_error,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERRep} = lists:unzip(ER), + {_,AllButERRep} = lists:unzip(AllButER), + ?line AllButERRep = AllRep -- ERRep, + + ok. + +filter_date() -> filter_date(suite). +filter_date(suite) -> []; +filter_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = rb_filter([],{Before,from},OutFile), + ?line All = rb_filter([],{After,to},OutFile), + ?line [] = rb_filter([],{Before,to},OutFile), + ?line [] = rb_filter([],{After,from},OutFile), + ?line All = rb_filter([],{Before,After},OutFile), + + %%?t:format("~p~n",[All]), + ?line AllButLast = [{N-1,R} || {N,R} <- tl(All)], + ?line AllButLast = rb_filter([],{Before,Between1},OutFile), + + ?line Last = hd(All), + ?line [Last] = rb_filter([],{Between2,After},OutFile), + + ok. + +filter_filter_and_date() -> filter_filter_and_date(suite). +filter_filter_and_date(suite) -> []; +filter_filter_and_date(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + + %% Insert some reports in the error log and start rb + init_error_logs(), + Between1 = calendar:local_time(), + timer:sleep(1000), + Between2 = calendar:local_time(), + ?line error_logger:error_report([{rb_SUITE,rb_test_filter}]), + ?line ok = start_rb(OutFile), + + Before = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) - 10), + After = calendar:gregorian_seconds_to_datetime( + calendar:datetime_to_gregorian_seconds(calendar:local_time()) + 1), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + ?line Last = hd(All), + + ?line [_,_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,After},OutFile), + ?line [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],{Before,Between1},OutFile), + ?line [_] = rb_filter([{rb_SUITE,"rb_test",re}],{Between2,After},OutFile), + ?line [_] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,After},OutFile), + ?line [] = rb_filter([{rb_SUITE,rb_test_filter}],{Before,Between1},OutFile), + ?line [Last] = rb_filter([{rb_SUITE,rb_test_filter,no}],{Between2,After},OutFile), + ?line {_,Str} = Last, + ?line false = contains(Str,"rb_test_filter"), + + ok. + + +filter_re_no() -> filter_re_no(suite). +filter_re_no(suite) -> []; +filter_re_no(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Insert some reports in the error log and start rb + init_error_logs(), + ?line ok = start_rb(OutFile), + + ?line All = check_report(fun() -> rb:show() end,OutFile), + + ?line E = [_,_] = rb_filter([{rb_SUITE,"rb_test",re}],OutFile), + ?line AllButE = rb_filter([{rb_SUITE,"rb_test",re,no}],OutFile), + + {_,AllRep} = lists:unzip(All), + {_,ERep} = lists:unzip(E), + {_,AllButERep} = lists:unzip(AllButE), + ?line AllButERep = AllRep -- ERep, + + ok. + + +rescan() -> rescan(suite). +rescan(suite) -> []; +rescan(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + + %% Start rb + ?line ok = start_rb(OutFile), + + %% Insert one more report and check that the list is longer. Note + %% that there might be two more reports, since the progress report + %% from starting rb_server might not be included before the rescan. + ?line AllBefore = capture(fun() -> rb:list() end), + ?line error_logger:error_report([{rb_SUITE,rb_test_rescan}]), + ?line ok = rb:rescan(), + ?line AllAfter = capture(fun() -> rb:list() end), + ?line Diff = length(AllAfter) - length(AllBefore), + ?line true = (Diff >= 1), + + ok. + + +start_stop_log() -> start_stop_log(suite). +start_stop_log(suite) -> []; +start_stop_log(Config) -> + ?line PrivDir = ?config(priv_dir,Config), + ?line OutFile = filename:join(PrivDir,"rb_SUITE_log.txt"), + ?line ok = file:write_file(OutFile,[]), + + %% Start rb and check that show is printed to standard_io + ?line ok = start_rb(), + ?line StdioResult = [_|_] = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Start log and check that show is printed to log and not to standad_io + ?line ok = rb:start_log(OutFile), + ?line [] = capture(fun() -> rb:show(1) end), + ?line {ok,Bin} = file:read_file(OutFile), + ?line true = (Bin =/= <<>>), + + %% Stop log and check that show is printed to standard_io and not to log + ?line ok = rb:stop_log(), + ?line ok = file:write_file(OutFile,[]), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + %% Test that standard_io is used if log file can not be opened + ?line ok = rb:start_log(filename:join(nonexistingdir,"newfile.txt")), + ?line StdioResult = capture(fun() -> rb:show(1) end), + ?line {ok,<<>>} = file:read_file(OutFile), + + ok. + + +%%%----------------------------------------------------------------- +%%% INTERNAL FUNCTIONS + +restart_sasl() -> + application:stop(sasl), + ok = application:start(sasl), + wait_for_sasl(). + +reset_sasl(Config) -> + application:unset_env(sasl,error_logger_mf_dir), + application:unset_env(sasl,error_logger_mf_maxbytes), + application:unset_env(sasl,error_logger_mf_maxfiles), + empty_error_logs(Config). + +empty_error_logs(Config) -> + application:stop(sasl), + catch delete_content(?config(rb_dir, Config)), + ok = application:start(sasl), + wait_for_sasl(). + +wait_for_sasl() -> + wait_for_sasl(50). +wait_for_sasl(0) -> + ?t:fail("sasl application did not start within 5 seconds"); +wait_for_sasl(N) -> + case lists:keymember(sasl,1,application:which_applications()) of + true -> + ok; + false -> + timer:sleep(100), + wait_for_sasl(N-1) + end. + +start_rb(OutFile) -> + do_start_rb([{start_log,OutFile}]). +start_rb() -> + do_start_rb([]). + +do_start_rb(Opts) -> + {ok,Pid} = rb:start(Opts), + + %% Wait for process to started, then wait a little bit more + sys:get_status(Pid), + timer:sleep(500), + + %% Make sure printouts (e.g. from rb:list(), come to the test log, + %% and that they can be captured. + group_leader(group_leader(),Pid), + ok. + + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + delete_content(Dir), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +delete_content(Dir) -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files). + +init_error_logs() -> + ?line error_logger:error_report([{rb_SUITE,rb_test_error}]), + ?line error_logger:error_msg("rb_test_error_msg"), + ?line error_logger:info_report([{rb_SUITE,rb_test_info}]), + ?line error_logger:info_msg("rb_test_info_msg"), + ?line _Pid = start(), + ?line Ref = erlang:monitor(process,?MODULE), + ?line gen_server:cast(?MODULE,crash), + ?line receive {'DOWN',Ref,process,_,{rb_SUITE,rb_test_crash}} -> ok + after 2000 -> + ?t:format("Got: ~p~n",[process_info(self(),messages)]), + ?t:fail("rb_SUITE server never died") + end, + ?line erlang:demonitor(Ref), + ?line wait_for_server(), + ok. + +wait_for_server() -> + case whereis(?MODULE) of + undefined -> + wait_for_server(); + Pid -> + timer:sleep(100), % allow the supervisor report to be written + Pid + end. + +capture(Fun) -> + ?t:capture_start(), + ok = Fun(), + timer:sleep(1000), + ?t:capture_stop(), + string:tokens(lists:append(?t:capture_get()),"\n"). + + +rb_filter(Filter,OutFile) -> + check_report(fun() -> rb:filter(Filter) end, OutFile). +rb_filter(Filter,Dates,OutFile) -> + check_report(fun() -> rb:filter(Filter,Dates) end, OutFile). + + +%% This function first empties the given report file, then executes +%% the fun and returns a list of {N,Report}, where Report is a report +%% read from the file and N is an integer. The newest report has the +%% lowest number. +%% If the fun was a call to rb:show() (i.e. with no arguments), then +%% the numbering (N) will be the same as rb's own numbering (as shown +%% by rb:list()). +check_report(Fun,File) -> + file:delete(File), + rb:rescan([{start_log,File}]), + ok = Fun(), + {ok,Bin} = file:read_file(File), + Reports = split_reports(binary_to_list(Bin),[],[]), + lists:zip(lists:seq(1,length(Reports)),Reports). + +-define(report_header_line,"\n===============================================================================\n"). +split_reports([],Report,Reports) -> + add_report(Report,Reports); +split_reports(Text,Report,Reports) -> + case Text of + ?report_header_line++Rest -> + {Heading,PrevReport} = lists:splitwith(fun($\n) -> false; + (_) -> true + end, + Report), + split_reports(Rest, + ?report_header_line++Heading, + add_report(PrevReport,Reports)); + [Ch|Rest] -> + split_reports(Rest,[Ch|Report],Reports) + end. + +add_report(Report,Reports) -> + case string:strip(Report,both,$\n) of + [] -> Reports; + Report1 -> [lists:reverse(Report1)|Reports] + end. + +%% Returns true if Substr is a substring of Str. +contains(Str,Substr) -> + 0 =/= string:str(Str,Substr). + +%% Sort the result of rb_list after report type +sort_list(List) -> + sort_list(List,dict:new()). +sort_list([H|T],D) -> + case re:run(H,"\s+[0-9]+\s+([a-z_]+)",[{capture,all_but_first,list}]) of + nomatch -> + sort_list(T,D); + {match,[TypeStr]} -> + sort_list(T,dict:append(list_to_atom(TypeStr),H,D)) + end; +sort_list([],D) -> + lists:sort(dict:to_list(D)). + + +%%%----------------------------------------------------------------- +%%% A dummy supervisor and gen_server used for creating crash- and +%%% supervisor reports +start() -> + {ok,Pid} = + supervisor:start_link({local, ?SUP}, ?MODULE, i_am_supervisor), + unlink(Pid), + Pid. +start_server() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, i_am_server, []). +init(i_am_server) -> + {ok, state}; +init(i_am_supervisor) -> + AChild = {?SUP,{?MODULE,start_server,[]}, + permanent,2000,worker,[?MODULE]}, + {ok,{{one_for_all,1,1}, [AChild]}}. +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. +handle_cast(crash, State) -> + exit({rb_SUITE,rb_test_crash}), + {noreply, State}. +handle_info(_Info, State) -> + {noreply, State}. +terminate(_Reason, _State) -> + ok. + diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl new file mode 100644 index 0000000000..efa775f344 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -0,0 +1,1651 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(release_handler_SUITE). + +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +% Default timetrap timeout (set in init_per_testcase). +%-define(default_timeout, ?t:minutes(40)). +-define(default_timeout, ?t:minutes(10)). + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +init_per_suite(Config) -> + application:start(sasl), + Config. + +end_per_suite(_Config) -> + ok. + +all() -> + case os:type() of + {unix, _} -> unix_cases(); + {win32, _} -> win32_cases() + end. + +unix_cases() -> + RunErl = filename:join([code:root_dir(),"bin","run_erl"]), + RunErlCases = case filelib:is_file(RunErl) of + true -> [{group, release}]; + false -> [no_run_erl] + end, + [target_system] ++ RunErlCases ++ cases(). + +win32_cases() -> + cases(). + +%% Cases that can be run on all platforms +cases() -> + [otp_2740, otp_2760, otp_5761, instructions, eval_appup]. + +groups() -> + [{release,[], + [ + {group,release_single}, + {group,release_gg} + ]}, + {release_single,[], + [ + upgrade, + client1, + client2 + ]}, + {release_gg,[], + [ + upgrade_gg + ]}]. + +%% {group,release} +%% Top group for all cases using run_erl +init_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + P1gInstall = filename:join(priv_dir(Config),p1g_install), + ok = do_create_p1g(Config,P1gInstall), + ok = create_p1h(Config), + ?t:timetrap_cancel(Dog); + +%% {group,release_single} +%% Subgroup of {group,release}, contains all cases that are not +%% related to global_group +init_per_group(release_single, Config) -> + Dog = ?t:timetrap(?default_timeout), + + %% Create some more releases to upgrade to + ok = create_p1i(Config), + ok = create_p2a(Config), + + ?t:timetrap_cancel(Dog); + +%% {group,release_gg} +%% Subgroup of {group,release}. global_group tests. +init_per_group(release_gg, Config0) -> + Config = [{sname_prefix,release_gg}|Config0], + + PrivDir = priv_dir(Config), + Dog = ?t:timetrap(?default_timeout), + + reg_print_proc(), %% starts a printer process on this node + + Snames = [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + gg_node_snames(Config), + + %% kill all possible nodes which are to be used + ok = stop_nodes([node_name(Sname) || Sname <- Snames]), + + %% For gg1, gg3, gg4 and gg5: create a target system running + %% P1G, and with P1H unpacked. + %% For gg2 and gg6: create a target system running P1H. + %% Use gg2 for unpacking and permanenting P1H. + ok = copy_installed(Config,p1g_install,[Gg2Sname]), + InstallNode = unpack_p1h(Config,Gg2Sname), + ok = copy_installed(Config,Gg2Sname,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname]), + ok = permanent_p1h(InstallNode), + ok = stop_nodes([InstallNode]), + ok = copy_installed(Config,Gg2Sname,[Gg6Sname]), + + %% Replace the sys.config files + %% The reason for not creating the releases with these configs in + %% the first place (create_p1g, create_p1h) is that then the + %% InstallNode (gg2) will be very slow started since it will try + %% to synch with the other nodes in the global group. + %% Also, the rpc call for installing the P1H release (in + %% permanent_p1h/1) would return {rpc,nodedown} due to change of + %% global groups. + lists:foreach( + fun(Sname) -> + ReleasesDir = filename:join([PrivDir,Sname,"releases"]), + write_term_file(filename:join([ReleasesDir,"P1G","sys.config"]), + gg_config([Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname])), + write_term_file(filename:join([ReleasesDir,"P1H","sys.config"]), + gg_config([Gg1Sname,Gg2Sname,Gg4Sname, + Gg5Sname,Gg6Sname])) + end, + Snames), + + ?t:timetrap_cancel(Dog), + [{snames,Snames}|Config]. + + +end_per_group(release, Config) -> + Dog = ?t:timetrap(?default_timeout), + stop_print_proc(), + delete_release(Config), + ?t:timetrap_cancel(Dog), + Config; +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(Case, Config0) -> + Dog = test_server:timetrap(?default_timeout), + Config = [{sname_prefix,Case},{watchdog, Dog}|Config0], + try apply(?MODULE,Case,[cleanup,Config]) + catch error:undef -> ok + end, + ?t:format("~n======= init_per_testcase done =======~n",[]), + Config. + +end_per_testcase(Case, Config) -> + ?t:format("~n======= start end_per_testcase =======~n",[]), + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + + %% DEBUG + case ?config(tc_status,Config) of + ok -> + ok; + _Fail -> + %% save logs from master and client nodes + PrivDir = priv_dir(Config), + SaveDir = filename:join(PrivDir,save), + FailDir = filename:join(SaveDir,lists:concat(["failed-",Case])), + ok = filelib:ensure_dir(filename:join(FailDir,"*")), + + LogDirs = filelib:wildcard(filename:join([PrivDir,"*",log])), + + lists:foreach( + fun(LogDir) -> + ["log",Sname|_] = lists:reverse(filename:split(LogDir)), + copy_tree(Config,LogDir,Sname,FailDir) + end, + LogDirs), + + case filelib:is_file("sasl_erl_crash.dump") of + true -> + copy_file("sasl_erl_crash.dump",FailDir); + _ -> + ok + end + + end, + %% End DEBUG + + %% Remove any remaining sasl_erl_crash.dump + %% These can occur when a new master@ is started, before + %% the old usage of the name is unregistered, causing the node to + %% terminate. (This has no effect on the test case, as the node is + %% 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) -> + [tc_sname(Config,X) || X <- [gg1,gg2,gg3,gg4,gg5,gg6]]. + + +%%%----------------------------------------------------------------- +%%% TEST CASES + + +%% Executed instead of release group when no run_erl program exists +no_run_erl(Config) when is_list(Config) -> + {comment, "No run_erl program"}. + + + +%% Test upgrade and downgrade of erts +upgrade(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + ?t:format("upgrade ~p~n",[reg_print_proc]), + PrivDir = priv_dir(Conf), + Sname = tc_sname(Conf), % nodename for use in this testcase + + %% Copy the P1G release to a directory for use in this testcase + ok = copy_installed(Conf,p1g_install,[Sname]), + + %% start the test node + [TestNode] = start_nodes(Conf,[Sname],"upgrade start"), + + %% unpack and install P1H + ok = rpc_inst(TestNode, install_1, [PrivDir]), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_1"), + + %% reinstall P1H and make it permanent + ok = rpc_inst(TestNode, install_2, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_2",[a]), + + %% check that P1H is permanent, unpack and install P1I, unpack and install P2A + TestNodeInit1 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_3, [PrivDir]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_3a, []), + wait_nodes_up([{TestNode,TestNodeInit1}],"install_3",[a]), + + %% check that P2A is used, reboot from P1I + ok = rpc_inst(TestNode, install_4, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_4",[a]), + + %% check that P1I, reinstall P2A + TestNodeInit2 = rpc:call(TestNode,erlang,whereis,[init]), + ok = rpc_inst(TestNode, install_5, []), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_5a, []), + wait_nodes_up([{TestNode,TestNodeInit2}],"install_5",[a]), + + %% check that P2A is used, make P2A permanent + ok = rpc_inst(TestNode, install_6, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_6",[a]), + + %% check that P2A is permanent, install old P1H + TestNodeInit3 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_7, []), + wait_nodes_up([{TestNode,TestNodeInit3}],"install_7",[a]), + + %% check that P1H is permanent, remove P1I and P2A + ok = rpc_inst(TestNode, install_8, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_8",[a]), + + %% check that P1H is permanent, reboot old P1G + TestNodeInit4 = rpc:call(TestNode,erlang,whereis,[init]), + stop_cover(TestNode), + ok = rpc_inst(TestNode, install_9, []), + wait_nodes_up([{TestNode,TestNodeInit4}],"install_9"), + + %% check that P1G is permanent, remove P1H + ok = rpc_inst(TestNode, install_10, []), + stop_cover(TestNode), + reboot_and_wait(TestNode,"install_10"), + + %% check that P1G is permanent + ok = rpc_inst(TestNode, install_11, []), + + ok. + +upgrade(cleanup,Config) -> + TestNode = tc_full_node_name(Config), + ok = stop_nodes([TestNode]). + +reboot_and_wait(Node,Tag) -> + reboot_and_wait(Node,Tag,[]). + +reboot_and_wait(Node,Tag,Apps) -> + InitPid = rpc:call(Node,erlang,whereis,[init]), + ok = rpc:call(Node,init,reboot,[]), + wait_nodes_up([{Node,InitPid}],Tag,Apps). + + +%% Test upgrade and downgrade of erts, diskless +client1(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + MasterDir = filename:join(PrivDir,Master), + + %% 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"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client1"), + + ok = rpc_inst(TestNode, client1_1, [PrivDir,MasterDir,Client]), + + ok. + +client1(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test diskless release handling when illegal master node +client2(Conf) when is_list(Conf) -> + reg_print_proc(), %% starts a printer process on test_server node + PrivDir = priv_dir(Conf), + Master = tc_sname(Conf,master), + Client = tc_sname(Conf,client), + + %% 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"), + + %% start the master node + [TestNode] = start_nodes(Conf,[Master],"client2"), + + ok = rpc_inst(TestNode, client2, [PrivDir,Client]), + + ok. + +client2(cleanup,Config) -> + MasterNode = tc_full_node_name(Config,master), + ClientNode = tc_full_node_name(Config,client), + ok = stop_nodes([MasterNode,ClientNode]). + + + +%% Test instructions _not_ tested by the installer module. +instructions(Conf) when is_list(Conf) -> + DataDir = ?config(data_dir, Conf), + + Dir = filename:join(DataDir, "c"), + true = code:add_path(Dir), + check_bstate("no", []), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("first", []), + FirstBB = whereis(bb), + + case whereis(cc) of + Pid when is_pid(Pid) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Stop and start cc process + S1 = [point_of_no_return, + {stop, [aa]}, + {apply, {?MODULE, no_cc, []}}, + {start, [aa]}], + {ok, _} = release_handler_1:eval_script(S1, [], []), + + case whereis(cc) of + Pid2 when is_pid(Pid2) -> ok; + _ -> ?t:fail("cc not started") + end, + + %% Make bb run old version of b. + S2 = [point_of_no_return, + {remove, {b, soft_purge, soft_purge}}], + {ok, [{b, soft_purge}]} = release_handler_1:eval_script(S2, [], []), + check_bstate("first", [FirstBB]), + + false = code:is_loaded(b), + {error,{old_processes,b}} = release_handler_1:eval_script(S2,[],[]), + check_bstate("first", [FirstBB]), + + %% Let supervisor restart bb with new code + S3 = [point_of_no_return, + {purge, [b]}], + {ok, []} = release_handler_1:eval_script(S3, [], []), + ok = wait_for(bb), + check_bstate("second", []), + SecondBB = whereis(bb), + + if + SecondBB =:= FirstBB -> ?t:fail("bb not killed"); + true -> ok + end, + + %% Restart bb yet another time + ok = application:stop(c), + ok = application:start(c), + ok = wait_for(bb), + check_bstate("third", []), + ThirdBB = whereis(bb), + + case ThirdBB of + _ when is_pid(ThirdBB) -> ok; + undefined -> ?t:fail("bb not started") + end, + + %% Make bb run old version of b. + %%c:l(b), + check_bstate("third", []), + false = code:purge(b), + check_bstate("third", []), + {module,b} = code:load_file(b), + check_bstate("third", [ThirdBB]), + + %% Let supervisor restart bb yet another time + S4 = [point_of_no_return, + {remove, {b, brutal_purge, soft_purge}}], + {ok, HopefullyEmpty} = release_handler_1:eval_script(S4, [], []), + ok = wait_for(bb), + FourthBB = whereis(bb), + + case HopefullyEmpty of + [{b, soft_purge}] -> + %% The process managed to start between purge and delete + check_bstate("fourth", [FourthBB]); + [] -> + %% The process started after delete + check_bstate("fourth", []) + end, + + application:stop(c), + check_bstate("no", []), + ok. + +instructions(cleanup,Conf) -> + application:stop(c), + really_del_code([aa,b,c_sup]), + code:del_path(filename:join(?config(data_dir,Conf), "c")), + ok. + +really_del_code(Mods) -> + lists:foreach(fun(Mod) -> + code:purge(Mod), % remove old code + code:delete(Mod),% make current code old + code:purge(Mod) % remove old code + end, + Mods). + +check_bstate(Slogan,ExpectedProcs) -> + BB = whereis(bb), + ActualProcs = lists:sort([P || P <- processes(), + erlang:check_process_code(P, b)]), + ExpectedProcs2 = lists:sort(ExpectedProcs), + ?t:format("check_bstate:~n~p~n~p~n", + [{"bb process", Slogan, BB}, + {"Processes running old b code", ActualProcs}]), + if + Slogan =:= "no", BB =/= undefined -> + ?t:fail("instructions failed; process bb is running"); + Slogan =/= "no", BB =:= undefined -> + ?t:fail("instructions failed; process bb is not running"); + ExpectedProcs2 =:= [], ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; old b processes are running"); + ActualProcs =/= ExpectedProcs2 -> + ?t:fail("instructions failed; wrong number of old b processes are running"); + true -> + ok + end. + +wait_for(Name) -> + case whereis(Name) of + undefined -> + timer:sleep(100), + wait_for(Name); + Pid when is_pid(Pid) -> + ok + end. + +no_cc() -> + case whereis(cc) of + Pid when is_pid(Pid) -> ?t:fail("cc not stopped"); + _ -> ok + end. + + + +%%%----------------------------------------------------------------- +%%% Testing of reported bugs and other tickets. +%%%----------------------------------------------------------------- + +%%----------------------------------------------------------------- +%% Ticket: OTP-2740 +%% Slogan: vsn not numeric doesn't work so good in release_handling +%%----------------------------------------------------------------- +%% Test vsn. +otp_2740(Conf) -> + DataDir = ?config(data_dir, Conf), + Dir = filename:join(DataDir, "otp_2740"), + true = code:add_path(Dir), + + {module, vsn_numeric} = c:l(vsn_numeric), + {module, vsn_tuple} = c:l(vsn_tuple), + {module, vsn_list} = c:l(vsn_list), + {module, vsn_atom} = c:l(vsn_atom), + {module, vsn_string} = c:l(vsn_string), + + 231894 = release_handler_1:get_current_vsn(vsn_numeric), + {tuple,["of",terms]} = release_handler_1:get_current_vsn(vsn_tuple), + [list,"of",{some,terms}] = release_handler_1:get_current_vsn(vsn_list), + atom = release_handler_1:get_current_vsn(vsn_atom), + "a string" = release_handler_1:get_current_vsn(vsn_string), + + true = code:del_path(Dir), + ok. + +%%----------------------------------------------------------------- +%% Ticket: OTP-2760 +%% Slogan: when an application is removed from a node it is not unloaded +%%----------------------------------------------------------------- +%% Test that when an application is removed from a node it is also unloaded. +otp_2760(Conf) -> + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_2760"), + DataDir = ?config(data_dir,Conf), + LibDir = filename:join([DataDir,app1_app2,lib1]), + + Rel1 = create_and_install_fake_first_release(Dir,[{app1,"1.0",LibDir}]), + Rel2 = create_fake_upgrade_release(Dir,"after",[],{Rel1,Rel1,[LibDir]}), + Rel2Dir = filename:dirname(Rel2), + + %% Start a node with Rel1.boot and check that the app1 module is loaded + {ok, Node} = t_start_node(otp_2760, Rel1, []), + {file, _} = rpc:call(Node, code, is_loaded, [app1]), + + %% Execute the relup script and check that app1 is unloaded + {ok, [{"after", [{_Rel1Vsn, _Descr, Script}], _}]} = + file:consult(filename:join(Rel2Dir, "relup")), + {ok, []} = rpc:call(Node, release_handler_1, eval_script, + [Script, [], []]), + false = rpc:call(Node, code, is_loaded, [app1]), + + true = stop_node(Node), + ok. + +%% Test upgrade using other filesystem than the defined in OTP and +%% option {update_paths, true} +otp_5761(Conf) when is_list(Conf) -> + + %% In the following test case, the release upgrade is somewhat + %% simplified (since it is not this procedure in itself we want to + %% test, but that application code directories are set correctly.) + %% Existing Erlang release is used as base, instead of creating + %% a new one. + + %% Set some paths + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_5761"), + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + LibDir1 = filename:join(RelDir, "lib1"), + LibDir2 = filename:join(RelDir, "lib2"), + + %% Create the releases + Rel1 = create_and_install_fake_first_release(Dir, + [{app1,"1.0",LibDir1}, + {app2,"1.0",LibDir1}]), + Rel2 = create_fake_upgrade_release(Dir, + "2", + [{app1,"2.0",LibDir2}, + {app2,"1.0",LibDir2}], + {Rel1,Rel1,[LibDir1]}), + Rel1Dir = filename:dirname(Rel1), + Rel2Dir = filename:dirname(Rel2), + + %% Start a slave node + {ok, Node} = t_start_node(otp_5761, Rel1, filename:join(Rel1Dir,"sys.config")), + + %% Bind some variable names that will be used in patternmatching below + App11Dir = filename:join([LibDir1, "app1-1.0"]), + App12Dir = filename:join([LibDir2, "app1-2.0"]), + App2aDir = filename:join([LibDir1, "app2-1.0"]), + App2bDir = filename:join([LibDir2, "app2-1.0"]), + + %% Make sure correct code paths are used + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Unpack rel2 (make sure it does not work if an AppDir is bad) + LibDir3 = filename:join(RelDir, "lib3"), + {error, {no_such_directory, _}} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir3}]]), + {ok, RelVsn2} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{app1,"2.0",LibDir2}, {app2,"1.0",LibDir2}]]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "relup")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "start.boot")]), + ok = rpc:call(Node, release_handler, install_file, + [RelVsn2, filename:join(Rel2Dir, "sys.config")]), + + %% Install RelVsn2 without {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn2]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn1]), + + %% Install RelVsn2 with {update_paths, true} option + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn2, [{update_paths, true}]]), + App12Dir = rpc:call(Node, code, lib_dir, [app1]), + App2bDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Install RelVsn1 again + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, + [RelVsn1, [{update_paths, true}]]), + App11Dir = rpc:call(Node, code, lib_dir, [app1]), + App2aDir = rpc:call(Node, code, lib_dir, [app2]), + + %% Stop the slave node + true = stop_node(Node), + ok. + +%% Test upgrade and downgrade of applications +eval_appup(Conf) when is_list(Conf) -> + + %% OTP-6162 + %% Create an ETS table which is updated by app1 if there is any + %% change made to the application configuration parameter 'var' + %% (see config_change/3 in myrel/lib1|2/app1-1|2.0/src/app1.erl) + ets:new(otp_6162, [set, public, named_table]), + + %% Set some paths + RelDir = filename:join(?config(data_dir, Conf), "app1_app2"), + App11Dir = filename:join([RelDir, "lib1", "app1-1.0"]), + App12Dir = filename:join([RelDir, "lib2", "app1-2.0"]), + EbinDir = filename:join(App11Dir, "ebin"), + + %% Start app1-1.0 + code:add_patha(EbinDir), + ok = application:start(app1), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% Upgrade to app1-2.0 + {ok, []} = release_handler:upgrade_app(app1, App12Dir), + App12Dir = code:lib_dir(app1), + error = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val2' + %% (see myrel/lib2/app1-2.0/ebin/app1.app) + [{var,val2}] = ets:lookup(otp_6162, var), + + %% Downgrade to app1-1.0 + {ok, []} = release_handler:downgrade_app(app1,"1.0",App11Dir), + App11Dir = code:lib_dir(app1), + ok = gen_server:call(harry, error), + + %% OTP-6162 + %% Value of config parameter 'var' should now be 'val1' + %% (see myrel/lib1/app1-1.0/ebin/app1.app) + [{var,val1}] = ets:lookup(otp_6162, var), + + ok = application:stop(app1), + ok = application:unload(app1), + + true = code:del_path(EbinDir), + ok. + + +%% Test the example/target_system.erl module +target_system(Conf) when is_list(Conf) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + TargetCreateDir = filename:join([PrivDir,"target_system","create"]), + TargetInstallDir = filename:join([PrivDir,"target_system","install"]), + + ok = filelib:ensure_dir(filename:join(TargetCreateDir,"xx")), + ok = filelib:ensure_dir(filename:join(TargetInstallDir,"xx")), + + + %% Create the .rel file + ErtsVsn = erlang:system_info(version), + RelName = filename:join(TargetCreateDir,"ts-1.0"), + RelFile = RelName++".rel", + RelVsn = "R1A", + create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,[{a, "1.0"}]), + + %% Build the target_system module + ExamplesEbin = filename:join([code:lib_dir(sasl),examples,ebin]), + TSPath = + case filelib:is_file(filename:join(ExamplesEbin,"target_system.beam")) of + true -> + ExamplesEbin; + false -> + {ok,_} = + compile:file(filename:join(DataDir,"target_system.erl"), + [{outdir,TargetCreateDir}]), + TargetCreateDir + end, + code:add_path(TSPath), + + %% Create the release + target_system:create(RelName,[{path,[filename:join([DataDir, + lib, + "a-1.0", + ebin])]}]), + + %% Install the release + target_system:install(RelName,TargetInstallDir), + + code:del_path(TSPath), + + %% Check that all files exist in installation + true = filelib:is_dir(filename:join(TargetInstallDir,"erts-"++ErtsVsn)), + LibDir = filename:join(TargetInstallDir,lib), + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + true = filelib:is_dir(filename:join(LibDir,"kernel-"++KernelVsn)), + true = filelib:is_dir(filename:join(LibDir,"stdlib-"++StdlibVsn)), + true = filelib:is_dir(filename:join(LibDir,"sasl-"++SaslVsn)), + true = filelib:is_dir(filename:join(LibDir,"a-1.0")), + RelDir = filename:join(TargetInstallDir,releases), + true = filelib:is_regular(filename:join(RelDir,"RELEASES")), + true = filelib:is_regular(filename:join(RelDir,"start_erl.data")), + true = filelib:is_regular(filename:join(RelDir, + filename:basename(RelFile))), + true = filelib:is_dir(filename:join(RelDir,RelVsn)), + true = filelib:is_regular(filename:join([RelDir,RelVsn,"start.boot"])), + BinDir = filename:join(TargetInstallDir,bin), + true = filelib:is_regular(filename:join(BinDir,"start.boot")), + true = filelib:is_regular(filename:join(BinDir,erl)), + true = filelib:is_regular(filename:join(BinDir,start_erl)), + true = filelib:is_regular(filename:join(BinDir,start)), + true = filelib:is_regular(filename:join(BinDir,epmd)), + true = filelib:is_regular(filename:join(BinDir,run_erl)), + true = filelib:is_regular(filename:join(BinDir,to_erl)), + + %% Check content of files + {ok,SED} = file:read_file(filename:join(RelDir,"start_erl.data")), + [ErtsVsn,RelVsn] = string:tokens(binary_to_list(SED),"\s\n"), + ok. + + + +%%%================================================================= +%%% Testing global groups. +%%%================================================================= + +%% This test case involves P1G and P1H with the sys.config as +%% specified in gg_config/1. The test case checks that the global +%% group information is correct before and after the upgrade and also +%% after terminating one of the nodes. The flow is as follows: +%% 1. Start all four nodes of global group gg1 with P1G +%% 2. Terminate one of the nodes, and upgrade the others to P1H. P1H +%% config adds to more nodes to the global group. +%% 3. Start the two remaining nodes with P1H +upgrade_gg(Conf) -> + [Gg1Sname,Gg2Sname,Gg3Sname,Gg4Sname,Gg5Sname,Gg6Sname] = + ?config(snames,Conf), + + %% start gg1, gg3, gg4, gg5 and check that global group info is ok + Nodes1 = [Gg1,Gg3,Gg4,Gg5] = + start_nodes(Conf,[Gg1Sname,Gg3Sname,Gg4Sname,Gg5Sname],"upgrade_gg"), + + %% Give some time to synch nodes, then check global group info. + timer:sleep(1000), + [check_gg_info(Node,Nodes1,[],Nodes1--[Node]) || Node <- Nodes1], + + %% register a process on each of the nodes + ok = rpc:call(Gg1, installer, reg_proc, [reg1]), + ok = rpc:call(Gg3, installer, reg_proc, [reg3]), + ok = rpc:call(Gg4, installer, reg_proc, [reg4]), + ok = rpc:call(Gg5, installer, reg_proc, [reg5]), + are_names_reg_gg(Gg1, [reg1, reg3, reg4, reg5]), + + %% Stop gg3, then upgrade gg1, gg4 and gg5 to P1H + ok = stop_nodes([Gg3]), + + ok = install_release_changed_gg(Gg1,"P1H"), + ok = install_release_changed_gg(Gg4,"P1H"), + ok = install_release_changed_gg(Gg5,"P1H"), + + %% Check global group info + Gg2 = node_name(Gg2Sname), + Gg6 = node_name(Gg6Sname), + Nodes2 = [Gg1,Gg4,Gg5], + [check_gg_info(Node,Nodes2,[Gg2,Gg6],Nodes2--[Node]) || Node <- Nodes2], + + %% start gg2 and gg6 + [Gg2,Gg6] = start_nodes(Conf,[Gg2Sname,Gg6Sname],"upgrade_gg start gg2/gg6"), + + %% reg proc on each of the nodes + ok = rpc:call(Gg2, installer, reg_proc, [reg2]), + ok = rpc:call(Gg6, installer, reg_proc, [reg6]), + are_names_reg_gg(Gg1, [reg1, reg2, reg4, reg5, reg6]), + + %% Check global group info + Nodes3 = [Gg1,Gg2,Gg4,Gg5,Gg6], + [check_gg_info(Node,Nodes3,[],Nodes3--[Node]) || Node <- Nodes3], + + ok. + +upgrade_gg(cleanup,Config) -> + Snames = ?config(snames,Config), + NodeNames = [node_name(Sname) || Sname <- Snames], + ok = stop_nodes(NodeNames). + + + + +%%%================================================================= +%%% Misceleaneous functions +%%%================================================================= +stop_nodes(Nodes) -> + ?t:format("Stopping nodes: ~p~n",[Nodes]), + Running = + lists:foldl(fun(Node,Acc) -> + Now = now(), + stop_cover(Node), + case rpc:call(Node,installer,stop,[Now]) of + {badrpc,nodedown} -> + Acc; + Other -> + ?t:format("Stop ~p(~p): ~p~n", + [Node,Now,Other]), + [Node|Acc] + end + end, [], Nodes), + wait_nodes_down(Running). + + +wait_nodes_down(Nodes) -> + ?t:format( "wait_nodes_down ~p:",[Nodes]), + wait_nodes_down(Nodes, 30). + +wait_nodes_down(Nodes, 0) -> + test_server:fail({error, {"could not kill nodes", Nodes}}); +wait_nodes_down(Nodes, N) -> + Fun = fun(Node, A) -> + case net_adm:ping(Node) of + pong -> + ?t:format( " net_adm:ping(~p) = pong", [Node]), + [Node|A]; + pang -> + ?t:format( " net_adm:ping(~p) = pang", [Node]), + A + end + end, + Pang = lists:foldl(Fun, [], Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_down(Pang, N-1) + end. + + + +wait_nodes_up(Nodes, Tag) -> + wait_nodes_up(Nodes, Tag, []). + +wait_nodes_up(Nodes0, Tag, Apps) -> + ?t:format("wait_nodes_up(~p, ~p, ~p):",[Nodes0, Tag, Apps]), + Nodes = fix_nodes(Nodes0), + wait_nodes_up(Nodes, Tag, lists:umerge(Apps,[kernel,stdlib,sasl]), 30). + +fix_nodes([{Node,InitPid}|Nodes]) -> + [{Node,InitPid} | fix_nodes(Nodes)]; +fix_nodes([Node|Nodes]) -> + [{Node,fake_init_pid} | fix_nodes(Nodes)]; +fix_nodes([]) -> + []. + +wait_nodes_up(Nodes, Tag, Apps, 0) -> + test_server:fail({error, {"nodes not started", Nodes, Tag, Apps}}); +wait_nodes_up(Nodes, Tag, Apps, N) -> + Fun = + fun(NodeInfo={Node,OldInitPid}, A) -> + case rpc:call(Node, application, which_applications, []) of + {badrpc, nodedown} -> + ?t:format( " ~p = {badarg, nodedown}",[Node]), + [NodeInfo | A]; + List when is_list(List)-> + ?t:format( " ~p = [~p]",[Node, List]), + case lists:all(fun(App) -> + lists:keymember(App,1,List) + end, Apps) of + true -> + case rpc:call(Node,erlang,whereis,[init]) of + OldInitPid -> + [NodeInfo | A]; + _ -> + start_cover(Node), + A + end; + false -> + [NodeInfo | A] + end + end + end, + Pang = lists:foldl(Fun,[],Nodes), + case Pang of + [] -> + ?t:format("",[]), + ok; + _ -> + timer:sleep(1000), + wait_nodes_up(Pang, Tag, Apps, N-1) + end. + + + + +are_names_reg_gg(Node, Names) -> + ?t:format( "are_names_reg_gg ~p~n",[Names]), + are_names_reg_gg(Node, Names, 30). + +are_names_reg_gg(Node, Names, N) -> + case lists:sort(rpc:call(Node, global, registered_names, [])) of + Names -> + ok; + Regs when N > 0 -> + timer:sleep(1000), + ?t:format( "are_names_reg_gg Regs ~p~n",[Regs]), + are_names_reg_gg(Node, Names, N-1); + Regs -> + ?t:fail({error, {"Names not registered", + {{"should :", Names}, + {"was :", Regs}}}}) + end. + + + +t_start_node(Name, Boot, SysConfig) -> + Args = + case Boot of + [] -> []; + _ -> " -boot " ++ Boot + end ++ + case SysConfig of + [] -> []; + _ -> " -config " ++ SysConfig + end, + test_server:start_node(Name, slave, [{args, Args}]). + +stop_node(Node) -> + ?t:stop_node(Node). + + +copy_client(Conf,Master,Sname,StartScript) -> + 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), + + Cli = filename:join([MasterDir, "clients", "type1", CliNode]), + ok = filelib:ensure_dir(filename:join([Cli,"bin","."])), + ok = filelib:ensure_dir(filename:join([Cli,"releases","."])), + ok = filelib:ensure_dir(filename:join([Cli,"log","."])), + + 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}]), + + StartErlData = filename:join([MasterDir, "releases", "start_erl.data"]), + CliRelDir = filename:join([Cli, "releases"]), + copy_file(StartErlData, CliRelDir), + + RR = filename:join([MasterDir, "releases", "RELEASES"]), + copy_file(RR, CliRelDir), + + ok. + + +delete_release(Conf) -> + PrivDir = priv_dir(Conf), + + {ok, OrigWd} = file:get_cwd(), + + ok = file:set_cwd(PrivDir), + ?t:format("======== current dir ~p~n",[PrivDir]), + {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 = 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. + + +delete_release_unix([]) -> + ok; +delete_release_unix(["save"|Dirs]) -> + delete_release_unix(Dirs); +delete_release_unix([Dir|Dirs]) -> + Rm = string:concat("rm -rf ", Dir), + ?t:format("============== COMMAND ~p~n",[Rm]), + case file:list_dir(Dir) of + {error, enotdir} -> + ok; + X -> + ?t:format("------- Dir ~p~n ~p~n",[Dir, X]) + end, + case os:cmd(Rm) of + [] -> + ?t:format("------- Result of COMMAND ~p~n",[ok]); + Y -> + ?t:format("!!!!!!! delete ERROR Dir ~p Error ~p~n",[Dir, Y]), + ?t:format("------- ls -al ~p~n",[os:cmd("ls -al " ++ Dir)]) + end, + + delete_release_unix(Dirs). + +delete_release_win32([]) -> + ok; +delete_release_win32(["save"|Dirs]) -> + delete_release_win32(Dirs); +delete_release_win32([Dir|Dirs]) -> + Rm = string:concat("rmdir /s ", Dir), + [] = os:cmd(Rm), + delete_release_win32(Dirs). + + +node_name(Sname) when is_atom(Sname) -> + {ok,Host} = inet:gethostname(), + list_to_atom(atom_to_list(Sname) ++ "@" ++ Host). + +copy_file(Src, Dest) -> + copy_file(Src, Dest, []). +copy_file(Src, Dest, Opts) -> + case file:copy(Src,Dest) of + {ok,_} -> + preserve(Src,Dest,Opts), + chmod(Dest,Opts), + ok; + {error,eisdir} -> + NewDest = filename:join(Dest, filename:basename(Src)), + case file:copy(Src,NewDest) of + {ok,_} -> + preserve(Src,NewDest,Opts), + chmod(NewDest,Opts); + {error,Reason} -> + copy_error(Src,Dest,Reason) + end; + {error,Reason} -> + copy_error(Src,Dest,Reason) + end. + +preserve(Src,Dest,Opts) -> + case lists:member(preserve, Opts) of + true -> + {ok, FileInfo} = file:read_file_info(Src), + ok = file:write_file_info(Dest, FileInfo); + false -> + ok + end. + +chmod(Dest,Opts) -> + case lists:keyfind(chmod,1,Opts) of + {chmod,Mode} -> + ok = file:change_mode(Dest, Mode); + false -> + ok + end. + + + +copy_error(Src, Dest, Reason) -> + io:format("Copy ~s to ~s failed: ~s\n", + [Src,Dest,file:format_error(Reason)]), + ?t:fail(file_copy_failed). + +copy_tree(Conf, Src, DestDir) -> + case catch copy_tree(Conf, Src, filename:basename(Src), DestDir) of + ok -> + ok; + {'EXIT', {{badmatch,Error},_Stack}} -> + %% Most probably, an erl_tar call has failed. + %% Known to happen on some platforms (symbolic_link_too_long) + Error; + {'EXIT', Reason} -> + {error, Reason} + end. + +copy_tree(Conf, Src, NewName, DestDir) -> + PrivDir = priv_dir(Conf), + TempTarName = filename:join(PrivDir, "temp_tar_file.tar"), + %% Not compressing tar file here since that would increase test + %% suite time by almost 100%, and the tar file is deleted + %% imediately anyway. + {ok,Tar} = erl_tar:open(TempTarName, [write]), + ok = erl_tar:add(Tar, Src, NewName, []), + ok = erl_tar:close(Tar), + ok = erl_tar:extract(TempTarName, [{cwd,DestDir}]), + ok = file:delete(TempTarName), + ok. + +%% subst_file(Src, Dest, Vars) +%% Src = Dest = string(), filename and path +%% Vars = [{Var, Val}] +%% Var = Val = string() +%% Substitute all occurrences of %Var% for Val in Src, using the list +%% of variables in Vars. Result is written to Dest. +%% +subst_file(Src, Dest, Vars) -> + subst_file(Src, Dest, Vars, []). +subst_file(Src, Dest, Vars, Opts) -> + {ok, Bin} = file:read_file(Src), + Conts = binary_to_list(Bin), + NConts = subst(Conts, Vars), + ok = file:write_file(Dest, NConts), + preserve(Src,Dest,Opts), + chmod(Dest,Opts). + +subst(Str, Vars) -> + subst(Str, Vars, []). + +subst([$%, C| Rest], Vars, Result) when $A =< C, C =< $Z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when $a =< C, C =< $z -> + subst_var([C| Rest], Vars, Result, []); +subst([$%, C| Rest], Vars, Result) when C == $_ -> + subst_var([C| Rest], Vars, Result, []); +subst([C| Rest], Vars, Result) -> + subst(Rest, Vars, [C| Result]); +subst([], _Vars, Result) -> + lists:reverse(Result). + +subst_var([$%| Rest], Vars, Result, VarAcc) -> + Key = lists:reverse(VarAcc), + case lists:keysearch(Key, 1, Vars) of + {value, {Key, Value}} -> + subst(Rest, Vars, lists:reverse(Value, Result)); + false -> + subst(Rest, Vars, [$%| VarAcc ++ [$%| Result]]) + end; +subst_var([C| Rest], Vars, Result, VarAcc) -> + subst_var(Rest, Vars, Result, [C| VarAcc]); +subst_var([], Vars, Result, VarAcc) -> + subst([], Vars, [VarAcc ++ [$%| Result]]). + + +priv_dir(Conf) -> + filename:absname(?config(priv_dir, Conf)). % Get rid of trailing slash + +latest_version(Dir) -> + List = filelib:wildcard(Dir ++ "*"), + lists:last(lists:sort(List)). + +%% A printer process which receives messages from other nodes and +%% prints in the log +reg_print_proc() -> + catch unregister(rh_print), + Pid = spawn_link(?MODULE, rh_print, []), + register(rh_print, Pid), + ok. + +rh_print() -> + receive + {print, {Module,Line}, [H|T]} -> + ?t:format("=== ~p:~p - ~p",[Module,Line,H]), + lists:foreach(fun(Term) -> ?t:format(" ~p",[Term]) end, T), + ?t:format("",[]), + rh_print(); + kill -> + exit(normal) + end. + +stop_print_proc() -> + case whereis(rh_print) of %%removes the printer process + undefined -> + ok; + Pid when is_pid(Pid) -> + rh_print ! kill + end. + +%% Create the first target release, vsn P1G. This release is used for +%% all test cases in {group,release} +create_p1g(Conf,Sname) -> + do_create_p1g(Conf,filename:join(priv_dir(Conf),Sname)). + +do_create_p1g(Conf,TargetDir) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + ErtsVsn = "4.4", + ErtsDir = "erts-"++ErtsVsn, + + %% Create dirs + BinDir = filename:join(TargetDir,bin), + ReleasesDir = filename:join(TargetDir,releases), + LogDir = filename:join(TargetDir,log), + ok = filelib:ensure_dir(filename:join(BinDir,"*")), + ok = filelib:ensure_dir(filename:join(ReleasesDir,"*")), + ok = filelib:ensure_dir(filename:join(LogDir,"*")), + + %% Copy stuff + 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]), + + copy_file(filename:join(DataDir, "../installer.beam"), + filename:join([DataDir,lib,"installer-1.0",ebin])), + + %% Create .rel, .script and .boot files + RelName = "rel0", + RelVsn = "P1G", + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + TarFile = create_basic_release(RelFile, RelVsn, {ErtsVsn,false}, + LibPath, [], [], [], []), + + %% Extract tar file in target directory (i.e. same directory as erts etc.) + ok = erl_tar:extract(TarFile, [{cwd, TargetDir}, compressed]), + + %% Create start_erl.data + StartErlDataFile = filename:join([ReleasesDir, "start_erl.data"]), + StartErlData = io_lib:fwrite("~s ~s~n", [ErtsVsn, RelVsn]), + ok = file:write_file(StartErlDataFile, StartErlData), + + %% 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 +%% Must have run create_p1g first!! +create_p1h(Conf) -> + create_upgrade_release(Conf,"rel1","P1H",{"4.4",false},[{a,"1.0"}], + [{a,[{key2,val2}]}],{"rel0",[new_appl]}). + +%% Create version P1I - which is P1H, but with application a upgraded to a-1.1 +%% Must have run create_p1h first!! +create_p1i(Conf) -> + create_upgrade_release(Conf,"rel2","P1I",{"4.4",false},[{a,"1.1"}], + [{a,[{key2,newval2}]}], + {"rel1",[{extra,gott}]}). + +%% Create version P2A - which is P1I, but with erts- +%% Must have run create_p1i first!! +create_p2a(Conf) -> + ErtsVsn = erlang:system_info(version), + create_upgrade_release(Conf,"rel3","P2A",{ErtsVsn,code:root_dir()}, + [{a,"1.1"}],[{a,[{key2,newval2}]}], + {"rel2",[new_emu]}). + +%% Create a release tar package which can be installed on top of P1G +create_upgrade_release(Conf,RelName,RelVsn,Erts,Apps,Config,{UpFromName,Descr}) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + + RelDir = filename:join(PrivDir,RelName), + RelFileName = filename:join(RelDir,RelName), + RelFile = RelFileName ++ ".rel", + ok = filelib:ensure_dir(RelFile), + LibPath = filename:join([DataDir,lib,"*",ebin]), + + UpFrom = [{filename:join([PrivDir,UpFromName,UpFromName]),Descr}], + + create_basic_release(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) -> + RelDir = filename:dirname(RelFile), + RelFileName = filename:rootname(RelFile), + + %% Create .rel file + create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps), + + %% Generate .script and .boot + ok = systools:make_script(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir}]), + + %% Generate relup + ok = systools:make_relup(RelFileName,UpFrom,DownTo,[{path,[LibPath]}, + {outdir,RelDir}]), + + %% Create sys.config + ok = write_term_file(filename:join(RelDir,"sys.config"),Config), + + + %% Create tar file (i.e. collect all lib/app-*/* and system files) + ok = systools:make_tar(RelFileName, + [{path,[LibPath]}, + {outdir,RelDir} | + case ErtsDir of + false -> []; + _ -> [{erts,ErtsDir}] + end]), + + RelFileName ++ ".tar.gz". + +%% Create a .rel file +create_installer_rel_file(RelFile,RelVsn,ErtsVsn,ExtraApps) -> + create_rel_file(RelFile,"SASL-test",RelVsn,ErtsVsn, + [{installer,"1.0"}|ExtraApps]). + +create_rel_file(RelFile,RelName,RelVsn,ErtsVsn,ExtraApps) -> + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + application:load(tools), + {ok,ToolsVsn} = application:get_key(tools,vsn), + application:load(runtime_tools), + {ok,RuntimeToolsVsn} = application:get_key(runtime_tools,vsn), + + RelFileContent = {release, + {RelName, RelVsn}, + {erts, ErtsVsn}, + [{kernel, KernelVsn}, + {stdlib, StdlibVsn}, + {sasl, SaslVsn}, + {runtime_tools, RuntimeToolsVsn}, + {tools, ToolsVsn} | + ExtraApps]}, + ok = write_term_file(RelFile,RelFileContent). + +%% Insert a term in a file, which can be read with file:consult/1. +write_term_file(File,Term) -> + ok = file:write_file(File,io_lib:format("~p.~n",[Term])). + + +%% Check that global group info is correct +check_gg_info(Node,OtherAlive,OtherDead,Synced) -> + GGI = rpc:call(Node, global_group, info, []), + GI = rpc:call(Node, global, info,[]), + try do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) + catch _:E -> + ?t:format("~ncheck_gg_info failed for ~p: ~p~nwhen GGI was: ~p~n" + "and GI was: ~p~n", + [Node,E,GGI,GI]), + ?t:fail("check_gg_info failed") + end. + +do_check_gg_info(OtherAlive,OtherDead,Synced,GGI,GI) -> + {_,gg1} = lists:keyfind(own_group_name,1,GGI), + {_,synced} = lists:keyfind(state,1,GGI), + {_,AllNodes} = lists:keyfind(own_group_nodes,1,GGI), + true = lists:sort(AllNodes) =:= lists:sort(OtherAlive++OtherDead), + {_,[]} = lists:keyfind(sync_error,1,GGI), + {_,[{gg2,[_,_]}]} = lists:keyfind(other_groups,1,GGI), + + %% There is a known bug in global_group (OTP-9177) which causes + %% the following to fail every now and then: + %% {_,SyncedNodes} = lists:keyfind(synced_nodes,1,GGI), + %% true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + %% {_,NoContact} = lists:keyfind(no_contact,1,GGI), + %% true = lists:sort(NoContact) =:= lists:sort(OtherDead), + + %% Therefore we use global:info instead for this part + {state,_,_,SyncedNodes,_,_,_,_,_,_,_} = GI, + true = lists:sort(SyncedNodes) =:= lists:sort(Synced), + + %% .. and we only check that all OtherDead are listed as + %% no_contact (due to th bug there might be more nodes in this + %% list) + {_,NoContact} = lists:keyfind(no_contact,1,GGI), + true = + lists:sort(OtherDead) =:= + lists:sort([NC || NC <- NoContact,lists:member(NC,OtherDead)]), + + ok. + +%% Return the configuration (to be inserted in sys.config) for global group tests +gg_config(Snames) -> + Nodes = [node_name(Sname) || Sname <- Snames], + [{kernel, [{sync_nodes_optional, Nodes}, + {sync_nodes_timeout, 10000}, + {global_groups, + [{gg1, Nodes}, + {gg2, [node_name(Sname) || Sname <- [ggq,ggw]]}]}]}, + {a, [{key2, val2}]}]. + +%% Start a node with short name SnameStr, and unpack P1H +unpack_p1h(Conf,Sname) -> + PrivDir = priv_dir(Conf), + [Node] = start_nodes(Conf,[Sname],"create_p1h"), + ok = rpc_inst(Node, unpack_p1h, [PrivDir]), + Node. + +%% On the given node, install P1H and make it permanent +%% This function is to be called after unpack_p1h/2, with the same node. +permanent_p1h(Node) -> + ok = rpc_inst(Node, permanent_p1h, []). + +%% For each node in ToNodes, create a target installation which is +%% indentical to the target installation for FromNode. +copy_installed(Conf,FromNode,ToNodes) -> + PrivDir = priv_dir(Conf), + DataDir = ?config(data_dir,Conf), + 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) + end, + ToNodes). + +start_nodes(Conf,Snames,Tag) -> + PrivDir = priv_dir(Conf), + Nodes = + lists:map( + 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]), + Node + end, + Snames), + wait_nodes_up(Nodes,Tag), + Nodes. + +tc_sname(Config) -> + tc_sname(Config,""). +tc_sname(Config,Fix) when is_atom(Fix) -> + tc_sname(Config,atom_to_list(Fix)); +tc_sname(Config,Fix) when is_list(Fix) -> + list_to_atom( + atom_to_list(?MODULE) + ++ "-" ++ atom_to_list(?config(sname_prefix, Config)) ++ + case Fix of + "" -> ""; + _ -> "-" ++ Fix + end). + +tc_full_node_name(Config) -> + tc_full_node_name(Config,""). +tc_full_node_name(Config,Fix) -> + node_name(tc_sname(Config,Fix)). + + +%% When installing a release for which the sys.config includes added +%% or changed global group(s), this node (test_sever@host) will be +%% disconnected from the test node (Node) by global_group.erl. This +%% will cause the rpc:call to terminate with {badrpc,nodedown} even if +%% the installation succeeds. This function installs the release, +%% accepts the faulty return value and then checks if the release was +%% successfully installed. +install_release_changed_gg(Node,RelVsn) -> + stop_cover(Node), + {badrpc,nodedown} = rpc:call(Node,release_handler,install_release,[RelVsn]), + timer:sleep(100), + wait_installed(Node,RelVsn,4). + +wait_installed(Node,RelVsn,0) -> + ?t:fail("install_release_changed_gg failed for " ++ RelVsn ++ + " on " ++ atom_to_list(Node)); +wait_installed(Node,RelVsn,N) -> + Rels = rpc:call(Node,release_handler,which_releases,[]), + case lists:keyfind(RelVsn, 2, Rels) of + {"SASL-test", RelVsn, _Libs, current} -> + start_cover(Node), + ok; + _ -> + timer:sleep(500), + wait_installed(Node,RelVsn,N-1) + end. + +%% Start/stop cover measurements on the given node +start_cover(Node) -> + cover_fun(Node,start). +stop_cover(Node) -> + cover_fun(Node,stop). + +cover_fun(Node,Func) -> + case ?t:is_cover() of + true -> + cover:Func(Node); + false -> + ok + end. + +%%%----------------------------------------------------------------- +%%% Create a fake release .... + +%% This function will create and install a release build on the +%% current running OTP release. It includes kernel, stdlib and sasl, +%% and possibly other applications if they are listed in AppDirs = +%% [{App,Vsn,LibDir}] +create_and_install_fake_first_release(Dir,AppDirs) -> + %% Create the first release + {RelName,RelVsn} = init:script_id(), + {Rel,_} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + ReleasesDir = filename:join(Dir, "releases"), + RelDir = filename:dirname(Rel), + + %% And install it + RelVsnDir = filename:join(ReleasesDir, RelVsn), + ok = filelib:ensure_dir(filename:join(RelVsnDir,"*")), + + ok = copy_file(Rel++".rel",RelVsnDir), + ok = copy_file(Rel++".boot",filename:join(RelVsnDir, "start.boot")), + ok = copy_file(filename:join(RelDir,"sys.config"),RelVsnDir), + + ok = release_handler:create_RELEASES(code:root_dir(), + ReleasesDir, + Rel++".rel", + AppDirs), + + Rel. + +%% This function create a new release, including a relup file. It can +%% be upgraded to from the release created by +%% create_and_install_fake_first_release/2. Unpack first by calls to +%% release_handler:set_unpacked and release_handler:install_file. +create_fake_upgrade_release(Dir,RelVsn,AppDirs,{UpFrom,DownTo,ExtraLibs}) -> + %% Create a new release + {RelName,_} = init:script_id(), + {Rel,Paths} = create_fake_release(Dir,RelName,RelVsn,AppDirs), + RelDir = filename:dirname(Rel), + + %% And a relup file so it can be upgraded to + RelupPath = Paths ++ [filename:join([Lib,"*","ebin"]) || Lib <- ExtraLibs], + ok = systools:make_relup(Rel,[UpFrom],[DownTo], + [{path,RelupPath}, + {outdir,RelDir}]), + + Rel. + + +create_fake_release(Dir,RelName,RelVsn,AppDirs) -> + %% Create .rel files + RelDir = filename:join(Dir,"rel_" ++ RelVsn), + Rel = filename:join([RelDir,"rel_" ++ RelVsn]), + ok = filelib:ensure_dir(Rel), + ErtsVsn = erlang:system_info(version), + + {Apps,Paths} = + lists:foldl(fun({App,Vsn,Lib},{As,Ps}) -> + {[{App,Vsn}|As], + lists:umerge([filename:join([Lib,"*",ebin])],Ps)} + end, + {[],[]}, + AppDirs), + + create_rel_file(Rel++".rel",RelName,RelVsn,ErtsVsn,Apps), + + %% Generate boot scripts + ok = systools:make_script(Rel,[local, + {path, Paths}, + {outdir,RelDir}]), + ok = copy_file(Rel++".boot", filename:join(RelDir,"start.boot")), + + %% Use an own 'releases' directory - we don't want to change the + %% contents of $OTP_ROOT/releases + %% Inform SASL about this via sys.config + ReleasesDir = filename:join(Dir, "releases"), + Config = [{sasl,[{releases_dir,ReleasesDir}]}], + ok = write_term_file(filename:join(RelDir,"sys.config"), Config), + + {Rel,Paths}. + + +rpc_inst(Node,Func,Args) -> + rpc:call(Node,installer,Func,[node()|Args]). diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src new file mode 100644 index 0000000000..85e25fdc2f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src @@ -0,0 +1,108 @@ +EFLAGS=+debug_info + +P2B= \ + P2B/a-2.0/ebin/a.beam \ + P2B/a-2.0/ebin/a_sup.beam + +LIB= \ + lib/a-1.1/ebin/a.beam \ + lib/a-1.1/ebin/a_sup.beam \ + lib/a-1.0/ebin/a.beam \ + lib/a-1.0/ebin/a_sup.beam \ + +APP= \ + app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@ \ + app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@ \ + app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@ + +OTP2740= \ + otp_2740/vsn_atom.@EMULATOR@ \ + otp_2740/vsn_list.@EMULATOR@ \ + otp_2740/vsn_numeric.@EMULATOR@ \ + otp_2740/vsn_tuple.@EMULATOR@ \ + otp_2740/vsn_string.@EMULATOR@ + +C= \ + c/aa.@EMULATOR@ \ + c/b.@EMULATOR@ \ + c/c_sup.@EMULATOR@ + + +all: $(P2B) $(LIB) $(APP) $(OTP2740) $(C) + +P2B/a-2.0/ebin/a.@EMULATOR@: P2B/a-2.0/src/a.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a.erl +P2B/a-2.0/ebin/a_sup.@EMULATOR@: P2B/a-2.0/src/a_sup.erl + erlc $(EFLAGS) -oP2B/a-2.0/ebin P2B/a-2.0/src/a_sup.erl + + +lib/a-1.0/ebin/a.@EMULATOR@: lib/a-1.0/src/a.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a.erl +lib/a-1.0/ebin/a_sup.@EMULATOR@: lib/a-1.0/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.0/ebin lib/a-1.0/src/a_sup.erl + + +lib/a-1.1/ebin/a.@EMULATOR@: lib/a-1.1/src/a.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a.erl +lib/a-1.1/ebin/a_sup.@EMULATOR@: lib/a-1.1/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.1/ebin lib/a-1.1/src/a_sup.erl + + +app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_sup.erl +app1_app2/lib1/app1-1.0/ebin/app1_server.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1_server.erl +app1_app2/lib1/app1-1.0/ebin/app1.@EMULATOR@: app1_app2/lib1/app1-1.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app1-1.0/ebin app1_app2/lib1/app1-1.0/src/app1.erl + + +app1_app2/lib1/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_sup.erl +app1_app2/lib1/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2_server.erl +app1_app2/lib1/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib1/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib1/app2-1.0/ebin app1_app2/lib1/app2-1.0/src/app2.erl + + +app1_app2/lib2/app1-2.0/ebin/app1_sup.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_sup.erl +app1_app2/lib2/app1-2.0/ebin/app1_server.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1_server.erl +app1_app2/lib2/app1-2.0/ebin/app1.@EMULATOR@: app1_app2/lib2/app1-2.0/src/app1.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app1-2.0/ebin app1_app2/lib2/app1-2.0/src/app1.erl + + +app1_app2/lib2/app2-1.0/ebin/app2_sup.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_sup.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_sup.erl +app1_app2/lib2/app2-1.0/ebin/app2_server.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2_server.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2_server.erl +app1_app2/lib2/app2-1.0/ebin/app2.@EMULATOR@: app1_app2/lib2/app2-1.0/src/app2.erl + erlc $(EFLAGS) -oapp1_app2/lib2/app2-1.0/ebin app1_app2/lib2/app2-1.0/src/app2.erl + + +otp_2740/vsn_atom.@EMULATOR@: otp_2740/vsn_atom.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_atom.erl +otp_2740/vsn_list.@EMULATOR@: otp_2740/vsn_list.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_list.erl +otp_2740/vsn_numeric.@EMULATOR@: otp_2740/vsn_numeric.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_numeric.erl +otp_2740/vsn_tuple.@EMULATOR@: otp_2740/vsn_tuple.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_tuple.erl +otp_2740/vsn_string.@EMULATOR@: otp_2740/vsn_string.erl + erlc $(EFLAGS) -ootp_2740 otp_2740/vsn_string.erl + +c/aa.@EMULATOR@: c/aa.erl + erlc $(EFLAGS) -oc c/aa.erl +c/b.@EMULATOR@: c/b.erl + erlc $(EFLAGS) -oc c/b.erl +c/c_sup.@EMULATOR@: c/c_sup.erl + erlc $(EFLAGS) -oc c/c_sup.erl diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app new file mode 100644 index 0000000000..200cfcfe47 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "2.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl new file mode 100644 index 0000000000..cfe38b55ce --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a.erl @@ -0,0 +1,47 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/P2B/a-2.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app new file mode 100644 index 0000000000..0489cb2595 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "1.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val1}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl new file mode 100644 index 0000000000..9b49e772cc --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_server.erl @@ -0,0 +1,32 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app1-1.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib1/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app new file mode 100644 index 0000000000..3c65adfbb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.app @@ -0,0 +1,9 @@ +{application, app1, + [{description, "very simple example application"}, + {id, "app1"}, + {vsn, "2.0"}, + {modules, [app1, app1_sup, app1_server]}, + {registered, [harry]}, + {applications, [kernel, stdlib, sasl]}, + {env, [{var,val2}]}, + {mod, {app1, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup new file mode 100644 index 0000000000..e5e0cbda0e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/ebin/app1.appup @@ -0,0 +1,4 @@ +{"2.0", + [{"1.0", [{load_module, app1_server}]}], + [{"1.0", [{load_module, app1_server}]}] +}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl new file mode 100644 index 0000000000..f123c8f470 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1.erl @@ -0,0 +1,22 @@ +-module(app1). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). +-export([config_change/3]). + +start(_Type, _StartArgs) -> + case app1_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. + +config_change(Changed, _New, _Removed) -> + catch ets:insert(otp_6162, hd(Changed)), + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl new file mode 100644 index 0000000000..660d095ebf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_server.erl @@ -0,0 +1,35 @@ +-module(app1_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, harry}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(error, _From, State) -> + Reply = error, + {reply, Reply, State}; +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl new file mode 100644 index 0000000000..e6ad9b6967 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app1-2.0/src/app1_sup.erl @@ -0,0 +1,17 @@ +-module(app1_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {harry,{app1_server,start_link,[]}, + permanent,2000,worker,[app1_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app new file mode 100644 index 0000000000..d48018cbda --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/ebin/app2.app @@ -0,0 +1,9 @@ +{application, app2, + [{description, "very simple example application"}, + {id, "app2"}, + {vsn, "1.0"}, + {modules, [app2, app2_sup, app2_server]}, + {registered, [ginny]}, + {applications, [kernel, stdlib, sasl]}, + {env, []}, + {mod, {app2, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl new file mode 100644 index 0000000000..a41c39730c --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2.erl @@ -0,0 +1,17 @@ +-module(app2). + +-behaviour(application). + +%% Application callbacks +-export([start/2, stop/1]). + +start(_Type, _StartArgs) -> + case app2_sup:start_link() of + {ok, Pid} -> + {ok, Pid}; + Error -> + Error + end. + +stop(_State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl new file mode 100644 index 0000000000..d8440230ff --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_server.erl @@ -0,0 +1,32 @@ +-module(app2_server). + +-behaviour(gen_server). + +%% API +-export([start_link/0]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +start_link() -> + gen_server:start_link({local, ginny}, ?MODULE, [], []). + +init([]) -> + {ok, []}. + +handle_call(_Request, _From, State) -> + Reply = ok, + {reply, Reply, State}. + +handle_cast(_Msg, State) -> + {noreply, State}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl new file mode 100644 index 0000000000..80b0952d4b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/app1_app2/lib2/app2-1.0/src/app2_sup.erl @@ -0,0 +1,17 @@ +-module(app2_sup). + +-behaviour(supervisor). + +%% API +-export([start_link/0]). + +%% Supervisor callbacks +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + AChild = {ginny,{app2_server,start_link,[]}, + permanent,2000,worker,[app2_server]}, + {ok,{{one_for_all,0,1}, [AChild]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/aa.erl b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl new file mode 100644 index 0000000000..1c853c85b2 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/aa.erl @@ -0,0 +1,41 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(aa). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/1, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, cc}, aa, [], []). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/b.erl b/lib/sasl/test/release_handler_SUITE_data/c/b.erl new file mode 100644 index 0000000000..d8426a515e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/b.erl @@ -0,0 +1,38 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(b). + + +%% External exports +-export([start_link/0]). +%% Internal exports +-export([init/0]). + +start_link() -> {ok, proc_lib:spawn_link(b, init, [])}. + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init() -> + register(bb, self()), + loop(). + +loop() -> + receive + hej -> ok + end. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c.app b/lib/sasl/test/release_handler_SUITE_data/c/c.app new file mode 100644 index 0000000000..908a94cf2d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c.app @@ -0,0 +1,8 @@ +{application, c, + [{description, "C CXC 138 11"}, + {vsn, "1.0"}, + {modules, [b, {aa, 1}, {c_sup,1}]}, + {registered, [cc,bb,c_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {c_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl new file mode 100644 index 0000000000..069eb3b99b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/c/c_sup.erl @@ -0,0 +1,40 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(c_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, c_sup}, c_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config1 = {c, + {aa, start_link, []}, + permanent, 2000, worker, [aa]}, + Config2 = {b, + {b, start_link, []}, + permanent, 2000, worker, [b]}, + {ok, {SupFlags, [Config1, Config2]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 new file mode 100755 index 0000000000..ee3d8c97cf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli1 @@ -0,0 +1,38 @@ +#!/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/clients/start_cli2 b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 new file mode 100755 index 0000000000..88912cf884 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/clients/start_cli2 @@ -0,0 +1,37 @@ +#!/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/[.].*//'` + +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\\',\\'master2@$TESTHOST\\'\] client_directory \\\"$CLIENTDIR\\\"" > /dev/null 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app new file mode 100644 index 0000000000..e938137f67 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.0"}, + {modules, [{a, 1}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app new file mode 100644 index 0000000000..1c3053b2fa --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.app @@ -0,0 +1,8 @@ +{application, a, + [{description, "A CXC 138 11"}, + {vsn, "1.1"}, + {modules, [{a, 2}, {a_sup,1}]}, + {registered, [a_sup]}, + {applications, [kernel, stdlib]}, + {env, [{key1, val1}]}, + {mod, {a_sup, []}}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup new file mode 100644 index 0000000000..05db4cb541 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/ebin/a.appup @@ -0,0 +1,3 @@ +{"1.1", + [{"1.0",[{update,a,{advanced,extra_par}}]}], + []}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl new file mode 100644 index 0000000000..c082ad5339 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a.erl @@ -0,0 +1,54 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +%% External exports +-export([start_link/0, a/0, b/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2, code_change/3]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). +b() -> gen_server:call(aa, b). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, {state, bval}}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}; + +handle_call(b, _From, State) -> + {reply, {ok, element(2, State)}, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(1, Extra, State) -> + {ok, {state, bval}}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.1/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. 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 new file mode 100644 index 0000000000..6f77317f6a --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/ebin/installer.app @@ -0,0 +1,6 @@ +{application, installer, + [{description, "Installer application"}, + {vsn, "1.0"}, + {modules, [{installer, 1}]}, + {registered, []}, + {applications, [kernel, stdlib, sasl]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl new file mode 120000 index 0000000000..c2f93b822d --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/installer-1.0/src/installer.erl @@ -0,0 +1 @@ +../../../../installer.erl \ No newline at end of file diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl new file mode 100644 index 0000000000..883688c231 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_atom.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_atom). + +-vsn(atom). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl new file mode 100644 index 0000000000..34c38307ba --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_list.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_list). + +-vsn([list, "of", {some, terms}]). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl new file mode 100644 index 0000000000..6bf52753fd --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_numeric.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_numeric). + +-vsn(231894). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl new file mode 100644 index 0000000000..aa430a0bb3 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_string.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_string). + +-vsn("a string"). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl new file mode 100644 index 0000000000..3ff1018994 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/otp_2740/vsn_tuple.erl @@ -0,0 +1,26 @@ +%% ``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 via the world wide web 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. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +-module(vsn_tuple). + +-vsn({tuple, ["of", terms]}). + +-export([ok/0]). + +ok() -> + ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/start b/lib/sasl/test/release_handler_SUITE_data/start new file mode 100755 index 0000000000..45e526c15f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/start @@ -0,0 +1,29 @@ +#!/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] +# +ROOTDIR=%ROOT% + +if [ "x${NODENAME}" = "x" ] +then + echo "ERROR: Variable \$NODENAME is not set!!" + exit 1 +fi + +if [ -z "$RELDIR" ] +then + RELDIR=$ROOTDIR/releases +fi + +HEART_COMMAND=$ROOTDIR/bin/start +HW_WD_DISABLE=true +export HW_WD_DISABLE HEART_COMMAND + +START_ERL_DATA=${1:-$RELDIR/start_erl.data} + +$ROOTDIR/bin/run_erl /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA -heart -sname $NODENAME" > $ROOTDIR/log/run_erl.out 2>&1 & diff --git a/lib/sasl/test/release_handler_SUITE_data/target_system.erl b/lib/sasl/test/release_handler_SUITE_data/target_system.erl new file mode 120000 index 0000000000..4d36c59632 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/target_system.erl @@ -0,0 +1 @@ +../../examples/src/target_system.erl \ No newline at end of file diff --git a/lib/sasl/test/sasl.cover b/lib/sasl/test/sasl.cover new file mode 100644 index 0000000000..d19d3d0180 --- /dev/null +++ b/lib/sasl/test/sasl.cover @@ -0,0 +1,2 @@ +{incl_app,sasl,details}. + diff --git a/lib/sasl/test/sasl.spec b/lib/sasl/test/sasl.spec new file mode 100644 index 0000000000..f3de90c9aa --- /dev/null +++ b/lib/sasl/test/sasl.spec @@ -0,0 +1 @@ +{suites,"../sasl_test",all}. diff --git a/lib/sasl/test/sasl_SUITE.erl b/lib/sasl/test/sasl_SUITE.erl new file mode 100644 index 0000000000..454095db6a --- /dev/null +++ b/lib/sasl/test/sasl_SUITE.erl @@ -0,0 +1,98 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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(sasl_SUITE). +-include_lib("common_test/include/ct.hrl"). + + +% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). +-define(application, sasl). + +% Test server specific exports +-export([all/0,groups/0,init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +% Test cases must be exported. +-export([app_test/1, + log_mf_h_env/1]). + +all() -> + [app_test, log_mf_h_env]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +init_per_testcase(_Case, Config) -> + ?line Dog=test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +app_test(Config) when is_list(Config) -> + ?line ?t:app_test(sasl, allow), + ok. + +%% OTP-9185 - fail sasl start if some but not all log_mf_h env vars +%% are given. +log_mf_h_env(Config) -> + PrivDir = ?config(priv_dir,Config), + LogDir = filename:join(PrivDir,sasl_SUITE_log_dir), + ok = file:make_dir(LogDir), + application:stop(sasl), + SaslEnv = application:get_all_env(sasl), + lists:foreach(fun({E,_V}) -> application:unset_env(sasl,E) end, SaslEnv), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxbytes,50000), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,"xx"), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_maxfiles,2), + ok = application:unset_env(sasl,error_logger_mf_dir), + match_error(missing_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,xx), + match_error(bad_config,application:start(sasl)), + + ok = application:set_env(sasl,error_logger_mf_dir,LogDir), + ok = application:start(sasl). + + +%%----------------------------------------------------------------- +%% Internal +match_error(Expected,{error,{bad_return,{_,{'EXIT',{Expected,{sasl,_}}}}}}) -> + ok; +match_error(Expected,Actual) -> + ?t:fail({unexpected_return,Expected,Actual}). diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl new file mode 100644 index 0000000000..9190b111ef --- /dev/null +++ b/lib/sasl/test/systools_SUITE.erl @@ -0,0 +1,2112 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 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% +%% +%% +%% Test suite for the systools module. +%% +%% The systools module is a wrapper for a number of modules that +%% handle large parts of the release building functionality +%% (e.g. checking app files, building a tar file, building +%% release upgrad scripts. +%% + + +-module(systools_SUITE). + +%-define(debug, true). + +-include_lib("test_server/include/test_server.hrl"). +-define(format(S, A), ok). +-define(datadir, ?config(data_dir, Config)). +-define(privdir, ?config(priv_dir, Config)). +-define(copydir, ?config(copy_dir, Config)). + +-include_lib("kernel/include/file.hrl"). + +-export([all/0,suite/0,groups/0,init_per_group/2,end_per_group/2]). + +-export([ script_options/1, normal_script/1, no_mod_vsn_script/1, + wildcard_script/1, variable_script/1, + abnormal_script/1, src_tests_script/1, crazy_script/1, + warn_shadow_script/1, + included_script/1, included_override_script/1, + included_fail_script/1, included_bug_script/1, exref_script/1]). +-export([ tar_options/1, normal_tar/1, no_mod_vsn_tar/1, variable_tar/1, + src_tests_tar/1, shadow_tar/1, var_tar/1, + exref_tar/1, link_tar/1]). +-export([ normal_relup/1, abnormal_relup/1, no_appup_relup/1, + bad_appup_relup/1, app_start_type_relup/1, otp_3065/1]). +-export([ + otp_6226/1]). +-export([init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-import(lists, [foldl/3]). + +-define(default_timeout, ?t:minutes(20)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +suite() -> + [{ct_hooks, [ts_install_cth]}]. + +all() -> + [{group, script}, {group, tar}, {group, relup}, + {group, tickets}]. + +groups() -> + [{script, [], + [script_options, normal_script, no_mod_vsn_script, + wildcard_script, variable_script, abnormal_script, + src_tests_script, crazy_script, warn_shadow_script, + included_script, included_override_script, + included_fail_script, included_bug_script, exref_script, + otp_3065]}, + {tar, [], + [tar_options, normal_tar, no_mod_vsn_tar, variable_tar, + src_tests_tar, shadow_tar, var_tar, + exref_tar, link_tar]}, + {relup, [], + [normal_relup, abnormal_relup, no_appup_relup, + bad_appup_relup, app_start_type_relup]}, + {tickets, [], [otp_6226]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +init_per_suite(Config) when is_list(Config) -> + %% Make of copy of the data directory. + DataDir = ?datadir, + PrivDir = ?privdir, + ?line CopyDir = fname(PrivDir, "datacopy"), + ?line TarFile = fname(PrivDir, "datacopy.tgz"), + ?line {ok, Tar} = erl_tar:open(TarFile, [write, compressed]), + ?line ok = erl_tar:add(Tar, DataDir, CopyDir, [compressed]), + ?line ok = erl_tar:close(Tar), + ?line ok = erl_tar:extract(TarFile, [compressed]), + ?line ok = file:delete(TarFile), + + %% Compile source files in the copy directory. + ?line Sources = filelib:wildcard(fname([CopyDir,'*','*','*','*','*.erl'])), + ?line lists:foreach(fun compile_source/1, Sources), + + %% To use in end_per_testcase + Path = code:get_path(), + {ok,Cwd} = file:get_cwd(), + + [{copy_dir, CopyDir}, {cwd,Cwd}, {path,Path} | Config]. + +compile_source(File) -> + %% The compiler will no longer create a Beam file + %% with a module name that does not match the output + %% file, so we must compile to a binary and write + %% the output file ourselves. + U = filename:dirname(filename:dirname(File)), + Base = filename:rootname(filename:basename(File)), + OutFile = filename:join([U,"ebin",Base++".beam"]), + OutFileTemp = OutFile ++ "#", + {ok,_,Code} = compile:file(File, [binary]), + ok = file:write_file(OutFileTemp, Code), + file:rename(OutFileTemp, OutFile). + +end_per_suite(Conf) when is_list(Conf) -> + %% Nothing. + Conf. + +init_per_testcase(link_tar, Config) -> + case os:type() of + {unix, _} -> init_per_testcase(dummy, Config); + {win32, _} -> {skip, "Skip on windows"} + end; +init_per_testcase(_Case, Config) -> + ?line Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + case {?config(path,Config),?config(cwd,Config)} of + {undefined,undefined} -> + ok; + {Path,Cwd} -> + true = code:set_path(Path), + ok = file:set_cwd(Cwd) + end, + ok. + + + +%% Usage: +%% systools:make_script("RelName") +%% Make a boot file from RelName.rel. +%% Generates RelName.{script,boot} +%% systools:make_tar("RelName") +%% Make a release package from RelName.rel. +%% Generates RelName.tar,Z +%% systools:script2boot(File) +%% File.script -> File.boot +%% systools:make_relup("Target", ["UpFromRel"...], ["DownToRel"...], Opts) +%% Gather all appup scripts to the relup file +%% + + +%% make_script +%% +script_options(suite) -> []; +script_options(doc) -> + ["Check illegal script options."]; +script_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_script("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[locall]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},locall])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_script("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, {"TEST", "/home/lib"}}]}, _}} = + (catch systools:make_script("release", + [{variables, {"TEST", "/home/lib"}}])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_script("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_script("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_script("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_script("release", [{machine,"appl"}])), + ok. + + +%% make_script +%% +normal_script(suite) -> []; +normal_script(doc) -> + ["Check that make_script handles normal case."]; +normal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName,[{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +no_mod_vsn_script(suite) -> []; +no_mod_vsn_script(doc) -> + ["Check that make_script handles normal case.", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P1 = fname([LibDir, 'db-3.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + + ?line true = code:add_patha(P1), + ?line true = code:add_patha(P2), + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(filename:basename(LatestName)), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Check the same but w. silent flag + ?line {ok, _, []} = systools:make_script(LatestName, [silent]), + + %% Use the local option + ?line ok = systools:make_script(LatestName, [local]), + ?line ok = check_script_path(LatestName), + + %% use the path option + ?line code:set_path(PSAVE), % Restore path + %% Mess up std path: + ?line true = code:add_patha(fname([LibDir, 'db-1.0', ebin])), + ?line true = code:add_patha(fname([LibDir, 'fe-2.1', ebin])), + + ?line error = systools:make_script(LatestName), %should fail + ?line ok = systools:make_script(LatestName, + [{path, [P1, P2]}]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + + +%% make_script +%% +wildcard_script(suite) -> []; +wildcard_script(doc) -> + ["Check that make_script handles wildcards in path."]; +wildcard_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line WildDir = fname([LibDir, '*', ebin]), + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_script(filename:basename(LatestName)), + + ?line ok = systools:make_script(LatestName, + [{path, [WildDir]}]), + + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +variable_script(suite) -> []; +variable_script(doc) -> + ["Add own installation dependent variable in script."]; +variable_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line ok = systools:make_script(LatestName, + [{path, P}, + {variables, [{"TEST", LibDir}]}]), + + %% Check variables + ?line ok = check_var_script_file([fname(['$TEST', 'db-2.1', ebin]), + fname(['$TEST', 'fe-3.1', ebin])], + P, + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +abnormal_script(suite) -> []; +abnormal_script(doc) -> + ["Abnormal cases."]; +abnormal_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + + ?line ok = file:set_cwd(LatestDir), + ?line LibDir = fname([DataDir, d_bad_app_vsn, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Check wrong app vsn + ?line error = systools:make_script(LatestName, [{path, P}]), + ?line {error, + systools_make, + [{error_reading, {db, {no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = + systools:make_script(LatestName, [silent, {path, P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +src_tests_script(suite) -> []; +src_tests_script(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + N = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than its .erl file + ?line Erl = filename:join([P1,"..","src","db1.erl"]), + ?line {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + ?line Beam = filename:join(P1,"db1.beam"), + ?line ok=file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + %% Then make script - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, {path, N}, src_tests]), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = + systools:make_script(LatestName, [silent, {path, N}]), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_script(LatestName, [silent, + {path, N}, + no_module_tests, + src_tests]), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + +%% make_script +%% +warn_shadow_script(suite) -> []; +warn_shadow_script(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +warn_shadow_script(Config) when is_list(Config) -> + %% This test has been removed since the 'vsn' attribute is + %% not used any more, starting with R6. No warning + %% 'obj_out_of_date' seemed to be generated. + true. + + +%% make_script +%% +crazy_script(suite) -> []; +crazy_script(doc) -> + ["Do the crazy cases."]; +crazy_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest, Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Run with bad path + ?line error = systools:make_script(LatestName), + ?line {error, _, [{error_reading, _}, {error_reading, _}]} = + systools:make_script(LatestName, [silent]), + + %% Run with .rel file lacking kernel + ?line {LatestDir2, LatestName2} = create_script(latest_nokernel, Config), + ?line ok = file:set_cwd(LatestDir2), + + ?line error = systools:make_script(LatestName2), + ?line {error, _, {missing_mandatory_app,[kernel,stdlib]}} = + systools:make_script(LatestName2, [silent,{path,P}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_script(suite) -> []; +included_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications."]; +included_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc1, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t5, t4, t6], + [t1, t3, t6]), + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_override_script(suite) -> []; +included_override_script(doc) -> + ["Check that make_script handles generation of script", + "for applications with included applications which are override by", + "the .rel file."]; +included_override_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc2, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t1, t2, t3, t4, t6, t5], + [t1, t3, t6, t5]), + + ?line {_, LatestName1} = create_include_files(inc3, Config), + ?line ok = systools:make_script(LatestName1), + ?line ok = check_include_script(LatestName1, + [t3, t5, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line {_, LatestName2} = create_include_files(inc4, Config), + ?line ok = systools:make_script(LatestName2), + ?line ok = check_include_script(LatestName2, + [t3, t4, t6, t5, t1, t2], + [t3, t6, t5, t1, t2]), + + ?line {_, LatestName3} = create_include_files(inc5, Config), + ?line ok = systools:make_script(LatestName3), + ?line ok = check_include_script(LatestName3, + [t3, t4, t6, t1, t2], + [t3, t6, t1, t2]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_fail_script(suite) -> []; +included_fail_script(doc) -> + ["Check that make_script handles errors then generating", + "script with included applications."]; +included_fail_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc6, Config), + ?line ok = file:set_cwd(LatestDir), + ?line {error, _, {undefined_applications,[t2]}} = + systools:make_script(LatestName, [silent]), + + ?line {_, LatestName1} = create_include_files(inc7, Config), + ?line {error, _, {duplicate_include,[{{t5,t7,_,_},{t5,t6,_,_}}]}} = + systools:make_script(LatestName1, [silent]), + + ?line {_, LatestName3} = create_include_files(inc9, Config), + ?line {error, _, {circular_dependencies,[{t10,_},{t8,_}]}} = + systools:make_script(LatestName3, [silent]), + + ?line {_, LatestName4} = create_include_files(inc10, Config), + ?line {error, _, [{error_reading,{t9,{override_include,[t7]}}}]} = + systools:make_script(LatestName4, [silent]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_script +%% +included_bug_script(suite) -> []; +included_bug_script(doc) -> + ["Check that make_script handles generation of script", + "with difficult dependency for included applications."]; +included_bug_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(inc11, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [t13, t11, t12], + [t11, t12]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +otp_3065(suite) -> []; +otp_3065(doc) -> + ["Circular dependencies in systools:make_script()."]; +otp_3065(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line {LatestDir, LatestName} = create_include_files(otp_3065, Config), + ?line ok = file:set_cwd(LatestDir), + ?line ok = systools:make_script(LatestName), + ?line ok = check_include_script(LatestName, + [aa12, chAts, chTraffic], + [chTraffic]), + ?line ok = file:set_cwd(OldDir), + ok. + + +%% make_script +%% +exref_script(suite) -> []; +exref_script(doc) -> + ["Check that make_script exref option works."]; +exref_script(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [{path,P}, silent]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_script(LatestName, [exref, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_script(LatestName, [{exref,[db]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_script(LatestName, [{exref,[fe]}, {path,P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_script(LatestName, [{exref,[db,stdlib]}, {path,P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line {ok, _} = read_script_file(LatestName), % Check readabillity + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), % Restore path + ok. + +check_exref_warnings(with_db1, W) -> + case get_exref(undef, W) of + {ok, [{db2,non_existing_func,0}, + {fe2,non_existing_func,0}, + {lists,non_existing_func,1}]} -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}); + _E -> + test_server:fail({bad_undef,_E}) + end; +check_exref_warnings(without_db1, W) -> + case get_exref(undef, W) of + false -> + ok; + {ok, L} -> + test_server:fail({exref_warning_undef, L}) + end. + +get_exref(undef, W) -> filter(no_hipe(get_exref1(exref_undef, W))). + +filter(false) -> + false; +filter({ok, W}) -> + {ok, filter(W)}; +filter(L) -> + lists:filter(fun%({hipe_consttab,_,_}) -> false; + ({int,_,_}) -> false; + ({i,_,_}) -> false; + ({crypto,_,_}) -> false; + (_) -> true + end, + L). + +get_exref1(T, [{warning, {T, Value}}|_]) -> {ok, Value}; +get_exref1(T, [_|W]) -> get_exref1(T, W); +get_exref1(_, []) -> false. + +no_hipe(false) -> + false; +no_hipe({ok, Value}) -> + case erlang:system_info(hipe_architecture) of + undefined -> + Hipe = "hipe", + Fun = fun({M,_,_}) -> not lists:prefix(Hipe, atom_to_list(M)) end, + NewValue = lists:filter(Fun, Value), + {ok, NewValue}; + _Arch -> + {ok, Value} + end. + +%% tar_options +%% +tar_options(suite) -> []; +tar_options(doc) -> + ["Check illegal tar options."]; +tar_options(Config) when is_list(Config) -> + ?line {'EXIT',{{badarg,[{path,["Path",12,"Another"]}]}, _}} = + (catch systools:make_tar("release", [{path,["Path",12,"Another"]}])), + ?line {'EXIT',{{badarg,[sillent]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},sillent])), + ?line {'EXIT',{{badarg,[{dirs,["dirs"]}]}, _}} = + (catch systools:make_tar("release", [{dirs, ["dirs"]}])), + ?line {'EXIT',{{badarg,[{erts, illegal}]}, _}} = + (catch systools:make_tar("release", [{erts, illegal}])), + ?line {'EXIT',{{badarg,[src_testsxx]}, _}} = + (catch systools:make_tar("release", + [{path,["Path"]},src_testsxx])), + ?line {'EXIT',{{badarg,[{variables, [{a, b}, {"a", "b"}]}]}, _}} = + (catch systools:make_tar("release", + [{variables, [{a, b}, {"a", "b"}]}])), + ?line {'EXIT',{{badarg,[{var_tar, illegal}]}, _}} = + (catch systools:make_tar("release", [{var_tar, illegal}])), + ?line {'EXIT',{{badarg,[exreff]}, _}} = + (catch systools:make_tar("release", + [{path,["Path","Another"]},exreff])), + ?line {'EXIT',{{badarg,[{exref,["appl"]}]}, _}} = + (catch systools:make_tar("release", [{exref,["appl"]}])), + ?line {'EXIT',{{badarg,[{machine, "appl"}]}, _}} = + (catch systools:make_tar("release", [{machine,"appl"}])), + ok. + + +%% normal_tar +%% +normal_tar(suite) -> []; +normal_tar(doc) -> + ["Check normal case"]; +normal_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% no_mod_vsn_tar +%% +no_mod_vsn_tar(suite) -> []; +no_mod_vsn_tar(doc) -> + ["Check normal case", + "Modules specified without version in .app file (db-3.1)." + "Note that this is now the normal way - i.e. systools now " + "ignores the module versions in the .app file."]; +no_mod_vsn_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest_no_mod_vsn,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-3.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + ?line ok = systools:make_tar(LatestName, [{path, P}]), + ?line ok = check_tar(fname([lib,'db-3.1',ebin,'db.app']), LatestName), + ?line {ok, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% variable_tar +%% +variable_tar(suite) -> []; +variable_tar(doc) -> + ["Use variable and create separate tar (included in generated tar)."]; +variable_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line {ok, _, _} = systools:make_tar(LatestName, + [{path, P}, silent, + {variables,[{"TEST", LibDir}]}]), + ?line ok = check_var_tar("TEST", LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% link_tar +%% +link_tar(suite) -> []; +link_tar(doc) -> + ["Check that symlinks in applications are handled correctly"]; +link_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_links, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + %% Make some links + ?line Db1Erl = fname(['db-2.1',src,'db1.erl']), + ?line NormalDb1Erl = fname([DataDir,d_normal,lib,Db1Erl]), + ?line LinkDb1Erl = fname([LibDir, Db1Erl]), + ?line ok = file:make_symlink(NormalDb1Erl, LinkDb1Erl), + ?line Db1Beam = fname(['db-2.1',ebin,'db1.beam']), + ?line NormalDb1Beam = fname([DataDir,d_normal,lib,Db1Beam]), + ?line LinkDb1Beam = fname([LibDir, Db1Beam]), + ?line ok = file:make_symlink(NormalDb1Beam, LinkDb1Beam), + ?line FeApp = fname(['fe-3.1',ebin,'fe.app']), + ?line NormalFeApp = fname([DataDir,d_normal,lib,FeApp]), + ?line LinkFeApp = fname([LibDir, FeApp]), + ?line ok = file:make_symlink(NormalFeApp, LinkFeApp), + + %% Create the tar and check that the linked files are included as + %% regular files + ?line ok = file:set_cwd(LatestDir), + + ?line {ok,_,[]} = systools:make_script(LatestName, [{path, P},silent]), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam])], + LatestName), + + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar_regular(?privdir, + [fname([lib,FeApp]), + fname([lib,Db1Beam]), + fname([lib,Db1Erl])], + LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% src_tests_tar +%% +src_tests_tar(suite) -> []; +src_tests_tar(doc) -> + ["Do not check date of object file or that source code can be found."]; +src_tests_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_missing_src, lib]), + ?line P1 = fname([LibDir, 'db-2.1', ebin]), + ?line P2 = fname([LibDir, 'fe-3.1', ebin]), + P = [P1, P2], + + ?line ok = file:set_cwd(LatestDir), + + %% Manipulate the modification date of a beam file so it seems + %% older than the .erl file + Erl = filename:join([P1,"..","src","db1.erl"]), + {ok, FileInfo=#file_info{mtime={{Y,M,D},T}}} = file:read_file_info(Erl), + Beam = filename:join(P1,"db1.beam"), + ok = file:write_file_info(Beam, FileInfo#file_info{mtime={{Y-1,M,D},T}}), + + %% Remove a .erl file + ?line Erl2 = filename:join([P1,"..","src","db2.erl"]), + ?line file:delete(Erl2), + + ?line ok = systools:make_script(LatestName, [{path, P}]), + + %% Then make tar - two warnings should be issued when + %% src_tests is given + %% 1. old object code for db1.beam + %% 2. missing source code for db2.beam + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Without the src_tests option, no warning should be issued + ?line {ok, _, []} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + %% Check that the old no_module_tests option (from the time when + %% it was default to do the src_test) is ignored + ?line {ok, _, [{warning,{obj_out_of_date,_}}, + {warning,{source_not_found,_}}]} = + systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}, + no_module_tests, + src_tests]), + ?line ok = check_tar(fname([lib,'db-2.1',src,'db1.erl']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% shadow_tar +%% +shadow_tar(suite) -> []; +shadow_tar(doc) -> + ["Check that jam file out of date warning doesn't", + "shadow bad module version error."]; +shadow_tar(Config) when is_list(Config) -> + % This test has been commented out since the 'vsn' attribute is not used + % any more, starting with R6. No warning 'obj_out_of_date' seemed to be + % generated. + true; +shadow_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, 'd_bad_mod+warn', lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent]), + ?line {error, _, _} = systools:make_tar(LatestName, [{path, P}, silent, + {dirs, [src]}]), + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% var_tar +%% +var_tar(suite) -> []; +var_tar(doc) -> + ["Check that make_tar handles generation and placement of tar", + "files for variables outside the main tar file.", + "Test the {var_tar, include | ownfile | omit} option."]; +var_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + ?line PSAVE = code:get_path(), % Save path + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, + [silent, + {path, P}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, ownfile}, + {variables,[{"TEST", LibDir}]}]), + + ?line true = exists_tar_file("TEST"), %% Also removes the file ! + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, omit}, + {variables,[{"TEST", LibDir}]}]), + + ?line {error, {not_generated, _}} = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = systools:make_tar(LatestName, [{path, P}, + {var_tar, include}, + {variables,[{"TEST", LibDir}]}]), + + ?line ok = check_var_tar("TEST", LatestName), + ?line false = exists_tar_file("TEST"), + + ?line ok = file:set_cwd(OldDir), + ?line code:set_path(PSAVE), + ok. + + +%% exref_tar +%% +exref_tar(suite) -> []; +exref_tar(doc) -> + ["Check exref option."]; +exref_tar(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'fe-3.1', ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line {ok, _, _} = systools:make_script(LatestName, [silent, {path, P}]), + + %% Complete exref + ?line {ok, _, W1} = + systools:make_tar(LatestName, [exref, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W1), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% Only exref the db application. + ?line {ok, _, W2} = + systools:make_tar(LatestName, [{exref, [db]}, {path, P}, silent]), + ?line check_exref_warnings(with_db1, W2), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + %% Only exref the fe application. + ?line {ok, _, W3} = + systools:make_tar(LatestName, [{exref, [fe]}, {path, P}, silent]), + ?line check_exref_warnings(without_db1, W3), + ?line ok = check_tar(fname([lib,'db-2.1',ebin,'db.app']), LatestName), + + %% exref the db and stdlib applications. + ?line {ok, _, W4} = + systools:make_tar(LatestName, [{exref, [db, stdlib]}, + {path, P}, silent]), + ?line check_exref_warnings(with_db1, W4), + ?line ok = check_tar(fname([lib,'fe-3.1',ebin,'fe.app']), LatestName), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% The relup stuff. +%% +%% + + +%% make_relup +%% +normal_relup(suite) -> []; +normal_relup(doc) -> + ["Check normal case"]; +normal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + ?line {_LatestDir2,LatestName2} = create_script(latest2,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = [fname([DataDir, d_normal, lib])], + ?line P = [fname([LibDir, '*', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% OTP-2561: Check that the option 'restart_emulator' generates a + %% "restart_new_emulator" instruction. + ?line {ok, _ , _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P},restart_emulator,silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line ok = check_restart_emulator(), + + %% This is the ultra normal case + ?line ok = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, []} = + systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{db, "2.1"}], [{db, "1.0"}]), + + %% Check that warnings get through + ?line ok = systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + ?line {ok, _, _, [{erts_vsn_changed, _}]} = + systools:make_relup(LatestName, [LatestName2], [LatestName1], + [{path, P}, silent]), + ?line ok = check_relup([{fe, "3.1"}, {db, "2.1"}], [{db, "1.0"}]), + + ?line ok = file:set_cwd(OldDir), + ok. + + +%% This test fails if wrong version numbers are seen in the relup file +%% or if any application is missing. This was triggered by OTP-1360. +check_relup(UpVsnL, DnVsnL) -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + UpVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Up]), + [] = foldl(fun(X, Acc) -> + true = lists:member(X, Acc), + lists:delete(X, Acc) end, + DnVsnL, + [{App, Vsn} || {load_object_code,{App,Vsn,_}} <- Dn]), + ok. + +check_restart_emulator() -> + {ok, [{_V1, [{_, _, Up}], [{_, _, Dn}]}]} = file:consult(relup), + restart_new_emulator = lists:last(Up), + restart_new_emulator = lists:last(Dn), + ok. + +%% make_relup +%% +no_appup_relup(suite) -> []; +no_appup_relup(doc) -> + ["Check that appup files may be missing, but only if we don't need them."]; +no_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest_small1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line P1 = [fname([DataDir, d_no_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that appup might be missing + ?line ok = + systools:make_relup(LatestName, [LatestName], [], [{path, P1}]), + ?line {ok,_, _, []} = + systools:make_relup(LatestName, [LatestName], [], + [silent, {path, P1}]), + + %% Check that appup might NOT be missing when we need it + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, P1}]), + ?line {error,_,{file_problem, {_,{error,{open,_,_}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, P1}]), + + %% Check that appups missing vsn traps + ?line P2 = [fname([DataDir, d_no_appup, lib, 'fe-2.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line error = + systools:make_relup(LatestName0, [LatestName1], [], [{path, P2}]), + ?line {error,_,{no_relup, _, _, _}} = + systools:make_relup(LatestName0, [], [LatestName1], + [silent, {path, P2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +bad_appup_relup(suite) -> []; +bad_appup_relup(doc) -> + ["Check that badly written appup files are detected"]; +bad_appup_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest_small,Config), + ?line {_LatestDir0,LatestName0} = create_script(latest_small0,Config), + + ?line DataDir = filename:absname(?copydir), + ?line N2 = [fname([DataDir, d_bad_appup, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + %% Check that bad appup is trapped + ?line error = + systools:make_relup(LatestName, [LatestName0], [], [{path, N2}]), + ?line {error,_,{file_problem, {_, {error, {parse,_, _}}}}} = + systools:make_relup(LatestName, [], [LatestName0], + [silent, {path, N2}]), + + ?line ok = file:set_cwd(OldDir), + ok. + +%% make_relup +%% +abnormal_relup(suite) -> []; +abnormal_relup(doc) -> + ["Check some abnormal cases"]; +abnormal_relup(Config) when is_list(Config) -> + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir,LatestName} = create_script(latest0,Config), + ?line {_LatestDir1,LatestName1} = create_script(latest1,Config), + + %% Check wrong app vsn + ?line DataDir = filename:absname(?copydir), + ?line P = [fname([DataDir, d_bad_app_vsn, lib, 'db-2.1', ebin]), + fname([DataDir, d_bad_app_vsn, lib, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + ?line error = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [{path, P}]), + ?line R0 = systools:make_relup(LatestName, [LatestName1], [LatestName1], + [silent, {path, P}]), + ?line {error,systools_make, + [{error_reading,{db,{no_valid_version, + {{"should be","2.1"}, + {"found file", _, "2.0"}}}}}]} = R0, + ?line ok = file:set_cwd(OldDir), + ok. + + +%% Check that application start type is used in relup +app_start_type_relup(suite) -> + []; +app_start_type_relup(doc) -> + ["Release upgrade file with various application start types"]; +app_start_type_relup(Config) when is_list(Config) -> + ?line PrivDir = ?config(priv_dir, Config), + ?line {Dir1,Name1} = create_script(latest_app_start_type1,Config), + ?line {Dir2,Name2} = create_script(latest_app_start_type2,Config), + ?line Release1 = filename:join(Dir1,Name1), + ?line Release2 = filename:join(Dir2,Name2), + + ?line {ok, Release2Relup, systools_relup, []} = systools:make_relup(Release2, [Release1], [Release1], [{outdir, PrivDir}, silent]), + ?line {"2", [{"1",[], UpInstructions}], [{"1",[], DownInstructions}]} = Release2Relup, + %% ?t:format("Up: ~p",[UpInstructions]), + %% ?t:format("Dn: ~p",[DownInstructions]), + ?line [{load_object_code, {mnesia, _, _}}, + {load_object_code, {sasl, _, _}}, + {load_object_code, {webtool, _, _}}, + {load_object_code, {snmp, _, _}}, + {load_object_code, {xmerl, _, _}}, + point_of_no_return + | UpInstructionsT] = UpInstructions, + ?line true = lists:member({apply,{application,start,[mnesia,permanent]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[sasl,transient]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,start,[webtool,temporary]}}, UpInstructionsT), + ?line true = lists:member({apply,{application,load,[snmp]}}, UpInstructionsT), + ?line false = lists:any(fun({apply,{application,_,[xmerl|_]}}) -> true; (_) -> false end, UpInstructionsT), + ?line [point_of_no_return | DownInstructionsT] = DownInstructions, + ?line true = lists:member({apply,{application,stop,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,stop,[xmerl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[mnesia]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[sasl]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[webtool]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[snmp]}}, DownInstructionsT), + ?line true = lists:member({apply,{application,unload,[xmerl]}}, DownInstructionsT), + ok. + + +otp_6226(suite) -> + []; +otp_6226(doc) -> + ["{outdir,Dir} option for systools:make_script()"]; +otp_6226(Config) when is_list(Config) -> + PrivDir = ?privdir, + ?line {ok, OldDir} = file:get_cwd(), + + ?line {LatestDir, LatestName} = create_script(latest0,Config), + ?line {_LatestDir, LatestName1} = create_script(latest1,Config), + + ?line DataDir = filename:absname(?copydir), + ?line LibDir = fname([DataDir, d_normal, lib]), + ?line P = [fname([LibDir, 'db-2.1', ebin]), + fname([LibDir, 'db-1.0', ebin]), + fname([LibDir, 'fe-3.1', ebin]), + fname([DataDir, lib, kernel, ebin]), + fname([DataDir, lib, stdlib, ebin])], + + ?line ok = file:set_cwd(LatestDir), + + + %% Create an outdir1 directory + ?line ok = file:make_dir("outdir1"), + + %% ==== Now test systools:make_script ==== + %% a) badarg + ?line {'EXIT', {{badarg,[{outdir,outdir1}]}, _}} = + (catch systools:make_script(LatestName, [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Outdir1 = filename:join(PrivDir, "outdir1"), + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Script1 = filename:join(Outdir1, LatestName ++ ".script"), + ?line Boot1 = filename:join(Outdir1, LatestName ++ ".boot"), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_script(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Script1), + ?line true = filelib:is_file(Boot1), + ?line ok = file:delete(Script1), + ?line ok = file:delete(Boot1), + + %% d) absolute but incorrect path + ?line Outdir2 = filename:join(PrivDir, "outdir2"), + ?line Script2 = filename:join(Outdir2, LatestName ++ ".script"), + ?line {error,_,{open,Script2,_}} = + systools:make_script(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{open,_,_}} = + systools:make_script(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_script(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(LatestName ++ ".script"), + ?line true = filelib:is_file(LatestName ++ ".boot"), + ?line ok = file:delete(LatestName ++ ".script"), + ?line ok = file:delete(LatestName ++ ".boot"), + ?line ok = file:set_cwd(LatestDir), + + %% ==== Now test systools:make_tar ===== + ?line {ok,_,[]} = systools:make_script(LatestName, [{path,P},silent]), + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_tar(LatestName,[{outdir,outdir1},{path,P},silent])), + + %% b) absolute path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,Outdir1}, + {path,P},silent]), + ?line Tar1 = filename:join(Outdir1,LatestName++".tar.gz"), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% c) relative path + ?line {ok,_,[]} = systools:make_tar(LatestName, [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:delete(Tar1), + + %% d) absolute but incorrect path + ?line Tar2 = filename:join(Outdir2,LatestName++".tar.gz"), + ?line {error,_,{tar_error,{open,Tar2,{Tar2,enoent}}}} = + systools:make_tar(LatestName, [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{tar_error,{open,_,_}}} = + systools:make_tar(LatestName, [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + ?line ok = file:set_cwd(Outdir1), + ?line {ok,_,[]} = systools:make_tar(filename:join(PrivDir, LatestName), + [{outdir,"."},{path,P},silent]), + ?line true = filelib:is_file(Tar1), + ?line ok = file:set_cwd(LatestDir), + + %% ===== Now test systools:make_relup ===== + %% a) badarg + ?line {'EXIT', {{badarg, [{outdir,outdir1}]}, _}} = + (catch systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,outdir1}, + {path,P},silent])), + + %% b) absolute path + Relup = filename:join(Outdir1, "relup"), + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir1}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% c) relative path + ?line {ok,_,_,[]} = systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir1"}, + {path,P},silent]), + ?line true = filelib:is_file(Relup), + ?line ok = file:delete(Relup), + + %% d) absolute but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,Outdir2},{path,P},silent]), + + %% e) relative but incorrect path + ?line {error,_,{file_problem,{"relup",enoent}}} = + systools:make_relup(LatestName,[LatestName1],[LatestName1], + [{outdir,"./outdir2"},{path,P},silent]), + + %% f) with .rel in another directory than cwd + %% -- not necessary to test since relup by default is placed in + %% cwd, not in the same directory as the .rel file -- + + %% Change back to previous working directory + ?line ok = file:set_cwd(OldDir), + ok. + + +%%%%%% +%%%%%% Utilities +%%%%%% + +check_script_path(RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + case lists:keysearch(path, 1, ListOfThings) of + {value, {path, [$$,$R,$O,$O,$T | _]}} -> %"$ROOT..." + false; + _ -> ok + end. + +check_var_script_file(VarDirs, NoExistDirs, RelName) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + AllPaths = lists:append(lists:map(fun({path, P}) -> P; + (_) -> [] + end, + ListOfThings)), + case lists:filter(fun(VarDir) -> lists:member(VarDir, AllPaths) end, + VarDirs) of + VarDirs -> + ok; + _ -> + test_server:fail("All variable dirs not in generated script") + end, + case lists:filter(fun(NoExistDir) -> lists:member(NoExistDir, AllPaths) end, + NoExistDirs) of + [] -> + ok; + _ -> + test_server:fail("Unexpected dirs in generated script") + end. + +check_include_script(RelName, ExpectedLoad, ExpectedStart) -> + {ok, [Conts]} = read_script_file(RelName), + {script, {_, _}, ListOfThings} = Conts, + + %% Check that the applications are loaded in given order ! + ActualLoad = + [App || {apply,{application,load,[{application,App,_}]}} <- ListOfThings, + App=/=kernel, + App=/=stdlib], + + if ActualLoad =:= ExpectedLoad -> ok; + true -> test_server:fail({bad_load_order, ActualLoad, ExpectedLoad}) + end, + + %% Check that applications are started in given order ! + ActualStart = + [App || {apply,{application,start_boot,[App|_]}} <- ListOfThings, + App =/= kernel, + App =/= stdlib], + + if ActualStart =:= ExpectedStart -> ok; + true -> test_server:fail({bad_start_order, ActualStart,ExpectedStart}) + end, + + ok. + +read_script_file(RelName) -> + file:consult(RelName ++ ".script"). + +check_var_tar(Variable, RelName) -> + Expected = tar_name(Variable), + case check_tar(Expected,RelName) of + ok -> + ok; + {error, {erroneous_tar_file, _, missing, _}} -> + {error, {not_generated, Expected}} + end. + +exists_tar_file(Name) -> + File = tar_name(Name), + case filelib:is_regular(File) of + true -> + ok = file:delete(File), + true; + _ -> + false + end. + +%% Take a snap of the generated tar file and check if a certain +%% file is included. +%% This ensures at least that the tar file is generated. +check_tar(File, RelName) -> + TarContents = tar_contents(RelName), + case lists:member(File,TarContents) of + true -> ok; + _ -> {error, {erroneous_tar_file, tar_name(RelName), missing, File}} + end. + +%% Check that the given files exist in the tar file, and that they are +%% not symlinks +check_tar_regular(PrivDir, Files, RelName) -> + TmpDir = fname(PrivDir,tmp), + ok = file:make_dir(TmpDir), + ok = erl_tar:extract(tar_name(RelName), + [{files,Files},{cwd,TmpDir},compressed]), + R = lists:foldl(fun(File,Acc) -> + case file:read_link_info(fname(TmpDir,File)) of + {ok,#file_info{type=regular}} -> + Acc; + {ok,#file_info{type=Other}} -> + [{File,Other}|Acc]; + _ -> + [{File,missing}|Acc] + end + end, + [], + Files), + delete_tree(TmpDir), + case R of + [] -> + ok; + NotThere -> + {error,{erroneous_tar_file,tar_name(RelName),NotThere}} + end. + +delete_tree(Dir) -> + case filelib:is_dir(Dir) of + true -> + {ok,Files} = file:list_dir(Dir), + lists:foreach(fun(File) -> delete_tree(filename:join(Dir,File)) end, + Files), + file:del_dir(Dir); + false -> + ok = file:delete(Dir) + end. + +tar_contents(Name) -> + {ok, Cont} = erl_tar:table(Name ++ ".tar.gz", [compressed]), + Cont. + +tar_name(Name) -> + Name ++ ".tar.gz". + +create_script(latest,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_no_mod_vsn,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATESTNOMOD\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}, \n" + " {db, \"3.1\"}, {fe, \"3.1\"}]}.\n", + [KernelVer,StdlibVer]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest0,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-2'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 1\", \"LATEST2\"}, \n" + " {erts, \"4.3\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {db, \"1.0\"}, {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small0,Config) -> %Differs in fe vsn + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small0'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL0\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"2.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_small1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-small1'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 2\", \"LATEST_SMALL1\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"1.0\"}, {stdlib, \"1.0\"}, \n" + " {fe, \"500.18.7\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_nokernel,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, 'latest-nokernel'), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"LATEST_NOKERNEL\"}, \n" + " {erts, \"4.4\"}, \n" + " [{db, \"2.1\"}, {fe, \"3.1\"}]}.\n", + []), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type1,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type1), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "1"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer}]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}; +create_script(latest_app_start_type2,Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, latest_app_start_type2), + ?line ErtsVer = erlang:system_info(version), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line OtherApps = [{mnesia,permanent}, + {sasl,transient}, + {webtool,temporary}, + {snmp,load}, + {xmerl,none}], + ?line lists:foreach(fun({App,_}) -> application:load(App) end, + OtherApps), + ?line Loaded = application:loaded_applications(), + ?line OtherAppsRel = + lists:map(fun({App,StartType}) -> + {_,_,Ver} = lists:keyfind(App,1,Loaded), + {App,Ver,StartType} + end, + OtherApps), + ?line {ok,Fd} = file:open(Name++".rel",write), + ?line RelfileContent = + {release,{"Test release", "2"}, + {erts,ErtsVer}, + [{kernel,KernelVer}, + {stdlib,StdlibVer} | OtherAppsRel]}, + ?line io:format(Fd,"~p.~n",[RelfileContent]), + ?line ok = file:close(Fd), + {filename:dirname(Name), filename:basename(Name)}. + +create_include_files(inc1, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc1), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc2, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc2), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc3, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc3), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc4, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc4), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does not include t2 ! + %% t6 does not include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc5, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc5), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t6 does not include t5 ! + %% exclude t5. + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\", [t4]}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\", []}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc6, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc6), + create_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t3 does include non existing t2 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc7, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc7), + create_apps(PrivDir), + create_app(t7, PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t7 and t6 does include t5 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t7, \"1.0\"}, {t6, \"1.0\"}, {t5, \"1.0\"}, \n" + " {t4, \"1.0\"}, {t3, \"1.0\"}, {t2, \"1.0\"}, \n" + " {t1, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc8, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc8), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9 and t10 includes t9 ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc9, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc9), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t8 uses t9, t9 uses t10 and t10 includes t8 ==> circular !! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\"}, {t10, \"1.0\", [t8]}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc10, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc10), + create_circular_apps(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + %% t9 tries to include not specified (in .app file) application ! + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t8, \"1.0\"}, {t9, \"1.0\", [t7]}, {t10, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(inc11, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, inc11), + create_apps2(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t11, \"1.0\"}, \n" + " {t12, \"1.0\"}, \n" + " {t13, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(otp_3065, Config) -> + ?line PrivDir = ?privdir, + ?line Name = fname(PrivDir, otp_3065), + create_apps_3065(PrivDir), + + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {chAts, \"1.0\"}, {aa12, \"1.0\"}, \n" + " {chTraffic, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}. + +create_apps(Dir) -> + T1 = "{application, t1,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [kernel, stdlib]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't1.app'), list_to_binary(T1)), + + T2 = "{application, t2,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t1]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't2.app'), list_to_binary(T2)), + + T3 = "{application, t3,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t2]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't3.app'), list_to_binary(T3)), + + T4 = "{application, t4,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't4.app'), list_to_binary(T4)), + + T5 = "{application, t5,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t3]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't5.app'), list_to_binary(T5)), + + T6 = "{application, t6,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t4, t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't6.app'), list_to_binary(T6)). + +create_app(t7, Dir) -> + T7 = "{application, t7,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t5]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't7.app'), list_to_binary(T7)). + +create_circular_apps(Dir) -> + T8 = "{application, t8,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t9]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't8.app'), list_to_binary(T8)), + + T9 = "{application, t9,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t10]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't9.app'), list_to_binary(T9)), + + T10 = "{application, t10,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t8, t9]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't10.app'), list_to_binary(T10)). + +create_apps2(Dir) -> + T11 = "{application, t11,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [t13]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't11.app'), list_to_binary(T11)), + + T12 = "{application, t12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t11]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't12.app'), list_to_binary(T12)), + + T13 = "{application, t13,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't13.app'), list_to_binary(T13)). + + + +create_apps_3065(Dir) -> + T11 = "{application, chTraffic,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [chAts]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chTraffic.app'), list_to_binary(T11)), + + T12 = "{application, chAts,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, [aa12]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'chAts.app'), list_to_binary(T12)), + + T13 = "{application, aa12,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [chAts]},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 'aa12.app'), list_to_binary(T13)). + +fname(N) -> + filename:join(N). + +fname(Dir, Basename) -> + filename:join(Dir, Basename). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..d375768b99 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_app_vsn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..dac4c00851 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_appup/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1" [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_bad_mod+warn/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..3e0ac3c3c9 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}, {db3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/db-2.1/src/db3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_links/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..191919f8d3 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..d3bd85cda6 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..4ab13c1bae --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {{advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..cdbb9ef532 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.1"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_missing_src/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-2.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_no_appup/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app new file mode 100644 index 0000000000..22530ee335 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.0"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.0/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app new file mode 100644 index 0000000000..7243a0a96a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/ebin/db.app @@ -0,0 +1,8 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "1.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {db1, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-1.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app new file mode 100644 index 0000000000..202d7f1234 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "2.1"}, + {modules, [{db1, "1.0"}, {db2, "1.0"}]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup new file mode 100644 index 0000000000..621838f0b4 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "2.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl new file mode 100644 index 0000000000..7cddf6bb63 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db1.erl @@ -0,0 +1,13 @@ +-module(db1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + lists:non_existing_func("dummy_list"), + b(). + +b() -> + fe2:non_existing_func(), + db2:non_existing_func(), + fe1:a(). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-2.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app new file mode 100644 index 0000000000..97643561eb --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.app @@ -0,0 +1,7 @@ +{application, db, + [{description, "ERICSSON NR FOR DB"}, + {vsn, "3.1"}, + {modules, [db1, db2]}, + {registered, []}, + {applications, []}, + {mod, {db1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup new file mode 100644 index 0000000000..e636eee158 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/ebin/db.appup @@ -0,0 +1,20 @@ +%% +%% Release upgrade script for db (data base) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]}, + {"1.1", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ], + +%%% Downgrade to: + [ + {"1.0", [{update, db1, soft, soft_purge, soft_purge, []}, + {update, db2, soft, soft_purge, soft_purge, [db1]}]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db1.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl new file mode 100644 index 0000000000..a17640316e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/db-3.1/src/db2.erl @@ -0,0 +1,2 @@ +-module(db2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app new file mode 100644 index 0000000000..c7ba1dfe91 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app new file mode 100644 index 0000000000..47ea248720 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/ebin/fe.app @@ -0,0 +1,8 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "2.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {env, []}, + {start, {fe2, start, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl new file mode 100644 index 0000000000..aa5bfa8098 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe1.erl @@ -0,0 +1,2 @@ +-module(fe1). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-2.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app new file mode 100644 index 0000000000..0696e2494c --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.app @@ -0,0 +1,7 @@ +{application, fe, + [{description, "ERICSSON NR FOR FE"}, + {vsn, "3.1"}, + {modules, [{fe1, "1.0"}, {fe2, "1.0"}, {fe3, "2.0"}]}, + {registered, []}, + {applications, []}, + {mod, {fe1, []}}]}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup new file mode 100644 index 0000000000..2db69eba76 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/ebin/fe.appup @@ -0,0 +1,27 @@ +%% +%% Release upgrade script for fe (front end) +%% + +{ + "3.1", +%%% Upgrade from: + [ + {"2.1.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, + [fe1, fe2]} + ]}, + + {"2.1", [{update, fe1, soft, soft_purge, soft_purge, []}, + {update, fe2, soft, soft_purge, soft_purge, [fe1]}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe1, fe2]} + ]} + ], + +%%% Downgrade to: + [ + {"2.1", [{update, fe2, soft, soft_purge, soft_purge, []}, + {update, fe3, {advanced, extra}, soft_purge, soft_purge, [fe2]} + ]} + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl new file mode 100644 index 0000000000..8aca89b2c7 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe1.erl @@ -0,0 +1,7 @@ +-module(fe1). +-vsn("1.0"). + +-export([a/0]). + +a() -> + ok. diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl new file mode 100644 index 0000000000..869f3b93c8 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe2.erl @@ -0,0 +1,2 @@ +-module(fe2). +-vsn("1.0"). diff --git a/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl new file mode 100644 index 0000000000..6473342f52 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/d_normal/lib/fe-3.1/src/fe3.erl @@ -0,0 +1,2 @@ +-module(fe3). +-vsn("2.0"). diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app new file mode 100644 index 0000000000..e33314be7a --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.app @@ -0,0 +1,6 @@ +{application, kernel, + [{description, "FAKE KERNEL"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup new file mode 100644 index 0000000000..c53f07591f --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/kernel/ebin/kernel.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for kernel +%% + +{ + "4.4.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app new file mode 100644 index 0000000000..288fcfe74e --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.app @@ -0,0 +1,6 @@ +{application, stdlib, + [{description, "FAKE STDLIB"}, + {vsn, "1.0"}, + {modules, []}, + {registered, []}, + {applications, []}]}. diff --git a/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup new file mode 100644 index 0000000000..b521eb5d71 --- /dev/null +++ b/lib/sasl/test/systools_SUITE_data/lib/stdlib/ebin/stdlib.appup @@ -0,0 +1,12 @@ +%% +%% Fake release upgrade script for stdlib +%% + +{ + "1.1", + [ + ], + + [ + ] +}. diff --git a/lib/sasl/test/systools_rc_SUITE.erl b/lib/sasl/test/systools_rc_SUITE.erl new file mode 100644 index 0000000000..bb93f38fa7 --- /dev/null +++ b/lib/sasl/test/systools_rc_SUITE.erl @@ -0,0 +1,488 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010. 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(systools_rc_SUITE). + +-include_lib("test_server/include/test_server.hrl"). +-include_lib("sasl/src/systools.hrl"). +-export([all/0,groups/0,init_per_group/2,end_per_group/2, + syntax_check/1, translate/1, translate_app/1]). + +%%----------------------------------------------------------------- +%% erl -compile systools_rc_SUITE @i ../src/ @i ../../test_server/include/ +%% c(systools_rc_SUITE, [{i, "../src"}, {i, "../../test_server/include"}]). +%%----------------------------------------------------------------- +all() -> + [syntax_check, translate, translate_app]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +syntax_check(suite) -> []; +syntax_check(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "0.1", + modules = [{foo,1},{bar,1},{baz,1},{old_mod,1}], + regs = [], + mod = {sasl, []}}, + #application{name = snmp, + description = "SNMP", + vsn = "1.0", + modules = [snmp], + regs = [], + mod = {snmp, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1},{new_mod,1}], + regs = [], + mod = {sasl, []}}], + S1 = [ + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, baz, 5000, soft, brutal_purge, brutal_purge, []}, + {add_module, new_mod}, + {remove_application, snmp} + ], + ?line {ok, _} = systools_rc:translate_scripts([S1], Apps, PreApps), + S2 = [ + {apply, {m, f, [a]}}, + {load_object_code, {tst, "1.0", [new_mod]}}, + point_of_no_return, + {update, bar, {advanced, extra}, brutal_purge, brutal_purge, []}, + {update, foo, soft, soft_purge, soft_purge, [bar]}, + {load, {new_mod, soft_purge, soft_purge}}, + {remove, {old_mod, soft_purge, soft_purge}}, + {purge, [m1, m2]}, + {suspend, [m1]}, + {code_change, [{m1, extra}]}, + {resume, [m1]}, + {stop, [m3,m4]}, + {start, [m3,m4]}, + {sync_nodes, id1, {m, f, [a]}}, + {sync_nodes, id2, [cp1, cp2]}, + {apply, {m,f,[a]}}, + restart_new_emulator + ], + ?line {ok, _} = systools_rc:translate_scripts([S2], Apps, []), + S3 = [{apply, {m, f, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3], Apps, []), + S3_1 = [{apply, {m, 3, a}}], + ?line {error, _, _} = systools_rc:translate_scripts([S3_1], Apps, []), + S4 = [{apply, {m, f}}], + ?line {error, _, _} = systools_rc:translate_scripts([S4], Apps, []), + S5 = [{load_object_code, hej}], + ?line {error, _, _} = systools_rc:translate_scripts([S5], Apps, []), + S6 = [{load_object_code, {342, "1.0", [foo]}}], + ?line {error, _, _} = systools_rc:translate_scripts([S6], Apps, []), + S7 = [{load_object_code, {tets, "1.0", foo}}], + ?line {error, _, _} = systools_rc:translate_scripts([S7], Apps, []), + S8 = [{suspend, [m1]}, point_of_no_return], + ?line {error, _, _} = systools_rc:translate_scripts([S8], Apps, []), + S9 = [{update, ba, {advanced, extra}, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S9], Apps, []), + S10 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [baz]}], + ?line {error, _, _} = systools_rc:translate_scripts([S10], Apps, []), + S11 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S11], Apps, []), + S12 = [{update, bar, advanced, brutal_purge, brutal_purge, []}], + ?line {error, _, _} = systools_rc:translate_scripts([S12], Apps, []), + S13 = [{update, bar, {advanced, extra}, rutal_purge, brutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S13], Apps, []), + S14 = [{update, bar, {advanced, extra}, brutal_purge, rutal_purge, [ba]}], + ?line {error, _, _} = systools_rc:translate_scripts([S14], Apps, []), + S15 = [{update, bar, {advanced, extra}, brutal_purge, brutal_purge, ba}], + ?line {error, _, _} = systools_rc:translate_scripts([S15], Apps, []), + S16 = [{code_change, [module]}], + ?line {error, _, _} = systools_rc:translate_scripts([S16], Apps, []), + ?line ok. + +translate(suite) -> []; +translate(Config) when is_list(Config) -> + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}, + {x,1},{y,1},{z,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{update, foo, soft, soft_purge, soft_purge, []}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X1, + + %% Simple translation (2) + Up2 = [{update, foo, {advanced, extra}, soft_purge, soft_purge, []}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2, + + ?line {ok, X22} = systools_rc:translate_scripts(dn,[Up2], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {code_change, down, [{foo, extra}]}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[foo]}] = X22, + + Up2a = [{update, foo, static, default, {advanced,extra}, + soft_purge, soft_purge, []}], + ?line {ok, X2a} = systools_rc:translate_scripts([Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, up, [{foo, extra}]}, + {resume,[foo]}] = X2a, + + ?line {ok, X22a} = systools_rc:translate_scripts(dn,[Up2a], Apps, []), + ?line [{load_object_code, {test,"1.0",[foo]}}, + point_of_no_return, + {suspend,[foo]}, + {load,{foo,soft_purge,soft_purge}}, + {code_change, down, [{foo, extra}]}, + {resume,[foo]}] = X22a, + + %% Simple dependency (1) + Up3 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, []}], + ?line {ok, X31} = systools_rc:translate_scripts([Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X31, + ?line {ok, X32} = systools_rc:translate_scripts(dn,[Up3], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X32, + + Up3a = [{update, foo, static, default, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, default, soft, soft_purge, soft_purge, []}], + ?line {ok, X3a1} = systools_rc:translate_scripts([Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo, bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a1, + ?line {ok, X3a2} = systools_rc:translate_scripts(dn,[Up3a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X3a2, + + %% Simple dependency (2) + Up4 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}], + ?line {ok, X4} = systools_rc:translate_scripts(up,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4, + + ?line {ok, X42} = systools_rc:translate_scripts(dn,[Up4], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42, + + Up4a = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, static, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4a} = systools_rc:translate_scripts(up,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4a, + + ?line {ok, X42a} = systools_rc:translate_scripts(dn,[Up4a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,foo]}] = X42a, + + Up4b = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, dynamic, infinity, {advanced, []}, + soft_purge, soft_purge, []}], + ?line {ok, X4b} = systools_rc:translate_scripts(up,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{bar,[]}]}, + {resume,[bar,foo]}] = X4b, + + ?line {ok, X42b} = systools_rc:translate_scripts(dn,[Up4b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,{bar,infinity}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X42b, + + %% More complex dependency + Up5 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, {advanced, []}, soft_purge, soft_purge, []}, + {update, baz, {advanced, baz}, soft_purge, soft_purge, [bar]}], + ?line {ok, X5} = systools_rc:translate_scripts([Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5, + + ?line {ok, X52} = systools_rc:translate_scripts(dn,[Up5], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[foo,baz,bar]}, + {code_change,down,[{baz,baz},{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,baz,foo]}] = X52, + + Up5a = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, static, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, dynamic, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5a} = systools_rc:translate_scripts([Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz}, {bar,[]}]}, + {resume,[bar,baz,foo]}] = X5a, + + ?line {ok, X52a} = systools_rc:translate_scripts(dn,[Up5a], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{baz,baz}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{bar,[]}]}, + {resume,[bar,baz,foo]}] = X52a, + + Up5b = [{update, foo, dynamic, infinity, soft, soft_purge, + soft_purge, [bar, baz]}, + {update, bar, dynamic, 7000, {advanced, []}, soft_purge, + soft_purge, []}, + {update, baz, static, default, {advanced, baz}, soft_purge, + soft_purge, [bar]}], + ?line {ok, X5b} = systools_rc:translate_scripts([Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {code_change,up,[{baz,baz},{bar,[]}]}, + {resume,[bar,baz,foo]}] = X5b, + + ?line {ok, X52b} = systools_rc:translate_scripts(dn,[Up5b], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,baz,bar]}}, + point_of_no_return, + {suspend,[{foo,infinity},baz,{bar,7000}]}, + {code_change,down,[{bar,[]}]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {code_change,down,[{baz,baz}]}, + {resume,[bar,baz,foo]}] = X52b, + + %% Simple circular dependency (1) + Up6 = [{update, foo, soft, soft_purge, soft_purge, [bar]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}], + ?line {ok, X61} = systools_rc:translate_scripts([Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X61, + ?line {ok, X62} = systools_rc:translate_scripts(dn,[Up6], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {suspend,[foo,bar]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {resume,[bar,foo]}] = X62, + + %% Simple circular dependency (2) + Up7 = [{update, foo, soft, soft_purge, soft_purge, [bar, baz]}, + {update, bar, soft, soft_purge, soft_purge, [foo]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X71} = systools_rc:translate_scripts([Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{baz,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{foo,soft_purge,soft_purge}}, + {resume,[baz, bar, foo]}] = X71, + ?line {ok, X72} = systools_rc:translate_scripts(dn,[Up7], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {suspend,[foo,bar,baz]}, + {load,{foo,soft_purge,soft_purge}}, + {load,{bar,soft_purge,soft_purge}}, + {load,{baz,soft_purge,soft_purge}}, + {resume,[baz,bar,foo]}] = X72, + + %% Complex circular dependencies, check only order + %% + Up8 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {update, baz, soft, soft_purge, soft_purge, [bar]}], + ?line {ok, X8} = systools_rc:translate_scripts([Up8], Apps, []), + ?line {value, {suspend, Order}} = lists:keysearch(suspend, 1, X8), + ?line Rest = case lists:reverse(Order) of + [bar, baz | R] -> R; + [baz, bar | R] -> R + end, + ?line case Rest of + [y, z, x, foo] -> ok; + [y, x, z, foo] -> ok; + [foo, y, z, x] -> ok; + [foo, y, x, z] -> ok; + [y, foo, z, x] -> ok; + [y, foo, x, z] -> ok + end, + + %% Check that order among other instructions isn't changed + Up9 = [{update, foo, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [1]}}, + {update, y, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [2]}}, + {update, x, soft, soft_purge, soft_purge, [y, z]}, + {apply, {m, f, [3]}}, + {update, z, soft, soft_purge, soft_purge, [x]}, + {apply, {m, f, [4]}}, + {update, bar, soft, soft_purge, soft_purge, [baz]}, + {apply, {m, f, [5]}}, + {update, baz, soft, soft_purge, soft_purge, [bar]}, + {apply, {m, f, [6]}}], + ?line {ok, X9} = systools_rc:translate_scripts([Up9], Apps, []), + Other2 = [X || {apply, {m, f, [X]}} <- X9], + ?line [1,2,3,4,5,6] = lists:sort(Other2), + ?line ok. + + +translate_app(suite) -> []; +translate_app(Config) when is_list(Config) -> + PreApps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}, + #application{name = pelle, + description = "PELLE", + vsn = "1.0", + modules = [pelle, kalle], + regs = [], + mod = {pelle, []}}], + Apps = + [#application{name = test, + description = "TEST", + vsn = "1.0", + modules = [{foo,1},{bar,1},{baz,1}], + regs = [], + mod = {sasl, []}}], + %% Simple translation (1) + Up1 = [{add_module, foo}, + {add_module, bar}], + ?line {ok, X1} = systools_rc:translate_scripts([Up1], Apps, []), + ?line [{load_object_code,{test,"1.0",[foo,bar]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}] = X1, + + %% Simple translation (2) + Up2 = [{add_application, test}], + ?line {ok, X2} = systools_rc:translate_scripts([Up2], Apps, []), +io:format("X2=~p~n", [X2]), + ?line [{load_object_code,{test,"1.0",[foo,bar,baz]}}, + point_of_no_return, + {load,{foo,brutal_purge,brutal_purge}}, + {load,{bar,brutal_purge,brutal_purge}}, + {load,{baz,brutal_purge,brutal_purge}}, + {apply,{application,start,[test,permanent]}}] = X2, + + %% Simple translation (3) + Up3 = [{remove_application, pelle}], + ?line {ok, X3} = systools_rc:translate_scripts([Up3], Apps, PreApps), + ?line [point_of_no_return, + {apply,{application,stop,[pelle]}}, + {remove,{pelle,brutal_purge,brutal_purge}}, + {remove,{kalle,brutal_purge,brutal_purge}}, + {purge,[pelle,kalle]}, + {apply,{application,unload,[pelle]}}] = X3, + ?line ok. -- cgit v1.2.3 From 5042beecbb520af5c88baee55f288e5488124e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 May 2011 12:03:34 +0200 Subject: Remove compiler warnings in inet_drv --- erts/emulator/drivers/common/inet_drv.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index aec456151c..40c4a0df08 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -1948,7 +1948,7 @@ static int http_response_inetdrv(void *arg, int major, int minor, tcp_descriptor* desc = (tcp_descriptor*) arg; int i = 0; ErlDrvTermData spec[27]; - ErlDrvTermData caller; + ErlDrvTermData caller = ERL_DRV_NIL; if (desc->inet.active == INET_PASSIVE) { /* {inet_async,S,Ref,{ok,{http_response,Version,Status,Phrase}}} */ @@ -2041,7 +2041,7 @@ http_request_inetdrv(void* arg, const http_atom_t* meth, const char* meth_ptr, tcp_descriptor* desc = (tcp_descriptor*) arg; int i = 0; ErlDrvTermData spec[43]; - ErlDrvTermData caller; + ErlDrvTermData caller = ERL_DRV_NIL; if (desc->inet.active == INET_PASSIVE) { /* {inet_async, S, Ref, {ok,{http_request,Meth,Uri,Version}}} */ @@ -2092,7 +2092,7 @@ http_header_inetdrv(void* arg, const http_atom_t* name, const char* name_ptr, tcp_descriptor* desc = (tcp_descriptor*) arg; int i = 0; ErlDrvTermData spec[26]; - ErlDrvTermData caller; + ErlDrvTermData caller = ERL_DRV_NIL; if (desc->inet.active == INET_PASSIVE) { /* {inet_async,S,Ref,{ok,{http_header,Bit,Name,IValue,Value}} */ @@ -2221,7 +2221,7 @@ int ssl_tls_inetdrv(void* arg, unsigned type, unsigned major, unsigned minor, tcp_descriptor* desc = (tcp_descriptor*) arg; int i = 0; ErlDrvTermData spec[28]; - ErlDrvTermData caller; + ErlDrvTermData caller = ERL_DRV_NIL; ErlDrvBinary* bin; int ret; @@ -7980,11 +7980,11 @@ static int tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, int len, timeout = get_int32(buf); if (desc->inet.state == TCP_STATE_ACCEPTING) { - unsigned long time_left; - int oid; - ErlDrvTermData ocaller; - int oreq; - unsigned otimeout; + unsigned long time_left = 0; + int oid = 0; + ErlDrvTermData ocaller = ERL_DRV_NIL; + int oreq = 0; + unsigned otimeout = 0; ErlDrvTermData caller = driver_caller(desc->inet.port); MultiTimerData *mtd = NULL,*omtd = NULL; ErlDrvMonitor monitor, omonitor; -- cgit v1.2.3 From 975c6fec571515e989f04d26322c42acb9030a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 May 2011 14:42:23 +0200 Subject: Remove unused variable in epmd_port --- lib/erl_interface/src/epmd/epmd_port.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/erl_interface/src/epmd/epmd_port.c b/lib/erl_interface/src/epmd/epmd_port.c index 698c75c217..6450f285cc 100644 --- a/lib/erl_interface/src/epmd/epmd_port.c +++ b/lib/erl_interface/src/epmd/epmd_port.c @@ -228,11 +228,8 @@ int ei_epmd_port (struct in_addr *addr, const char *alive, int *dist) return ei_epmd_port_tmo (addr, alive, dist, 0); } -int ei_epmd_port_tmo (struct in_addr *addr, const char *alive, int *dist, - unsigned ms) +int ei_epmd_port_tmo (struct in_addr *addr, const char *alive, int *dist, unsigned ms) { - int i; - - return ei_epmd_r4_port(addr,alive,dist,ms); + return ei_epmd_r4_port(addr,alive,dist,ms); } -- cgit v1.2.3 From a7d1348d1a4e29e9aa423d30fe18468fc78f085d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn-Egil=20Dahlberg?= Date: Tue, 17 May 2011 16:16:20 +0200 Subject: Remove unused variable warning in inet_res --- lib/kernel/src/inet_res.erl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index d5a8a2f134..2276ddcd08 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -872,7 +872,7 @@ query_ns(S0, Id, Buffer, IP, Port, Timer, Retry, I, end end. -query_udp(_S, _Id, _Buffer, _IP, _Port, 0, Verbose) -> +query_udp(_S, _Id, _Buffer, _IP, _Port, 0, _Verbose) -> timeout; query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) -> ?verbose(Verbose, "Try UDP server : ~p:~p (timeout=~w)\n", @@ -908,7 +908,7 @@ query_udp(S, Id, Buffer, IP, Port, Timeout, Verbose) -> {error,econnrefused} end. -query_tcp(0, _Id, _Buffer, _IP, _Port, Verbose) -> +query_tcp(0, _Id, _Buffer, _IP, _Port, _Verbose) -> timeout; query_tcp(Timeout, Id, Buffer, IP, Port, Verbose) -> ?verbose(Verbose, "Try TCP server : ~p:~p (timeout=~w)\n", -- cgit v1.2.3 From 88d149aacbda437b0e2732d69bdb1f08e63eadb2 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 17 May 2011 15:53:59 +0200 Subject: Add escript to bootstrap/bin --- Makefile.in | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index b17713f182..5acd390333 100644 --- a/Makefile.in +++ b/Makefile.in @@ -502,6 +502,7 @@ ifeq ($(TARGET),win32) bootstrap_setup: check_recreate_primary_bootstrap bootstrap_setup_target @rm -f $(BOOTSTRAP_ROOT)/bootstrap/bin/erl.exe \ $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc.exe \ + $(BOOTSTRAP_ROOT)/bootstrap/bin/escript.exe \ $(BOOTSTRAP_ROOT)/bootstrap/bin/erl.ini \ $(BOOTSTRAP_ROOT)/bootstrap/bin/beam.dll make_bootstrap_ini.sh $(BOOTSTRAP_ROOT)/bootstrap \ @@ -510,8 +511,10 @@ bootstrap_setup: check_recreate_primary_bootstrap bootstrap_setup_target $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc.exe @cp $(ERL_TOP)/bin/$(TARGET)/erl.exe \ $(BOOTSTRAP_ROOT)/bootstrap/bin/erl.exe + @cp $(ERL_TOP)/bin/$(TARGET)/escript.exe \ + $(BOOTSTRAP_ROOT)/bootstrap/bin/escript.exe else -bootstrap_setup: check_recreate_primary_bootstrap bootstrap_setup_target $(BOOTSTRAP_ROOT)/bootstrap/bin/erl $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc +bootstrap_setup: check_recreate_primary_bootstrap bootstrap_setup_target $(BOOTSTRAP_ROOT)/bootstrap/bin/erl $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc $(BOOTSTRAP_ROOT)/bootstrap/bin/escript $(BOOTSTRAP_ROOT)/bootstrap/bin/erl: $(ERL_TOP)/erts/etc/unix/erl.src.src $(BOOTSTRAP_ROOT)/bootstrap/target @rm -f $(BOOTSTRAP_ROOT)/bootstrap/bin/erl @@ -526,6 +529,11 @@ $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc: $(ERL_TOP)/bin/$(TARGET)/erlc $(BOOTSTRAP_ @rm -f $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc @cp $(ERL_TOP)/bin/$(TARGET)/erlc $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc @chmod 755 $(BOOTSTRAP_ROOT)/bootstrap/bin/erlc + +$(BOOTSTRAP_ROOT)/bootstrap/bin/escript: $(ERL_TOP)/bin/$(TARGET)/escript $(BOOTSTRAP_ROOT)/bootstrap/target + @rm -f $(BOOTSTRAP_ROOT)/bootstrap/bin/escript + @cp $(ERL_TOP)/bin/$(TARGET)/escript $(BOOTSTRAP_ROOT)/bootstrap/bin/escript + @chmod 755 $(BOOTSTRAP_ROOT)/bootstrap/bin/escript endif bootstrap_setup_target: -- cgit v1.2.3 From 2b31edf742b3d9236dfc35b947b3b0c356010236 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 17 May 2011 17:41:22 +0200 Subject: In TLS 1.1, failure to properly close a connection no longer requires that a session not be resumed. This is a change from TLS 1.0 to conform with widespread implementation practice. Erlang ssl will now in TLS 1.0 conform to the widespread implementation practice instead of the specification to avoid performance issues. --- lib/ssl/src/ssl_connection.erl | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 0a86e9bd29..2c452837f8 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -941,17 +941,23 @@ handle_info({Protocol, _, Data}, StateName, handle_info({CloseTag, Socket}, _StateName, #state{socket = Socket, close_tag = CloseTag, - negotiated_version = Version, host = Host, - port = Port, socket_options = Opts, + negotiated_version = Version, + socket_options = Opts, user_application = {_Mon,Pid}, from = From, - role = Role, session = Session} = State) -> - %% Debug option maybe, the user do NOT want to see these in their logs - %% error_logger:info_report("SSL: Peer did not send close notify alert."), + role = Role} = State) -> + %% Note that as of TLS 1.1, + %% failure to properly close a connection no longer requires that a + %% session not be resumed. This is a change from TLS 1.0 to conform + %% with widespread implementation practice. case Version of {1, N} when N >= 1 -> ok; _ -> - invalidate_session(Role, Host, Port, Session) + %% As invalidate_sessions here causes performance issues, + %% we will conform to the widespread implementation + %% practice and go aginst the spec + %%invalidate_session(Role, Host, Port, Session) + ok end, alert_user(Opts#socket_options.active, Pid, From, ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY), Role), -- cgit v1.2.3 From d2ad69103045a942cb012746205cfed9cfd32719 Mon Sep 17 00:00:00 2001 From: Ingela Anderton Andin Date: Tue, 26 Apr 2011 18:04:07 +0200 Subject: Corrected documentation error and added examples to Users Guide --- lib/public_key/doc/src/Makefile | 3 +- lib/public_key/doc/src/part.xml | 3 +- lib/public_key/doc/src/public_key.xml | 24 +- lib/public_key/doc/src/using_public_key.xml | 504 ++++++++++++++++++++++++++++ 4 files changed, 520 insertions(+), 14 deletions(-) create mode 100644 lib/public_key/doc/src/using_public_key.xml diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile index 298c28a740..afb17399da 100644 --- a/lib/public_key/doc/src/Makefile +++ b/lib/public_key/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2010. All Rights Reserved. +# Copyright Ericsson AB 2008-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 @@ -52,6 +52,7 @@ XML_CHAPTER_FILES = \ introduction.xml \ public_key_records.xml \ cert_records.xml \ + using_public_key.xml \ notes.xml BOOK_FILES = book.xml diff --git a/lib/public_key/doc/src/part.xml b/lib/public_key/doc/src/part.xml index c338a71613..ea3123b5bd 100644 --- a/lib/public_key/doc/src/part.xml +++ b/lib/public_key/doc/src/part.xml @@ -1,4 +1,4 @@ - + @@ -38,5 +38,6 @@ + diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index c5f57214b1..d60d91cd83 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -43,7 +43,7 @@

    All records used in this manual - are generated from asn1 specifications + are generated from ASN.1 specifications and are documented in the User's Guide. See Public key records and X.509 Certificate records. @@ -150,12 +150,12 @@ Decodes a public key asn1 der encoded entity. Asn1Type = atom() - - Asn1 type present in the public_key applications + ASN.1 type present in the public_key applications asn1 specifications. Der = der_encoded() -

    Decodes a public key asn1 der encoded entity.

    +

    Decodes a public key ASN.1 der encoded entity.

    @@ -165,25 +165,25 @@ Asn1Type = atom() Asn1 type present in the public_key applications - asn1 specifications. + ASN.1 specifications. Entity = term() - The erlang representation of Asn1Type -

    Encodes a public key entity with asn1 DER encoding.

    +

    Encodes a public key entity with ASN.1 DER encoding.

    pem_decode(PemBin) -> [pem_entry()] Decode PEM binary data and return - entries as asn1 der encoded entities. + entries as ASN.1 der encoded entities. PemBin = binary() Example {ok, PemBin} = file:read_file("cert.pem").

    Decode PEM binary data and return - entries as asn1 der encoded entities.

    + entries as ASN.1 der encoded entities.

    @@ -258,13 +258,13 @@ pkix_decode_cert(Cert, otp|plain) -> #'Certificate'{} | #'OTPCertificate'{} - Decodes an asn1 der encoded pkix x509 certificate. + Decodes an ASN.1 der encoded pkix x509 certificate. Cert = der_encoded() -

    Decodes an asn1 der encoded pkix certificate. The otp option - will use the customized asn1 specification OTP-PKIX.asn1 for +

    Decodes an ASN.1 der encoded pkix certificate. The otp option + will use the customized ASN.1 specification OTP-PKIX.asn1 for decoding and also recursively decode most of the standard parts.

    @@ -276,7 +276,7 @@ certificate. Asn1Type = atom() - The asn1 type can be 'Certificate', 'OTPCertificate' or a subtype of either . + The ASN.1 type can be 'Certificate', 'OTPCertificate' or a subtype of either .

    Der encodes a pkix x509 certificate or part of such a @@ -394,7 +394,7 @@ signed or in the case that digest type is none it is the hashed value of "plain text" i.e. the digest. DigestType = rsa_digest_type() | dsa_digest_type() - Key = rsa_public_key() | dsa_public_key() + Key = rsa_private_key() | dsa_private_key()

    Creates a digital signature.

    diff --git a/lib/public_key/doc/src/using_public_key.xml b/lib/public_key/doc/src/using_public_key.xml new file mode 100644 index 0000000000..f0eaeb8654 --- /dev/null +++ b/lib/public_key/doc/src/using_public_key.xml @@ -0,0 +1,504 @@ + + + + +
    + + 20112011 + Ericsson AB. 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. + + + + Using the public_key API + using_public_key.xml +
    + +
    + General information + +

    This chapter is dedicated to showing some + examples of how to use the public_key API. Keys and certificates + used in the following sections are generated only for the purpose + of testing the public key application.

    + +

    Note that some shell printouts, in the following examples, + have been abbreviated for increased readability.

    + +
    + +
    + PEM files +

    Pulic key data (keys, certificates etc) may be stored in PEM format. PEM files + comes from the Private Enhanced Mail Internet standard and has a + structure that looks like this:

    + + <text> + -----BEGIN <SOMETHING>----- + <Attribute> : <Value> + <Base64 encoded DER data> + -----END <SOMETHING>----- + <text> + +

    A file can contain several BEGIN/END blocks. Text lines between + blocks are ignored. Attributes, if present, are currently ignored except + for Proc-Type and DEK-Info that are used when the DER data is + encrypted.

    + +
    + DSA private key + +

    Note file handling is not done by the public_key application.

    + 1> {ok, PemBin} = file:read_file("dsa.pem"). +{ok,<<"-----BEGIN DSA PRIVATE KEY-----\nMIIBuw"...>>} + +

    This PEM file only has one entry a private DSA key.

    + 2> [DSAEntry] = public_key:pem_decode(PemBin). +[{'DSAPrivateKey',<<48,130,1,187,2,1,0,2,129,129,0,183, + 179,230,217,37,99,144,157,21,228,204, + 162,207,61,246,...>>, + not_encrypted}] + + 3> Key = public_key:pem_entry_decode(DSAEntry). +#'DSAPrivateKey'{version = 0, + p = 12900045185019966618...6593, + q = 1216700114794736143432235288305776850295620488937, + g = 10442040227452349332...47213, + y = 87256807980030509074...403143, + x = 510968529856012146351317363807366575075645839654} +
    + +
    + RSA private key encrypted with a password. + + 1> {ok, PemBin} = file:read_file("rsa.pem"). +{ok,<<"Bag Attribut"...>>} + +

    This PEM file only has one entry a private RSA key.

    + 2>[RSAEntry] = public_key:pem_decode(PemBin). +[{'RSAPrivateKey',<<224,108,117,203,152,40,15,77,128,126, + 221,195,154,249,85,208,202,251,109, + 119,120,57,29,89,19,9,...>>, + {"DES-EDE3-CBC",<<"kÙeø¼pµL">>}}] + + + +

    In this example the password is "abcd1234".

    + 3> Key = public_key:pem_entry_decode(RSAEntry, "abcd1234"). + #'RSAPrivateKey'{version = 'two-prime', + modulus = 1112355156729921663373...2737107, + publicExponent = 65537, + privateExponent = 58064406231183...2239766033, + prime1 = 11034766614656598484098...7326883017, + prime2 = 10080459293561036618240...77738643771, + exponent1 = 77928819327425934607...22152984217, + exponent2 = 36287623121853605733...20588523793, + coefficient = 924840412626098444...41820968343, + otherPrimeInfos = asn1_NOVALUE} +
    + +
    + X509 Certificates + + 1> {ok, PemBin} = file:read_file("cacerts.pem"). +{ok,<<"-----BEGIN CERTIFICATE-----\nMIIC7jCCAl"...>>} + +

    This file includes two certificates

    + 2> [CertEntry1, CertEntry2] = public_key:pem_decode(PemBin). +[{'Certificate',<<48,130,2,238,48,130,2,87,160,3,2,1,2,2, + 9,0,230,145,97,214,191,2,120,150,48,13, + ...>>, + not_encrypted}, + {'Certificate',<<48,130,3,200,48,130,3,49,160,3,2,1,2,2,1, + 1,48,13,6,9,42,134,72,134,247,...>>>, + not_encrypted}] + +

    Certificates may of course be decoded as usual ...

    + 2> Cert = public_key:pem_entry_decode(CertEntry1). +#'Certificate'{ + tbsCertificate = + #'TBSCertificate'{ + version = v3,serialNumber = 16614168075301976214, + signature = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = <<5,0>>}, + issuer = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = <<19,8,101,114,108,97,110,103,67,65>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = <<19,11,69,114,105,99,115,115,111,110,32,65,66>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = <<19,9,83,116,111,99,107,104,111,108,109>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,6}, + value = <<19,2,83,69>>}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = <<22,22,112,101,116,101,114,64,101,114,...>>}]]}, + validity = + #'Validity'{ + notBefore = {utcTime,"080109082929Z"}, + notAfter = {utcTime,"080208082929Z"}}, + subject = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = <<19,8,101,114,108,97,110,103,67,65>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = <<19,10,69,114,108,97,110,103,32,79,84,80>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = <<19,11,69,114,105,99,115,115,111,110,32,...>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = <<19,9,83,116,111,99,107,104,111,108,...>>}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,6}, + value = <<19,2,83,69>>}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = <<22,22,112,101,116,101,114,64,...>>}]]}, + subjectPublicKeyInfo = + #'SubjectPublicKeyInfo'{ + algorithm = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,1}, + parameters = <<5,0>>}, + subjectPublicKey = + {0,<<48,129,137,2,129,129,0,203,209,187,77,73,231,90,...>>}}, + issuerUniqueID = asn1_NOVALUE, + subjectUniqueID = asn1_NOVALUE, + extensions = + [#'Extension'{ + extnID = {2,5,29,19}, + critical = true, + extnValue = [48,3,1,1,255]}, + #'Extension'{ + extnID = {2,5,29,15}, + critical = false, + extnValue = [3,2,1,6]}, + #'Extension'{ + extnID = {2,5,29,14}, + critical = false, + extnValue = [4,20,27,217,65,152,6,30,142|...]}, + #'Extension'{ + extnID = {2,5,29,17}, + critical = false, + extnValue = [48,24,129,22,112,101,116,101|...]}]}, + signatureAlgorithm = + #'AlgorithmIdentifier'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = <<5,0>>}, + signature = + {0, + <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, + 165,2,52,196,195,109,167,192,...>>}} + + +

    Parts of certificates can be decoded with + public_key:der_decode/2 using that parts ASN.1 type. + Although application specific certificate + extension requires application specific ASN.1 decode/encode-functions. + Example, the first value of the rdnSequence above is of ASN.1 type + 'X520CommonName'. ({2,5,4,3} = ?id-at-commonName)

    + + public_key:der_decode('X520CommonName', <<19,8,101,114,108,97,110,103,67,65>>). +{printableString,"erlangCA"} + +

    ... but certificates can also be decode using the pkix_decode_cert/2 that + can customize and recursively decode standard parts of a certificate.

    + 3>{_, DerCert, _} = CertEntry1. + 4> public_key:pkix_decode_cert(DerCert, otp). +#'OTPCertificate'{ + tbsCertificate = + #'OTPTBSCertificate'{ + version = v3,serialNumber = 16614168075301976214, + signature = + #'SignatureAlgorithm'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = 'NULL'}, + issuer = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = {printableString,"erlangCA"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = {printableString,"Erlang OTP"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = {printableString,"Ericsson AB"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = {printableString,"Stockholm"}}], + [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = "peter@erix.ericsson.se"}]]}, + validity = + #'Validity'{ + notBefore = {utcTime,"080109082929Z"}, + notAfter = {utcTime,"080208082929Z"}}, + subject = + {rdnSequence, + [[#'AttributeTypeAndValue'{ + type = {2,5,4,3}, + value = {printableString,"erlangCA"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,11}, + value = {printableString,"Erlang OTP"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,10}, + value = {printableString,"Ericsson AB"}}], + [#'AttributeTypeAndValue'{ + type = {2,5,4,7}, + value = {printableString,"Stockholm"}}], + [#'AttributeTypeAndValue'{type = {2,5,4,6},value = "SE"}], + [#'AttributeTypeAndValue'{ + type = {1,2,840,113549,1,9,1}, + value = "peter@erix.ericsson.se"}]]}, + subjectPublicKeyInfo = + #'OTPSubjectPublicKeyInfo'{ + algorithm = + #'PublicKeyAlgorithm'{ + algorithm = {1,2,840,113549,1,1,1}, + parameters = 'NULL'}, + subjectPublicKey = + #'RSAPublicKey'{ + modulus = + 1431267547247997...37419, + publicExponent = 65537}}, + issuerUniqueID = asn1_NOVALUE, + subjectUniqueID = asn1_NOVALUE, + extensions = + [#'Extension'{ + extnID = {2,5,29,19}, + critical = true, + extnValue = + #'BasicConstraints'{ + cA = true,pathLenConstraint = asn1_NOVALUE}}, + #'Extension'{ + extnID = {2,5,29,15}, + critical = false, + extnValue = [keyCertSign,cRLSign]}, + #'Extension'{ + extnID = {2,5,29,14}, + critical = false, + extnValue = [27,217,65,152,6,30,142,132,245|...]}, + #'Extension'{ + extnID = {2,5,29,17}, + critical = false, + extnValue = [{rfc822Name,"peter@erix.ericsson.se"}]}]}, + signatureAlgorithm = + #'SignatureAlgorithm'{ + algorithm = {1,2,840,113549,1,1,5}, + parameters = 'NULL'}, + signature = + {0, + <<163,186,7,163,216,152,63,47,154,234,139,73,154,96,120, + 165,2,52,196,195,109,167,192,...>>}} + + +

    This call is equivalent to public_key:pem_entry_decode(CertEntry1)

    + 5> public_key:pkix_decode_cert(DerCert, plain). +#'Certificate'{ ...} + +
    + +
    + Encoding public key data to PEM format + +

    If you have public key data and and want to create a PEM file + you can do that by calling the functions + public_key:pem_entry_encode/2 and pem_encode/1 and then saving the + result to a file. For example assume you have PubKey = + 'RSAPublicKey'{} then you can create a PEM-"RSA PUBLIC KEY" file + (ASN.1 type 'RSAPublicKey') or a PEM-"PUBLIC KEY" file + ('SubjectPublicKeyInfo' ASN.1 type).

    + +

    The second element of the PEM-entry will be the ASN.1 DER encoded + key data.

    + + 1> PemEntry = public_key:pem_entry_encode('RSAPublicKey', RSAPubKey). +{'RSAPublicKey', <<48,72,...>>, not_encrypted} + +2> PemBin = public_key:pem_encode([PemEntry]). +<<"-----BEGIN RSA PUBLIC KEY-----\nMEgC...>> + +3> file:write_file("rsa_pub_key.pem", PemBin). +ok + +

    or

    + + 1> PemBin = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey). +{'SubjectPublicKeyInfo', <<48,92...>>, not_encrypted} + +2> PemBin = public_key:pem_encode([PemEntry]). +<<"-----BEGIN PUBLIC KEY-----\nMFw...>> + +3> file:write_file("pub_key.pem", PemBin). +ok + +
    +
    + +
    + RSA public key cryptography +

    Suppose you have PrivateKey = #'RSAPrivateKey{}' and the + plaintext Msg = binary() and the corresponding public key + PublicKey = #'RSAPublicKey'{} then you can do the following. + Note that you normally will only do one of the encrypt or + decrypt operations and the peer will do the other. +

    + +

    Encrypt with the private key

    + RsaEncrypted = public_key:encrypt_private(Msg, PrivateKey), +Msg = public_key:decrypt_public(RsaEncrypted, PublicKey), + +

    Encrypt with the public key

    + RsaEncrypted = public_key:encrypt_public(Msg, PublicKey), +Msg = public_key:decrypt_private(RsaEncrypted, PrivateKey), +
    + +
    + Digital signatures + +

    Suppose you have PrivateKey = #'RSAPrivateKey{}'or + #'DSAPrivateKey'{} and the plaintext Msg = binary() and the + corresponding public key PublicKey = #'RSAPublicKey'{} or + {integer(), #'DssParams'{}} then you can do the following. Note + that you normally will only do one of the sign or verify operations + and the peer will do the other.

    + + Signature = public_key:sign(Msg, sha, PrivateKey), +true = public_key:verify(Msg, sha, Signature, PublicKey), + +

    It might be appropriate to calculate the message digest before + calling sign or verify and then you can use the none as second + argument.

    + + Digest = crypto:sha(Msg), +Signature = public_key:sign(Digest, none, PrivateKey), +true = public_key:verify(Digest, none, Signature, PublicKey), + + +
    + +
    + SSH files + +

    SSH typically uses PEM files for private keys but has its + own file format for storing public keys. The erlang public_key + application can be used to parse the content of SSH public key files.

    + +
    + RFC 4716 SSH public key files + +

    RFC 4716 SSH files looks confusingly like PEM files, + but there are some differences.

    + 1> {ok, SshBin} = file:read_file("ssh2_rsa_pub"). +{ok, <<"---- BEGIN SSH2 PUBLIC KEY ----\nAAAA"...>>} + +

    This is equivalent to calling public_key:ssh_decode(SshBin, rfc4716_public_key). +

    + 2> public_key:ssh_decode(SshBin, public_key). +[{#'RSAPublicKey'{modulus = 794430685...91663, + publicExponent = 35}, []}] + + +
    + +
    + Openssh public key format + 1> {ok, SshBin} = file:read_file("openssh_dsa_pub"). +{ok,<<"ssh-dss AAAAB3Nza"...>>} + +

    This is equivalent to calling public_key:ssh_decode(SshBin, openssh_public_key). +

    + 2> public_key:ssh_decode(SshBin, public_key). +[{{15642692...694280725, + #'Dss-Parms'{p = 17291273936...696123221, + q = 1255626590179665817295475654204371833735706001853, + g = 10454211196...480338645}}, + [{comment,"dhopson@VMUbuntu-DSH"}]}] + +
    + +
    + Known hosts - openssh format + + 1> {ok, SshBin} = file:read_file("known_hosts"). +{ok,<<"hostname.domain.com,192.168.0.1 ssh-rsa AAAAB...>>} + +

    Returns a list of public keys and their related attributes + each pair of key and attributes corresponds to one entry in + the known hosts file.

    + + 2> public_key:ssh_decode(SshBin, known_hosts). +[{#'RSAPublicKey'{modulus = 1498979460408...72721699, + publicExponent = 35}, + [{hostnames,["hostname.domain.com","192.168.0.1"]}]}, + {#'RSAPublicKey'{modulus = 14989794604088...2721699, + publicExponent = 35}, + [{comment,"foo@bar.com"}, + {hostnames,["|1|BWO5qDxk/cFH0wa05JLdHn+j6xQ=|rXQvIxh5cDD3C43k5DPDamawVNA="]}]}] + +
    + +
    + Authorized keys - openssh format + + 1> {ok, SshBin} = file:read_file("auth_keys"). +{ok, <<"command=\"dump /home\",no-pty,no-port-forwarding ssh-rsa AAA...>>} + +

    Returns a list of public keys and their related attributes + each pair of key and attributes corresponds to one entry in + the authorized key file.

    + + 2> public_key:ssh_decode(SshBin, auth_keys). +[{#'RSAPublicKey'{modulus = 794430685...691663, + publicExponent = 35}, + [{comment,"dhopson@VMUbuntu-DSH"}, + {options,["command=\"dump/home\"","no-pty", + "no-port-forwarding"]}]}, + {{1564269258491...607694280725, + #'Dss-Parms'{p = 17291273936185...763696123221, + q = 1255626590179665817295475654204371833735706001853, + g = 10454211195705...60511039590076780999046480338645}}, + [{comment,"dhopson@VMUbuntu-DSH"}]}] + +
    + +
    + Creating an SSH file from public key data + +

    If you got a public key PubKey and a related list of + attributes Attributes as returned + by ssh_decode/2 you can create a new ssh file for example

    + N> SshBin = public_key:ssh_encode([{PubKey, Attributes}], openssh_public_key), +<<"ssh-rsa "...>> +N+1> file:write_file("id_rsa.pub", SshBin). +ok +
    +
    +
    -- cgit v1.2.3