diff options
Diffstat (limited to 'lib')
206 files changed, 5454 insertions, 2561 deletions
diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 984e04b90f..ece592e320 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -31,8 +31,6 @@ -export([on_tc_skip/2]). -export([on_tc_fail/2]). --type proplist() :: [{atom(),term()}]. - %% If you change this, remember to update ct_util:look -> stop clause as well. -define(config_name, ct_hooks). @@ -59,7 +57,7 @@ terminate(Hooks) -> %% @doc Called as each test case is started. This includes all configuration %% tests. -spec init_tc(Mod :: atom(), Func :: atom(), Args :: list()) -> - NewConfig :: proplist() | + NewConfig :: proplists:proplist() | {skip, Reason :: term()} | {auto_skip, Reason :: term()} | {fail, Reason :: term()}. @@ -92,7 +90,7 @@ init_tc(_Mod, TC, Config) -> Args :: list(), Result :: term(), Resturn :: term()) -> - NewConfig :: proplist() | + NewConfig :: proplists:proplist() | {skip, Reason :: term()} | {auto_skip, Reason :: term()} | {fail, Reason :: term()} | diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl index ebebfd18a9..71ed61b4c0 100644 --- a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/empty_cth.erl @@ -59,8 +59,7 @@ -include_lib("common_test/src/ct_util.hrl").
-include_lib("common_test/include/ct_event.hrl").
--type proplist() :: list({atom(),term()}).
--type config() :: proplist().
+-type config() :: proplists:proplist(). -type reason() :: term().
-type skip_or_fail() :: {skip, reason()} |
{auto_skip, reason()} |
@@ -71,7 +70,7 @@ %% @doc Always called before any other callback function. Use this to initiate
%% any common state. It should return an state for this CTH.
--spec init(Id :: term(), Opts :: proplist()) ->
+-spec init(Id :: term(), Opts :: proplists:proplist()) -> State :: #state{}.
init(Id, Opts) ->
gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
@@ -81,7 +80,7 @@ init(Id, Opts) -> %% @doc The ID is used to uniquly identify an CTH instance, if two CTH's
%% return the same ID the seconds CTH is ignored. This function should NOT
%% have any side effects as it might be called multiple times by common test.
--spec id(Opts :: proplist()) ->
+-spec id(Opts :: proplists:proplist()) -> Id :: term().
id(Opts) ->
gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index 7137dbc036..8cb16d8f09 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -1414,6 +1414,17 @@ do_clause(C, Arg, ArgType0, OrigArgType, Map, false -> true end; + [Pat0, Pat1] -> % binary comprehension + case cerl:is_c_cons(Pat0) of + true -> + not (cerl:is_c_var(cerl:cons_hd(Pat0)) andalso + cerl:is_c_var(cerl:cons_tl(Pat0)) andalso + cerl:is_c_var(Pat1) andalso + cerl:is_literal(Guard) andalso + (cerl:concrete(Guard) =:= true)); + false -> + true + end; _ -> true end; false -> @@ -2915,7 +2926,7 @@ state__get_warnings(#state{tree_map = TreeMap, fun_tab = FunTab, {Warn, Msg} = case dialyzer_callgraph:lookup_name(FunLbl, Callgraph) of error -> {true, {unused_fun, []}}; - {ok, {_M, F, A}} = MFA -> + {ok, {_M, F, A} = MFA} -> {not sets:is_element(MFA, NoWarnUnused), {unused_fun, [F, A]}} end, diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index c45615d670..65c2ff76bb 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -1684,11 +1684,14 @@ solve_scc(SCC, Map, State, TryingUnit) -> true -> ?debug("SCC ~w reached fixpoint\n", [SCC]), NewTypes = unsafe_lookup_type_list(Funs, Map2), - case lists:all(fun(T) -> t_is_none(t_fun_range(T)) end, NewTypes) + case erl_types:any_none([t_fun_range(T) || T <- NewTypes]) andalso TryingUnit =:= false of true -> - UnitTypes = [t_fun(state__fun_arity(F, State), t_unit()) - || F <- Funs], + UnitTypes = + [case t_is_none(t_fun_range(T)) of + false -> T; + true -> t_fun(t_fun_args(T), t_unit()) + end || T <- NewTypes], Map3 = enter_type_lists(Funs, UnitTypes, Map2), solve_scc(SCC, Map3, State, true); false -> diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile index 69a8fd742e..47deb17f1d 100644 --- a/lib/dialyzer/test/Makefile +++ b/lib/dialyzer/test/Makefile @@ -26,7 +26,7 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_tests_spec: $(INSTALL_DIR) $(RELSYSDIR) - chmod -f -R u+w $(RELSYSDIR) + chmod -R u+w $(RELSYSDIR) $(INSTALL_DATA) $(AUXILIARY_FILES) $(RELSYSDIR) @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) cd $(RELSYSDIR);\ diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/asn1 b/lib/dialyzer/test/r9c_SUITE_data/results/asn1 index ac83366bc8..571e562d0d 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/results/asn1 +++ b/lib/dialyzer/test/r9c_SUITE_data/results/asn1 @@ -2,7 +2,7 @@ asn1ct.erl:1500: The variable Err can never match since previous clauses completely covered the type #type{} asn1ct.erl:1596: The variable _ can never match since previous clauses completely covered the type 'ber_bin_v2' asn1ct.erl:1673: The pattern 'all' can never match the type 'asn1_module' | 'exclusive_decode' | 'partial_decode' -asn1ct.erl:672: The pattern <{'false', Result}, _, _> can never match the type <{'true','true'},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()],[any()]> +asn1ct.erl:672: The pattern <{'false', Result}, _, _> can never match the type <{'true','true'},atom() | binary() | [atom() | [any()] | char()],[any()]> asn1ct.erl:909: Guard test is_atom(Ext::[49 | 97 | 98 | 100 | 110 | 115]) can never succeed asn1ct_check.erl:1698: The pattern {'error', _} can never match the type [any()] asn1ct_check.erl:2733: The pattern {'type', Tag, _, _, _, _} can never match the type 'ASN1_OPEN_TYPE' | {_,_} | {'fixedtypevaluefield',_,_} diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/inets b/lib/dialyzer/test/r9c_SUITE_data/results/inets index fd5e36a3cd..6b16dba2ff 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/results/inets +++ b/lib/dialyzer/test/r9c_SUITE_data/results/inets @@ -7,9 +7,6 @@ http_lib.erl:286: The call http_lib:close('ip_comm' | {'ssl',_},any()) will neve http_lib.erl:424: The variable _ can never match since previous clauses completely covered the type any() http_lib.erl:438: The variable _ can never match since previous clauses completely covered the type any() http_lib.erl:99: Function getHeaderValue/2 will never be called -httpc_handler.erl:322: Function status_continue/2 has no local return -httpc_handler.erl:37: Function init_connection/2 has no local return -httpc_handler.erl:65: Function next_response_with_request/2 has no local return httpc_handler.erl:660: Function exit_session_ok/2 has no local return httpc_manager.erl:145: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}} httpc_manager.erl:160: The pattern {ErrorReply, State2} can never match the type {{'ok',number()},number(),#state{reqid::number()}} @@ -27,7 +24,7 @@ httpd_manager.erl:933: Function acceptor_status/1 will never be called httpd_request_handler.erl:374: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 66 | 98 | 100 | 103 | 105 | 111 | 116 | 121,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any()) httpd_request_handler.erl:378: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 77 | 97 | 100 | 101 | 104 | 108 | 110 | 111 | 116 | 119,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any()) httpd_request_handler.erl:401: The call httpd_response:send_status(Info::#mod{parsed_header::maybe_improper_list()},417,[32 | 77 | 97 | 100 | 101 | 104 | 108 | 110 | 111 | 116 | 119,...]) will never return since it differs in the 2nd argument from the success typing arguments: (#mod{socket_type::'ip_comm' | {'ssl',_}},100 | 301 | 304 | 400 | 401 | 403 | 404 | 412 | 414 | 416 | 500 | 501 | 503,any()) -httpd_request_handler.erl:644: The call lists:reverse(Fields0::{'error',_} | {'ok',[[any()]]}) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) +httpd_request_handler.erl:644: The call lists:reverse(Fields0::{'error',_} | {'ok',_}) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) httpd_request_handler.erl:645: Function will never be called httpd_sup.erl:63: The variable Else can never match since previous clauses completely covered the type {'error',_} | {'ok',[any()],_,_} httpd_sup.erl:88: The pattern {'error', Reason} can never match the type {'ok',_,_} @@ -38,17 +35,17 @@ mod_auth_plain.erl:100: The variable _ can never match since previous clauses co mod_auth_plain.erl:159: The variable _ can never match since previous clauses completely covered the type [any()] mod_auth_plain.erl:83: The variable O can never match since previous clauses completely covered the type [any()] mod_cgi.erl:372: The pattern {'http_response', NewAccResponse} can never match the type 'ok' -mod_dir.erl:101: The call lists:flatten(nonempty_improper_list(atom() | binary() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) +mod_dir.erl:101: The call lists:flatten(nonempty_improper_list(atom() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) mod_dir.erl:72: The pattern {'error', Reason} can never match the type {'ok',[[[any()] | char()],...]} -mod_get.erl:135: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | binary() | [any()] | char()]> -mod_head.erl:80: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]> +mod_get.erl:135: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]> +mod_head.erl:80: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]> mod_htaccess.erl:460: The pattern {'error', BadData} can never match the type {'ok',_} -mod_include.erl:193: The pattern {_, Name, {[], []}} can never match the type {[any()],[any()],maybe_improper_list()} -mod_include.erl:195: The pattern {_, Name, {PathInfo, []}} can never match the type {[any()],[any()],maybe_improper_list()} -mod_include.erl:197: The pattern {_, Name, {PathInfo, QueryString}} can never match the type {[any()],[any()],maybe_improper_list()} -mod_include.erl:201: The variable Gurka can never match since previous clauses completely covered the type {[any()],[any()],maybe_improper_list()} -mod_include.erl:692: The pattern <{'read', Reason}, Info, Path> can never match the type <{'open',atom()},#mod{},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]> -mod_include.erl:706: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]> +mod_include.erl:193: The pattern {_, Name, {[], []}} can never match the type {[any()],[any()],string()} +mod_include.erl:195: The pattern {_, Name, {PathInfo, []}} can never match the type {[any()],[any()],string()} +mod_include.erl:197: The pattern {_, Name, {PathInfo, QueryString}} can never match the type {[any()],[any()],string()} +mod_include.erl:201: The variable Gurka can never match since previous clauses completely covered the type {[any()],[any()],string()} +mod_include.erl:692: The pattern <{'read', Reason}, Info, Path> can never match the type <{'open',atom()},#mod{},atom() | binary() | [atom() | [any()] | char()]> +mod_include.erl:706: The pattern <{'enfile', _}, _Info, Path> can never match the type <atom(),#mod{},atom() | binary() | [atom() | [any()] | char()]> mod_include.erl:716: Function read_error/3 will never be called mod_include.erl:719: Function read_error/4 will never be called mod_security_server.erl:386: The variable O can never match since previous clauses completely covered the type [any()] diff --git a/lib/dialyzer/test/r9c_SUITE_data/results/mnesia b/lib/dialyzer/test/r9c_SUITE_data/results/mnesia index e199581a0e..b0f4d12ae5 100644 --- a/lib/dialyzer/test/r9c_SUITE_data/results/mnesia +++ b/lib/dialyzer/test/r9c_SUITE_data/results/mnesia @@ -6,6 +6,9 @@ mnesia_bup.erl:111: The created fun has no local return mnesia_bup.erl:574: Function fallback_receiver/2 has no local return mnesia_bup.erl:967: Function uninstall_fallback_master/2 has no local return mnesia_checkpoint.erl:1014: The variable Error can never match since previous clauses completely covered the type {'ok',#checkpoint_args{nodes::[any()],retainers::[any(),...]}} +mnesia_checkpoint.erl:1209: Function system_continue/3 has no local return +mnesia_checkpoint.erl:792: Function retainer_loop/1 has no local return +mnesia_checkpoint.erl:894: The call sys:handle_system_msg(Msg::any(),From::any(),'no_parent','mnesia_checkpoint',[],Cp::#checkpoint_args{}) breaks the contract (Msg,From,Parent,Module,Debug,Misc) -> Void when is_subtype(Msg,term()), is_subtype(From,{pid(),Tag::_}), is_subtype(Parent,pid()), is_subtype(Module,module()), is_subtype(Debug,[dbg_opt()]), is_subtype(Misc,term()), is_subtype(Void,term()) mnesia_controller.erl:1666: The variable Tab can never match since previous clauses completely covered the type [any()] mnesia_controller.erl:1679: The pattern {'stop', Reason, Reply, State2} can never match the type {'noreply',_} | {'reply',_,_} | {'stop','shutdown',#state{}} mnesia_controller.erl:1685: The pattern {'noreply', State2, _Timeout} can never match the type {'reply',_,_} @@ -15,6 +18,7 @@ mnesia_frag.erl:294: The call mnesia_frag:remote_collect(Ref::reference(),{'erro mnesia_frag.erl:304: The call mnesia_frag:remote_collect(Ref::reference(),{'error',{'node_not_running',_}},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()])) mnesia_frag.erl:312: The call mnesia_frag:remote_collect(Ref::reference(),LocalRes::{'error',_},[],OldSelectFun::fun(() -> [any()])) will never return since it differs in the 2nd argument from the success typing arguments: (reference(),'ok',[any()],fun(() -> [any()])) mnesia_index.erl:52: The call mnesia_lib:other_val(Var::{_,'commit_work' | 'index' | 'setorbag' | 'storage_type' | {'index',_}},_ReASoN_::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any()) +mnesia_lib.erl:1028: The pattern {'EXIT', Reason} can never match the type [any()] | {'error',_} mnesia_lib.erl:957: The pattern {'ok', {0, _}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()} mnesia_lib.erl:959: The pattern {'ok', {_, Bin}} can never match the type 'eof' | {'error',atom()} | {'ok',binary() | string()} mnesia_loader.erl:36: The call mnesia_lib:other_val(Var::{_,'access_mode' | 'cstruct' | 'db_nodes' | 'setorbag' | 'snmp' | 'storage_type'},Reason::any()) will never return since it differs in the 1st argument from the success typing arguments: ({_,'active_replicas' | 'where_to_read' | 'where_to_write'},any()) @@ -30,5 +34,6 @@ mnesia_schema.erl:1258: Guard test FromS::'disc_copies' | 'disc_only_copies' | ' mnesia_schema.erl:1639: The pattern {'false', 'mandatory'} can never match the type {'false','optional'} mnesia_schema.erl:2434: The variable Reason can never match since previous clauses completely covered the type {'error',_} | {'ok',_} mnesia_schema.erl:451: Guard test UseDirAnyway::'false' == 'true' can never succeed +mnesia_text.erl:180: The variable T can never match since previous clauses completely covered the type {'error',{integer(),atom() | tuple(),_}} | {'ok',_} mnesia_tm.erl:1522: Function commit_participant/5 has no local return mnesia_tm.erl:2169: Function system_terminate/4 has no local return diff --git a/lib/dialyzer/test/race_SUITE_data/results/extract_translations b/lib/dialyzer/test/race_SUITE_data/results/extract_translations index f7d5abc6f5..62aa1aa511 100644 --- a/lib/dialyzer/test/race_SUITE_data/results/extract_translations +++ b/lib/dialyzer/test/race_SUITE_data/results/extract_translations @@ -1,5 +1,5 @@ -extract_translations.erl:140: The call ets:insert('files',{atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]) call in extract_translations.erl on line 135 +extract_translations.erl:140: The call ets:insert('files',{atom() | binary() | [atom() | [any()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | [any()] | char()]) call in extract_translations.erl on line 135 extract_translations.erl:146: The call ets:insert('translations',{_,[]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('translations',Str::any()) call in extract_translations.erl on line 126 -extract_translations.erl:152: The call ets:insert('files',{atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | binary() | [atom() | binary() | [any()] | char()] | char()]) call in extract_translations.erl on line 148 +extract_translations.erl:152: The call ets:insert('files',{atom() | binary() | [atom() | [any()] | char()]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('files',File::atom() | binary() | [atom() | [any()] | char()]) call in extract_translations.erl on line 148 extract_translations.erl:154: The call ets:insert('translations',{_,[]}) might have an unintended effect due to a possible race condition caused by its combination with the ets:lookup('translations',Str::any()) call in extract_translations.erl on line 126 diff --git a/lib/dialyzer/test/small_SUITE_data/results/flatten b/lib/dialyzer/test/small_SUITE_data/results/flatten index 4571214e49..8aa44dd002 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/flatten +++ b/lib/dialyzer/test/small_SUITE_data/results/flatten @@ -1,2 +1,2 @@ -flatten.erl:17: The call lists:flatten(nonempty_improper_list(atom() | binary() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) +flatten.erl:17: The call lists:flatten(nonempty_improper_list(atom() | [any()] | char(),atom())) will never return since it differs in the 1st argument from the success typing arguments: ([any()]) diff --git a/lib/dialyzer/test/small_SUITE_data/src/binary_lc_bug.erl b/lib/dialyzer/test/small_SUITE_data/src/binary_lc_bug.erl new file mode 100644 index 0000000000..c1e82bfa59 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/binary_lc_bug.erl @@ -0,0 +1,8 @@ +-module(test). + +-export([bin_compr/0]). + +bin_compr() -> +% [ 0 || {N, V} <- [{a, b}] ]. % Works ok + << <<>> || {A, B} <- [{a, b}] >>. % Complains +% << <<>> || X <- [{a, b}] >>. % Works ok diff --git a/lib/dialyzer/test/small_SUITE_data/src/codec_can.erl b/lib/dialyzer/test/small_SUITE_data/src/codec_can.erl new file mode 100644 index 0000000000..8abf872b37 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/codec_can.erl @@ -0,0 +1,35 @@ +%%--------------------------------------------------------------------- +%% From: Peer Stritzinger +%% Date: 1 May 2011 +%% Subject: Dialyzer v2.2.0 crash +%% +%% Binaries of the form <<_:N,_:_*M>> in specs resulted in a crash: +%% dialyzer: Analysis failed with error: {{case_clause,8}, +%% [{erl_types,t_form_to_string,1}, +%% {erl_types,t_form_to_string,1}, +%% {dialyzer_contracts,contract_to_string_1,1}, +%% {dialyzer_contracts,extra_contract_warning,6}, +%% {dialyzer_contracts,picky_contract_check,7}, +%% because function erl_types:t_form_to_string/1 was not the inverse +%% of erl_types:t_to_string/2. +%% +%% Fixed on the same date and send to OTP for inclusion. +%%--------------------------------------------------------------------- +-module(codec_can). + +-export([recv/3, decode/1]). + +-record(can_pkt, {id, data :: binary(), timestamp}). + +-type can_pkt() :: #can_pkt{}. +-type channel() :: atom() | pid() | {atom(),_}. + +-spec recv(<<_:64,_:_*8>>, fun((can_pkt()) -> R), channel()) -> R. +recv(Packet, Fun, Chan) -> + #can_pkt{id = Can_id, data = Can_data} = P = decode(Packet), + Fun(P). + +-spec decode(<<_:64,_:_*8>>) -> #can_pkt{id::<<_:11>>,timestamp::char()}. +decode(<<_:12, Len:4, Timestamp:16, 0:3, Id:11/bitstring, 0:18, + Data:Len/binary, _/binary>>) -> + #can_pkt{id = Id, data = Data, timestamp = Timestamp}. diff --git a/lib/dialyzer/test/small_SUITE_data/src/file_open_encoding.erl b/lib/dialyzer/test/small_SUITE_data/src/file_open_encoding.erl index 4f1268eba8..086df3464b 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/file_open_encoding.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/file_open_encoding.erl @@ -6,9 +6,7 @@ -export([parse/1]). --type proplist() :: [{atom(), any()}]. - --spec parse(string()) -> proplist(). +-spec parse(string()) -> proplists:proplist(). parse(FileName) -> {ok, IoDevice} = file:open(FileName, [read, binary, {encoding, utf8}]), do_parse(IoDevice, []). diff --git a/lib/dialyzer/test/small_SUITE_data/src/list_to_bitstring.erl b/lib/dialyzer/test/small_SUITE_data/src/list_to_bitstring.erl new file mode 100644 index 0000000000..2da708cb15 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/list_to_bitstring.erl @@ -0,0 +1,21 @@ +%%===================================================================== +%% From: Ken Robinson +%% Date: 28/04/2011, 17:26 +%% +%% Program that produced borus "Function has no local return" warnings +%% due to erlang:list_to_bitstring/1 having erroneous hard coded type +%% information, namely accepting iolist() instead of bitstrlist(). +%% Fixed 29/04/2011. +%%===================================================================== + +-module(list_to_bitstring). + +-export([l2bs/0, l2bs_ok/0]). + +%% This function was producing a warning +l2bs() -> + erlang:list_to_bitstring([<<42>>, <<42:13>>]). + +%% while this one was ok. +l2bs_ok() -> + erlang:list_to_bitstring([<<42>>, <<42,42>>]). diff --git a/lib/dialyzer/test/small_SUITE_data/src/no_return_bug.erl b/lib/dialyzer/test/small_SUITE_data/src/no_return_bug.erl new file mode 100644 index 0000000000..5c24902590 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/no_return_bug.erl @@ -0,0 +1,42 @@ +%% Dialyzer couldn't infer that monitor_diskspace would go in an infinite loop +%% instead of crashing due to the existence of list comprehensions that have a +%% normal success typing. These were added to the monitor_diskspace's SCC and +%% dialyzer_typesig didn't try to assign unit() to monitor_diskspace, as it +%% required all the members of the SCC to return none(). +%% +%% Testcase was submitted in erlang-questions mailing list by Prashanth Mundkur +%% (http://erlang.org/pipermail/erlang-questions/2011-May/058063.html) + +-module(no_return_bug). +-export([diskspace/1, monitor_diskspace/2, refresh_tags/1, monitor_launch/0]). + +-type diskinfo() :: {non_neg_integer(), non_neg_integer()}. + +-spec diskspace(nonempty_string()) -> {'ok', diskinfo()} | {'error', term()}. +diskspace(Path) -> + case Path of + "a" -> {ok, {0,0}}; + _ -> {error, error} + end. + +-spec monitor_diskspace(nonempty_string(), + [{diskinfo(), nonempty_string()}]) -> + no_return(). +monitor_diskspace(Root, Vols) -> + Df = fun(VolName) -> + diskspace(filename:join([Root, VolName])) + end, + NewVols = [{Space, VolName} + || {VolName, {ok, Space}} + <- [{VolName, Df(VolName)} + || {_OldSpace, VolName} <- Vols]], + monitor_diskspace(Root, NewVols). + +-spec refresh_tags(nonempty_string()) -> no_return(). +refresh_tags(Root) -> + {ok, _} = diskspace(Root), + refresh_tags(Root). + +monitor_launch() -> + spawn_link(fun() -> refresh_tags("abc") end), + spawn_link(fun() -> monitor_diskspace("root", [{{0,0}, "a"}]) end). diff --git a/lib/dialyzer/test/small_SUITE_data/src/nowarnunused.erl b/lib/dialyzer/test/small_SUITE_data/src/nowarnunused.erl new file mode 100644 index 0000000000..63daeee9e3 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/nowarnunused.erl @@ -0,0 +1,7 @@ +-module(nowarnunused). + +-compile({nowarn_unused_function, return_error/2}). + +-spec return_error(integer(), any()) -> no_return(). +return_error(Line, Message) -> + throw({error, {Line, ?MODULE, Message}}). diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml index a87f59bad5..e7c530f1b8 100644 --- a/lib/diameter/doc/src/diameter_dict.xml +++ b/lib/diameter/doc/src/diameter_dict.xml @@ -105,7 +105,7 @@ quantity is insignificant.</p> <p> The tags, their arguments and the contents of each corresponding section are as follows. -Each section can occur only once unless otherwise specified. +Each section can occur at most once unless otherwise specified. The order in which sections are specified is unimportant.</p> <taglist> @@ -115,6 +115,7 @@ The order in which sections are specified is unimportant.</p> <p> Defines the integer Number as the Diameter Application Id of the application in question. +Required if the dictionary defines <c>@messages</c>. The section has empty content.</p> <p> @@ -370,7 +371,11 @@ Integer values can be prefixed with 0x to be interpreted as hexidecimal.</p> <p> -Can occur 0 or more times (with different values of Name).</p> +Can occur 0 or more times (with different values of Name). +The AVP in question can be defined in an inherited dictionary in order +to introduce additional values. +An AVP so extended must be referenced by in a <c>@messages</c> or +<c>@grouped</c> section.</p> <p> Example:</p> diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index 213ba0d22c..30caebc544 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -250,9 +250,14 @@ f_name(Name) -> %%% ------------------------------------------------------------------------ f_id(Spec) -> - Id = orddict:fetch(id, Spec), {?function, id, 0, - [{?clause, [], [], [?INTEGER(Id)]}]}. + [c_id(orddict:find(id, Spec))]}. + +c_id({ok, Id}) -> + {?clause, [], [], [?INTEGER(Id)]}; + +c_id(error) -> + ?UNEXPECTED(0). %%% ------------------------------------------------------------------------ %%% # vendor_id/0 @@ -454,9 +459,10 @@ avp(Spec) -> Native = get_value(avp_types, Spec), Custom = get_value(custom_types, Spec), Imported = get_value(import_avps, Spec), - avp([{N,T} || {N,_,T,_,_} <- Native], Imported, Custom). + Enums = get_value(enums, Spec), + avp([{N,T} || {N,_,T,_,_} <- Native], Imported, Custom, Enums). -avp(Native, Imported, Custom) -> +avp(Native, Imported, Custom, Enums) -> Dict = orddict:from_list(Native), report(native, Dict), @@ -470,8 +476,8 @@ avp(Native, Imported, Custom) -> false == lists:member(N, CustomNames) end, Native)) - ++ lists:flatmap(fun c_imported_avp/1, Imported) - ++ lists:flatmap(fun(C) -> c_custom_avp(C, Dict) end, Custom). + ++ lists:flatmap(fun(I) -> cs_imported_avp(I, Enums) end, Imported) + ++ lists:flatmap(fun(C) -> cs_custom_avp(C, Dict) end, Custom). c_base_avp({AvpName, T}) -> {?clause, [?VAR('T'), ?VAR('Data'), ?ATOM(AvpName)], @@ -487,23 +493,35 @@ base_avp(AvpName, 'Grouped') -> base_avp(_, Type) -> ?APPLY(diameter_types, Type, [?VAR('T'), ?VAR('Data')]). -c_imported_avp({Mod, Avps}) -> - lists:map(fun(A) -> imported_avp(Mod, A) end, Avps). +cs_imported_avp({Mod, Avps}, Enums) -> + lists:map(fun(A) -> imported_avp(Mod, A, Enums) end, Avps). -imported_avp(_Mod, {AvpName, _, 'Grouped' = T, _, _}) -> +imported_avp(_Mod, {AvpName, _, 'Grouped' = T, _, _}, _) -> c_base_avp({AvpName, T}); -imported_avp(Mod, {AvpName, _, _, _, _}) -> +imported_avp(Mod, {AvpName, _, 'Enumerated' = T, _, _}, Enums) -> + case lists:keymember(AvpName, 1, Enums) of + true -> + c_base_avp({AvpName, T}); + false -> + c_imported_avp(Mod, AvpName) + end; + +imported_avp(Mod, {AvpName, _, _, _, _}, _) -> + c_imported_avp(Mod, AvpName). + +c_imported_avp(Mod, AvpName) -> {?clause, [?VAR('T'), ?VAR('Data'), ?ATOM(AvpName)], [], [?APPLY(Mod, avp, [?VAR('T'), ?VAR('Data'), ?ATOM(AvpName)])]}. -c_custom_avp({Mod, Avps}, Dict) -> - lists:map(fun(N) -> custom_avp(Mod, N, orddict:fetch(N, Dict)) end, Avps). +cs_custom_avp({Mod, Avps}, Dict) -> + lists:map(fun(N) -> c_custom_avp(Mod, N, orddict:fetch(N, Dict)) end, + Avps). -custom_avp(Mod, AvpName, Type) -> +c_custom_avp(Mod, AvpName, Type) -> {?clause, [?VAR('T'), ?VAR('Data'), ?ATOM(AvpName)], [], [?APPLY(Mod, AvpName, [?VAR('T'), ?ATOM(Type), ?VAR('Data')])]}. @@ -516,9 +534,25 @@ f_enumerated_avp(Spec) -> {?function, enumerated_avp, 3, enumerated_avp(Spec) ++ [?UNEXPECTED(3)]}. enumerated_avp(Spec) -> - lists:flatmap(fun c_enumerated_avp/1, get_value(enums, Spec)). + Enums = get_value(enums, Spec), + lists:flatmap(fun cs_enumerated_avp/1, Enums) + ++ lists:flatmap(fun({M,Es}) -> enumerated_avp(M, Es, Enums) end, + get_value(import_enums, Spec)). + +enumerated_avp(Mod, Es, Enums) -> + lists:flatmap(fun({N,_}) -> + cs_enumerated_avp(lists:keymember(N, 1, Enums), + Mod, + N) + end, + Es). -c_enumerated_avp({AvpName, Values}) -> +cs_enumerated_avp(true, Mod, Name) -> + [c_imported_avp(Mod, Name)]; +cs_enumerated_avp(false, _, _) -> + []. + +cs_enumerated_avp({AvpName, Values}) -> lists:flatmap(fun(V) -> c_enumerated_avp(AvpName, V) end, Values). c_enumerated_avp(AvpName, {I,_}) -> @@ -537,10 +571,14 @@ f_msg_header(Spec) -> {?function, msg_header, 1, msg_header(Spec) ++ [?UNEXPECTED(1)]}. msg_header(Spec) -> + msg_header(get_value(messages, Spec), Spec). + +msg_header([], _) -> + []; +msg_header(Msgs, Spec) -> ApplId = orddict:fetch(id, Spec), - lists:map(fun({M,C,F,_,_}) -> c_msg_header(M, C, F, ApplId) end, - get_value(messages, Spec)). + lists:map(fun({M,C,F,_,_}) -> c_msg_header(M, C, F, ApplId) end, Msgs). %% Note that any application id in the message header spec is ignored. @@ -616,10 +654,12 @@ f_empty_value(Spec) -> {?function, empty_value, 1, empty_value(Spec)}. empty_value(Spec) -> + Imported = lists:flatmap(fun avps/1, get_value(import_enums, Spec)), Groups = get_value(grouped, Spec) ++ lists:flatmap(fun avps/1, get_value(import_groups, Spec)), - Enums = get_value(enums, Spec) - ++ lists:flatmap(fun avps/1, get_value(import_enums, Spec)), + Enums = [T || {N,_} = T <- get_value(enums, Spec), + not lists:keymember(N, 1, Imported)] + ++ Imported, lists:map(fun c_empty_value/1, Groups ++ Enums) ++ [{?clause, [?VAR('Name')], [], [?CALL(empty, [?VAR('Name')])]}]. diff --git a/lib/diameter/src/compiler/diameter_spec_util.erl b/lib/diameter/src/compiler/diameter_spec_util.erl index 322d53a199..b60886b678 100644 --- a/lib/diameter/src/compiler/diameter_spec_util.erl +++ b/lib/diameter/src/compiler/diameter_spec_util.erl @@ -39,11 +39,11 @@ parse(Path, Options) -> {ok, B} = file:read_file(Path), Chunks = chunk(B), Spec = make_spec(Chunks), - true = enums_defined(Spec), %% sanity checks - true = groups_defined(Spec), %% + true = groups_defined(Spec), %% sanity checks true = customs_defined(Spec), %% Full = import_enums(import_groups(import_avps(insert_codes(Spec), Options))), + true = enums_defined(Full), %% sanity checks true = v_flags_set(Spec), Full. @@ -243,35 +243,48 @@ get_value(Key, Spec) -> %% with an appropriate type. enums_defined(Spec) -> - is_defined(Spec, 'Enumerated', enums). + Avps = get_value(avp_types, Spec), + Import = get_value(import_enums, Spec), + lists:all(fun({N,_}) -> + true = enum_defined(N, Avps, Import) + end, + get_value(enums, Spec)). -groups_defined(Spec) -> - is_defined(Spec, 'Grouped', grouped). +enum_defined(Name, Avps, Import) -> + case lists:keyfind(Name, 1, Avps) of + {Name, _, 'Enumerated', _, _} -> + true; + {Name, _, T, _, _} -> + ?ERROR({avp_has_wrong_type, Name, 'Enumerated', T}); + false -> + lists:any(fun({_,Is}) -> lists:keymember(Name, 1, Is) end, Import) + orelse ?ERROR({avp_not_defined, Name, 'Enumerated'}) + end. +%% Note that an AVP is imported only if referenced by a message or +%% grouped AVP, so the final branch will fail if an enum definition is +%% extended without this being the case. -is_defined(Spec, Type, Key) -> +groups_defined(Spec) -> Avps = get_value(avp_types, Spec), - lists:all(fun(T) -> true = is_local(name(Key, T), Type, Avps) end, - get_value(Key, Spec)). + lists:all(fun({N,_,_,_}) -> true = group_defined(N, Avps) end, + get_value(grouped, Spec)). -name(enums, {N,_}) -> N; -name(grouped, {N,_,_,_}) -> N. - -is_local(Name, Type, Avps) -> +group_defined(Name, Avps) -> case lists:keyfind(Name, 1, Avps) of - {Name, _, Type, _, _} -> + {Name, _, 'Grouped', _, _} -> true; {Name, _, T, _, _} -> - ?ERROR({avp_has_wrong_type, Name, Type, T}); + ?ERROR({avp_has_wrong_type, Name, 'Grouped', T}); false -> - ?ERROR({avp_not_defined, Name, Type}) + ?ERROR({avp_not_defined, Name, 'Grouped'}) end. customs_defined(Spec) -> Avps = get_value(avp_types, Spec), - lists:all(fun(A) -> true = is_local(A, Avps) end, + lists:all(fun(A) -> true = custom_defined(A, Avps) end, lists:flatmap(fun last/1, get_value(custom_types, Spec))). -is_local(Name, Avps) -> +custom_defined(Name, Avps) -> case lists:keyfind(Name, 1, Avps) of {Name, _, T, _, _} when T == 'Grouped'; T == 'Enumerated' -> @@ -510,6 +523,9 @@ choose(false, _, X) -> X. %% ------------------------------------------------------------------------ %% import_groups/1 %% import_enums/1 +%% +%% For each inherited module, store the content of imported AVP's of +%% type grouped/enumerated in a new key. import_groups(Spec) -> orddict:store(import_groups, import(grouped, Spec), Spec). diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile index 823e2f0311..b3648c7bb1 100644 --- a/lib/diameter/test/Makefile +++ b/lib/diameter/test/Makefile @@ -404,5 +404,5 @@ release_tests_spec: tests # $(HRL_FILES) $(ERL_FILES) \ # $(RELSYSDIR) # - chmod -f -R u+w $(RELSYSDIR) + chmod -R u+w $(RELSYSDIR) diff --git a/lib/edoc/Makefile b/lib/edoc/Makefile index e512e390e3..1add669398 100644 --- a/lib/edoc/Makefile +++ b/lib/edoc/Makefile @@ -13,8 +13,6 @@ # Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings # AB. All Rights Reserved.'' # -# $Id$ -# include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk diff --git a/lib/edoc/doc/Makefile b/lib/edoc/doc/Makefile index a0f6484382..c5f68b25d0 100644 --- a/lib/edoc/doc/Makefile +++ b/lib/edoc/doc/Makefile @@ -13,8 +13,6 @@ # Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings # AB. All Rights Reserved.'' # -# $Id: Makefile,v 1.1.1.1 2004/10/04 13:53:33 richardc Exp $ -# include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk diff --git a/lib/edoc/doc/overview.edoc b/lib/edoc/doc/overview.edoc index bd603b7a13..fa699c6f08 100644 --- a/lib/edoc/doc/overview.edoc +++ b/lib/edoc/doc/overview.edoc @@ -1084,10 +1084,11 @@ Details: the Erlang programming language.</li> <li>`boolean()' is the subset of `atom()' consisting of the atoms `true' and `false'.</li> - <li>`char()' is a subset of - `integer()' representing character codes.</li> + <li>`char()' is the subset of `integer()' representing + Unicode character codes: hex 000000-10FFFF.</li> <li>`tuple()' is the set of all tuples `{...}'.</li> - <li>`list(T)' is just an alias for `[T]'.</li> + <li>`list(T)' is just an alias for `[T]'; list() is an alias + for `list(any())', i.e., `[any()]'.</li> <li>`nil()' is an alias for the empty list `[]'.</li> <li>`cons(H,T)' is the list constructor. This is usually not used directly. It is possible to recursively define `list(T) diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile index 5ee0096f0f..b933094464 100644 --- a/lib/edoc/doc/src/Makefile +++ b/lib/edoc/doc/src/Makefile @@ -13,8 +13,6 @@ # Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings # AB. All Rights Reserved.'' # -# $Id$ -# include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk diff --git a/lib/edoc/include/Makefile b/lib/edoc/include/Makefile index 0533c27567..5b2ad38c9d 100644 --- a/lib/edoc/include/Makefile +++ b/lib/edoc/include/Makefile @@ -13,8 +13,6 @@ # Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings # AB. All Rights Reserved.'' # -# $Id$ -# include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk diff --git a/lib/edoc/priv/edoc_generate.src b/lib/edoc/priv/edoc_generate.src index e87fdbc902..7ec89207b0 100644 --- a/lib/edoc/priv/edoc_generate.src +++ b/lib/edoc/priv/edoc_generate.src @@ -14,9 +14,6 @@ # Portions created by Ericsson are Copyright 1999-2000, Ericsson # Utvecklings AB. All Rights Reserved.'' # -# $Id$ -# -# #EDOC_DIR=/clearcase/otp/internal_tools/edoc EDOC_DIR=/home/otp/sgml/edoc-%EDOC_VSN% diff --git a/lib/edoc/src/Makefile b/lib/edoc/src/Makefile index 9c5a9d30d1..fcb0b61292 100644 --- a/lib/edoc/src/Makefile +++ b/lib/edoc/src/Makefile @@ -23,7 +23,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/edoc-$(VSN) EBIN = ../ebin XMERL = ../../xmerl -ERL_COMPILE_FLAGS += -I../include -I$(XMERL)/include +warn_unused_vars +nowarn_shadow_vars +warn_unused_import +warn_deprecated_guard +ERL_COMPILE_FLAGS += -pa $(XMERL) -I../include -I$(XMERL)/include +warn_unused_vars +nowarn_shadow_vars +warn_unused_import +warn_deprecated_guard SOURCES= \ edoc.erl edoc_data.erl edoc_doclet.erl edoc_extract.erl \ diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl index 360f2dbc9e..a279f7dcb3 100644 --- a/lib/edoc/src/edoc.erl +++ b/lib/edoc/src/edoc.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2001-2007 Richard Carlsson %% @author Richard Carlsson <[email protected]> %% @version {@version} @@ -60,8 +58,6 @@ -compile({no_auto_import,[error/1]}). --import(edoc_report, [report/2, report/3, error/1, error/3]). - -include("edoc.hrl"). @@ -179,8 +175,8 @@ application(App, Options) when is_atom(App) -> Dir when is_list(Dir) -> application(App, Dir, Options); _ -> - report("cannot find application directory for '~s'.", - [App]), + edoc_report:report("cannot find application directory for '~s'.", + [App]), exit(error) end. @@ -663,8 +659,8 @@ read_source(Name, Opts0) -> check_forms(Forms, Name), Forms; {error, R} -> - error({"error reading file '~s'.", - [edoc_lib:filename(Name)]}), + edoc_report:error({"error reading file '~s'.", + [edoc_lib:filename(Name)]}), exit({error, R}) end. @@ -688,11 +684,10 @@ check_forms(Fs, Name) -> error_marker -> case erl_syntax:error_marker_info(F) of {L, M, D} -> - error(L, Name, {format_error, M, D}); - + edoc_report:error(L, Name, {format_error, M, D}); Other -> - report(Name, "unknown error in " - "source code: ~w.", [Other]) + edoc_report:report(Name, "unknown error in " + "source code: ~w.", [Other]) end, exit(error); _ -> diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl index 27f43dca5a..e3b5a0d51b 100644 --- a/lib/edoc/src/edoc_data.erl +++ b/lib/edoc/src/edoc_data.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @private %% @copyright 2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> diff --git a/lib/edoc/src/edoc_doclet.erl b/lib/edoc/src/edoc_doclet.erl index 30eef3e63a..c66be9d7c7 100644 --- a/lib/edoc/src/edoc_doclet.erl +++ b/lib/edoc/src/edoc_doclet.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2003-2006 Richard Carlsson %% @author Richard Carlsson <[email protected]> %% @see edoc @@ -52,7 +50,7 @@ -define(IMAGE, "erlang.png"). -define(NL, "\n"). --include("xmerl.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). %% Sources is the list of inputs in the order they were found. Packages %% and Modules are sorted lists of atoms without duplicates. (They diff --git a/lib/edoc/src/edoc_extract.erl b/lib/edoc/src/edoc_extract.erl index 5e28762c53..1209d86fe5 100644 --- a/lib/edoc/src/edoc_extract.erl +++ b/lib/edoc/src/edoc_extract.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id: $ -%% %% @copyright 2001-2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> %% @see edoc @@ -238,8 +236,8 @@ file(File, Context, Env, Opts) -> case file:read_file(File) of {ok, Bin} -> {ok, text(binary_to_list(Bin), Context, Env, Opts, File)}; - {error, _R} = Error -> - Error + {error, _} = Error -> + Error end. @@ -298,8 +296,8 @@ get_module_info(Forms, File) -> {Name, Vars} = case lists:keyfind(module, 1, L) of {module, N} when is_atom(N) -> {N, none}; - {module, {N, _Vs} = NVs} when is_atom(N) -> - NVs; + {module, {N, _}=Mod} when is_atom(N) -> + Mod; _ -> report(File, "module name missing.", []), exit(error) diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl index 3ec87b7060..1c0841815f 100644 --- a/lib/edoc/src/edoc_layout.erl +++ b/lib/edoc/src/edoc_layout.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id: $ -%% %% @author Richard Carlsson <[email protected]> %% @copyright 2001-2006 Richard Carlsson %% @see edoc @@ -33,7 +31,7 @@ -import(edoc_report, [report/2]). --include("xmerl.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). -define(HTML_EXPORT, xmerl_html). -define(DEFAULT_XML_EXPORT, ?HTML_EXPORT). @@ -959,12 +957,16 @@ local_label(R) -> xhtml(Title, CSS, Body) -> [{html, [?NL, - {head, [?NL, - {title, Title}, - ?NL] ++ CSS}, - ?NL, - {body, [{bgcolor, "white"}], Body}, - ?NL] + {head, [?NL, + {meta, [{'http-equiv',"Content-Type"}, + {content, "text/html; charset=ISO-8859-1"}], + []}, + ?NL, + {title, Title}, + ?NL] ++ CSS}, + ?NL, + {body, [{bgcolor, "white"}], Body}, + ?NL] }, ?NL]. diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index 585e30a2d2..6c698e83ef 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2001-2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> %% @see edoc @@ -40,7 +38,7 @@ -import(edoc_report, [report/2, warning/2]). -include("edoc.hrl"). --include("xmerl.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). -define(FILE_BASE, "/"). @@ -494,7 +492,7 @@ uri_get_file(File0) -> uri_get_http(URI) -> %% Try using option full_result=false case catch {ok, httpc:request(get, {URI,[]}, [], - [{full_result, false}])} of + [{full_result, false}])} of {'EXIT', _} -> uri_get_http_r10(URI); Result -> diff --git a/lib/edoc/src/edoc_parser.yrl b/lib/edoc/src/edoc_parser.yrl index 6943f1bdb8..3ce4cde4fb 100644 --- a/lib/edoc/src/edoc_parser.yrl +++ b/lib/edoc/src/edoc_parser.yrl @@ -23,9 +23,6 @@ %% USA %% %% Author contact: [email protected] -%% -%% $Id $ -%% %% ===================================================================== Nonterminals @@ -362,10 +359,10 @@ parse_spec(S, L) -> {ok, Spec} -> Spec; {error, E} -> - throw_error(E, L) + throw_error({parse_spec, E}, L) end; {error, E, _} -> - throw_error(E, L) + throw_error({parse_spec, E}, L) end. %% --------------------------------------------------------------------- diff --git a/lib/edoc/src/edoc_report.erl b/lib/edoc/src/edoc_report.erl index ee54c60c90..f082513bee 100644 --- a/lib/edoc/src/edoc_report.erl +++ b/lib/edoc/src/edoc_report.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @private %% @copyright 2001-2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> diff --git a/lib/edoc/src/edoc_run.erl b/lib/edoc/src/edoc_run.erl index 96e5ea4631..1355db840f 100644 --- a/lib/edoc/src/edoc_run.erl +++ b/lib/edoc/src/edoc_run.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> %% @see edoc diff --git a/lib/edoc/src/edoc_scanner.erl b/lib/edoc/src/edoc_scanner.erl index 9d2e6f3aed..8e895ad1ad 100644 --- a/lib/edoc/src/edoc_scanner.erl +++ b/lib/edoc/src/edoc_scanner.erl @@ -13,8 +13,6 @@ %% AB. Portions created by Ericsson are Copyright 1999, Ericsson %% Utvecklings AB. All Rights Reserved.'' %% -%% $Id: $ -%% %% @private %% @copyright Richard Carlsson 2001-2003. Portions created by Ericsson %% are Copyright 1999, Ericsson Utvecklings AB. All Rights Reserved. diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 79a5d142bc..5acf8ac0d5 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -27,7 +27,6 @@ -include("edoc.hrl"). -include("edoc_types.hrl"). --type proplist() :: [proplists:property()]. -type syntaxTree() :: erl_syntax:syntaxTree(). -define(TOP_TYPE, term). @@ -87,8 +86,9 @@ dummy_spec(Form) -> #tag{name = spec, line = element(2, hd(TypeSpecs)), origin = code, data = S}. --spec docs(Forms::[syntaxTree()], CommentFun) -> dict() when - CommentFun :: fun(([syntaxTree()], Line :: term()) -> #tag{}). +-spec docs(Forms::[syntaxTree()], + CommentFun :: fun( ([syntaxTree()], Line :: term()) -> #tag{} )) + -> dict(). %% @doc Find comments after -type/-opaque declarations. %% Postcomments "inside" the type are skipped. @@ -98,7 +98,7 @@ docs(Forms, CommentFun) -> -type entry() :: #entry{}. -type module_info() :: #module{}. -type entries() :: [entry()]. --spec add_data(Entries::entries(), Options::proplist(), +-spec add_data(Entries::entries(), Options::proplists:proplist(), File::file:filename(), Module::module_info()) -> entries(). %% @doc Create tags a la EDoc for Erlang specifications and types. diff --git a/lib/edoc/src/edoc_tags.erl b/lib/edoc/src/edoc_tags.erl index 8ee8f87b5f..80989428ce 100644 --- a/lib/edoc/src/edoc_tags.erl +++ b/lib/edoc/src/edoc_tags.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @private %% @copyright 2001-2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> diff --git a/lib/edoc/src/edoc_types.erl b/lib/edoc/src/edoc_types.erl index e784b3359a..a54544868c 100644 --- a/lib/edoc/src/edoc_types.erl +++ b/lib/edoc/src/edoc_types.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @private %% @copyright 2001-2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> @@ -34,13 +32,13 @@ %% @headerfile "edoc_types.hrl" -include("edoc_types.hrl"). --include("xmerl.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). is_predefined(any, 0) -> true; is_predefined(atom, 0) -> true; is_predefined(binary, 0) -> true; -is_predefined(bool, 0) -> true; +is_predefined(bool, 0) -> true; % kept for backwards compatibility is_predefined(char, 0) -> true; is_predefined(cons, 2) -> true; is_predefined(deep_string, 0) -> true; diff --git a/lib/edoc/src/edoc_wiki.erl b/lib/edoc/src/edoc_wiki.erl index ba33198787..2f2d14853c 100644 --- a/lib/edoc/src/edoc_wiki.erl +++ b/lib/edoc/src/edoc_wiki.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @private %% @copyright 2001-2003 Richard Carlsson %% @author Richard Carlsson <[email protected]> @@ -70,7 +68,7 @@ -export([parse_xml/2, expand_text/2]). -include("edoc.hrl"). --include("xmerl.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). -define(BASE_HEADING, 3). @@ -82,8 +80,8 @@ parse_xml(Data, Line) -> parse_xml_1(Text, Line) -> Text1 = "<doc>" ++ Text ++ "</doc>", - Options = [{line, Line}, {encoding, "iso-8859-1"}], - case catch {ok, xmerl_scan:string(Text1, Options)} of + Opts = [{line, Line}, {encoding, 'iso-8859-1'}], + case catch {ok, xmerl_scan:string(Text1, Opts)} of {ok, {E, _}} -> E#xmlElement.content; {'EXIT', {fatal, {Reason, L, _C}}} -> diff --git a/lib/edoc/src/otpsgml_layout.erl b/lib/edoc/src/otpsgml_layout.erl index 45f74b299e..d425dc0ed8 100644 --- a/lib/edoc/src/otpsgml_layout.erl +++ b/lib/edoc/src/otpsgml_layout.erl @@ -14,8 +14,6 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @author Richard Carlsson <[email protected]> %% @author Kenneth Lundin <[email protected]> %% @copyright 2001-2004 Richard Carlsson @@ -34,7 +32,7 @@ -import(edoc_report, [report/2]). --include("xmerl.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). -define(SGML_EXPORT, xmerl_otpsgml). -define(DEFAULT_XML_EXPORT, ?SGML_EXPORT). diff --git a/lib/edoc/test/edoc_SUITE.erl b/lib/edoc/test/edoc_SUITE.erl index 0d57591e3e..5b95c35756 100644 --- a/lib/edoc/test/edoc_SUITE.erl +++ b/lib/edoc/test/edoc_SUITE.erl @@ -13,8 +13,6 @@ %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% -%% $Id$ -%% -module(edoc_SUITE). -include_lib("test_server/include/test_server.hrl"). diff --git a/lib/erl_interface/src/connect/ei_resolve.c b/lib/erl_interface/src/connect/ei_resolve.c index 50c5a4161d..ba8f8fbce3 100644 --- a/lib/erl_interface/src/connect/ei_resolve.c +++ b/lib/erl_interface/src/connect/ei_resolve.c @@ -185,7 +185,12 @@ static int verify_dns_configuration(void) * align: increment buf until it is dword-aligned, reduce len by same amount. * advance: increment buf by n bytes, reduce len by same amount . */ -#define align_buf(buf,len) for (;(((unsigned)buf)&0x3); (buf)++, len--) +#if defined SIZEOF_VOID_P +#define ALIGNBYTES (SIZEOF_VOID_P - 1) +#else +#define ALIGNBYTES (sizeof(void*) - 1) +#endif +#define align_buf(buf,len) for (;(((unsigned)buf) & ALIGNBYTES); (buf)++, len--) #define advance_buf(buf,len,n) ((buf)+=(n),(len)-=(n)) /* "and now the tricky part..." */ diff --git a/lib/et/src/et_wx_viewer.erl b/lib/et/src/et_wx_viewer.erl index 7d4286ed9d..386f8fc86b 100644 --- a/lib/et/src/et_wx_viewer.erl +++ b/lib/et/src/et_wx_viewer.erl @@ -257,10 +257,10 @@ parse_opt([H | T], S, CollectorOpt) -> Actors = [create_actor(Name) || Name <- ActorNames2], parse_opt(T, S#state{actors = Actors}, CollectorOpt); {include, ActorNames} when is_list(ActorNames) -> - Actors = [opt_create_actor(Name, include, S#state.actors) || Name <- ActorNames], + Actors = [opt_create_actor(Name, include, S) || Name <- ActorNames], parse_opt(T, S#state{actors = Actors}, CollectorOpt); {exclude, ActorNames} when is_list(ActorNames) -> - Actors = [opt_create_actor(Name, exclude, S#state.actors) || Name <- ActorNames], + Actors = [opt_create_actor(Name, exclude, S) || Name <- ActorNames], parse_opt(T, S#state{actors = Actors}, CollectorOpt); {first_event, _FirstKey} -> %% NYI diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc index be05a13fba..2583f0be25 100644 --- a/lib/eunit/doc/overview.edoc +++ b/lib/eunit/doc/overview.edoc @@ -913,7 +913,7 @@ To make the descriptions simpler, we first list some definitions: <td>`CleanupX'</td><td>`(X::any(), R::any()) -> any()'</td> </tr> <tr> -<td>`Instantiator'</td><td>`((R::any()) -> Tests | {with, [AbstractTestFun::((any()) -> any())]}'</td> +<td>`Instantiator'</td><td>`((R::any()) -> Tests) | {with, [AbstractTestFun::((any()) -> any())]}'</td> </tr> <tr> <td>`Where'</td><td>`local | spawn | {spawn, Node::atom()}'</td> diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index 82ba982f03..493ba60a2d 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -39,6 +39,7 @@ -ifndef(EUNIT_HRL). -define(EUNIT_HRL, true). + %% allow defining TEST to override NOTEST -ifdef(TEST). -undef(NOTEST). @@ -164,7 +165,7 @@ %% This is mostly a convenience which gives more detailed reports. %% Note: Guard is a guarded pattern, and can not be used for value. -ifdef(NOASSERT). --define(assertMatch(Guard,Expr),ok). +-define(assertMatch(Guard, Expr), ok). -else. -define(assertMatch(Guard, Expr), ((fun () -> @@ -174,17 +175,37 @@ [{module, ?MODULE}, {line, ?LINE}, {expression, (??Expr)}, - {expected, (??Guard)}, + {pattern, (??Guard)}, {value, __V}]}) end end)())). -endif. -define(_assertMatch(Guard, Expr), ?_test(?assertMatch(Guard, Expr))). +%% This is the inverse case of assertMatch, for convenience. +-ifdef(NOASSERT). +-define(assertNotMatch(Guard, Expr), ok). +-else. +-define(assertNotMatch(Guard, Expr), + ((fun () -> + __V = (Expr), + case __V of + Guard -> .erlang:error({assertNotMatch_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {pattern, (??Guard)}, + {value, __V}]}); + _ -> ok + end + end)())). +-endif. +-define(_assertNotMatch(Guard, Expr), ?_test(?assertNotMatch(Guard, Expr))). + %% This is a convenience macro which gives more detailed reports when %% the expected LHS value is not a pattern, but a computed value -ifdef(NOASSERT). --define(assertEqual(Expect,Expr),ok). +-define(assertEqual(Expect, Expr), ok). -else. -define(assertEqual(Expect, Expr), ((fun (__X) -> @@ -201,9 +222,29 @@ -endif. -define(_assertEqual(Expect, Expr), ?_test(?assertEqual(Expect, Expr))). +%% This is the inverse case of assertEqual, for convenience. +-ifdef(NOASSERT). +-define(assertNotEqual(Unexpected, Expr), ok). +-else. +-define(assertNotEqual(Unexpected, Expr), + ((fun (__X) -> + case (Expr) of + __X -> .erlang:error({assertNotEqual_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {value, __X}]}); + _ -> ok + end + end)(Unexpected))). +-endif. +-define(_assertNotEqual(Unexpected, Expr), + ?_test(?assertNotEqual(Unexpected, Expr))). + %% Note: Class and Term are patterns, and can not be used for value. +%% Term can be a guarded pattern, but Class cannot. -ifdef(NOASSERT). --define(assertException(Class, Term, Expr),ok). +-define(assertException(Class, Term, Expr), ok). -else. -define(assertException(Class, Term, Expr), ((fun () -> @@ -212,7 +253,7 @@ [{module, ?MODULE}, {line, ?LINE}, {expression, (??Expr)}, - {expected, + {pattern, "{ "++(??Class)++" , "++(??Term) ++" , [...] }"}, {unexpected_success, __V}]}) @@ -223,7 +264,7 @@ [{module, ?MODULE}, {line, ?LINE}, {expression, (??Expr)}, - {expected, + {pattern, "{ "++(??Class)++" , "++(??Term) ++" , [...] }"}, {unexpected_exception, @@ -243,6 +284,43 @@ -define(_assertExit(Term, Expr), ?_assertException(exit, Term, Expr)). -define(_assertThrow(Term, Expr), ?_assertException(throw, Term, Expr)). +%% This is the inverse case of assertException, for convenience. +%% Note: Class and Term are patterns, and can not be used for value. +%% Both Class and Term can be guarded patterns. +-ifdef(NOASSERT). +-define(assertNotException(Class, Term, Expr), ok). +-else. +-define(assertNotException(Class, Term, Expr), + ((fun () -> + try (Expr) of + _ -> ok + catch + __C:__T -> + case __C of + Class -> + case __T of + Term -> + .erlang:error({assertNotException_failed, + [{module, ?MODULE}, + {line, ?LINE}, + {expression, (??Expr)}, + {pattern, + "{ "++(??Class)++" , " + ++(??Term)++" , [...] }"}, + {unexpected_exception, + {__C, __T, + .erlang:get_stacktrace() + }}]}); + _ -> ok + end; + _ -> ok + end + end + end)())). +-endif. +-define(_assertNotException(Class, Term, Expr), + ?_test(?assertNotException(Class, Term, Expr))). + %% Macros for running operating system commands. (Note that these %% require EUnit to be present at runtime, or at least eunit_lib.) @@ -267,7 +345,7 @@ %% these are only used for testing; they always return 'ok' on success, %% and have no effect if debugging/testing is turned off -ifdef(NOASSERT). --define(assertCmdStatus(N, Cmd),ok). +-define(assertCmdStatus(N, Cmd), ok). -else. -define(assertCmdStatus(N, Cmd), ((fun () -> @@ -285,7 +363,7 @@ -define(assertCmd(Cmd), ?assertCmdStatus(0, Cmd)). -ifdef(NOASSERT). --define(assertCmdOutput(T, Cmd),ok). +-define(assertCmdOutput(T, Cmd), ok). -else. -define(assertCmdOutput(T, Cmd), ((fun () -> @@ -313,11 +391,12 @@ -define(debugHere, ok). -define(debugFmt(S, As), ok). -define(debugVal(E), (E)). --define(debugTime(S,E), (E)). +-define(debugTime(S, E), (E)). -else. -define(debugMsg(S), (begin - .io:fwrite(user, <<"~s:~w: ~s\n">>, [?FILE, ?LINE, S]), + .io:fwrite(user, <<"~s:~w:~w: ~s\n">>, + [?FILE, ?LINE, self(), S]), ok end)). -define(debugHere, (?debugMsg("<-"))). @@ -327,7 +406,7 @@ ?debugFmt(<<"~s = ~P">>, [(??E), __V, 15]), __V end)(E))). --define(debugTime(S,E), +-define(debugTime(S, E), ((fun () -> {__T0, _} = statistics(wall_clock), __V = (E), @@ -337,4 +416,5 @@ end)())). -endif. + -endif. % EUNIT_HRL diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src index 4fd76588c3..5e16dfa2ce 100644 --- a/lib/eunit/src/eunit.app.src +++ b/lib/eunit/src/eunit.app.src @@ -5,17 +5,17 @@ {vsn, "%VSN%"}, {modules, [eunit, eunit_autoexport, - eunit_striptests, - eunit_server, + eunit_data, + eunit_lib, + eunit_listener, eunit_proc, eunit_serial, + eunit_server, + eunit_striptests, + eunit_surefire, eunit_test, eunit_tests, - eunit_lib, - eunit_listener, - eunit_data, - eunit_tty, - eunit_surefire]}, + eunit_tty]}, {registered,[]}, - {applications, [stdlib]}, + {applications, [kernel,stdlib]}, {env, []}]}. diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl index da35c5c2ec..15fc3bdf32 100644 --- a/lib/eunit/src/eunit.erl +++ b/lib/eunit/src/eunit.erl @@ -16,7 +16,7 @@ %% $Id: eunit.erl 339 2009-04-05 14:10:47Z rcarlsson $ %% %% @copyright 2004-2009 Micka�l R�mond, Richard Carlsson -%% @author Mickaël Rémond <[email protected]> +%% @author Micka�l R�mond <[email protected]> %% [http://www.process-one.net/] %% @author Richard Carlsson <[email protected]> %% [http://user.it.uu.se/~richardc/] diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl index 0543b6c543..288dd74ddf 100644 --- a/lib/eunit/src/eunit_data.erl +++ b/lib/eunit/src/eunit_data.erl @@ -146,8 +146,10 @@ iter_next(I = #iter{next = [T | Ts]}) -> iter_prev(#iter{prev = []}) -> none; -iter_prev(#iter{prev = [T | Ts], next = Next, pos = Pos} = I) -> - {T, I#iter{prev = Ts, next = [T | Next], pos = Pos - 1}}. +iter_prev(#iter{prev = [T | Ts]} = I) -> + {T, I#iter{prev = Ts, + next = [T | I#iter.next], + pos = I#iter.pos - 1}}. %% --------------------------------------------------------------------- @@ -363,7 +365,8 @@ parse({file, F} = T) when is_list(F) -> parse({dir, D}=T) when is_list(D) -> case eunit_lib:is_string(D) of true -> - {data, {"directory \"" ++ D ++ "\"", get_directory_modules(D)}}; + {data, {"directory \"" ++ D ++ "\"", + get_directory_module_tests(D)}}; false -> bad_test(T) end; @@ -385,10 +388,10 @@ parse({S, T1} = T) when is_list(S) -> end; parse({S, T1}) when is_binary(S) -> group(#group{tests = T1, desc = S}); -parse(T) when tuple_size(T) > 2, is_list(element(1, T)) -> +parse(T) when is_tuple(T), size(T) > 2, is_list(element(1, T)) -> [S | Es] = tuple_to_list(T), parse({S, list_to_tuple(Es)}); -parse(T) when tuple_size(T) > 2, is_binary(element(1, T)) -> +parse(T) when is_tuple(T), size(T) > 2, is_binary(element(1, T)) -> [S | Es] = tuple_to_list(T), parse({S, list_to_tuple(Es)}); parse(M) when is_atom(M) -> @@ -596,7 +599,7 @@ testfuns(Es, M, TestSuffix, GeneratorSuffix) -> %% --------------------------------------------------------------------- -%% Getting a test set from a file +%% Getting a test set from a file (text file or object file) %% @throws {file_read_error, {Reason::atom(), Message::string(), %% fileName()}} @@ -625,17 +628,23 @@ get_file_tests(F) -> is_module_filename(F) -> filename:extension(F) =:= code:objfile_extension(). +objfile_test({M, File}) -> + {setup, + fun () -> + %% TODO: better error/stacktrace for this internal fun + code:purge(M), + {module,M} = code:load_abs(filename:rootname(File)), + ok + end, + {module, M}}; objfile_test(File) -> + objfile_test({objfile_module(File), File}). + +objfile_module(File) -> try - {module, M} = lists:keyfind(module, 1, beam_lib:info(File)), - {setup, - fun () -> - %% TODO: better error/stacktrace for this internal fun - code:purge(M), - {module,M} = code:load_abs(filename:rootname(File)), - ok - end, - {module, M}} + {value, {module, M}} = lists:keysearch(module, 1, + beam_lib:info(File)), + M catch _:_ -> throw({file_read_error, @@ -644,15 +653,34 @@ objfile_test(File) -> %% --------------------------------------------------------------------- -%% Getting a list of module names from object files in a directory - -%% @throws {file_read_error, {Reason::atom(), Message::string(), -%% fileName()}} +%% Getting a set of module tests from the object files in a directory + +%% @throws {file_read_error, +%% {Reason::atom(), Message::string(), fileName()}} + +get_directory_module_tests(D) -> + Ms = get_directory_modules(D), + %% for all 'm' in the set, remove 'm_tests' if present + F = fun ({M,_}, S) -> + Name = atom_to_list(M), + case lists:suffix(?DEFAULT_TESTMODULE_SUFFIX, Name) of + false -> + Name1 = Name ++ ?DEFAULT_TESTMODULE_SUFFIX, + M1 = list_to_atom(Name1), + dict:erase(M1, S); + true -> + S + end + end, + [objfile_test(Obj) + || Obj <- dict:to_list(lists:foldl(F, dict:from_list(Ms), Ms))]. %% TODO: handle packages (recursive search for files) - get_directory_modules(D) -> - [objfile_test(filename:join(D, F)) + [begin + F1 = filename:join(D, F), + {objfile_module(F1), F1} + end || F <- eunit_lib:list_dir(D), is_module_filename(F)]. diff --git a/lib/eunit/src/eunit_server.erl b/lib/eunit/src/eunit_server.erl index bf1bb9bcef..2cdfef2668 100644 --- a/lib/eunit/src/eunit_server.erl +++ b/lib/eunit/src/eunit_server.erl @@ -59,8 +59,9 @@ watch(Server, Module, Opts) when is_atom(Module) -> watch_path(Server, Path, Opts) -> command(Server, {watch, {path, filename:flatten(Path)}, Opts}). +%% note that the user must use $ at the end to match whole paths only watch_regexp(Server, Regex, Opts) -> - case regexp:parse(Regex) of + case re:compile(Regex,[anchored]) of {ok, R} -> command(Server, {watch, {regexp, R}, Opts}); {error, _}=Error -> @@ -278,8 +279,8 @@ is_watched(Path, St) -> match_any(sets:to_list(St#state.regexps), Path). match_any([R | Rs], Str) -> - case regexp:first_match(Str, R) of - {match, _, _} -> true; + case re:run(Str, R, [{capture,none}]) of + match -> true; _ -> match_any(Rs, Str) end; match_any([], _Str) -> false. diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl index dfb08c90b2..6e0a447105 100644 --- a/lib/eunit/src/eunit_surefire.erl +++ b/lib/eunit/src/eunit_surefire.erl @@ -15,7 +15,7 @@ %% %% $Id: $ %% -%% @author Mickaël Rémond <[email protected]> +%% @author Micka�l R�mond <[email protected]> %% @copyright 2009 Micka�l R�mond, Paul Guyot %% @see eunit %% @doc Surefire reports for EUnit (Format used by Maven and Atlassian @@ -64,6 +64,7 @@ }). -record(testsuite, { + id = 0 :: integer(), name = <<>> :: binary(), time = 0 :: integer(), output = <<>> :: binary(), @@ -76,7 +77,7 @@ -record(state, {verbose = false, indent = 0, xmldir = ".", - testsuite = #testsuite{} + testsuites = [] :: [#testsuite{}] }). start() -> @@ -89,55 +90,60 @@ init(Options) -> XMLDir = proplists:get_value(dir, Options, ?XMLDIR), St = #state{verbose = proplists:get_bool(verbose, Options), xmldir = XMLDir, - testsuite = #testsuite{}}, + testsuites = []}, receive {start, _Reference} -> St end. terminate({ok, _Data}, St) -> - TestSuite = St#state.testsuite, + TestSuites = St#state.testsuites, XmlDir = St#state.xmldir, - write_report(TestSuite, XmlDir), + write_reports(TestSuites, XmlDir), ok; terminate({error, _Reason}, _St) -> %% Don't report any errors here, since eunit_tty takes care of that. %% Just terminate. ok. -handle_begin(group, Data, St) -> +handle_begin(Kind, Data, St) when Kind == group; Kind == test -> + %% Run this code both for groups and tests; test is a bit + %% surprising: This is a workaround for the fact that we don't get + %% a group (handle_begin(group, ...) for testsuites (modules) + %% which only have one test case. In that case we get a test case + %% with an id comprised of just one integer - the group id. NewId = proplists:get_value(id, Data), case NewId of [] -> St; - [_GroupId] -> + [GroupId] -> Desc = proplists:get_value(desc, Data), - TestSuite = St#state.testsuite, - NewTestSuite = TestSuite#testsuite{name = Desc}, - St#state{testsuite=NewTestSuite}; + TestSuite = #testsuite{id = GroupId, name = Desc}, + St#state{testsuites=store_suite(TestSuite, St#state.testsuites)}; %% Surefire format is not hierarchic: Ignore subgroups: _ -> St - end; -handle_begin(test, _Data, St) -> - St. + end. handle_end(group, Data, St) -> %% Retrieve existing test suite: case proplists:get_value(id, Data) of [] -> St; - [_GroupId|_] -> - TestSuite = St#state.testsuite, + [GroupId|_] -> + TestSuites = St#state.testsuites, + TestSuite = lookup_suite_by_group_id(GroupId, TestSuites), %% Update TestSuite data: Time = proplists:get_value(time, Data), Output = proplists:get_value(output, Data), NewTestSuite = TestSuite#testsuite{ time = Time, output = Output }, - St#state{testsuite=NewTestSuite} + St#state{testsuites=store_suite(NewTestSuite, TestSuites)} end; handle_end(test, Data, St) -> %% Retrieve existing test suite: - TestSuite = St#state.testsuite, + [GroupId|_] = proplists:get_value(id, Data), + TestSuites = St#state.testsuites, + TestSuite = lookup_suite_by_group_id(GroupId, TestSuites), %% Create test case: Name = format_name(proplists:get_value(source, Data), @@ -149,7 +155,7 @@ handle_end(test, Data, St) -> TestCase = #testcase{name = Name, description = Desc, time = Time,output = Output}, NewTestSuite = add_testcase_to_testsuite(Result, TestCase, TestSuite), - St#state{testsuite=NewTestSuite}. + St#state{testsuites=store_suite(NewTestSuite, TestSuites)}. %% Cancel group does not give information on the individual cancelled test case %% We ignore this event @@ -157,7 +163,9 @@ handle_cancel(group, _Data, St) -> St; handle_cancel(test, Data, St) -> %% Retrieve existing test suite: - TestSuite = St#state.testsuite, + [GroupId|_] = proplists:get_value(id, Data), + TestSuites = St#state.testsuites, + TestSuite = lookup_suite_by_group_id(GroupId, TestSuites), %% Create test case: Name = format_name(proplists:get_value(source, Data), @@ -171,7 +179,7 @@ handle_cancel(test, Data, St) -> NewTestSuite = TestSuite#testsuite{ skipped = TestSuite#testsuite.skipped+1, testcases=[TestCase|TestSuite#testsuite.testcases] }, - St#state{testsuite=NewTestSuite}. + St#state{testsuites=store_suite(NewTestSuite, TestSuites)}. format_name({Module, Function, Arity}, Line) -> lists:flatten([atom_to_list(Module), ":", atom_to_list(Function), "/", @@ -183,6 +191,12 @@ format_desc(Desc) when is_binary(Desc) -> format_desc(Desc) when is_list(Desc) -> Desc. +lookup_suite_by_group_id(GroupId, TestSuites) -> + #testsuite{} = lists:keyfind(GroupId, #testsuite.id, TestSuites). + +store_suite(#testsuite{id=GroupId} = TestSuite, TestSuites) -> + lists:keystore(GroupId, #testsuite.id, TestSuites, TestSuite). + %% Add testcase to testsuite depending on the result of the test. add_testcase_to_testsuite(ok, TestCaseTmp, TestSuite) -> TestCase = TestCaseTmp#testcase{ result = ok }, @@ -214,6 +228,10 @@ add_testcase_to_testsuite({error, Exception}, TestCaseTmp, TestSuite) -> %% Write a report to the XML directory. %% This function opens the report file, calls write_report_to/2 and closes the file. %% ---------------------------------------------------------------------------- +write_reports(TestSuites, XmlDir) -> + lists:foreach(fun(TestSuite) -> write_report(TestSuite, XmlDir) end, + TestSuites). + write_report(#testsuite{name = Name} = TestSuite, XmlDir) -> Filename = filename:join(XmlDir, lists:flatten(["TEST-", escape_suitename(Name)], ".xml")), case file:open(Filename, [write, raw]) of diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl index d322c4b420..9ac1d1e7d9 100644 --- a/lib/eunit/src/eunit_test.erl +++ b/lib/eunit/src/eunit_test.erl @@ -131,12 +131,27 @@ macro_test_() -> [{module,_}, {line,_}, {expression,_}, - {expected,"[ _ ]"}, + {pattern,"[ _ ]"}, {value,[]}]}, _}} = run_testfun(F) end), ?_test(begin + {?LINE, F} = ?_assertNotMatch(ok, error), + {ok, ok} = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertNotMatch([_], [42]), + {error,{error,{assertNotMatch_failed, + [{module,_}, + {line,_}, + {expression,_}, + {pattern,"[ _ ]"}, + {value,[42]}]}, + _}} + = run_testfun(F) + end), + ?_test(begin {?LINE, F} = ?_assertEqual(ok, ok), {ok, ok} = run_testfun(F) end), @@ -152,6 +167,20 @@ macro_test_() -> = run_testfun(F) end), ?_test(begin + {?LINE, F} = ?_assertNotEqual(1, 0), + {ok, ok} = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertNotEqual(2, 1+1), + {error,{error,{assertNotEqual_failed, + [{module,_}, + {line,_}, + {expression,_}, + {value,2}]}, + _}} + = run_testfun(F) + end), + ?_test(begin {?LINE, F} = ?_assertException(error, badarith, erlang:error(badarith)), {ok, ok} = run_testfun(F) @@ -162,7 +191,7 @@ macro_test_() -> [{module,_}, {line,_}, {expression,_}, - {expected,_}, + {pattern,_}, {unexpected_success,ok}]}, _}} = run_testfun(F) @@ -174,15 +203,48 @@ macro_test_() -> [{module,_}, {line,_}, {expression,_}, - {expected,_}, + {pattern,_}, + {unexpected_exception, + {error,badarith,_}}]}, + _}} + = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertError(badarith, + erlang:error(badarith)), + {ok, ok} = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertExit(normal, exit(normal)), + {ok, ok} = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertThrow(foo, throw(foo)), + {ok, ok} = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertNotException(error, badarith, 42), + {ok, ok} = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertNotException(error, badarith, + erlang:error(badarg)), + {ok, ok} = run_testfun(F) + end), + ?_test(begin + {?LINE, F} = ?_assertNotException(error, badarith, + erlang:error(badarith)), + {error,{error,{assertNotException_failed, + [{module,_}, + {line,_}, + {expression,_}, + {pattern,_}, {unexpected_exception, {error,badarith,_}}]}, _}} = run_testfun(F) end) ]}. - -under_eunit_test() -> ?assert(?UNDER_EUNIT). -endif. diff --git a/lib/eunit/src/eunit_tests.erl b/lib/eunit/src/eunit_tests.erl index 37c0b4d6ae..a63d102d98 100644 --- a/lib/eunit/src/eunit_tests.erl +++ b/lib/eunit/src/eunit_tests.erl @@ -26,17 +26,17 @@ -include("eunit.hrl"). -ifdef(TEST). -%% Cause all the other modules to be tested as well as this one. -full_test_() -> - %%{application, eunit}. % this currently causes a loop - %% We use the below until loop detection is implemented - [eunit_autoexport, - eunit_striptests, - eunit_server, - eunit_proc, - eunit_serial, - eunit_test, - eunit_lib, - eunit_data, - eunit_tty]. +id(X) -> X. % for suppressing compiler warnings -endif. + +under_eunit_test() -> ?assert(?UNDER_EUNIT). + +let_test() -> ?assertEqual(42, ?LET(X, 17, X+25)). + +if_test_() -> + [?_assertEqual(17, ?IF(id(1) > 0, 17, 42)), + ?_assertEqual(42, ?IF(id(1) < 0, 17, 42))]. + +matches_test_() -> + [?_assert(?MATCHES("hel"++_, "hello")), + ?_assertNot(?MATCHES("hal"++_, "hello"))]. diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk index d7edd7977b..d933085bbc 100644 --- a/lib/eunit/vsn.mk +++ b/lib/eunit/vsn.mk @@ -1 +1 @@ -EUNIT_VSN = 2.1.7 +EUNIT_VSN = 2.2.0 diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index f1be658054..82e3675938 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -672,6 +672,9 @@ type(erlang, call_on_load_function, 1, Xs) -> type(erlang, cancel_timer, 1, Xs) -> strict(arg_types(erlang, cancel_timer, 1), Xs, fun (_) -> t_sup(t_integer(), t_atom('false')) end); +type(erlang, check_old_code, 1, Xs) -> + strict(arg_types(erlang, check_old_code, 1), Xs, + fun (_) -> t_boolean() end); type(erlang, check_process_code, 2, Xs) -> strict(arg_types(erlang, check_process_code, 2), Xs, fun (_) -> t_boolean() end); @@ -3395,6 +3398,8 @@ arg_types(erlang, call_on_load_function, 1) -> [t_atom()]; arg_types(erlang, cancel_timer, 1) -> [t_reference()]; +arg_types(erlang, check_old_code, 1) -> + [t_atom()]; arg_types(erlang, check_process_code, 2) -> [t_pid(), t_atom()]; arg_types(erlang, concat_binary, 1) -> diff --git a/lib/hipe/main/hipe.hrl.src b/lib/hipe/main/hipe.hrl.src index a1fbeda9cf..ec55c707ef 100644 --- a/lib/hipe/main/hipe.hrl.src +++ b/lib/hipe/main/hipe.hrl.src @@ -50,9 +50,8 @@ %% Flags: %% DEBUG - Turns on debugging. (Can be defined to a integer %% value to determine the level of debugging) -%% VERBOSE - More info is printed... %% HIPE_LOGGING - Turn on logging of messages with erl_logger. -%% DO_ASSERT - Turn on Assertions. +%% DO_ASSERT - Turn on assertions. %% TIMING - Turn on timing. %% HIPE_INSTRUMENT_COMPILER - Turn on instrumentation of the compiler. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -107,13 +106,9 @@ %% %% Define the exit macro %% --ifdef(VERBOSE). --define(EXIT(Reason), erlang:error({?MODULE,?LINE,Reason})). --else. -define(EXIT(Reason), ?msg("EXITED with reason ~w @~w:~w\n", [Reason,?MODULE,?LINE]), erlang:error({?MODULE,?LINE,Reason})). --endif. %% %% Assertions. diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml index 5f6c31069c..de519d5f84 100644 --- a/lib/ic/doc/src/notes.xml +++ b/lib/ic/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1998</year><year>2010</year> + <year>1998</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -31,6 +31,22 @@ </header> <section> + <title>IC 4.2.27</title> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p> + Reduced compile overhead (Thanks to Haitao Li).</p> + <p> + Own Id: OTP-9460 </p> + </item> + </list> + </section> + </section> + + <section> <title>IC 4.2.26</title> <section> diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl index db06118d32..8b53473caa 100644 --- a/lib/ic/src/ic_pp.erl +++ b/lib/ic/src/ic_pp.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 @@ -92,6 +92,14 @@ %% %%====================================================================================== +%% Multiple Include Optimization +%% +%% Algorithm described at: +%% http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html +-record(mio, {valid = true, %% multiple include valid + cmacro, %% controlling macro of the current conditional directive + depth = 0, %% conditional directive depth + included = []}). @@ -130,7 +138,7 @@ run(FileList, FileName, IncDir, Flags) -> %%---------------------------------------------------------- %% Run the second phase, i.e expand macros %%---------------------------------------------------------- - {Out, Err, War, _Defs, IfCou} = expand(File, FileName, IncDir, Flags), + {Out, Err, War, _Defs, _Mio, IfCou} = expand(File, FileName, IncDir, Flags), %%---------------------------------------------------------- %% Check if all #if #ifdef #ifndef have a matching #endif @@ -155,9 +163,9 @@ run(FileList, FileName, IncDir, Flags) -> %% The entry for all included files %% %% -%% Output {Out, Defs, Err, War} +%% Output {Out, Err, War, Defs, MultipleIncludeValid} %%====================================================================================== -run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir) -> +run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir, Mio) -> %%---------------------------------------------------------- %% Run the first phase, i.e tokenise the file @@ -169,18 +177,21 @@ run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir) %%---------------------------------------------------------- %% Run the second phase, i.e expand macros %%---------------------------------------------------------- - - %% Try first pass without file info start/end - {OutT, ErrT, WarT, DefsT, IfCouT} = - expand(File, Defs, Err, War, [FileName|IncFile], IncDir), - - {Out2, Err2, War2, Defs2, IfCou2} = - case only_nls(OutT) of - true -> %% The file is defined before - {["\n"], ErrT, WarT, DefsT, IfCouT}; - false -> %% The file is not defined before, try second pass - expand([FileInfoStart|File]++FileInfoEnd, Defs, Err, War, [FileName|IncFile], IncDir) - end, + {Out2, Err2, War2, Defs2, Mio2, IfCou2} = + expand([FileInfoStart|File]++FileInfoEnd, Defs, Err, War, + [FileName|IncFile], IncDir, + #mio{included=Mio#mio.included}), + + MergeIncluded = sets:to_list(sets:from_list(Mio#mio.included ++ Mio2#mio.included)), + + Mio3 = + case {Mio2#mio.valid, Mio2#mio.cmacro} of + {V, Macro} when V == false; + Macro == undefined -> + update_mio(Mio#mio{included=MergeIncluded}); + {true, _} -> + update_mio({include, FileName}, Mio#mio{included=MergeIncluded}) + end, %%---------------------------------------------------------- %% Check if all #if #ifdef #ifndef have a matching #endif @@ -192,26 +203,7 @@ run_include(FileName, FileList, _Out, Defs, Err, War, IncLine, IncFile, IncDir) [] end, - {Out2, Defs2, Err2++IfError, War2}. - - - -%% Return true if there is no data -%% other than new lines -only_nls([]) -> - true; -only_nls(["\n"|Rem]) -> - only_nls(Rem); -only_nls(["\r","\n"|Rem]) -> - only_nls(Rem); -only_nls([_|_Rem]) -> - false. - - - - - - + {Out2, Defs2, Err2++IfError, War2, Mio3}. @@ -647,87 +639,86 @@ expand(List, FileName, IncDir, Flags) -> %% Get all definitions from preprocessor commnads %% and merge them on top of the file collected. CLDefs = get_cmd_line_defs(Flags), - expand(List, [], [], CLDefs, [FileName], IncDir, check_all, [], [], 1, FileName). - -expand(List, Defs, Err, War, [FileName|IncFile], IncDir) -> - expand(List, [], [], Defs, [FileName|IncFile], IncDir, check_all, Err, War, 1, FileName). + expand(List, [], [], CLDefs, [FileName], IncDir, #mio{}, check_all, [], [], 1, FileName). +expand(List, Defs, Err, War, [FileName|IncFile], IncDir, Mio) -> + expand(List, [], [], Defs, [FileName|IncFile], IncDir, Mio, check_all, Err, War, 1, FileName). %%======================================================= %% Main loop for the expansion %%======================================================= -expand([], Out, _SelfRef, Defs, _IncFile, _IncDir, IfCou, Err, War, _L, _FN) -> +expand([], Out, _SelfRef, Defs, _IncFile, _IncDir, Mio, IfCou, Err, War, _L, _FN) -> % io:format("~n ===============~n"), % io:format(" definitions ~p~n",[lists:reverse(Defs)]), % io:format(" found warnings ~p~n",[lists:reverse(War)]), % io:format(" found errors ~p~n",[lists:reverse(Err)]), % io:format(" ===============~n~n~n"), - {Out, Err, War, Defs, IfCou}; + {Out, Err, War, Defs, Mio, IfCou}; -expand([{file_info, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, Str++Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([{file_info, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, Str++Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN); %%--------------------------------------- %% Searching for endif, %% i.e skip all source lines until matching %% end if is encountered %%--------------------------------------- -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) when Command == "ifdef" -> {_Removed, Rem2, _Nl} = read_to_nl(Rem), IfCou2 = {endif, Endif+1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L, FN); + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN); -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) when Command == "ifndef" -> {_Removed, Rem2, _Nl} = read_to_nl(Rem), IfCou2 = {endif, Endif+1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L, FN); + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN); -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) when Command == "if" -> - case pp_command(Command, Rem, Defs, IncDir, Err, War, L, FN) of + case pp_command(Command, Rem, Defs, IncDir, Mio, Err, War, L, FN) of {{'if', true}, Rem2, Err2, War2, Nl} -> IfCou2 = {endif, Endif+1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); %% {{'if', false}, Rem2, Err2, War2, Nl} -> Not implemented yet {{'if', error}, Rem2, Err2, War2, Nl} -> IfCou2 = {endif, Endif, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN) + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN) end; -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) when Command == "endif" -> {_Removed, Rem2, Nl} = read_to_nl(Rem), case Endif of 1 -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L+Nl, FN); _ -> IfCou2 = {endif, Endif-1, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L+Nl, FN) + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L+Nl, FN) end; -expand([{command,_Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> +expand([{command,_Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> {_Removed, Rem2, _Nl} = read_to_nl(Rem), IfCou2 = {endif, Endif, IfLine}, - expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, IfCou2, Err, War, L, FN); + expand(Rem2, Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err, War, L, FN); %% Solves a bug when spaces in front of hashmark ! -expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> - expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN); +expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> + expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN); -expand([{nl,_Nl} | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> - expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN); +expand([{nl,_Nl} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> + expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN); -expand([_X | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN) -> +expand([_X | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN) -> {_Removed, Rem2, Nl} = read_to_nl(Rem), - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, Err, War, L, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, {endif, Endif, IfLine}, Err, War, L, FN); @@ -736,121 +727,132 @@ expand([_X | Rem], Out, SelfRef, Defs, IncFile, IncDir, {endif, Endif, IfLine}, %%--------------------------------------- %% Check all tokens %%--------------------------------------- -expand([{nl, _N} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [$\n | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L+1, FN); +expand([{nl, _N} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, [$\n | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L+1, FN); -expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([space | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN); -expand([space_exp | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([space_exp | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, [?space | Out], SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN); -expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L, FN) -> - case pp_command(Command, Rem, Defs, IncDir, Err, War, L, FN) of +expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L, FN) -> + case pp_command(Command, Rem, Defs, IncDir, Mio, Err, War, L, FN) of {define, Rem2, Defs2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN); {undef, Rem2, Defs2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs2, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN); {{include, ok}, FileName, FileCont, Rem2, Nl, Err2, War2} -> - {Out3, Defs3, Err3, War3} = - run_include(FileName, FileCont, Out, Defs, Err2, War2, L+Nl, IncFile, IncDir), + {Out3, Defs3, Err3, War3, Mio2} = + run_include(FileName, FileCont, Out, Defs, Err2, War2, L+Nl, IncFile, IncDir, Mio), Nls = [], Out4 = Out3++Nls++Out, - expand(Rem2, Out4, SelfRef, Defs3, IncFile, IncDir, check_all, Err3, War3, L+Nl, FN); + expand(Rem2, Out4, SelfRef, Defs3, IncFile, IncDir, Mio2, check_all, Err3, War3, L+Nl, FN); {{include, error}, Rem2, Nl, Err2, War2} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err2, War2, L+Nl, FN); + + {{include, skip}, Rem2} -> + Out2 = [$\n|Out], + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+1, FN); {{ifdef, true}, Rem2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], + Out2 = lists:duplicate(Nl,$\n) ++ Out, IfCou2 = {endif, 1, L}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); {{ifdef, false}, Rem2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + Mio2 = update_mio(ifdef, Mio), + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); {{ifndef, true}, Rem2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], + Out2 = lists:duplicate(Nl,$\n) ++ Out, IfCou2 = {endif, 1, L}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); - {{ifndef, false}, Rem2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); + {{ifndef, false}, Macro, Rem2, Err2, War2, Nl} -> + Out2 = lists:duplicate(Nl,$\n) ++ Out, + Mio2 = update_mio({ifndef, Macro}, Mio), + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); {endif, Rem2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + Mio2 = update_mio(endif, Mio), + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); {{'if', true}, Rem2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], + Out2 = lists:duplicate(Nl,$\n) ++ Out, IfCou2 = {endif, 1, L}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, IfCou2, Err2, War2, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio, IfCou2, Err2, War2, L+Nl, FN); %% {{'if', false}, Removed, Rem2, Nl} -> Not implemented at present {{'if', error}, Rem2, Err2, War2, Nl} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err2, War2, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + Mio2 = update_mio('if', Mio), + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err2, War2, L+Nl, FN); {'else', {_Removed, Rem2, Nl}} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], + Out2 = lists:duplicate(Nl,$\n) ++ Out, Err2 = {FN, L, "`else' command is not implemented at present"}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + Mio2 = update_mio('else', Mio), + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN); {'elif', {_Removed, Rem2, Nl}} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], + Out2 = lists:duplicate(Nl,$\n) ++ Out, Err2 = {FN, L, "`elif' command is not implemented at present"}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + Mio2 = update_mio('elif', Mio), + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN); {warning, {WarningText, Rem2, Nl}} -> [FileName|_More] = IncFile, War2 = {FileName, L, "warning: #warning "++detokenise(WarningText)}, - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, [War2|War], L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, [War2|War], L+Nl, FN); {error, {ErrorText, Rem2, Nl}} -> [FileName|_More] = IncFile, Err2 = {FileName, L, detokenise(ErrorText)}, - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, [Err2|Err], War, L+Nl, FN); {{line, ok}, {_Removed, Rem2, Nl}, L2, FN2, LineText} -> Out2 = lists:duplicate(Nl,$\n)++LineText++Out, [_X|IF] = IncFile, IncFile2 = [FN2|IF], - expand(Rem2, Out2, SelfRef, Defs, IncFile2, IncDir, check_all, Err, War, L2, FN2); + expand(Rem2, Out2, SelfRef, Defs, IncFile2, IncDir, update_mio(Mio), check_all, Err, War, L2, FN2); {{line, error}, {_Removed, Rem2, Nl}, Err2} -> - Out2 = [lists:duplicate(Nl,$\n)|Out], - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN); + Out2 = lists:duplicate(Nl,$\n) ++ Out, + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, [Err2|Err], War, L+Nl, FN); hash_mark -> - expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L, FN); + expand(Rem, Out, SelfRef, Defs, IncFile, IncDir, Mio, check_all, Err, War, L, FN); {pragma, Rem2, Nl, Text} -> Out2 = lists:duplicate(Nl,$\n)++Text++Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+Nl, FN); {ident, Rem2, Nl, Text} -> Out2 = lists:duplicate(Nl,$\n)++Text++Out, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, update_mio(Mio), check_all, Err, War, L+Nl, FN); {not_recognised, {Removed, Rem2, Nl}} -> Text = lists:reverse([$#|Command]), RemovedS = lists:reverse([?space|detokenise(Removed)]), Out2 = [$\n|RemovedS]++Text++Out, + Mio2 = update_mio(Mio), case Command of [X|_T] when ?is_upper(X) -> - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN); [X|_T] when ?is_lower(X) -> - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN); [X|_T] when ?is_underline(X) -> - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, Err, War, L+Nl, FN); + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, Err, War, L+Nl, FN); _ -> Err2 = {FN, L, "invalid preprocessing directive name"}, - expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, check_all, [Err2|Err], War, L+Nl, FN) + expand(Rem2, Out2, SelfRef, Defs, IncFile, IncDir, Mio2, check_all, [Err2|Err], War, L+Nl, FN) end; Else -> @@ -859,19 +861,19 @@ expand([{command,Command} | Rem], Out, SelfRef, Defs, IncFile, IncDir, check_all end; -expand([{var, "__LINE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{var, "__LINE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> LL = io_lib:format("~p",[L]), - expand(Rem, [LL | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + expand(Rem, [LL | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{var, "__FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [$",FN,$" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([{var, "__FILE__"}|Rem], Out, SelfRef, Defs, IncFile, Mio, IncDir, IfCou, Err, War, L, FN) -> + expand(Rem, [$",FN,$" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{var, "__DATE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{var, "__DATE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> {{Y,M,D},{_H,_Mi,_S}} = calendar:universal_time(), Date = io_lib:format("\"~s ~p ~p\"",[month(M),D,Y]), - expand(Rem, [Date | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + expand(Rem, [Date | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{var, "__TIME__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{var, "__TIME__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> {{_Y,_M,_D},{H,Mi,S}} = calendar:universal_time(), HS = if H < 10 -> "0"++integer_to_list(H); true -> integer_to_list(H) @@ -883,40 +885,40 @@ expand([{var, "__TIME__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, true -> integer_to_list(S) end, Time = io_lib:format("\"~s:~s:~s\"",[HS,MiS,SS]), - expand(Rem, [Time | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + expand(Rem, [Time | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{var, "__INCLUDE_LEVEL__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{var, "__INCLUDE_LEVEL__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> IL = io_lib:format("~p",[length(IncFile)-1]), - expand(Rem, [IL | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + expand(Rem, [IL | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{var, "__BASE_FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{var, "__BASE_FILE__"}|Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> [BF|_T] = lists:reverse(IncFile), - expand(Rem, [$",BF,$" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + expand(Rem, [$",BF,$" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{var, Var} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{var, Var} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> {Out2, Err2, War2, Rem2, SelfRef2} = source_line(Var, Rem, SelfRef, Defs, Err, War, L, FN), - expand(Rem2, [Out2 | Out], SelfRef2, Defs, IncFile, IncDir, IfCou, Err2, War2, L, FN); + expand(Rem2, [Out2 | Out], SelfRef2, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err2, War2, L, FN); -expand([{char, Char} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [Char | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([{char, Char} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, [Char | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{number, Number} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [Number | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([{number, Number} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, [Number | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{expanded, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [Str | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([{expanded, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, [Str | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{self_ref, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{self_ref, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> SelfRef2 = lists:delete(Str,SelfRef), - expand(Rem, Out, SelfRef2, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); + expand(Rem, Out, SelfRef2, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{string, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> - expand(Rem, [$", Str, $" | Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN); +expand([{string, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> + expand(Rem, [$", Str, $" | Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L, FN); -expand([{string_part, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L, FN) -> +expand([{string_part, Str} | Rem], Out, SelfRef, Defs, IncFile, IncDir, Mio, IfCou, Err, War, L, FN) -> {Str2, Rem2, Nl} = expand_string_part([$"|Str], Rem), - expand(Rem2, [Str2| Out], SelfRef, Defs, IncFile, IncDir, IfCou, Err, War, L+Nl, FN). + expand(Rem2, [Str2| Out], SelfRef, Defs, IncFile, IncDir, update_mio(Mio), IfCou, Err, War, L+Nl, FN). @@ -954,13 +956,14 @@ expand_string_part([{string_part, Str_part} | Rem], Str, Nl) -> get_cmd_line_defs(Flags) -> Adjusted = parse_cmd_line(Flags,[]), - {_Out, _Err, _War, Defs, _IfCou} = + {_Out, _Err, _War, Defs, _IfCou, _Mio} = expand(tokenise(Adjusted,""), [], [], [], [], [], + #mio{}, check_all, [], [], @@ -1030,10 +1033,10 @@ collect_undefine([C|Rest],Found) -> %%====================================================================================== %%====================================================================================== -pp_command(Command, [space|File], Defs, IncDir, Err, War, L, FN) -> - pp_command(Command, File, Defs, IncDir, Err, War, L, FN); +pp_command(Command, [space|File], Defs, IncDir, Mio, Err, War, L, FN) -> + pp_command(Command, File, Defs, IncDir, Mio, Err, War, L, FN); -pp_command(Command, File, Defs, IncDir, Err, War, L, FN) -> +pp_command(Command, File, Defs, IncDir, Mio, Err, War, L, FN) -> case Command of %%---------------------------------------- @@ -1081,14 +1084,16 @@ pp_command(Command, File, Defs, IncDir, Err, War, L, FN) -> %% #include %%---------------------------------------- "include" -> - case include(File, IncDir) of - {error, Rem, Nl, Err2} -> - {{include, error}, Rem, Nl, [{FN, L, Err2}|Err], War}; - {error, Rem, Nl, Err2, NameNl} -> - {{include, error}, Rem, Nl, [{FN, L+ NameNl, Err2}|Err], War}; - {ok, FileName, FileCont, Rem, Nl} -> - {{include, ok}, FileName, FileCont, Rem, Nl, Err, War} - end; + case include(File, IncDir, Mio) of + {error, Rem, Nl, Err2} -> + {{include, error}, Rem, Nl, [{FN, L, Err2}|Err], War}; + {error, Rem, Nl, Err2, NameNl} -> + {{include, error}, Rem, Nl, [{FN, L+ NameNl, Err2}|Err], War}; + {ok, FileNamePath, FileCont, Rem, Nl} -> + {{include, ok}, FileNamePath, FileCont, Rem, Nl, Err, War}; + {skip, Rem} -> + {{include, skip}, Rem} + end; %%---------------------------------------- %% #ifdef @@ -1127,14 +1132,14 @@ pp_command(Command, File, Defs, IncDir, Err, War, L, FN) -> yes -> {{ifndef, true}, Rem, Err2, War2, Nl}; no -> - {{ifndef, false}, Rem, Err2, War2, Nl} + {{ifndef, false}, Name, Rem, Err2, War2, Nl} end; {ok, Rem, Name, No_of_para, _Parameters, _Macro, Err2, War2, Nl} -> case is_defined_before(Name, No_of_para, Defs) of yes -> {{ifndef, true}, Rem, Err2, War2, Nl}; no -> - {{ifndef, false}, Rem, Err2, War2, Nl} + {{ifndef, false}, Name, Rem, Err2, War2, Nl} end end; @@ -1408,29 +1413,32 @@ undef(_Rem) -> %%=============================================================== %%=============================================================== -include(File, IncDir) -> +include(File, IncDir, Mio) -> case include2(File) of - {ok, FileName, Rem, Nl, FileType} -> - %% The error handling is lite strange just to make it compatible to gcc - case {read_inc_file(FileName, IncDir), Nl, FileType} of - {{ok, FileList, FileNamePath}, _, _} -> - {ok, FileNamePath, FileList, Rem, Nl}; - {{error, Text}, _, own_file} -> - NameNl = count_nl(FileName,0), - Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), - {error, Rem, Nl, Error, NameNl}; - {{error, Text}, 1, sys_file} -> - NameNl = count_nl(FileName,0), - Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), - {error, Rem, Nl, Error, NameNl}; - {{error, _Text}, _, sys_file} -> - {error, Rem, Nl, "`#include' expects \"FILENAME\" or <FILENAME>"} - end; - - {error, {_Removed, Rem, Nl}} -> - {error, Rem, Nl, "`#include' expects \"FILENAME\" or <FILENAME>"} + {ok, FileName, Rem, Nl, FileType} -> + Result = read_inc_file(FileName, IncDir, Mio), + case {Result, Nl, FileType} of + {{ok, FileNamePath, FileCont}, _, _} -> + {ok, FileNamePath, FileCont, Rem, Nl}; + {skip, _, _} -> + {skip, Rem}; + {{error, Text}, _, own_file} -> + NameNl = count_nl(FileName,0), + Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), + {error, Rem, Nl, Error, NameNl}; + {{error, Text}, 1, sys_file} -> + NameNl = count_nl(FileName,0), + Error = lists:flatten(io_lib:format("~s: ~s",[FileName,Text])), + {error, Rem, Nl, Error, NameNl}; + {{error, _Text}, _, sys_file} -> + {error, Rem, Nl, "`#include' expects \"FILENAME\" or <FILENAME>"} + end; + {error, {_Removed, Rem, Nl}} -> + {error, Rem, Nl, "`#include' expects \#FILENAME\" or <FILENAME>"} end. + + count_nl([],Nl) -> Nl; count_nl([$\n|T],Nl) -> @@ -1909,18 +1917,37 @@ include_dir(Flags,IncDir) -> %% Read a included file. Try current dir first then the IncDir list %%=============================================================== -read_inc_file(FileName, IncDir) -> - case catch file:read_file(FileName) of - {ok, Bin} -> - FileList = binary_to_list(Bin), - {ok, FileList, FileName}; +read_inc_file(FileName, IncDir, Mio) -> + case find_inc_file(FileName, IncDir) of + {ok, AbsFile} -> + %% is included before? + case lists:member(FileName, Mio#mio.included) of + false -> + case catch file:read_file(AbsFile) of + {ok, Bin} -> + FileList = binary_to_list(Bin), + {ok, AbsFile, FileList}; + {error, Text} -> + {error, Text} + end; + true -> + skip + end; + {error, Text} -> + {error, Text} + end. + +find_inc_file(FileName, IncDir) -> + case catch file:read_file_info(FileName) of + {ok, _} -> + {ok, FileName}; {error, _} -> - read_inc_file2(FileName, IncDir) + find_inc_file2(FileName, IncDir) end. -read_inc_file2(_FileName, []) -> +find_inc_file2(_FileName, []) -> {error, "No such file or directory"}; -read_inc_file2(FileName, [D|Rem]) -> +find_inc_file2(FileName, [D|Rem]) -> Dir = case lists:last(D) of $/ -> D; @@ -1928,17 +1955,14 @@ read_inc_file2(FileName, [D|Rem]) -> D++"/" end, - case catch file:read_file(Dir++FileName) of - {ok, Bin} -> - FileList = binary_to_list(Bin), - {ok, FileList, Dir++FileName}; + case catch file:read_file_info(Dir++FileName) of + {ok, _} -> + {ok, Dir++FileName}; {error, _} -> - read_inc_file2(FileName, Rem) + find_inc_file2(FileName, Rem) end. - - %%=============================================================== %% Read parameters of a macro or a variable in a source line %%=============================================================== @@ -2135,5 +2159,73 @@ month(11) -> "Nov"; month(12) -> "Dec". +%% Multiple Include Optimization +%% +%% Algorithm described at: +%% http://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html +update_mio({include, FileName}, #mio{included=Inc}=Mio) -> + Mio#mio{valid=false, included=[FileName|Inc]}; + +%% valid=false & cmacro=undefined indicates it is already decided this file is +%% not subject to MIO +update_mio(_, #mio{valid=false, depth=0, cmacro=undefined}=Mio) -> + Mio; + +%% if valid=true, there is no non-whitespace tokens before this ifndef +update_mio({'ifndef', Macro}, #mio{valid=true, depth=0, cmacro=undefined}=Mio) -> + Mio#mio{valid=false, cmacro=Macro, depth=1}; + +%% detect any tokens before top level #ifndef +update_mio(_, #mio{valid=true, depth=0, cmacro=undefined}=Mio) -> + Mio#mio{valid=false}; + +%% If cmacro is alreay set, this is after the top level #endif +update_mio({'ifndef', _}, #mio{valid=true, depth=0}=Mio) -> + Mio#mio{valid=false, cmacro=undefined}; + +%% non-top level conditional, just update depth +update_mio({'ifndef', _}, #mio{depth=D}=Mio) when D > 0 -> + Mio#mio{depth=D+1}; +update_mio('ifdef', #mio{depth=D}=Mio) -> + Mio#mio{depth=D+1}; +update_mio('if', #mio{depth=D}=Mio) -> + Mio#mio{depth=D+1}; + +%% top level #else #elif invalidates multiple include optimization +update_mio('else', #mio{depth=1}=Mio) -> + Mio#mio{valid=false, cmacro=undefined}; +update_mio('else', Mio) -> + Mio; +update_mio('elif', #mio{depth=1}=Mio) -> + Mio#mio{valid=false, cmacro=undefined}; +update_mio('elif', Mio) -> + Mio; + +%% AT exit to top level, if the controlling macro is not set, this could be the +%% end of a non-ifndef conditional block, or there were tokens before entering +%% the #ifndef block. In either way, this invalidates the MIO +%% +%% It doesn't matter if `valid` is true at the time of exiting, it is set to +%% true. This will be used to detect if more tokens are following the top +%% level #endif. +update_mio('endif', #mio{depth=1, cmacro=undefined}=Mio) -> + Mio#mio{valid=false, depth=0}; +update_mio('endif', #mio{depth=1}=Mio) -> + Mio#mio{valid=true, depth=0}; +update_mio('endif', #mio{depth=D}=Mio) when D > 1 -> + Mio#mio{valid=true, depth=D-1}; + +%%if more tokens are following the top level #endif. +update_mio('endif', #mio{depth=1, cmacro=undefined}=Mio) -> + Mio#mio{valid=false, depth=0}; +update_mio('endif', #mio{depth=D}=Mio) when D > 0 -> + Mio#mio{valid=true, depth=D-1}; +update_mio(_, Mio) -> + Mio#mio{valid=false}. + +%% clear `valid`, this doesn't matter since #endif will restore it if +%% appropriate +update_mio(Mio) -> + Mio#mio{valid=false}. diff --git a/lib/ic/src/ic_pragma.erl b/lib/ic/src/ic_pragma.erl index 45cb64c9c8..7f2216b9dc 100644 --- a/lib/ic/src/ic_pragma.erl +++ b/lib/ic/src/ic_pragma.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 @@ -1600,9 +1600,8 @@ remove_inheriters(S,RS,InheriterList) -> [_OneOnly] -> ReducedInhList; _Other -> - EtsList = ets:tab2list(S), CleanList = - [X || X <- EtsList, element(1,X) == inherits], + ets:match(S, {inherits,'_','_'}), % CodeOptList = % [X || X <- EtsList, element(1,X) == codeopt], NoInheriters =remove_inheriters2(S,ReducedInhList,CleanList), @@ -1648,9 +1647,8 @@ remove_inh([X],[Y],List,EtsList) -> %%% from others in the list %%%---------------------------------------------- remove_inherited(S,InheriterList) -> - EtsList = ets:tab2list(S), CleanList = - [X || X <- EtsList, element(1,X) == inherits], + ets:match(S, {inherits, '_', '_'}), remove_inherited(S,InheriterList,CleanList). @@ -1694,11 +1692,8 @@ remove_inhed([X],[Y],List,EtsList) -> %% are inherited from scope in the list %%%---------------------------------------------- get_inherited(S,Scope,OpScopeList) -> - EtsList = ets:tab2list(S), - [[element(3,X)] || X <- EtsList, - element(1,X) == inherits, - element(2,X) == Scope, - member([element(3,X)],OpScopeList)]. + EtsList1 = ets:match(S, {inherits, Scope, '$1'}), + [X || X <- EtsList1, member(X, OpScopeList)]. @@ -1771,9 +1766,7 @@ inherits2(_X,Y,Z,EtsList) -> %% false otherwise %% is_inherited_by(Interface1,Interface2,PragmaTab) -> - FullList = ets:tab2list(PragmaTab), - InheritsList = - [X || X <- FullList, element(1,X) == inherits], + InheritsList = ets:match(PragmaTab, {inherits, '_', '_'}), inherits(Interface2,Interface1,InheritsList). diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk index 6d6c7fa625..6561ccd2a7 100644 --- a/lib/ic/vsn.mk +++ b/lib/ic/vsn.mk @@ -1 +1 @@ -IC_VSN = 4.2.26 +IC_VSN = 4.2.27 diff --git a/lib/inets/Makefile b/lib/inets/Makefile index ec05efa461..f4c2746b0a 100644 --- a/lib/inets/Makefile +++ b/lib/inets/Makefile @@ -36,6 +36,8 @@ SPECIAL_TARGETS = # ---------------------------------------------------- include $(ERL_TOP)/make/otp_subdir.mk +.PHONY: info gclean + info: @echo "OS: $(OS)" @echo "DOCB: $(DOCB)" @@ -44,3 +46,5 @@ info: @echo "APP_VSN: $(APP_VSN)" @echo "" +gclean: + git clean -fXd diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index f6b6827e93..d1671ac9bd 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -144,7 +144,7 @@ filename() = string() <v>Result = {status_line(), headers(), Body} | {status_code(), Body} | request_id() </v> <v>Body = string() | binary()</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> <v>Reason = term() </v> </type> <desc> @@ -194,16 +194,16 @@ filename() = string() <v>Result = {status_line(), headers(), Body} | {status_code(), Body} | request_id() </v> <v>Body = string() | binary()</v> - <v>Profile = profile() </v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> <v>Reason = {connect_failed, term()} | {send_failed, term()} | term() </v> </type> <desc> <p>Sends a HTTP-request. The function can be both synchronous - and asynchronous. In the later case the function will return - <c>{ok, RequestId}</c> and later on the information will be delivered - to the <c>receiver</c> depending on that value. </p> + and asynchronous. In the later case the function will return + <c>{ok, RequestId}</c> and later on the information will be delivered + to the <c>receiver</c> depending on that value. </p> <p>Http option (<c>http_option()</c>) details: </p> <taglist> @@ -211,7 +211,7 @@ filename() = string() <item> <p>Timeout time for the request. </p> <p>The clock starts ticking as soon as the request has been - sent. </p> + sent. </p> <p>Time is in milliseconds. </p> <p>Defaults to <c>infinity</c>. </p> </item> @@ -219,7 +219,7 @@ filename() = string() <tag><c><![CDATA[connect_timeout]]></c></tag> <item> <p>Connection timeout time, used during the initial request, - when the client is <em>connecting</em> to the server. </p> + when the client is <em>connecting</em> to the server. </p> <p>Time is in milliseconds. </p> <p>Defaults to the value of the <c>timeout</c> option. </p> </item> @@ -227,60 +227,61 @@ filename() = string() <tag><c><![CDATA[ssl]]></c></tag> <item> <p>This is the default ssl config option, currently defaults to - <c>essl</c>, see below. </p> + <c>essl</c>, see below. </p> <p>Defaults to <c>[]</c>. </p> </item> <tag><c><![CDATA[ossl]]></c></tag> <item> <p>If using the OpenSSL based (old) implementation of SSL, - these SSL-specific options are used. </p> + these SSL-specific options are used. </p> <p>Defaults to <c>[]</c>. </p> </item> <tag><c><![CDATA[essl]]></c></tag> <item> <p>If using the Erlang based (new) implementation of SSL, - these SSL-specific options are used. </p> + these SSL-specific options are used. </p> <p>Defaults to <c>[]</c>. </p> </item> <tag><c><![CDATA[autoredirect]]></c></tag> <item> - <p>Should the client automatically retrieve the information - from the new URI and return that as the result instead - of a 30X-result code. </p> - <p>Note that for some 30X-result codes automatic redirect - is not allowed. In these cases the 30X-result will always - be returned. </p> - <p>Defaults to <c>true</c>. </p> + <p>Should the client automatically retrieve the information + from the new URI and return that as the result instead + of a 30X-result code. </p> + <p>Note that for some 30X-result codes automatic redirect + is not allowed. In these cases the 30X-result will always + be returned. </p> + <p>Defaults to <c>true</c>. </p> </item> <tag><c><![CDATA[proxy_auth]]></c></tag> <item> <p>A proxy-authorization header using the provided user name and - password will be added to the request. </p> + password will be added to the request. </p> </item> <tag><c><![CDATA[version]]></c></tag> <item> <p>Can be used to make the client act as an <c>HTTP/1.0</c> or - <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c> - client. When using <c>HTTP/1.0</c> persistent connections will - not be used. </p> - <p>Defaults to the string <c>"HTTP/1.1"</c>. </p> + <c>HTTP/0.9</c> client. By default this is an <c>HTTP/1.1</c> + client. When using <c>HTTP/1.0</c> persistent connections will + not be used. </p> + <p>Defaults to the string <c>"HTTP/1.1"</c>. </p> </item> <tag><c><![CDATA[relaxed]]></c></tag> <item> - <p>If set to <c>true</c> workarounds for known server deviations from - the HTTP-standard are enabled. </p> + <p>If set to <c>true</c> workarounds for known server deviations + from the HTTP-standard are enabled. </p> <p>Defaults to <c>false</c>. </p> </item> <tag><c><![CDATA[url_encode]]></c></tag> <item> - <p>Will apply Percent-encoding, also known as URL encoding on the URL.</p> + <p>Will apply Percent-encoding, also known as URL encoding on the + URL.</p> <p>Defaults to <c>false</c>. </p> </item> </taglist> @@ -296,77 +297,77 @@ filename() = string() <tag><c><![CDATA[stream]]></c></tag> <item> <p>Streams the body of a 200 or 206 response to the calling - process or to a file. When streaming to the calling process - using the option <c>self</c> the following stream messages - will be sent to that process: <c>{http, {RequestId, - stream_start, Headers}, {http, {RequestId, stream, - BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When - streaming to to the calling processes using the option - <c>{self, once}</c> the first message will have an additional - element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>, - this is the process id that should be used as an argument to - <c>http:stream_next/1</c> to trigger the next message to be sent to - the calling process. </p> + process or to a file. When streaming to the calling process + using the option <c>self</c> the following stream messages + will be sent to that process: <c>{http, {RequestId, + stream_start, Headers}, {http, {RequestId, stream, + BinBodyPart}, {http, {RequestId, stream_end, Headers}</c>. When + streaming to to the calling processes using the option + <c>{self, once}</c> the first message will have an additional + element e.i. <c>{http, {RequestId, stream_start, Headers, Pid}</c>, + this is the process id that should be used as an argument to + <c>http:stream_next/1</c> to trigger the next message to be sent to + the calling process. </p> <p>Note that it is possible that chunked encoding will add - headers so that there are more headers in the <c>stream_end</c> - message than in the <c>stream_start</c>. - When streaming to a file and the request is asynchronous the - message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p> + headers so that there are more headers in the <c>stream_end</c> + message than in the <c>stream_start</c>. + When streaming to a file and the request is asynchronous the + message <c>{http, {RequestId, saved_to_file}}</c> will be sent. </p> <p>Defaults to <c>none</c>. </p> </item> <tag><c><![CDATA[body_format]]></c></tag> <item> <p>Defines if the body shall be delivered as a string or as a - binary. This option is only valid for the synchronous - request. </p> + binary. This option is only valid for the synchronous + request. </p> <p>Defaults to <c>string</c>. </p> </item> <tag><c><![CDATA[full_result]]></c></tag> <item> <p>Should a "full result" be returned to the caller (that is, - the body, the headers and the entire status-line) or not - (the body and the status code). </p> + the body, the headers and the entire status-line) or not + (the body and the status code). </p> <p>Defaults to <c>true</c>. </p> </item> <tag><c><![CDATA[header_as_is]]></c></tag> <item> <p>Shall the headers provided by the user be made - lower case or be regarded as case sensitive. </p> + lower case or be regarded as case sensitive. </p> <p>Note that the http standard requires them to be - case insenstive. This feature should only be used if there is - no other way to communicate with the server or for testing - purpose. Also note that when this option is used no headers - will be automatically added, all necessary headers have to be - provided by the user. </p> - <p>Defaults to <c>false</c>. </p> + case insenstive. This feature should only be used if there is + no other way to communicate with the server or for testing + purpose. Also note that when this option is used no headers + will be automatically added, all necessary headers have to be + provided by the user. </p> + <p>Defaults to <c>false</c>. </p> </item> <tag><c><![CDATA[socket_opts]]></c></tag> <item> <p>Socket options to be used for this and subsequent - request(s). </p> - <p>Overrides any value set by the - <seealso marker="#set_options">set_options</seealso> - function. </p> + request(s). </p> + <p>Overrides any value set by the + <seealso marker="#set_options">set_options</seealso> + function. </p> <p>Note that the validity of the options are <em>not</em> - checked in any way. </p> + checked in any way. </p> <p>Note that this may change the socket behaviour - (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>) - for an already existing one, and therefore an already connected - request handler. </p> + (see <seealso marker="kernel:inet#setopts/2">inet:setopts/2</seealso>) + for an already existing one, and therefore an already connected + request handler. </p> <p>By default the socket options set by the - <seealso marker="#set_options">set_options/1,2</seealso> - function are used when establishing a connection. </p> + <seealso marker="#set_options">set_options/1,2</seealso> + function are used when establishing a connection. </p> </item> <tag><c><![CDATA[receiver]]></c></tag> <item> <p>Defines how the client will deliver the result of an - asynchroneous request (<c>sync</c> has the value - <c>false</c>). </p> + asynchroneous request (<c>sync</c> has the value + <c>false</c>). </p> <taglist> <tag><c><![CDATA[pid()]]></c></tag> @@ -380,7 +381,7 @@ filename() = string() <tag><c><![CDATA[function/1]]></c></tag> <item> <p>Information will be delivered to the receiver via calls - to the provided fun: </p> + to the provided fun: </p> <pre> Receiver(ReplyInfo) </pre> @@ -389,7 +390,7 @@ Receiver(ReplyInfo) <tag><c><![CDATA[{Module, Funcion, Args}]]></c></tag> <item> <p>Information will be delivered to the receiver via calls - to the callback function: </p> + to the callback function: </p> <pre> apply(Module, Function, [ReplyInfo | Args]) </pre> @@ -410,7 +411,7 @@ apply(Module, Function, [ReplyInfo | Args]) </pre> <p>Defaults to the <c>pid()</c> of the process calling the request - function (<c>self()</c>). </p> + function (<c>self()</c>). </p> </item> </taglist> @@ -425,7 +426,7 @@ apply(Module, Function, [ReplyInfo | Args]) <type> <v>RequestId = request_id() - A unique identifier as returned by request/4</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> <p>Cancels an asynchronous HTTP-request. </p> @@ -514,11 +515,10 @@ apply(Module, Function, [ReplyInfo | Args]) This option is used to switch on (or off) different levels of erlang trace on the client. It is a debug feature.</d> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> - <p>Sets options to be used for subsequent - requests.</p> + <p>Sets options to be used for subsequent requests.</p> <note> <p>If possible the client will keep its connections alive and use persistent connections @@ -548,7 +548,7 @@ apply(Module, Function, [ReplyInfo | Args]) </type> <desc> <p>Triggers the next message to be streamed, e.i. - same behavior as active once for sockets.</p> + same behavior as active once for sockets. </p> <marker id="verify_cookies"></marker> <marker id="store_cookies"></marker> @@ -562,14 +562,14 @@ apply(Module, Function, [ReplyInfo | Args]) <type> <v>SetCookieHeaders = headers() - where field = "set-cookie"</v> <v>Url = url()</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> <p>Saves the cookies defined in SetCookieHeaders - in the client profile's cookie database. You need to - call this function if you have set the option <c>cookies</c> to <c>verify</c>. - If no profile is specified the default profile will be used. - </p> + in the client profile's cookie database. You need to + call this function if you have set the option <c>cookies</c> + to <c>verify</c>. + If no profile is specified the default profile will be used. </p> <marker id="cookie_header"></marker> </desc> @@ -582,13 +582,12 @@ apply(Module, Function, [ReplyInfo | Args]) making a request to Url using the profile <c>Profile</c>.</fsummary> <type> <v>Url = url()</v> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> <p>Returns the cookie header that would be sent - when making a request to <c>Url</c> using the profile <c>Profile</c>. - If no profile is specified the default profile will be used. - </p> + when making a request to <c>Url</c> using the profile <c>Profile</c>. + If no profile is specified the default profile will be used. </p> <marker id="reset_cookies"></marker> </desc> @@ -600,12 +599,12 @@ apply(Module, Function, [ReplyInfo | Args]) <name>reset_cookies(Profile) -> void()</name> <fsummary>Reset the cookie database.</fsummary> <type> - <v>Profile = profile()</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> - <p>Resets (clears) the cookie database for the specified <c>Profile</c>. - If no profile is specified the default profile will be used. - </p> + <p>Resets (clears) the cookie database for the specified + <c>Profile</c>. If no profile is specified the default profile + will be used. </p> </desc> </func> @@ -615,17 +614,16 @@ apply(Module, Function, [ReplyInfo | Args]) <name>which_cookies(Profile) -> cookies()</name> <fsummary>Dumps out the entire cookie database.</fsummary> <type> - <v>Profile = profile()</v> - <v>cookies() = [cooie_stores()]</v> - <v>cookie_stores() = {cookies, icookies()} | {session_cookies, icookies()}</v> - <v>icookies() = [icookie()]</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>cookies() = [cookie_stores()]</v> + <v>cookie_stores() = {cookies, cookies()} | {session_cookies, cookies()}</v> + <v>cookies() = [cookie()]</v> <v>cookie() = term()</v> </type> <desc> <p>This function produces a list of the entire cookie database. - It is intended for debugging/testing purposes. - If no profile is specified the default profile will be used. - </p> + It is intended for debugging/testing purposes. + If no profile is specified the default profile will be used. </p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 0926df8581..34f26bf45b 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -32,6 +32,63 @@ <file>notes.xml</file> </header> + <section><title>Inets 5.7</title> + + <section><title>Improvements and New Features</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[httpc|httpd] Added support for IPv6 with ssl. </p> + <p>Own Id: OTP-5566</p> + </item> + + </list> + + </section> + + <section><title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list> + <item> + <p>[httpc] Remove unnecessary usage of iolist_to_binary when + processing body (for PUT and POST). </p> + <p>Filipe David Manana</p> + <p>Own Id: OTP-9317</p> + </item> + + <item> + <p>[ftp] FTP client doesn't work with IPv6 host.</p> + <p>Attila Rajmund Nohl</p> + <p>Own Id: OTP-9342 Aux Id: seq11853</p> + </item> + + <item> + <p>[httpd] Peer/sockname resolv doesn't work with IPv6 addrs + in HTTP. </p> + <p>Attila Rajmund Nohl.</p> + <p>Own Id: OTP-9343</p> + </item> + + <item> + <p>[httpc] Clients started stand-alone not properly handled. + Also it was not documented how to use them, that is that + once started, they are represented by a <c>pid()</c> and not by + their <c>profile()</c>. </p> + <p>Own Id: OTP-9365</p> + </item> + + </list> + </section> + + </section> <!-- 5.7 --> + + <section><title>Inets 5.6</title> <section><title>Improvements and New Features</title> diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index fe6cb0c191..ac72963347 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1038,10 +1038,12 @@ handle_call({_, {open, ip_comm, Opts}}, From, State) -> Port = key_search(port, Opts, ?FTP_PORT), Timeout = key_search(timeout, Opts, ?CONNECTION_TIMEOUT), Progress = key_search(progress, Opts, ignore), + IpFamily = key_search(ipfamily, Opts, inet), State2 = State#state{client = From, mode = Mode, - progress = progress(Progress)}, + progress = progress(Progress), + ipfamily = IpFamily}, ?fcrd("handle_call(open) -> setup ctrl connection with", [{host, Host}, {port, Port}, {timeout, Timeout}]), diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index 6ffa5e8ba5..fe8e93af1f 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -64,17 +64,16 @@ default_profile() -> profile_name(?DEFAULT_PROFILE) -> httpc_manager; +profile_name(Profile) when is_pid(Profile) -> + Profile; profile_name(Profile) -> - profile_name("httpc_manager_", Profile). + Prefix = lists:flatten(io_lib:format("~w_", [?MODULE])), + profile_name(Prefix, Profile). profile_name(Prefix, Profile) when is_atom(Profile) -> list_to_atom(Prefix ++ atom_to_list(Profile)); -profile_name(Prefix, Profile) when is_pid(Profile) -> - ProfileStr0 = - string:strip(string:strip(erlang:pid_to_list(Profile), left, $<), right, $>), - F = fun($.) -> $_; (X) -> X end, - ProfileStr = [F(C) || C <- ProfileStr0], - list_to_atom(Prefix ++ "pid_" ++ ProfileStr). +profile_name(_Prefix, Profile) when is_pid(Profile) -> + Profile. %%-------------------------------------------------------------------------- @@ -115,9 +114,11 @@ request(Url, Profile) -> %% {keyfile, path()} | {password, string()} | {cacertfile, path()} | %% {ciphers, string()} %% Options - [Option] -%% Option - {sync, Boolean} | {body_format, BodyFormat} | -%% {full_result, Boolean} | {stream, To} | -%% {headers_as_is, Boolean} +%% Option - {sync, Boolean} | +%% {body_format, BodyFormat} | +%% {full_result, Boolean} | +%% {stream, To} | +%% {headers_as_is, Boolean} %% StatusLine = {HTTPVersion, StatusCode, ReasonPhrase}</v> %% HTTPVersion = string() %% StatusCode = integer() @@ -518,17 +519,15 @@ mk_chunkify_fun(ProcessBody) -> eof -> {ok, <<"0\r\n\r\n">>, eof_body}; {ok, Data, NewAcc} -> - {ok, mk_chunk_bin(Data), NewAcc} + Chunk = [ + integer_to_list(iolist_size(Data), 16), + "\r\n", + Data, + "\r\n"], + {ok, Chunk, NewAcc} end end. -mk_chunk_bin(Data) -> - Bin = iolist_to_binary(Data), - iolist_to_binary([hex_size(Bin), "\r\n", Bin, "\r\n"]). - -hex_size(Bin) -> - hd(io_lib:format("~.16B", [size(Bin)])). - handle_answer(RequestId, false, _) -> {ok, RequestId}; @@ -552,9 +551,7 @@ return_answer(Options, {{"HTTP/0.9",_,_}, _, BinBody}) -> {ok, Body}; return_answer(Options, {StatusLine, Headers, BinBody}) -> - Body = maybe_format_body(BinBody, Options), - case proplists:get_value(full_result, Options, true) of true -> {ok, {StatusLine, Headers, Body}}; diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index 1f0e012e7e..9ac9ee6f7b 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -515,7 +515,7 @@ handle_info({Proto, _Socket, Data}, {stop, normal, NewState} end, - ?hcri("data processed", []), + ?hcri("data processed", [{final_result, FinalResult}]), FinalResult; @@ -629,8 +629,9 @@ handle_info(timeout_queue, #state{timers = Timers} = State) -> Timers#timers{queue_timer = undefined}}}; %% Setting up the connection to the server somehow failed. -handle_info({init_error, _, ClientErrMsg}, +handle_info({init_error, Tag, ClientErrMsg}, State = #state{request = Request}) -> + ?hcrv("init error", [{tag, Tag}, {client_error, ClientErrMsg}]), NewState = answer_request(Request, ClientErrMsg, State), {stop, normal, NewState}; @@ -707,9 +708,9 @@ terminate(normal, %% And, just in case, close our side (**really** overkill) http_transport:close(SocketType, Socket); -terminate(Reason, #state{session = #session{id = Id, - socket = Socket, - socket_type = SocketType}, +terminate(Reason, #state{session = #session{id = Id, + socket = Socket, + socket_type = SocketType}, request = undefined, profile_name = ProfileName, timers = Timers, @@ -1403,7 +1404,7 @@ try_to_enable_pipeline_or_keep_alive( answer_request(#request{id = RequestId, from = From} = Request, Msg, #state{timers = Timers, profile_name = ProfileName} = State) -> - ?hcrt("answer request", [{request, Request}]), + ?hcrt("answer request", [{request, Request}, {msg, Msg}]), httpc_response:send(From, Msg), RequestTimers = Timers#timers.request_timers, TimerRef = diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 7f66b477eb..9015bf1ce2 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -52,7 +52,7 @@ cancel = [], % [{RequestId, HandlerPid, ClientPid}] handler_db, % ets() - Entry: #handler_info{} cookie_db, % cookie_db() - session_db, % ets() - Entry: #tcp_session{} + session_db, % ets() - Entry: #session{} profile_name, % atom() options = #options{} }). @@ -178,7 +178,7 @@ request_done(RequestId, ProfileName) -> %%-------------------------------------------------------------------- %% Function: insert_session(Session, ProfileName) -> _ -%% Session - #tcp_session{} +%% Session - #session{} %% ProfileName - atom() %% %% Description: Inserts session information into the httpc manager @@ -669,7 +669,7 @@ select_session(Method, HostPort, Scheme, SessionType, (SessionType =:= keep_alive) of true -> %% Look for handlers connecting to this host (HostPort) - %% tcp_session with record name field (tcp_session) and + %% session with record name field (session) and %% socket fields ignored. The fields id (part of: HostPort), %% client_close, scheme and type specified. %% The fields id (part of: HandlerPid) and queue_length diff --git a/lib/inets/src/http_lib/http_transport.erl b/lib/inets/src/http_lib/http_transport.erl index 01b51d531a..9b8190ebed 100644 --- a/lib/inets/src/http_lib/http_transport.erl +++ b/lib/inets/src/http_lib/http_transport.erl @@ -33,8 +33,8 @@ peername/2, sockname/2, resolve/0 ]). - -export([negotiate/3]). +-export([ipv4_name/1, ipv6_name/1]). -include_lib("inets/src/inets_app/inets_internal.hrl"). -include("http_internal.hrl"). @@ -142,8 +142,8 @@ connect({ossl, SslConfig}, {Host, Port}, _, Timeout) -> ERROR end; -connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> - Opts = [binary, {active, false}, {ssl_imp, new}] ++ SslConfig, +connect({essl, SslConfig}, {Host, Port}, Opts0, Timeout) -> + Opts = [binary, {active, false}, {ssl_imp, new} | Opts0] ++ SslConfig, ?hlrt("connect using essl", [{host, Host}, {port, Port}, @@ -176,8 +176,8 @@ connect({essl, SslConfig}, {Host, Port}, _, Timeout) -> listen(SocketType, Port) -> listen(SocketType, undefined, Port). -listen(ip_comm = SocketType, Addr, Port) -> - listen(SocketType, Addr, Port, undefined); +listen(ip_comm = _SocketType, Addr, Port) -> + listen_ip_comm(Addr, Port, undefined); %% Wrapper for backaward compatibillity listen({ssl, SSLConfig}, Addr, Port) -> @@ -187,35 +187,33 @@ listen({ssl, SSLConfig}, Addr, Port) -> {ssl_config, SSLConfig}]), listen({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Addr, Port); -listen({ossl, SSLConfig} = Ssl, Addr, Port) -> +listen({ossl, SSLConfig}, Addr, Port) -> ?hlrt("listen (ossl)", [{addr, Addr}, {port, Port}, {ssl_config, SSLConfig}]), - Opt = sock_opt(Ssl, Addr, SSLConfig), - ?hlrt("listen options", [{opt, Opt}]), - ssl:listen(Port, [{ssl_imp, old} | Opt]); + listen_ssl(Addr, Port, [{ssl_imp, old} | SSLConfig]); -listen({essl, SSLConfig} = Ssl, Addr, Port) -> +listen({essl, SSLConfig}, Addr, Port) -> ?hlrt("listen (essl)", [{addr, Addr}, {port, Port}, {ssl_config, SSLConfig}]), - Opt = sock_opt(Ssl, Addr, SSLConfig), - ?hlrt("listen options", [{opt, Opt}]), - Opt2 = [{ssl_imp, new}, {reuseaddr, true} | Opt], - ssl:listen(Port, Opt2). + listen_ssl(Addr, Port, [{ssl_imp, new}, {reuseaddr, true} | SSLConfig]). + listen(ip_comm, Addr, Port, Fd) -> - case (catch listen_ip_comm(Addr, Port, Fd)) of + listen_ip_comm(Addr, Port, Fd). + +listen_ip_comm(Addr, Port, Fd) -> + case (catch do_listen_ip_comm(Addr, Port, Fd)) of {'EXIT', Reason} -> {error, {exit, Reason}}; Else -> Else end. - -listen_ip_comm(Addr, Port, Fd) -> +do_listen_ip_comm(Addr, Port, Fd) -> {NewPort, Opts, IpFamily} = get_socket_info(Addr, Port, Fd), case IpFamily of inet6fb4 -> @@ -248,6 +246,41 @@ listen_ip_comm(Addr, Port, Fd) -> gen_tcp:listen(NewPort, Opts2) end. + +listen_ssl(Addr, Port, Opts0) -> + IpFamily = ipfamily_default(Addr, Port), + BaseOpts = [{backlog, 128}, {reuseaddr, true} | Opts0], + Opts = sock_opts(Addr, BaseOpts), + case IpFamily of + inet6fb4 -> + Opts2 = [inet6 | Opts], + ?hlrt("try ipv6 listen", [{opts, Opts2}]), + case (catch ssl:listen(Port, Opts2)) of + {error, Reason} when ((Reason =:= nxdomain) orelse + (Reason =:= eafnosupport)) -> + Opts3 = [inet | Opts], + ?hlrt("ipv6 listen failed - try ipv4 instead", + [{reason, Reason}, {opts, Opts3}]), + ssl:listen(Port, Opts3); + + {'EXIT', Reason} -> + Opts3 = [inet | Opts], + ?hlrt("ipv6 listen exit - try ipv4 instead", + [{reason, Reason}, {opts, Opts3}]), + ssl:listen(Port, Opts3); + + Other -> + ?hlrt("ipv6 listen done", [{other, Other}]), + Other + end; + + _ -> + Opts2 = [IpFamily | Opts], + ?hlrt("listen", [{opts, Opts2}]), + ssl:listen(Port, Opts2) + end. + + ipfamily_default(Addr, Port) -> httpd_conf:lookup(Addr, Port, ipfamily, inet6fb4). @@ -257,9 +290,9 @@ get_socket_info(Addr, Port, Fd0) -> %% The presence of a file descriptor takes precedence case get_fd(Port, Fd0, IpFamilyDefault) of {Fd, IpFamily} -> - {0, sock_opt(ip_comm, Addr, [{fd, Fd} | BaseOpts]), IpFamily}; + {0, sock_opts(Addr, [{fd, Fd} | BaseOpts]), IpFamily}; undefined -> - {Port, sock_opt(ip_comm, Addr, BaseOpts), IpFamilyDefault} + {Port, sock_opts(Addr, BaseOpts), IpFamilyDefault} end. get_fd(Port, undefined = _Fd, IpFamilyDefault) -> @@ -499,44 +532,28 @@ close({essl, _}, Socket) -> %% connection, usning either gen_tcp or ssl. %%------------------------------------------------------------------------- peername(ip_comm, Socket) -> - case inet:peername(Socket) of - {ok,{{A, B, C, D}, Port}} -> - PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, PeerName}; - {ok,{{A, B, C, D, E, F, G, H}, Port}} -> - PeerName = http_util:integer_to_hexlist(A) ++ ":"++ - http_util:integer_to_hexlist(B) ++ ":" ++ - http_util:integer_to_hexlist(C) ++ ":" ++ - http_util:integer_to_hexlist(D) ++ ":" ++ - http_util:integer_to_hexlist(E) ++ ":" ++ - http_util:integer_to_hexlist(F) ++ ":" ++ - http_util:integer_to_hexlist(G) ++":"++ - http_util:integer_to_hexlist(H), - {Port, PeerName}; - {error, _} -> - {-1, "unknown"} - end; + do_peername(inet:peername(Socket)); %% Wrapper for backaward compatibillity peername({ssl, SSLConfig}, Socket) -> peername({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); peername({ossl, _}, Socket) -> - peername_ssl(Socket); + do_peername(ssl:peername(Socket)); peername({essl, _}, Socket) -> - peername_ssl(Socket). - -peername_ssl(Socket) -> - case ssl:peername(Socket) of - {ok,{{A, B, C, D}, Port}} -> - PeerName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, PeerName}; - {error, _} -> - {-1, "unknown"} - end. + do_peername(ssl:peername(Socket)). + +do_peername({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + PeerName = ipv4_name(Addr), + {Port, PeerName}; +do_peername({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + PeerName = ipv6_name(Addr), + {Port, PeerName}; +do_peername({error, _}) -> + {-1, "unknown"}. %%------------------------------------------------------------------------- @@ -550,44 +567,28 @@ peername_ssl(Socket) -> %% other end of connection, using either gen_tcp or ssl. %%------------------------------------------------------------------------- sockname(ip_comm, Socket) -> - case inet:sockname(Socket) of - {ok,{{A, B, C, D}, Port}} -> - SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, SockName}; - {ok,{{A, B, C, D, E, F, G, H}, Port}} -> - SockName = http_util:integer_to_hexlist(A) ++ ":"++ - http_util:integer_to_hexlist(B) ++ ":" ++ - http_util:integer_to_hexlist(C) ++ ":" ++ - http_util:integer_to_hexlist(D) ++ ":" ++ - http_util:integer_to_hexlist(E) ++ ":" ++ - http_util:integer_to_hexlist(F) ++ ":" ++ - http_util:integer_to_hexlist(G) ++":"++ - http_util:integer_to_hexlist(H), - {Port, SockName}; - {error, _} -> - {-1, "unknown"} - end; + do_sockname(inet:sockname(Socket)); %% Wrapper for backaward compatibillity sockname({ssl, SSLConfig}, Socket) -> sockname({?HTTP_DEFAULT_SSL_KIND, SSLConfig}, Socket); sockname({ossl, _}, Socket) -> - sockname_ssl(Socket); + do_sockname(ssl:sockname(Socket)); sockname({essl, _}, Socket) -> - sockname_ssl(Socket). - -sockname_ssl(Socket) -> - case ssl:sockname(Socket) of - {ok,{{A, B, C, D}, Port}} -> - SockName = integer_to_list(A)++"."++integer_to_list(B)++"."++ - integer_to_list(C)++"."++integer_to_list(D), - {Port, SockName}; - {error, _} -> - {-1, "unknown"} - end. + do_sockname(ssl:sockname(Socket)). + +do_sockname({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 4) -> + SockName = ipv4_name(Addr), + {Port, SockName}; +do_sockname({ok, {Addr, Port}}) + when is_tuple(Addr) andalso (size(Addr) =:= 8) -> + SockName = ipv6_name(Addr), + {Port, SockName}; +do_sockname({error, _}) -> + {-1, "unknown"}. %%------------------------------------------------------------------------- @@ -601,29 +602,49 @@ resolve() -> Name. +%%------------------------------------------------------------------------- +%% ipv4_name(Ipv4Addr) -> string() +%% ipv6_name(Ipv6Addr) -> string() +%% Ipv4Addr = ip4_address() +%% Ipv6Addr = ip6_address() +%% +%% Description: Returns the local hostname. +%%------------------------------------------------------------------------- +ipv4_name({A, B, C, D}) -> + integer_to_list(A) ++ "." ++ + integer_to_list(B) ++ "." ++ + integer_to_list(C) ++ "." ++ + integer_to_list(D). + +ipv6_name({A, B, C, D, E, F, G, H}) -> + http_util:integer_to_hexlist(A) ++ ":"++ + http_util:integer_to_hexlist(B) ++ ":" ++ + http_util:integer_to_hexlist(C) ++ ":" ++ + http_util:integer_to_hexlist(D) ++ ":" ++ + http_util:integer_to_hexlist(E) ++ ":" ++ + http_util:integer_to_hexlist(F) ++ ":" ++ + http_util:integer_to_hexlist(G) ++ ":" ++ + http_util:integer_to_hexlist(H). + + %%%======================================================================== %%% Internal functions %%%======================================================================== +%% -- sock_opts -- %% Address any comes from directive: BindAddress "*" -sock_opt(ip_comm, any = Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]); -sock_opt(ip_comm, undefined, Opts) -> - sock_opt2(Opts); -sock_opt(_, any = _Addr, Opts) -> - sock_opt2(Opts); -sock_opt(_, undefined = _Addr, Opts) -> - sock_opt2(Opts); -sock_opt(_, {_,_,_,_} = Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]); -sock_opt(ip_comm, Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]); -sock_opt(_, Addr, Opts) -> - sock_opt2([{ip, Addr} | Opts]). - -sock_opt2(Opts) -> +sock_opts(undefined, Opts) -> + sock_opts(Opts); +sock_opts(any = Addr, Opts) -> + sock_opts([{ip, Addr} | Opts]); +sock_opts(Addr, Opts) -> + sock_opts([{ip, Addr} | Opts]). + +sock_opts(Opts) -> [{packet, 0}, {active, false} | Opts]. + +%% -- negotiate -- negotiate(ip_comm,_,_) -> ?hlrt("negotiate(ip_comm)", []), ok; diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 47f3fbba58..8b0fcb185d 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,6 +18,15 @@ {"%VSN%", [ + {"5.6", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {load_module, http_transport, soft_purge, soft_purge, [http_transport]}, + {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]}, + {update, ftp, soft, soft_purge, soft_purge, []} + ] + }, {"5.5.2", [ {restart_application, inets} @@ -40,6 +49,15 @@ } ], [ + {"5.6", + [ + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {load_module, http_transport, soft_purge, soft_purge, [http_transport]}, + {update, httpc_handler, soft, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [httpc_handler]}, + {update, ftp, soft, soft_purge, soft_purge, []} + ] + }, {"5.5.2", [ {restart_application, inets} diff --git a/lib/inets/test/ftp_suite_lib.erl b/lib/inets/test/ftp_suite_lib.erl index d0d07a8358..3ebd02229e 100644 --- a/lib/inets/test/ftp_suite_lib.erl +++ b/lib/inets/test/ftp_suite_lib.erl @@ -1129,10 +1129,16 @@ ticket_6035(Config) -> LogFile = filename:join([PrivDir,"ticket_6035.log"]), try begin + p("ticket_6035 -> select ftpd host"), Host = dirty_select_ftpd_host(Config), + p("ticket_6035 -> ftpd host selected (~p) => now spawn ftp owner", [Host]), Pid = spawn(?MODULE, open_wait_6035, [Host, self()]), + p("ticket_6035 -> waiter spawned: ~p => now open error logfile (~p)", + [Pid, LogFile]), error_logger:logfile({open, LogFile}), - ok = kill_ftp_proc_6035(Pid,LogFile), + p("ticket_6035 -> error logfile open => now kill waiter process"), + true = kill_ftp_proc_6035(Pid, LogFile), + p("ticket_6035 -> waiter process killed => now close error logfile"), error_logger:logfile(close), p("ticket_6035 -> done", []), ok @@ -1146,7 +1152,7 @@ kill_ftp_proc_6035(Pid, LogFile) -> p("kill_ftp_proc_6035 -> entry"), receive open -> - p("kill_ftp_proc_6035 -> received open: send shutdown"), + p("kill_ftp_proc_6035 -> received open => now issue shutdown"), exit(Pid, shutdown), kill_ftp_proc_6035(Pid, LogFile); {open_failed, Reason} -> @@ -1159,11 +1165,11 @@ kill_ftp_proc_6035(Pid, LogFile) -> is_error_report_6035(LogFile) end. -open_wait_6035(FtpServer, From) -> - p("open_wait_6035 -> try connect to ~s", [FtpServer]), +open_wait_6035({Tag, FtpServer}, From) -> + p("open_wait_6035 -> try connect to [~p] ~s for ~p", [Tag, FtpServer, From]), case ftp:open(FtpServer, [{timeout, timer:seconds(15)}]) of {ok, Pid} -> - p("open_wait_6035 -> connected, now login"), + p("open_wait_6035 -> connected (~p), now login", [Pid]), LoginResult = ftp:user(Pid,"anonymous","kldjf"), p("open_wait_6035 -> login result: ~p", [LoginResult]), From ! open, @@ -1191,22 +1197,27 @@ is_error_report_6035(LogFile) -> Res = case file:read_file(LogFile) of {ok, Bin} -> - p("is_error_report_6035 -> logfile read"), - read_log_6035(binary_to_list(Bin)); + Txt = binary_to_list(Bin), + p("is_error_report_6035 -> logfile read: ~n~p", [Txt]), + read_log_6035(Txt); _ -> - ok + false end, p("is_error_report_6035 -> logfile read result: " "~n ~p", [Res]), - file:delete(LogFile), + %% file:delete(LogFile), Res. read_log_6035("=ERROR REPORT===="++_Rest) -> - error_report; -read_log_6035([_H|T]) -> + p("read_log_6035 -> ERROR REPORT detected"), + true; +read_log_6035([H|T]) -> + p("read_log_6035 -> OTHER: " + "~p", [H]), read_log_6035(T); read_log_6035([]) -> - ok. + p("read_log_6035 -> done"), + false. %%-------------------------------------------------------------------- diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 1998bd3950..6edd5371af 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -64,16 +64,6 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [ - proxy_options, - proxy_head, - proxy_get, - proxy_trace, - proxy_post, - proxy_put, - proxy_delete, - proxy_auth, - proxy_headers, - proxy_emulate_lower_versions, http_options, http_head, http_get, @@ -88,15 +78,6 @@ all() -> http_headers, http_headers_dummy, http_bad_response, - ssl_head, - ossl_head, - essl_head, - ssl_get, - ossl_get, - essl_get, - ssl_trace, - ossl_trace, - essl_trace, http_redirect, http_redirect_loop, http_internal_server_error, @@ -106,21 +87,44 @@ all() -> http_emulate_lower_versions, http_relaxed, page_does_not_exist, - proxy_page_does_not_exist, - proxy_https_not_supported, - http_stream, - http_stream_once, - proxy_stream, parse_url, options, - ipv6, headers_as_is, + {group, proxy}, + {group, ssl}, + {group, stream}, + {group, ipv6}, {group, tickets}, initial_server_connect ]. groups() -> - [{tickets, [], [hexed_query_otp_6191, + [ + {proxy, [], [proxy_options, + proxy_head, + proxy_get, + proxy_trace, + proxy_post, + proxy_put, + proxy_delete, + proxy_auth, + proxy_headers, + proxy_emulate_lower_versions, + proxy_page_does_not_exist, + proxy_https_not_supported]}, + {ssl, [], [ssl_head, + ossl_head, + essl_head, + ssl_get, + ossl_get, + essl_get, + ssl_trace, + ossl_trace, + essl_trace]}, + {stream, [], [http_stream, + http_stream_once, + proxy_stream]}, + {tickets, [], [hexed_query_otp_6191, empty_body_otp_6243, empty_response_header_otp_6830, transfer_encoding_otp_6807, @@ -139,7 +143,10 @@ groups() -> {otp_8154, [], [otp_8154_1]}, {otp_8106, [], [otp_8106_pid, otp_8106_fun, - otp_8106_mfa]}]. + otp_8106_mfa]}, + {ipv6, [], [ipv6_ipcomm, ipv6_essl]} + ]. + init_per_group(_GroupName, Config) -> @@ -213,36 +220,38 @@ end_per_suite(Config) -> %% Note: This function is free to add any key/value pairs to the Config %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- + init_per_testcase(otp_8154_1 = Case, Config) -> init_per_testcase(Case, 5, Config); -init_per_testcase(initial_server_connect, Config) -> +init_per_testcase(initial_server_connect = Case, Config) -> %% Try to check if crypto actually exist or not, %% this test case does not work unless it does - case (catch crypto:start()) of - ok -> - application:start(public_key), - application:start(ssl), + try + begin + ensure_started(crypto), + ensure_started(public_key), + ensure_started(ssl), inets:start(), - Config; - _ -> - {skip,"Could not start crypto"} + Config + end + catch + throw:{error, {failed_starting, App, ActualError}} -> + tsp("init_per_testcase(~w) -> failed starting ~w: " + "~n ~p", [Case, App, ActualError]), + SkipString = + "Could not start " ++ atom_to_list(App), + {skip, SkipString}; + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + {skip, SkipString} end; init_per_testcase(Case, Config) -> init_per_testcase(Case, 2, Config). -init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> - tsp("init_per_testcase_ssl -> stop ssl"), - application:stop(ssl), - Config2 = lists:keydelete(local_ssl_server, 1, Config), - %% Will start inets - tsp("init_per_testcase_ssl -> try start http server (including inets)"), - Server = inets_test_lib:start_http_server( - filename:join(PrivDir, SslConfFile), Tag), - tsp("init_per_testcase -> Server: ~p", [Server]), - [{local_ssl_server, Server} | Config2]. - init_per_testcase(Case, Timeout, Config) -> io:format(user, "~n~n*** INIT ~w:~w[~w] ***~n~n", [?MODULE, Case, Timeout]), @@ -261,13 +270,16 @@ init_per_testcase(Case, Timeout, Config) -> NewConfig = case atom_to_list(Case) of [$s, $s, $l | _] -> - init_per_testcase_ssl(ssl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); + init_per_testcase_ssl(ssl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); [$o, $s, $s, $l | _] -> - init_per_testcase_ssl(ossl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); + init_per_testcase_ssl(ossl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); [$e, $s, $s, $l | _] -> - init_per_testcase_ssl(essl, PrivDir, SslConfFile, [{watchdog, Dog} | TmpConfig]); + init_per_testcase_ssl(essl, PrivDir, SslConfFile, + [{watchdog, Dog} | TmpConfig]); "proxy_" ++ Rest -> io:format("init_per_testcase -> Rest: ~p~n", [Rest]), @@ -275,16 +287,23 @@ init_per_testcase(Case, Timeout, Config) -> "https_not_supported" -> tsp("init_per_testcase -> [proxy case] start inets"), inets:start(), - tsp("init_per_testcase -> [proxy case] start ssl"), - application:start(crypto), - application:start(public_key), - case (catch application:start(ssl)) of + tsp("init_per_testcase -> " + "[proxy case] start crypto, public_key and ssl"), + try ensure_started([crypto, public_key, ssl]) of ok -> - [{watchdog, Dog} | TmpConfig]; - _ -> - [{skip, "SSL does not seem to be supported"} - | TmpConfig] + [{watchdog, Dog} | TmpConfig] + catch + throw:{error, {failed_starting, App, _}} -> + SkipString = + "Could not start " ++ atom_to_list(App), + {skip, SkipString}; + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + {skip, SkipString} end; + _ -> %% We use erlang.org for the proxy tests %% and after the switch to erlang-web, many @@ -321,6 +340,33 @@ init_per_testcase(Case, Timeout, Config) -> [{skip, "proxy not responding"} | TmpConfig] end end; + + "ipv6_" ++ _Rest -> + %% Ensure needed apps (crypto, public_key and ssl) started + try ensure_started([crypto, public_key, ssl]) of + ok -> + Profile = ipv6, + %% A stand-alone profile is represented by a pid() + {ok, ProfilePid} = + inets:start(httpc, + [{profile, Profile}, + {data_dir, PrivDir}], stand_alone), + httpc:set_options([{ipfamily, inet6}], ProfilePid), + tsp("httpc profile pid: ~p", [ProfilePid]), + [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig] + catch + throw:{error, {failed_starting, App, ActualError}} -> + tsp("init_per_testcase(~w) -> failed starting ~w: " + "~n ~p", [Case, App, ActualError]), + SkipString = + "Could not start " ++ atom_to_list(App), + {skip, SkipString}; + _:X -> + SkipString = + lists:flatten( + io_lib:format("Failed starting apps: ~p", [X])), + {skip, SkipString} + end; _ -> TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), Server = @@ -330,9 +376,7 @@ init_per_testcase(Case, Timeout, Config) -> [{watchdog, Dog}, {local_server, Server} | TmpConfig2] end, - %% httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, - %% ["localhost", ?IPV6_LOCAL_HOST]}}]), - + %% This will fail for the ipv6_ - cases (but that is ok) httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ["localhost", ?IPV6_LOCAL_HOST]}}, {ipfamily, inet6fb4}]), @@ -341,6 +385,19 @@ init_per_testcase(Case, Timeout, Config) -> NewConfig. +init_per_testcase_ssl(Tag, PrivDir, SslConfFile, Config) -> + tsp("init_per_testcase_ssl(~w) -> stop ssl", [Tag]), + application:stop(ssl), + Config2 = lists:keydelete(local_ssl_server, 1, Config), + %% Will start inets + tsp("init_per_testcase_ssl(~w) -> try start http server (including inets)", + [Tag]), + Server = inets_test_lib:start_http_server( + filename:join(PrivDir, SslConfFile), Tag), + tsp("init_per_testcase(~w) -> Server: ~p", [Tag, Server]), + [{local_ssl_server, Server} | Config2]. + + %%-------------------------------------------------------------------- %% Function: end_per_testcase(Case, Config) -> _ %% Case - atom() @@ -349,13 +406,36 @@ init_per_testcase(Case, Timeout, Config) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(http_save_to_file, Config) -> - PrivDir = ?config(priv_dir, Config), +end_per_testcase(http_save_to_file = Case, Config) -> + io:format(user, "~n~n*** END ~w:~w ***~n~n", + [?MODULE, Case]), + PrivDir = ?config(priv_dir, Config), FullPath = filename:join(PrivDir, "dummy.html"), file:delete(FullPath), finish(Config); -end_per_testcase(_, Config) -> +end_per_testcase(Case, Config) -> + io:format(user, "~n~n*** END ~w:~w ***~n~n", + [?MODULE, Case]), + case atom_to_list(Case) of + "ipv6_" ++ _Rest -> + tsp("end_per_testcase(~w) -> stop ssl", [Case]), + application:stop(ssl), + tsp("end_per_testcase(~w) -> stop public_key", [Case]), + application:stop(public_key), + tsp("end_per_testcase(~w) -> stop crypto", [Case]), + application:stop(crypto), + ProfilePid = ?config(profile, Config), + tsp("end_per_testcase(~w) -> stop httpc profile (~p)", + [Case, ProfilePid]), + unlink(ProfilePid), + inets:stop(stand_alone, ProfilePid), + tsp("end_per_testcase(~w) -> httpc profile (~p) stopped", + [Case, ProfilePid]), + ok; + _ -> + ok + end, finish(Config). finish(Config) -> @@ -364,6 +444,7 @@ finish(Config) -> undefined -> ok; _ -> + tsp("finish -> stop watchdog (~p)", [Dog]), test_server:timetrap_cancel(Dog) end. @@ -565,7 +646,7 @@ http_relaxed(suite) -> http_relaxed(Config) when is_list(Config) -> ok = httpc:set_options([{ipv6, disabled}]), % also test the old option %% ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_reason_phrase.html", @@ -591,7 +672,7 @@ http_dummy_pipe(suite) -> []; http_dummy_pipe(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/foobar.html", @@ -905,7 +986,7 @@ http_headers_dummy(suite) -> []; http_headers_dummy(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy_headers.html", @@ -970,7 +1051,7 @@ http_bad_response(suite) -> []; http_bad_response(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_crlf.html", @@ -1089,9 +1170,9 @@ ssl_get(SslTag, Config) when is_list(Config) -> httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []), inets_test_lib:check_body(Body); {ok, _} -> - {skip, "Failed to start local http-server"}; + {skip, "local http-server not started"}; _ -> - {skip, "Failed to start SSL"} + {skip, "SSL not started"} end. @@ -1149,9 +1230,9 @@ ssl_trace(SslTag, Config) when is_list(Config) -> tsf({failed, Error}) end; {ok, _} -> - {skip, "Failed to start local http-server"}; + {skip, "local http-server not started"}; _ -> - {skip, "Failed to start SSL"} + {skip, "SSL not started"} end. @@ -1170,7 +1251,7 @@ http_redirect(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), tsp("http_redirect -> start dummy server inet"), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), tsp("http_redirect -> server port = ~p", [Port]), URL300 = ?URL_START ++ integer_to_list(Port) ++ "/300.html", @@ -1282,7 +1363,7 @@ http_redirect_loop(suite) -> []; http_redirect_loop(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/redirectloop.html", @@ -1299,7 +1380,7 @@ http_internal_server_error(suite) -> []; http_internal_server_error(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL500 = ?URL_START ++ integer_to_list(Port) ++ "/500.html", @@ -1335,7 +1416,7 @@ http_userinfo(suite) -> http_userinfo(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URLAuth = "http://alladin:sesame@localhost:" ++ integer_to_list(Port) ++ "/userinfo.html", @@ -1361,7 +1442,7 @@ http_cookie(suite) -> []; http_cookie(Config) when is_list(Config) -> ok = httpc:set_options([{cookies, enabled}, {ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URLStart = ?URL_START ++ integer_to_list(Port), @@ -1735,7 +1816,7 @@ http_stream_once(Config) when is_list(Config) -> p("http_stream_once -> set ipfamily to inet", []), ok = httpc:set_options([{ipfamily, inet}]), p("http_stream_once -> start dummy server", []), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), PortStr = integer_to_list(Port), p("http_stream_once -> once", []), @@ -1871,28 +1952,79 @@ parse_url(Config) when is_list(Config) -> %%------------------------------------------------------------------------- -ipv6() -> - [{require,ipv6_hosts}]. -ipv6(doc) -> - ["Test ipv6."]; -ipv6(suite) -> - []; -ipv6(Config) when is_list(Config) -> - {ok, Hostname} = inet:gethostname(), - - case lists:member(list_to_atom(Hostname), - ct:get_config(ipv6_hosts)) of - true -> - {DummyServerPid, Port} = dummy_server(self(), ipv6), - - URL = "http://[" ++ ?IPV6_LOCAL_HOST ++ "]:" ++ + +ipv6_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_ipcomm(doc) -> + ["Test ip_comm ipv6."]; +ipv6_ipcomm(suite) -> + []; +ipv6_ipcomm(Config) when is_list(Config) -> + HTTPOptions = [], + SocketType = ip_comm, + Scheme = "http", + Extra = [], + ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). + + +%%------------------------------------------------------------------------- + +ipv6_essl() -> + [{require, ipv6_hosts}]. +ipv6_essl(doc) -> + ["Test essl ipv6."]; +ipv6_essl(suite) -> + []; +ipv6_essl(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + CertFile = filename:join(DataDir, "ssl_client_cert.pem"), + SSLOptions = [{certfile, CertFile}, {keyfile, CertFile}], + SSLConfig = {essl, SSLOptions}, + tsp("ssl_ipv6 -> make request using: " + "~n SSLOptions: ~p", [SSLOptions]), + HTTPOptions = [{ssl, SSLConfig}], + SocketType = essl, + Scheme = "https", + Extra = SSLOptions, + ipv6(SocketType, Scheme, HTTPOptions, Extra, Config). + + +%%------------------------------------------------------------------------- + +ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> + %% Check if we are a IPv6 host + tsp("ipv6 -> verify ipv6 support", []), + case inets_test_lib:has_ipv6_support(Config) of + {ok, Addr} -> + tsp("ipv6 -> ipv6 supported: ~p", [Addr]), + {DummyServerPid, Port} = dummy_server(SocketType, ipv6, Extra), + Profile = ?config(profile, Config), + URL = + Scheme ++ + "://[" ++ http_transport:ipv6_name(Addr) ++ "]:" ++ integer_to_list(Port) ++ "/foobar.html", - {ok, {{_,200,_}, [_ | _], [_|_]}} = - httpc:request(get, {URL, []}, [], []), - - DummyServerPid ! stop, + tsp("ipv6 -> issue request with: " + "~n URL: ~p" + "~n HTTPOptions: ~p", [URL, HTTPOptions]), + case httpc:request(get, {URL, []}, HTTPOptions, [], Profile) of + {ok, {{_,200,_}, [_ | _], [_|_]}} -> + tsp("ipv6 -> expected result"), + DummyServerPid ! stop, + ok; + {ok, Unexpected} -> + tsp("ipv6 -> unexpected result: " + "~n ~p", [Unexpected]), + DummyServerPid ! stop, + tsf({unexpected_result, Unexpected}); + {error, Reason} -> + tsp("ipv6 -> error: " + "~n Reason: ~p", [Reason]), + DummyServerPid ! stop, + tsf(Reason) + end, ok; - false -> + _ -> + tsp("ipv6 -> ipv6 not supported", []), {skip, "Host does not support IPv6"} end. @@ -1945,7 +2077,7 @@ http_invalid_http(suite) -> []; http_invalid_http(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/invalid_http.html", @@ -2002,7 +2134,7 @@ transfer_encoding_otp_6807(suite) -> []; transfer_encoding_otp_6807(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/capital_transfer_encoding.html", @@ -2035,7 +2167,7 @@ empty_response_header_otp_6830(suite) -> []; empty_response_header_otp_6830(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/no_headers.html", {ok, {{_,200,_}, [], [_ | _]}} = httpc:request(URL), @@ -2052,7 +2184,7 @@ no_content_204_otp_6982(suite) -> []; no_content_204_otp_6982(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/no_content.html", {ok, {{_,204,_}, [], []}} = httpc:request(URL), @@ -2070,7 +2202,7 @@ missing_CR_otp_7304(suite) -> []; missing_CR_otp_7304(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/missing_CR.html", {ok, {{_,200,_}, _, [_ | _]}} = httpc:request(URL), @@ -2089,7 +2221,7 @@ otp_7883_1(suite) -> otp_7883_1(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", {error, socket_closed_remotely} = httpc:request(URL), @@ -2105,7 +2237,7 @@ otp_7883_2(suite) -> otp_7883_2(Config) when is_list(Config) -> ok = httpc:set_options([{ipfamily, inet}]), - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/just_close.html", Method = get, @@ -2626,7 +2758,7 @@ otp_8371(suite) -> []; otp_8371(Config) when is_list(Config) -> ok = httpc:set_options([{ipv6, disabled}]), % also test the old option - {DummyServerPid, Port} = dummy_server(self(), ipv4), + {DummyServerPid, Port} = dummy_server(ipv4), URL = ?URL_START ++ integer_to_list(Port) ++ "/ensure_host_header_with_port.html", @@ -2864,75 +2996,179 @@ receive_streamed_body(RequestId, Body, Pid) -> test_server:fail(Msg) end. +%% Perform a synchronous stop +dummy_server_stop(Pid) -> + Pid ! {stop, self()}, + receive + {stopped, Pid} -> + ok + end. + +dummy_server(IpV) -> + dummy_server(self(), ip_comm, IpV, []). +dummy_server(SocketType, IpV, Extra) -> + dummy_server(self(), SocketType, IpV, Extra). -dummy_server(Caller, IpV) -> - Pid = spawn(httpc_SUITE, dummy_server_init, [Caller, IpV]), +dummy_server(Caller, SocketType, IpV, Extra) -> + Args = [Caller, SocketType, IpV, Extra], + Pid = spawn(httpc_SUITE, dummy_server_init, Args), receive {port, Port} -> {Pid, Port} end. -dummy_server_init(Caller, IpV) -> +dummy_server_init(Caller, ip_comm, IpV, _) -> + BaseOpts = [binary, {packet, 0}, {reuseaddr,true}, {active, false}], {ok, ListenSocket} = case IpV of ipv4 -> - gen_tcp:listen(0, [binary, inet, {packet, 0}, - {reuseaddr,true}, - {active, false}]); + tsp("ip_comm ipv4 listen", []), + gen_tcp:listen(0, [inet | BaseOpts]); ipv6 -> - gen_tcp:listen(0, [binary, inet6, {packet, 0}, - {reuseaddr,true}, - {active, false}]) + tsp("ip_comm ipv6 listen", []), + gen_tcp:listen(0, [inet6 | BaseOpts]) end, {ok, Port} = inet:port(ListenSocket), - tsp("dummy_server_init -> Port: ~p", [Port]), + tsp("dummy_server_init(ip_comm) -> Port: ~p", [Port]), Caller ! {port, Port}, - dummy_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, - [], ListenSocket). + dummy_ipcomm_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, + [], ListenSocket); +dummy_server_init(Caller, essl, IpV, SSLOptions) -> + BaseOpts = [{ssl_imp, new}, + {backlog, 128}, binary, {reuseaddr,true}, {active, false} | + SSLOptions], + dummy_ssl_server_init(Caller, BaseOpts, IpV); +dummy_server_init(Caller, ossl, IpV, SSLOptions) -> + BaseOpts = [{ssl_imp, old}, + {backlog, 128}, binary, {active, false} | SSLOptions], + dummy_ssl_server_init(Caller, BaseOpts, IpV). + +dummy_ssl_server_init(Caller, BaseOpts, IpV) -> + {ok, ListenSocket} = + case IpV of + ipv4 -> + tsp("dummy_ssl_server_init -> ssl ipv4 listen", []), + ssl:listen(0, [inet | BaseOpts]); + ipv6 -> + tsp("dummy_ssl_server_init -> ssl ipv6 listen", []), + ssl:listen(0, [inet6 | BaseOpts]) + end, + tsp("dummy_ssl_server_init -> ListenSocket: ~p", [ListenSocket]), + {ok, {_, Port}} = ssl:sockname(ListenSocket), + tsp("dummy_ssl_server_init -> Port: ~p", [Port]), + Caller ! {port, Port}, + dummy_ssl_server_loop({httpd_request, parse, [?HTTP_MAX_HEADER_SIZE]}, + [], ListenSocket). -dummy_server_loop(MFA, Handlers, ListenSocket) -> +dummy_ipcomm_server_loop(MFA, Handlers, ListenSocket) -> receive stop -> - lists:foreach(fun(Handler) -> Handler ! stop end, Handlers) + tsp("dummy_ipcomm_server_loop -> stop handlers", []), + lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); + {stop, From} -> + tsp("dummy_ipcomm_server_loop -> " + "stop command from ~p for handlers (~p)", [From, Handlers]), + Stopper = fun(Handler) -> Handler ! stop end, + lists:foreach(Stopper, Handlers), + From ! {stopped, self()} after 0 -> + tsp("dummy_ipcomm_server_loop -> await accept", []), {ok, Socket} = gen_tcp:accept(ListenSocket), + tsp("dummy_ipcomm_server_loop -> accepted: ~p", [Socket]), HandlerPid = dummy_request_handler(MFA, Socket), + tsp("dummy_icomm_server_loop -> handler created: ~p", [HandlerPid]), gen_tcp:controlling_process(Socket, HandlerPid), - HandlerPid ! controller, - dummy_server_loop(MFA, [HandlerPid | Handlers], + tsp("dummy_ipcomm_server_loop -> " + "control transfered to handler", []), + HandlerPid ! ipcomm_controller, + tsp("dummy_ipcomm_server_loop -> " + "handler informed about control transfer", []), + dummy_ipcomm_server_loop(MFA, [HandlerPid | Handlers], ListenSocket) end. +dummy_ssl_server_loop(MFA, Handlers, ListenSocket) -> + receive + stop -> + tsp("dummy_ssl_server_loop -> stop handlers", []), + lists:foreach(fun(Handler) -> Handler ! stop end, Handlers); + {stop, From} -> + tsp("dummy_ssl_server_loop -> " + "stop command from ~p for handlers (~p)", [From, Handlers]), + Stopper = fun(Handler) -> Handler ! stop end, + lists:foreach(Stopper, Handlers), + From ! {stopped, self()} + after 0 -> + tsp("dummy_ssl_server_loop -> await accept", []), + {ok, Socket} = ssl:transport_accept(ListenSocket), + tsp("dummy_ssl_server_loop -> accepted: ~p", [Socket]), + HandlerPid = dummy_request_handler(MFA, Socket), + tsp("dummy_ssl_server_loop -> handler created: ~p", [HandlerPid]), + ssl:controlling_process(Socket, HandlerPid), + tsp("dummy_ssl_server_loop -> control transfered to handler", []), + HandlerPid ! ssl_controller, + tsp("dummy_ssl_server_loop -> " + "handler informed about control transfer", []), + dummy_ssl_server_loop(MFA, [HandlerPid | Handlers], + ListenSocket) + end. + dummy_request_handler(MFA, Socket) -> + tsp("spawn request handler", []), spawn(httpc_SUITE, dummy_request_handler_init, [MFA, Socket]). dummy_request_handler_init(MFA, Socket) -> - receive - controller -> - inet:setopts(Socket, [{active, true}]) - end, - dummy_request_handler_loop(MFA, Socket). + SockType = + receive + ipcomm_controller -> + tsp("dummy_request_handler_init -> " + "received ip_comm controller - activate", []), + inet:setopts(Socket, [{active, true}]), + ip_comm; + ssl_controller -> + tsp("dummy_request_handler_init -> " + "received ssl controller - activate", []), + ssl:setopts(Socket, [{active, true}]), + ssl + end, + dummy_request_handler_loop(MFA, SockType, Socket). -dummy_request_handler_loop({Module, Function, Args}, Socket) -> +dummy_request_handler_loop({Module, Function, Args}, SockType, Socket) -> tsp("dummy_request_handler_loop -> entry with" "~n Module: ~p" "~n Function: ~p" "~n Args: ~p", [Module, Function, Args]), receive - {tcp, _, Data} -> - tsp("dummy_request_handler_loop -> Data ~p", [Data]), - case handle_request(Module, Function, [Data | Args], Socket) of - stop -> + {Proto, _, Data} when (Proto =:= tcp) orelse (Proto =:= ssl) -> + tsp("dummy_request_handler_loop -> [~w] Data ~p", [Proto, Data]), + case handle_request(Module, Function, [Data | Args], Socket, Proto) of + stop when Proto =:= tcp -> gen_tcp:close(Socket); + stop when Proto =:= ssl -> + ssl:close(Socket); NewMFA -> - dummy_request_handler_loop(NewMFA, Socket) + dummy_request_handler_loop(NewMFA, SockType, Socket) end; - stop -> - gen_tcp:close(Socket) + stop when SockType =:= ip_comm -> + gen_tcp:close(Socket); + stop when SockType =:= ssl -> + ssl:close(Socket) end. -handle_request(Module, Function, Args, Socket) -> + +mk_close(tcp) -> fun(Sock) -> gen_tcp:close(Sock) end; +mk_close(ssl) -> fun(Sock) -> ssl:close(Sock) end. + +mk_send(tcp) -> fun(Sock, Data) -> gen_tcp:send(Sock, Data) end; +mk_send(ssl) -> fun(Sock, Data) -> ssl:send(Sock, Data) end. + +handle_request(Module, Function, Args, Socket, Proto) -> + Close = mk_close(Proto), + Send = mk_send(Proto), + handle_request(Module, Function, Args, Socket, Close, Send). + +handle_request(Module, Function, Args, Socket, Close, Send) -> tsp("handle_request -> entry with" "~n Module: ~p" "~n Function: ~p" @@ -2941,7 +3177,7 @@ handle_request(Module, Function, Args, Socket) -> {ok, Result} -> tsp("handle_request -> ok" "~n Result: ~p", [Result]), - case (catch handle_http_msg(Result, Socket)) of + case (catch handle_http_msg(Result, Socket, Close, Send)) of stop -> stop; <<>> -> @@ -2949,7 +3185,8 @@ handle_request(Module, Function, Args, Socket) -> {httpd_request, parse, [[<<>>, ?HTTP_MAX_HEADER_SIZE]]}; Data -> handle_request(httpd_request, parse, - [Data |[?HTTP_MAX_HEADER_SIZE]], Socket) + [Data |[?HTTP_MAX_HEADER_SIZE]], Socket, + Close, Send) end; NewMFA -> tsp("handle_request -> " @@ -2957,7 +3194,7 @@ handle_request(Module, Function, Args, Socket) -> NewMFA end. -handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> +handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket, Close, Send) -> tsp("handle_http_msg -> entry with: " "~n RelUri: ~p" "~n Headers: ~p" @@ -3114,16 +3351,16 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> "Expires:Sat, 29 Oct 1994 19:43:31 GMT\r\n" ++ "Proxy-Authenticate:#1Basic" ++ "\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), - gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")), + Send(Socket, Head), + Send(Socket, http_chunk:encode("<HTML><BODY>fo")), + Send(Socket, http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); "/capital_transfer_encoding.html" -> Head = "HTTP/1.1 200 ok\r\n" ++ "Transfer-Encoding:Chunked\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), - gen_tcp:send(Socket, http_chunk:encode("obar</BODY></HTML>")), + Send(Socket, Head), + Send(Socket, http_chunk:encode("<HTML><BODY>fo")), + Send(Socket, http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); "/cookie.html" -> "HTTP/1.1 200 ok\r\n" ++ @@ -3142,20 +3379,20 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> "/once_chunked.html" -> Head = "HTTP/1.1 200 ok\r\n" ++ "Transfer-Encoding:Chunked\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, http_chunk:encode("<HTML><BODY>fo")), - gen_tcp:send(Socket, + Send(Socket, Head), + Send(Socket, http_chunk:encode("<HTML><BODY>fo")), + Send(Socket, http_chunk:encode("obar</BODY></HTML>")), http_chunk:encode_last(); "/once.html" -> Head = "HTTP/1.1 200 ok\r\n" ++ "Content-Length:32\r\n\r\n", - gen_tcp:send(Socket, Head), - gen_tcp:send(Socket, "<HTML><BODY>fo"), + Send(Socket, Head), + Send(Socket, "<HTML><BODY>fo"), test_server:sleep(1000), - gen_tcp:send(Socket, "ob"), + Send(Socket, "ob"), test_server:sleep(1000), - gen_tcp:send(Socket, "ar</BODY></HTML>"); + Send(Socket, "ar</BODY></HTML>"); "/invalid_http.html" -> "HTTP/1.1 301\r\nDate:Sun, 09 Dec 2007 13:04:18 GMT\r\n" ++ "Transfer-Encoding:chunked\r\n\r\n"; @@ -3178,9 +3415,9 @@ handle_http_msg({_, RelUri, _, {_, Headers}, Body}, Socket) -> ok; close -> %% Nothing to send, just close - gen_tcp:close(Socket); + Close(Socket); _ when is_list(Msg) orelse is_binary(Msg) -> - gen_tcp:send(Socket, Msg) + Send(Socket, Msg) end, tsp("handle_http_msg -> done"), NextRequest. @@ -3316,3 +3553,20 @@ dummy_ssl_server_hang_loop(_) -> stop -> ok end. + + +ensure_started([]) -> + ok; +ensure_started([App|Apps]) -> + ensure_started(App), + ensure_started(Apps); +ensure_started(App) when is_atom(App) -> + case (catch application:start(App)) of + ok -> + ok; + {error, {already_started, _}} -> + ok; + Error -> + throw({error, {failed_starting, App, Error}}) + end. + diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index fde5178879..c4d4bf969b 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -207,8 +207,11 @@ -export([ticket_5775/1,ticket_5865/1,ticket_5913/1,ticket_6003/1, ticket_7304/1]). -%%% Misc --export([ipv6_hostname/1, ipv6_address/1]). +%%% IPv6 tests +-export([ipv6_hostname_ipcomm/0, ipv6_hostname_ipcomm/1, + ipv6_address_ipcomm/0, ipv6_address_ipcomm/1, + ipv6_hostname_essl/0, ipv6_hostname_essl/1, + ipv6_address_essl/0, ipv6_address_essl/1]). %% Help functions -export([cleanup_mnesia/0, setup_mnesia/0, setup_mnesia/1]). @@ -241,9 +244,15 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [{group, ip}, {group, ssl}, {group, http_1_1_ip}, - {group, http_1_0_ip}, {group, http_0_9_ip}, - {group, tickets}]. + [ + {group, ip}, + {group, ssl}, + {group, http_1_1_ip}, + {group, http_1_0_ip}, + {group, http_0_9_ip}, + {group, ipv6}, + {group, tickets} + ]. groups() -> [{ip, [], @@ -329,7 +338,8 @@ groups() -> {http_1_0_ip, [], [ip_head_1_0, ip_get_1_0, ip_post_1_0]}, {http_0_9_ip, [], [ip_get_0_9]}, - {ipv6, [], [ipv6_hostname, ipv6_address]}, + {ipv6, [], [ipv6_hostname_ipcomm, ipv6_address_ipcomm, + ipv6_hostname_essl, ipv6_address_essl]}, {tickets, [], [ticket_5775, ticket_5865, ticket_5913, ticket_6003, ticket_7304]}]. @@ -408,10 +418,10 @@ init_per_testcase2(Case, Config) -> "~n Config: ~p" "~n", [?MODULE, Case, Config]), - IpNormal = integer_to_list(?IP_PORT) ++ ".conf", - IpHtacess = integer_to_list(?IP_PORT) ++ "htacess.conf", - SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", - SslHtacess = integer_to_list(?SSL_PORT) ++ "htacess.conf", + IpNormal = integer_to_list(?IP_PORT) ++ ".conf", + IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf", + SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", + SslHtaccess = integer_to_list(?SSL_PORT) ++ "htaccess.conf", DataDir = ?config(data_dir, Config), SuiteTopDir = ?config(suite_top_dir, Config), @@ -471,9 +481,9 @@ init_per_testcase2(Case, Config) -> io:format(user, "~w:init_per_testcase2(~w) -> ip testcase setups~n", [?MODULE, Case]), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], - normal_acess, IpNormal), + normal_access, IpNormal), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], - mod_htaccess, IpHtacess), + mod_htaccess, IpHtaccess), %% To be used by SSL test cases io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n", @@ -491,9 +501,9 @@ init_per_testcase2(Case, Config) -> end, create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], - normal_acess, SslNormal), + normal_access, SslNormal), create_config([{port, ?SSL_PORT}, {sock_type, SocketType} | NewConfig], - mod_htaccess, SslHtacess), + mod_htaccess, SslHtaccess), %% To be used by IPv6 test cases. Case-clause is so that %% you can do ts:run(inets, httpd_SUITE, <test case>) @@ -501,22 +511,52 @@ init_per_testcase2(Case, Config) -> %% on 'test_host_ipv6_only' that will only be present %% when you run the whole test suite due to shortcomings %% of the test server. - %% case (catch ?config(test_host_ipv6_only, Config)) of - %% {_,IPv6Host,IPv6Adress,_,_} -> - %% create_ipv6_config([{port, ?IP_PORT}, - %% {sock_type, ip_comm} | NewConfig], - %% "ipv6_hostname.conf", IPv6Host), - %% create_ipv6_config([{port, ?IP_PORT}, - %% {sock_type, ip_comm} | NewConfig], - %% "ipv6_address.conf", IPv6Adress); - %% _ -> - %% ok - %% end, - + + io:format(user, "~w:init_per_testcase2(~w) -> " + "maybe generate IPv6 config file(s)", [?MODULE, Case]), + NewConfig2 = + case atom_to_list(Case) of + "ipv6_" ++ _ -> + case (catch inets_test_lib:has_ipv6_support(NewConfig)) of + {ok, IPv6Address0} -> + {ok, Hostname} = inet:gethostname(), + IPv6Address = http_transport:ipv6_name(IPv6Address0), + create_ipv6_config([{port, ?IP_PORT}, + {sock_type, ip_comm}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_hostname_ipcomm.conf", + Hostname), + create_ipv6_config([{port, ?IP_PORT}, + {sock_type, ip_comm}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_address_ipcomm.conf", + IPv6Address), + create_ipv6_config([{port, ?SSL_PORT}, + {sock_type, essl}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_hostname_essl.conf", + Hostname), + create_ipv6_config([{port, ?SSL_PORT}, + {sock_type, essl}, + {ipv6_host, IPv6Address} | + NewConfig], + "ipv6_address_essl.conf", + IPv6Address), + [{ipv6_host, IPv6Address} | NewConfig]; + _ -> + NewConfig + end; + _ -> + NewConfig + end, + io:format(user, "~w:init_per_testcase2(~w) -> done~n", [?MODULE, Case]), - NewConfig. + NewConfig2. init_per_testcase3(Case, Config) -> @@ -547,10 +587,10 @@ init_per_testcase3(Case, Config) -> [?MODULE, Case]), inets:disable_trace(); _ -> - %% TraceLevel = max, io:format(user, "~w:init_per_testcase3(~w) -> enabling trace", [?MODULE, Case]), - TraceLevel = 70, + %% TraceLevel = 70, + TraceLevel = max, TraceDest = io, inets:enable_trace(TraceLevel, TraceDest, httpd) end, @@ -569,7 +609,7 @@ init_per_testcase3(Case, Config) -> inets_test_lib:start_http_server( filename:join(TcTopDir, integer_to_list(?IP_PORT) ++ - "htacess.conf")), + "htaccess.conf")), "mod_htaccess"; "ip_" ++ Rest -> inets_test_lib:start_http_server( @@ -602,7 +642,7 @@ init_per_testcase3(Case, Config) -> case inets_test_lib:start_http_server_ssl( filename:join(TcTopDir, integer_to_list(?SSL_PORT) ++ - "htacess.conf"), SslTag) of + "htaccess.conf"), SslTag) of ok -> "mod_htaccess"; Other -> @@ -627,16 +667,13 @@ init_per_testcase3(Case, Config) -> {skip, "SSL does not seem to be supported"} end; "ipv6_" ++ _ = TestCaseStr -> - {ok, Hostname} = inet:gethostname(), - - case lists:member(list_to_atom(Hostname), - ?config(ipv6_hosts, Config)) of - true -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> inets_test_lib:start_http_server( filename:join(TcTopDir, TestCaseStr ++ ".conf")); - false -> + _ -> {skip, "Host does not support IPv6"} end end, @@ -650,8 +687,8 @@ init_per_testcase3(Case, Config) -> "mod_htaccess" -> ServerRoot = ?config(server_root, Config), Path = filename:join([ServerRoot, "htdocs"]), - catch remove_htacess(Path), - create_htacess_data(Path, ?config(address, Config)), + catch remove_htaccess(Path), + create_htaccess_data(Path, ?config(address, Config)), [{watchdog, Dog} | NewConfig]; "range" -> ServerRoot = ?config(server_root, Config), @@ -2409,30 +2446,76 @@ ip_mod_cgi_chunked_encoding_test(Config) when is_list(Config) -> ok. %------------------------------------------------------------------------- -ipv6_hostname(doc) -> + +ipv6_hostname_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_hostname_ipcomm(X) -> + SocketType = ip_comm, + Port = ?IP_PORT, + ipv6_hostname(SocketType, Port, X). + +ipv6_hostname_essl() -> + [{require, ipv6_hosts}]. +ipv6_hostname_essl(X) -> + SocketType = essl, + Port = ?SSL_PORT, + ipv6_hostname(SocketType, Port, X). + +ipv6_hostname(_SocketType, _Port, doc) -> ["Test standard ipv6 address"]; -ipv6_hostname(suite)-> +ipv6_hostname(_SocketType, _Port, suite)-> []; -ipv6_hostname(Config) when is_list(Config) -> +ipv6_hostname(SocketType, Port, Config) when is_list(Config) -> + tsp("ipv6_hostname -> entry with" + "~n SocketType: ~p" + "~n Port: ~p" + "~n Config: ~p", [SocketType, Port, Config]), Host = ?config(host, Config), - httpd_test_lib:verify_request(ip_comm, Host, ?IP_PORT, node(), - "GET / HTTP/1.1\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.1"}]), + URI = "GET HTTP://" ++ + Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", + tsp("ipv6_hostname -> Host: ~p", [Host]), + httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], + node(), + URI, + [{statuscode, 200}, {version, "HTTP/1.1"}]), ok. %%------------------------------------------------------------------------- -ipv6_address(doc) -> + +ipv6_address_ipcomm() -> + [{require, ipv6_hosts}]. +ipv6_address_ipcomm(X) -> + SocketType = ip_comm, + Port = ?IP_PORT, + ipv6_address(SocketType, Port, X). + +ipv6_address_essl() -> + [{require, ipv6_hosts}]. +ipv6_address_essl(X) -> + SocketType = essl, + Port = ?SSL_PORT, + ipv6_address(SocketType, Port, X). + +ipv6_address(_SocketType, _Port, doc) -> ["Test standard ipv6 address"]; -ipv6_address(suite)-> +ipv6_address(_SocketType, _Port, suite)-> []; -ipv6_address(Config) when is_list(Config) -> - httpd_test_lib:verify_request(ip_comm, ?IPV6_LOCAL_HOST, ?IP_PORT, - node(), "GET / HTTP/1.1\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.1"}]), +ipv6_address(SocketType, Port, Config) when is_list(Config) -> + tsp("ipv6_address -> entry with" + "~n SocketType: ~p" + "~n Port: ~p" + "~n Config: ~p", [SocketType, Port, Config]), + Host = ?config(host, Config), + tsp("ipv6_address -> Host: ~p", [Host]), + URI = "GET HTTP://" ++ + Host ++ ":" ++ integer_to_list(Port) ++ "/ HTTP/1.1\r\n\r\n", + httpd_test_lib:verify_request(SocketType, Host, Port, [inet6], + node(), + URI, + [{statuscode, 200}, {version, "HTTP/1.1"}]), ok. + %%-------------------------------------------------------------------- ticket_5775(doc) -> ["Tests that content-length is correct"]; @@ -2805,22 +2888,22 @@ cleanup_mnesia() -> mnesia:delete_schema([node()]), ok. -create_htacess_data(Path, IpAddress)-> - create_htacess_dirs(Path), +create_htaccess_data(Path, IpAddress)-> + create_htaccess_dirs(Path), create_html_file(filename:join([Path,"ht/open/dummy.html"])), create_html_file(filename:join([Path,"ht/blocknet/dummy.html"])), create_html_file(filename:join([Path,"ht/secret/dummy.html"])), create_html_file(filename:join([Path,"ht/secret/top_secret/dummy.html"])), - create_htacess_file(filename:join([Path,"ht/open/.htaccess"]), + create_htaccess_file(filename:join([Path,"ht/open/.htaccess"]), Path, "user one Aladdin"), - create_htacess_file(filename:join([Path,"ht/secret/.htaccess"]), + create_htaccess_file(filename:join([Path,"ht/secret/.htaccess"]), Path, "group group1 group2"), - create_htacess_file(filename:join([Path, + create_htaccess_file(filename:join([Path, "ht/secret/top_secret/.htaccess"]), Path, "user four"), - create_htacess_file(filename:join([Path,"ht/blocknet/.htaccess"]), + create_htaccess_file(filename:join([Path,"ht/blocknet/.htaccess"]), Path, nouser, IpAddress), create_user_group_file(filename:join([Path,"ht","users.file"]), @@ -2835,7 +2918,7 @@ create_html_file(PathAndFileName)-> "<html><head><title>test</title></head> <body>testar</body></html>")). -create_htacess_file(PathAndFileName, BaseDir, RequireData)-> +create_htaccess_file(PathAndFileName, BaseDir, RequireData)-> file:write_file(PathAndFileName, list_to_binary( "AuthUserFile "++ BaseDir ++ @@ -2844,7 +2927,7 @@ create_htacess_file(PathAndFileName, BaseDir, RequireData)-> " Basic\n<Limit>\nrequire " ++ RequireData ++ "\n</Limit>")). -create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> +create_htaccess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> file:write_file(PathAndFileName,list_to_binary( "AuthUserFile "++ BaseDir ++ "/ht/users.file\nAuthGroupFile " ++ @@ -2858,14 +2941,14 @@ create_htacess_file(PathAndFileName, BaseDir, nouser, IpAddress)-> create_user_group_file(PathAndFileName, Data)-> file:write_file(PathAndFileName, list_to_binary(Data)). -create_htacess_dirs(Path)-> +create_htaccess_dirs(Path)-> ok = file:make_dir(filename:join([Path,"ht"])), ok = file:make_dir(filename:join([Path,"ht/open"])), ok = file:make_dir(filename:join([Path,"ht/blocknet"])), ok = file:make_dir(filename:join([Path,"ht/secret"])), ok = file:make_dir(filename:join([Path,"ht/secret/top_secret"])). -remove_htacess_dirs(Path)-> +remove_htaccess_dirs(Path)-> file:del_dir(filename:join([Path,"ht/secret/top_secret"])), file:del_dir(filename:join([Path,"ht/secret"])), file:del_dir(filename:join([Path,"ht/blocknet"])), @@ -2888,7 +2971,7 @@ format_ip(IpAddress,Pos)when Pos > 0-> format_ip(IpAddress, _Pos)-> "1" ++ IpAddress. -remove_htacess(Path)-> +remove_htaccess(Path)-> file:delete(filename:join([Path,"ht/open/dummy.html"])), file:delete(filename:join([Path,"ht/secret/dummy.html"])), file:delete(filename:join([Path,"ht/secret/top_secret/dummy.html"])), @@ -2899,7 +2982,7 @@ remove_htacess(Path)-> file:delete(filename:join([Path,"ht/secret/top_secret/.htaccess"])), file:delete(filename:join([Path,"ht","users.file"])), file:delete(filename:join([Path,"ht","groups.file"])), - remove_htacess_dirs(Path). + remove_htaccess_dirs(Path). dos_hostname_poll(Type, Host, Port, Node, Hosts) -> @@ -2939,35 +3022,66 @@ create_range_data(Path) -> "12345678901234567890", "12345678901234567890"])). -%% create_ipv6_config(Config, FileName, Ipv6Address) -> -%% ServerRoot = ?config(server_root, Config), -%% TcTopDir = ?config(tc_top_dir, Config), -%% Port = ?config(port, Config), -%% SockType = ?config(sock_type, Config), -%% -%% MaxHdrSz = io_lib:format("~p", [256]), -%% MaxHdrAct = io_lib:format("~p", [close]), -%% -%% Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" -%% " mod_include mod_dir mod_get mod_head" -%% " mod_log mod_disk_log mod_trace", -%% -%% HttpConfig = [cline(["BindAddress ", "[" ++ Ipv6Address ++"]|inet6"]), -%% cline(["Port ", integer_to_list(Port)]), -%% cline(["ServerName ", "httpc_test"]), -%% cline(["SocketType ", atom_to_list(SockType)]), -%% cline([Mod_order]), -%% cline(["ServerRoot ", ServerRoot]), -%% cline(["DocumentRoot ", -%% filename:join(ServerRoot, "htdocs")]), -%% cline(["MaxHeaderSize ",MaxHdrSz]), -%% cline(["MaxHeaderAction ",MaxHdrAct]), -%% cline(["DirectoryIndex ", "index.html "]), -%% cline(["DefaultType ", "text/plain"])], -%% ConfigFile = filename:join([TcTopDir,FileName]), -%% {ok, Fd} = file:open(ConfigFile, [write]), -%% ok = file:write(Fd, lists:flatten(HttpConfig)), -%% ok = file:close(Fd). +create_ipv6_config(Config, FileName, Ipv6Address) -> + ServerRoot = ?config(server_root, Config), + TcTopDir = ?config(tc_top_dir, Config), + Port = ?config(port, Config), + SockType = ?config(sock_type, Config), + Mods = io_lib:format("~p", [httpd_mod]), + Funcs = io_lib:format("~p", [ssl_password_cb]), + Host = ?config(ipv6_host, Config), + + MaxHdrSz = io_lib:format("~p", [256]), + MaxHdrAct = io_lib:format("~p", [close]), + + Mod_order = "Modules mod_alias mod_auth mod_esi mod_actions mod_cgi" + " mod_include mod_dir mod_get mod_head" + " mod_log mod_disk_log mod_trace", + + SSL = + if + (SockType =:= ssl) orelse + (SockType =:= ossl) orelse + (SockType =:= essl) -> + [cline(["SSLCertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCertificateKeyFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLCACertificateFile ", + filename:join(ServerRoot, "ssl/ssl_server.pem")]), + cline(["SSLPasswordCallbackModule ", Mods]), + cline(["SSLPasswordCallbackFunction ", Funcs]), + cline(["SSLVerifyClient 0"]), + cline(["SSLVerifyDepth 1"])]; + true -> + [] + end, + + BindAddress = "[" ++ Ipv6Address ++"]|inet6", + + HttpConfig = + [cline(["BindAddress ", BindAddress]), + cline(["Port ", integer_to_list(Port)]), + cline(["ServerName ", Host]), + cline(["SocketType ", atom_to_list(SockType)]), + cline([Mod_order]), + cline(["ServerRoot ", ServerRoot]), + cline(["DocumentRoot ", filename:join(ServerRoot, "htdocs")]), + cline(["MaxHeaderSize ",MaxHdrSz]), + cline(["MaxHeaderAction ",MaxHdrAct]), + cline(["DirectoryIndex ", "index.html "]), + cline(["DefaultType ", "text/plain"]), + SSL], + ConfigFile = filename:join([TcTopDir,FileName]), + {ok, Fd} = file:open(ConfigFile, [write]), + ok = file:write(Fd, lists:flatten(HttpConfig)), + ok = file:close(Fd). + + +%% tsp(F) -> +%% inets_test_lib:tsp(F). +tsp(F, A) -> + inets_test_lib:tsp(F, A). tsf(Reason) -> - test_server:fail(Reason). + inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 3189a758a5..2903aaafa5 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -22,7 +22,7 @@ -include("inets_test_lib.hrl"). %% Poll functions --export([verify_request/6, verify_request/7, is_expect/1]). +-export([verify_request/6, verify_request/7, verify_request/8, is_expect/1]). -record(state, {request, % string() socket, % socket() @@ -81,33 +81,57 @@ %%------------------------------------------------------------------ verify_request(SocketType, Host, Port, Node, RequestStr, Options) -> verify_request(SocketType, Host, Port, Node, RequestStr, Options, 30000). -verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) -> - {ok, Socket} = inets_test_lib:connect_bin(SocketType, Host, Port), +verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options) + when is_list(TranspOpts) -> + verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, 30000); +verify_request(SocketType, Host, Port, Node, RequestStr, Options, TimeOut) + when (is_integer(TimeOut) orelse (TimeOut =:= infinity)) -> + verify_request(SocketType, Host, Port, [], Node, RequestStr, Options, TimeOut). +verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, Options, TimeOut) -> + tsp("verify_request -> entry with" + "~n SocketType: ~p" + "~n Host: ~p" + "~n Port: ~p" + "~n TranspOpts: ~p" + "~n Node: ~p" + "~n Options: ~p" + "~n TimeOut: ~p", + [SocketType, Host, Port, TranspOpts, Node, Options, TimeOut]), + case (catch inets_test_lib:connect_bin(SocketType, Host, Port, TranspOpts)) of + {ok, Socket} -> + tsp("verify_request -> connected - now send message"), + SendRes = inets_test_lib:send(SocketType, Socket, RequestStr), + tsp("verify_request -> send result: " + "~n ~p", [SendRes]), + State = case inets_regexp:match(RequestStr, "printenv") of + nomatch -> + #state{}; + _ -> + #state{print = true} + end, + + case request(State#state{request = RequestStr, + socket = Socket}, TimeOut) of + {error, Reason} -> + tsp("request failed: " + "~n Reason: ~p", [Reason]), + {error, Reason}; + NewState -> + tsp("validate reply: " + "~n NewState: ~p", [NewState]), + ValidateResult = + validate(RequestStr, NewState, Options, Node, Port), + tsp("validation result: " + "~n ~p", [ValidateResult]), + inets_test_lib:close(SocketType, Socket), + ValidateResult + end; - _SendRes = inets_test_lib:send(SocketType, Socket, RequestStr), - - State = case inets_regexp:match(RequestStr, "printenv") of - nomatch -> - #state{}; - _ -> - #state{print = true} - end, - - case request(State#state{request = RequestStr, - socket = Socket}, TimeOut) of - {error, Reason} -> - tsp("request failed: " - "~n Reason: ~p", [Reason]), - {error, Reason}; - NewState -> - tsp("validate reply: " - "~n NewState: ~p", [NewState]), - ValidateResult = validate(RequestStr, NewState, Options, - Node, Port), - tsp("validation result: " - "~n ~p", [ValidateResult]), - inets_test_lib:close(SocketType, Socket), - ValidateResult + ConnectError -> + tsp("verify_request -> connect failed: " + "~n ~p" + "~n", [ConnectError]), + tsf({connect_failure, ConnectError}) end. request(#state{mfa = {Module, Function, Args}, @@ -214,7 +238,10 @@ validate(RequestStr, #state{status_line = {Version, StatusCode, _}, headers = Headers, body = Body}, Options, N, P) -> - %io:format("Status~p: H:~p B:~p~n", [StatusCode, Headers, Body]), + %% tsp("validate -> entry with" + %% "~n StatusCode: ~p" + %% "~n Headers: ~p" + %% "~n Body: ~p", [StatusCode, Headers, Body]), check_version(Version, Options), case lists:keysearch(statuscode, 1, Options) of {value, _} -> @@ -342,8 +369,10 @@ print(_, _, #state{print = false}) -> ok. -%% tsp(F) -> -%% tsp(F, []). +tsp(F) -> + inets_test_lib:tsp(F). tsp(F, A) -> - test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). + inets_test_lib:tsp(F, A). +tsf(Reason) -> + inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index 6cedaf9638..2e19c41f16 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -26,18 +26,64 @@ -export([start_http_server/1, start_http_server/2]). -export([start_http_server_ssl/1, start_http_server_ssl/2]). -export([hostname/0]). --export([connect_bin/3, connect_byte/3, send/3, close/2]). +-export([connect_bin/3, connect_bin/4, + connect_byte/3, connect_byte/4, + send/3, close/2]). -export([copy_file/3, copy_files/2, copy_dirs/2, del_dirs/1]). -export([info/4, log/4, debug/4, print/4]). +-export([tsp/1, tsp/2, tsf/1]). -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). --export([oscmd/1]). +-export([oscmd/1, has_ipv6_support/1]). -export([non_pc_tc_maybe_skip/4, os_based_skip/1, skip/3, fail/3]). -export([flush/0]). -export([start_node/1, stop_node/1]). %% -- Misc os command and stuff +has_ipv6_support(Config) -> + case lists:keysearch(ipv6_hosts, 1, Config) of + false -> + %% Do a basic check to se if + %% our own host has a working IPv6 address... + tsp("has_ipv6_support -> no ipv6_hosts config"), + {ok, Hostname} = inet:gethostname(), + case inet:getaddrs(Hostname, inet6) of + {ok, [Addr|_]} when is_tuple(Addr) andalso + (element(1, Addr) =/= 0) -> + %% We actually need to test that the addr can be used, + %% this is done by attempting to create a (tcp) + %% listen socket + tsp("has_ipv6_support -> check Addr: ~p", [Addr]), + case (catch gen_tcp:listen(0, [inet6, {ip, Addr}])) of + {ok, LSock} -> + tsp("has_ipv6_support -> we are ipv6 host"), + gen_tcp:close(LSock), + {ok, Addr}; + _ -> + undefined + end; + _ -> + undefined + end; + {value, {_, Hosts}} when is_list(Hosts) -> + %% Check if our host is in the list of *known* IPv6 hosts + tsp("has_ipv6_support -> Hosts: ~p", [Hosts]), + {ok, Hostname} = inet:gethostname(), + case lists:member(list_to_atom(Hostname), Hosts) of + true -> + tsp("has_ipv6_support -> we are known ipv6 host"), + {ok, [Addr|_]} = inet:getaddrs(Hostname, inet6), + {ok, Addr}; + false -> + undefined + end; + + _ -> + undefined + + end. + oscmd(Cmd) -> string:strip(os:cmd(Cmd), right, $\n). @@ -87,31 +133,34 @@ start_http_server(Conf) -> start_http_server(Conf, ?HTTP_DEFAULT_SSL_KIND). start_http_server(Conf, essl = _SslTag) -> + tsp("start_http_server(essl) -> entry - try start crypto and public_key"), application:start(crypto), + application:start(public_key), do_start_http_server(Conf); -start_http_server(Conf, _SslTag) -> +start_http_server(Conf, SslTag) -> + tsp("start_http_server(~w) -> entry", [SslTag]), do_start_http_server(Conf). do_start_http_server(Conf) -> - tsp("start http server with " + tsp("do_start_http_server -> entry with" "~n Conf: ~p" "~n", [Conf]), application:load(inets), case application:set_env(inets, services, [{httpd, Conf}]) of ok -> + tsp("start_http_server -> httpd conf stored in inets app env"), case application:start(inets) of ok -> + tsp("start_http_server -> inets started"), ok; Error1 -> - test_server:format("<ERROR> Failed starting application: " - "~n Error: ~p" - "~n", [Error1]), + tsp("<ERROR> Failed starting application: " + "~n Error1: ~p", [Error1]), Error1 end; Error2 -> - test_server:format("<ERROR> Failed set application env: " - "~n Error: ~p" - "~n", [Error2]), + tsp("<ERROR> Failed set application env: " + "~n Error: ~p", [Error2]), Error2 end. @@ -285,29 +334,45 @@ os_based_skip(_) -> %% Host -> atom() | string() | {A, B, C, D} %% Port -> integer() -connect_bin(ssl, Host, Port) -> - connect(ssl, Host, Port, [binary, {packet,0}]); -connect_bin(ossl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, old}, binary, {packet,0}]); -connect_bin(essl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true}]); -connect_bin(ip_comm, Host, Port) -> - Opts = [inet6, binary, {packet,0}], +connect_bin(SockType, Host, Port) -> + connect_bin(SockType, Host, Port, []). + +connect_bin(ssl, Host, Port, Opts0) -> + Opts = [binary, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_bin(ossl, Host, Port, Opts0) -> + Opts = [{ssl_imp, old}, binary, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_bin(essl, Host, Port, Opts0) -> + Opts = [{ssl_imp, new}, binary, {packet,0}, {reuseaddr, true} | Opts0], + connect(ssl, Host, Port, Opts); +connect_bin(ip_comm, Host, Port, Opts0) -> + Opts = [binary, {packet, 0} | Opts0], connect(ip_comm, Host, Port, Opts). + +connect_byte(SockType, Host, Port) -> + connect_byte(SockType, Host, Port, []). -connect_byte(ssl, Host, Port) -> - connect(ssl, Host, Port, [{packet,0}]); -connect_byte(ossl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, old}, {packet,0}]); -connect_byte(essl, Host, Port) -> - connect(ssl, Host, Port, [{ssl_imp, new}, {packet,0}]); -connect_byte(ip_comm, Host, Port) -> - Opts = [inet6, {packet,0}], +connect_byte(ssl, Host, Port, Opts0) -> + Opts = [{packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_byte(ossl, Host, Port, Opts0) -> + Opts = [{ssl_imp, old}, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_byte(essl, Host, Port, Opts0) -> + Opts = [{ssl_imp, new}, {packet,0} | Opts0], + connect(ssl, Host, Port, Opts); +connect_byte(ip_comm, Host, Port, Opts0) -> + Opts = [{packet,0} | Opts0], connect(ip_comm, Host, Port, Opts). connect(ssl, Host, Port, Opts) -> + tsp("connect(ssl) -> entry with" + "~n Host: ~p" + "~n Port: ~p" + "~n Opts: ~p", [Host, Port, Opts]), ssl:start(), %% Does not support ipv6 in old ssl case ssl:connect(Host, Port, Opts) of @@ -319,21 +384,28 @@ connect(ssl, Host, Port, Opts) -> Error end; connect(ip_comm, Host, Port, Opts) -> + tsp("connect(ip_comm) -> entry with" + "~n Host: ~p" + "~n Port: ~p" + "~n Opts: ~p", [Host, Port, Opts]), case gen_tcp:connect(Host,Port, Opts) of {ok, Socket} -> - %% tsp("connect success"), + tsp("connect success"), {ok, Socket}; {error, nxdomain} -> - tsp("nxdomain opts: ~p", [Opts]), + tsp("connect error nxdomain when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, eafnosupport} -> - tsp("eafnosupport opts: ~p", [Opts]), + tsp("connect error eafnosupport when opts: ~p", [Opts]), + connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); + {error, econnreset} -> + tsp("connect error econnreset when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, enetunreach} -> - tsp("eafnosupport opts: ~p", [Opts]), + tsp("connect error eafnosupport when opts: ~p", [Opts]), connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); {error, {enfile,_}} -> - tsp("Error enfile"), + tsp("connect error enfile when opts: ~p", [Opts]), {error, enfile}; Error -> tsp("Unexpected error: " @@ -414,7 +486,22 @@ flush() -> tsp(F) -> tsp(F, []). tsp(F, A) -> - test_server:format("~p ~p ~p:" ++ F ++ "~n", [node(), self(), ?MODULE | A]). + Timestamp = formated_timestamp(), + test_server:format("*** ~s ~p ~p ~w:" ++ F ++ "~n", + [Timestamp, node(), self(), ?MODULE | A]). tsf(Reason) -> test_server:fail(Reason). + +formated_timestamp() -> + format_timestamp( os:timestamp() ). + +format_timestamp({_N1, _N2, N3} = Now) -> + {Date, Time} = calendar:now_to_datetime(Now), + {YYYY,MM,DD} = Date, + {Hour,Min,Sec} = Time, + FormatDate = + io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", + [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), + lists:flatten(FormatDate). + diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index c0e25a30e3..4abc1733d3 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.6 +INETS_VSN = 5.7 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 5ceb82ae41..c0126ed8c1 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -63,7 +63,6 @@ <item><seealso marker="#options">SCTP SOCKET OPTIONS</seealso></item> <item><seealso marker="#examples">SCTP EXAMPLES</seealso></item> <item><seealso marker="#seealso">SEE ALSO</seealso></item> - <item><seealso marker="#authors">AUTHORS</seealso></item> </list> <marker id="types"></marker> </section> @@ -80,36 +79,18 @@ </desc> </datatype> <datatype> - <name name="hostname"/> - </datatype> - <datatype> - <name name="ip_address"/> - <desc> - <p>Represents an address of an SCTP socket. - It is a tuple as explained in - <seealso marker="inet">inet(3)</seealso>.</p> - </desc> - </datatype> - <datatype> - <name name="port_number"/> - </datatype> - <datatype> - <name name="posix"/> - <desc> - <p>See <seealso marker="inet#error_codes"> - inet(3); POSIX Error Codes</seealso>.</p> - </desc> - </datatype> - <datatype> - <name name="sctp_option"/> + <name name="option"/> <desc> <p>One of the <seealso marker="#options">SCTP Socket Options.</seealso></p> - <marker id="type-sctp_socket"></marker> </desc> </datatype> <datatype> - <name name="sctp_socket"/> + <name name="option_name"/> + <desc><marker id="type-sctp_socket"></marker></desc> + </datatype> + <datatype> + <name><marker id="type-sctp_socket">sctp_socket()</marker></name> <desc> <p>Socket identifier returned from <c>open/*</c>.</p> <marker id="exports"></marker> @@ -175,8 +156,8 @@ #sctp_assoc_change{ state = atom(), error = atom(), - outbound_streams = int(), - inbound_streams = int(), + outbound_streams = integer(), + inbound_streams = integer(), assoc_id = assoc_id() } </pre> <p>The number of outbound and inbound streams can be set by @@ -300,6 +281,19 @@ The default <c><anno>IP</anno></c> and <c><anno>Port</anno></c> are <c>any</c> and <c>0</c>, meaning bind to all local addresses on any one free port.</p> + + <p>Other options are:</p> + <taglist> + <tag><c>inet6</c></tag> + <item> + <p>Set up the socket for IPv6.</p> + </item> + <tag><c>inet</c></tag> + <item> + <p>Set up the socket for IPv4. This is the default.</p> + </item> + </taglist> + <p>A default set of socket <seealso marker="#options">options</seealso> is used. In particular, the socket is opened in <seealso marker="#option-binary">binary</seealso> and @@ -350,7 +344,7 @@ #sctp_paddr_change{ addr = {ip_address(),port()}, state = atom(), - error = int(), + error = integer(), assoc_id = assoc_id() } </pre> <p>Indicates change of the status of the peer's IP address given by @@ -387,7 +381,7 @@ <pre> #sctp_send_failed{ flags = true | false, - error = int(), + error = integer(), info = #sctp_sndrcvinfo{}, assoc_id = assoc_id() data = binary() @@ -407,7 +401,7 @@ <item> <pre> #sctp_adaptation_event{ - adaptation_ind = int(), + adaptation_ind = integer(), assoc_id = assoc_id() } </pre> <p>Delivered when a peer sends an Adaptation Layer Indication @@ -505,7 +499,7 @@ </list> <marker id="option-buffer"></marker> </item> - <tag><c>{buffer, int()}</c></tag> + <tag><c>{buffer, integer()}</c></tag> <item> <p>Determines the size of the user-level software buffer used by the SCTP driver. Not to be confused with <c>sndbuf</c> @@ -515,7 +509,7 @@ In fact, the <c>val(buffer)</c> is automatically set to the above maximum when <c>sndbuf</c> or <c>recbuf</c> values are set.</p> </item> - <tag><c>{tos, int()}</c></tag> + <tag><c>{tos, integer()}</c></tag> <item> <p>Sets the Type-Of-Service field on the IP datagrams being sent, to the given value, which effectively determines a prioritization @@ -523,7 +517,7 @@ are system-dependent. TODO: we do not provide symbolic names for these values yet.</p> </item> - <tag><c>{priority, int()}</c></tag> + <tag><c>{priority, integer()}</c></tag> <item> <p>A protocol-independent equivalent of <c>tos</c> above. Setting priority implies setting tos as well.</p> @@ -542,7 +536,7 @@ required for high-throughput servers).</p> <marker id="option-linger"></marker> </item> - <tag><c>{linger, {true|false, int()}</c></tag> + <tag><c>{linger, {true|false, integer()}</c></tag> <item> <p>Determines the timeout in seconds for flushing unsent data in the <c>gen_sctp:close/1</c> socket call. If the 1st component of the value @@ -552,14 +546,14 @@ the flushing time-out in seconds.</p> <marker id="option-sndbuf"></marker> </item> - <tag><c>{sndbuf, int()}</c></tag> + <tag><c>{sndbuf, integer()}</c></tag> <item> <p>The size, in bytes, of the *kernel* send buffer for this socket. Sending errors would occur for datagrams larger than <c>val(sndbuf)</c>. Setting this option also adjusts the size of the driver buffer (see <c>buffer</c> above).</p> </item> - <tag><c>{recbuf, int()}</c></tag> + <tag><c>{recbuf, integer()}</c></tag> <item> <p>The size, in bytes, of the *kernel* recv buffer for this socket. Sending errors would occur for datagrams larger than @@ -571,9 +565,9 @@ <pre> #sctp_rtoinfo{ assoc_id = assoc_id(), - initial = int(), - max = int(), - min = int() + initial = integer(), + max = integer(), + min = integer() } </pre> <p>Determines re-transmission time-out parameters, in milliseconds, for the association(s) given by <c>assoc_id</c>. @@ -586,11 +580,11 @@ <pre> #sctp_assocparams{ assoc_id = assoc_id(), - asocmaxrxt = int(), - number_peer_destinations = int(), - peer_rwnd = int(), - local_rwnd = int(), - cookie_life = int() + asocmaxrxt = integer(), + number_peer_destinations = integer(), + peer_rwnd = integer(), + local_rwnd = integer(), + cookie_life = integer() } </pre> <p>Determines association parameters for the association(s) given by <c>assoc_id</c>. <c>assoc_id = 0</c> (default) indicates @@ -601,10 +595,10 @@ <item> <pre> #sctp_initmsg{ - num_ostreams = int(), - max_instreams = int(), - max_attempts = int(), - max_init_timeo = int() + num_ostreams = integer(), + max_instreams = integer(), + max_attempts = integer(), + max_init_timeo = integer() } </pre> <p>Determines the default parameters which this socket attempts to negotiate with its peer while establishing an association with it. @@ -630,10 +624,11 @@ </list> <p></p> </item> - <tag><c>{sctp_autoclose, int()|infinity}</c></tag> + <tag><c>{sctp_autoclose, integer() >= 0}</c></tag> <item> <p>Determines the time (in seconds) after which an idle association is - automatically closed.</p> + automatically closed. <c>0</c> means that the association is + never automatically closed.</p> </item> <tag><c>{sctp_nodelay, true|false}</c></tag> <item> @@ -655,7 +650,7 @@ <p>Turns on|off automatic mapping of IPv4 addresses into IPv6 ones (if the socket address family is AF_INET6).</p> </item> - <tag><c>{sctp_maxseg, int()}</c></tag> + <tag><c>{sctp_maxseg, integer()}</c></tag> <item> <p>Determines the maximum chunk size if message fragmentation is used. If <c>0</c>, the chunk size is limited by the Path MTU only.</p> @@ -693,7 +688,7 @@ <marker id="record-sctp_setadaptation"></marker> <pre> #sctp_setadaptation{ - adaptation_ind = int() + adaptation_ind = integer() } </pre> <p>When set, requests that the local endpoint uses the value given by <c>adaptation_ind</c> as the Adaptation Indication parameter for @@ -707,10 +702,10 @@ #sctp_paddrparams{ assoc_id = assoc_id(), address = {IP, Port}, - hbinterval = int(), - pathmaxrxt = int(), - pathmtu = int(), - sackdelay = int(), + hbinterval = integer(), + pathmaxrxt = integer(), + pathmtu = integer(), + sackdelay = integer(), flags = list() } IP = ip_address() @@ -771,14 +766,14 @@ <marker id="record-sctp_sndrcvinfo"></marker> <pre> #sctp_sndrcvinfo{ - stream = int(), - ssn = int(), + stream = integer(), + ssn = integer(), flags = list(), - ppid = int(), - context = int(), - timetolive = int(), - tsn = int(), - cumtsn = int(), + ppid = integer(), + context = integer(), + timetolive = integer(), + tsn = integer(), + cumtsn = integer(), assoc_id = assoc_id() } </pre> <p><c>#sctp_sndrcvinfo{}</c> is used both in this socket option, and as @@ -853,7 +848,7 @@ <pre> #sctp_assoc_value{ assoc_id = assoc_id(), - assoc_value = int() + assoc_value = integer() } </pre> <p>Rarely used. Determines the ACK time (given by <c>assoc_value</c> in milliseconds) for @@ -866,12 +861,12 @@ #sctp_status{ assoc_id = assoc_id(), state = atom(), - rwnd = int(), - unackdata = int(), - penddata = int(), - instrms = int(), - outstrms = int(), - fragmentation_point = int(), + rwnd = integer(), + unackdata = integer(), + penddata = integer(), + instrms = integer(), + outstrms = integer(), + fragmentation_point = integer(), primary = #sctp_paddrinfo{} } </pre> <p>This option is read-only. It determines the status of @@ -946,10 +941,10 @@ assoc_id = assoc_id(), address = {IP, Port}, state = inactive | active, - cwnd = int(), - srtt = int(), - rto = int(), - mtu = int() + cwnd = integer(), + srtt = integer(), + rto = integer(), + mtu = integer() } IP = ip_address() Port = port_number() </pre> @@ -990,7 +985,7 @@ server(IP, Port) when is_tuple(IP) orelse IP == any orelse IP == loopback, is_integer(Port) -> - {ok,S} = gen_sctp:open([{ip,IP},{port,Port}],[{recbuf,65536}]), + {ok,S} = gen_sctp:open(Port, [{recbuf,65536}, {ip,IP}]), io:format("Listening on ~w:~w. ~w~n", [IP,Port,S]), ok = gen_sctp:listen(S, true), server_loop(S). @@ -1119,7 +1114,6 @@ client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, AssocId2) -> <seealso marker="gen_udp">gen_udp(3)</seealso>, <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> (Stream Control Transmission Protocol), <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP.</url></p> - <marker id="authors"></marker> </section> </erlref> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index f1d42d9faa..8a5d40bb16 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -37,7 +37,7 @@ binary and closing the connection:</p> <code type="none"> client() -> - SomeHostInNet = "localhost" % to make it runnable on one machine + SomeHostInNet = "localhost", % to make it runnable on one machine {ok, Sock} = gen_tcp:connect(SomeHostInNet, 5678, [binary, {packet, 0}]), ok = gen_tcp:send(Sock, "Some Data"), @@ -65,25 +65,16 @@ do_recv(Sock, Bs) -> <datatypes> <datatype> - <name name="hostname"/> + <name name="option"/> </datatype> <datatype> - <name name="ip_address"/> - <desc> - <p>Represents an address of a TCP socket. - It is a tuple as explained in - <seealso marker="inet">inet(3)</seealso>.</p> - </desc> + <name name="option_name"/> </datatype> <datatype> - <name name="port_number"/> + <name name="connect_option"/> </datatype> <datatype> - <name name="posix"/> - <desc> - <p>See <seealso marker="inet#error_codes"> - inet(3); POSIX Error Codes</seealso>.</p> - </desc> + <name name="listen_option"/> </datatype> <datatype> <name><marker id="type-socket">socket()</marker></name> @@ -122,7 +113,7 @@ do_recv(Sock, Bs) -> <item> <p>Specify which local port number to use.</p> </item> - <tag><c>{fd, int()}</c></tag> + <tag><c>{fd, integer() >= 0}</c></tag> <item> <p>If a socket has somehow been connected without using <c>gen_tcp</c>, use this option to pass the file @@ -196,6 +187,10 @@ do_recv(Sock, Bs) -> <p>If the host has several network interfaces, this option specifies which one to listen on.</p> </item> + <tag><c>{port, Port}</c></tag> + <item> + <p>Specify which local port number to use.</p> + </item> <tag><c>{fd, Fd}</c></tag> <item> <p>If a socket has somehow been connected without using diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index c0e783f508..daa9b7d887 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -36,25 +36,10 @@ <datatypes> <datatype> - <name name="hostname"/> + <name name="option"/> </datatype> <datatype> - <name name="ip_address"/> - <desc> - <p>Represents an address of a TCP socket. - It is a tuple as explained in - <seealso marker="inet">inet(3)</seealso>.</p> - </desc> - </datatype> - <datatype> - <name name="port_number"/> - </datatype> - <datatype> - <name name="posix"/> - <desc> - <p>See <seealso marker="inet#error_codes"> - inet(3); POSIX Error Codes</seealso>.</p> - </desc> + <name name="option_name"/> </datatype> <datatype> <name><marker id="type-socket">socket()</marker></name> @@ -87,7 +72,7 @@ <p>If the host has several network interfaces, this option specifies which one to use.</p> </item> - <tag><c>{fd, int()}</c></tag> + <tag><c>{fd, integer() >= 0}</c></tag> <item> <p>If a socket has somehow been opened without using <c>gen_udp</c>, use this option to pass the file diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index fd843b00d9..b36c28e027 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -105,6 +105,9 @@ fe80::204:acff:fe17:bf38 <name name="ip6_address"/> </datatype> <datatype> + <name name="port_number"/> + </datatype> + <datatype> <name name="posix"/> <desc><p>An atom which is named from the Posix error codes used in Unix, and in the runtime libraries of most @@ -119,7 +122,7 @@ fe80::204:acff:fe17:bf38 </desc> </datatype> <datatype> - <name name="family_option"/> + <name name="address_family"/> </datatype> </datatypes> @@ -250,26 +253,15 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name>getopts(Socket, Options) -> {ok, OptionValues} | {error, posix()}</name> + <name name="getopts" arity="2"/> <fsummary>Get one or more options for a socket</fsummary> - <type> - <v>Socket = term()</v> - <v>Options = [Opt | RawOptReq]</v> - <v>Opt = atom()</v> - <v>RawOptReq = {raw, Protocol, OptionNum, ValueSpec}</v> - <v>Protocol = integer()</v> - <v>OptionNum = integer()</v> - <v>ValueSpec = ValueSize | ValueBin</v> - <v>ValueSize = integer()</v> - <v>ValueBin = binary()</v> - <v>OptionValues = [{Opt, Val} | {raw, Protocol, OptionNum, ValueBin}]</v> - </type> <type name="socket_getopt"/> + <type name="socket_setopt"/> <desc> <p>Gets one or more options for a socket. See <seealso marker="#setopts/2">setopts/2</seealso> for a list of available options.</p> - <p>The number of elements in the returned <c>OptionValues</c> + <p>The number of elements in the returned <c><anno>OptionValues</anno></c> list does not necessarily correspond to the number of options asked for. If the operating system fails to support an option, it is simply left out in the returned list. An error tuple is only @@ -277,12 +269,12 @@ fe80::204:acff:fe17:bf38 (i.e. the socket is closed or the buffer size in a raw request is too large). This behavior is kept for backward compatibility reasons.</p> - <p>A <c>RawOptReq</c> can be used to get information about + <p>A raw option request <c>RawOptReq = {raw, Protocol, OptionNum, ValueSpec}</c> can be used to get information about socket options not (explicitly) supported by the emulator. The use of raw socket options makes the code non portable, but allows the Erlang programmer to take advantage of unusual features present on the current platform.</p> - <p>The <c>RawOptReq</c> consists of the tag <c>raw</c> followed + <p>The <c>RawOptReq</c> consists of the tag <c>raw</c> followed by the protocol level, the option number and either a binary or the size, in bytes, of the buffer in which the option value is to be stored. A binary @@ -325,19 +317,14 @@ fe80::204:acff:fe17:bf38 </func> <func> - <name>getstat(Socket)</name> - <name>getstat(Socket, Options) -> {ok, OptionValues} | {error, posix()}</name> + <name name="getstat" arity="1"/> + <name name="getstat" arity="2"/> <fsummary>Get one or more statistic options for a socket</fsummary> - <type> - <v>Socket = term()</v> - <v>Options = [Opt]</v> - <v>OptionValues = [{Opt, Val}]</v> - <v> Opt, Val -- see below</v> - </type> + <type name="stat_option"/> <desc> <p>Gets one or more statistic options for a socket.</p> - <p><c>getstat(Socket)</c> is equivalent to - <c>getstat(Socket, [recv_avg, recv_cnt, recv_dvi, recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max, send_oct])</c></p> + <p><c>getstat(<anno>Socket</anno>)</c> is equivalent to + <c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi, recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max, send_oct])</c></p> <p>The following options are available:</p> <taglist> <tag><c>recv_avg</c></tag> @@ -394,12 +381,8 @@ fe80::204:acff:fe17:bf38 </desc> </func> <func> - <name>port(Socket) -> {ok, Port} | {error, any()}</name> + <name name="port" arity="1"/> <fsummary>Return the local port number for a socket</fsummary> - <type> - <v>Socket = socket()</v> - <v>Port = integer()</v> - </type> <desc> <p>Returns the local port number for a socket.</p> </desc> @@ -412,16 +395,9 @@ fe80::204:acff:fe17:bf38 </desc> </func> <func> - <name>setopts(Socket, Options) -> ok | {error, posix()}</name> + <name name="setopts" arity="2"/> <fsummary>Set one or more options for a socket</fsummary> - <type> - <v>Socket = term()</v> - <v>Options = [{Opt, Val} | {raw, Protocol, Option, ValueBin}]</v> - <v>Protocol = integer()</v> - <v>OptionNum = integer()</v> - <v>ValueBin = binary()</v> - <v> Opt, Val -- see below</v> - </type> + <type name="socket_setopt"/> <desc> <p>Sets one or more options for a socket. The following options are available:</p> diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 4a1fc7df34..85bbff9cc3 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1379,8 +1379,12 @@ absname_vr([[X, $:]|Name], _, _AbsBase) -> %% Kill all processes running code from *old* Module, and then purge the %% module. Return true if any processes killed, else false. -do_purge(Mod) -> - do_purge(processes(), to_atom(Mod), false). +do_purge(Mod0) -> + Mod = to_atom(Mod0), + case erlang:check_old_code(Mod) of + false -> false; + true -> do_purge(processes(), Mod, false) + end. do_purge([P|Ps], Mod, Purged) -> case erlang:check_process_code(P, Mod) of @@ -1399,16 +1403,19 @@ do_purge([], Mod, Purged) -> Purged. %% do_soft_purge(Module) -%% Purge old code only if no procs remain that run old code +%% Purge old code only if no procs remain that run old code. %% Return true in that case, false if procs remain (in this %% case old code is not purged) do_soft_purge(Mod) -> - catch do_soft_purge(processes(), Mod). + case erlang:check_old_code(Mod) of + false -> true; + true -> do_soft_purge(processes(), Mod) + end. do_soft_purge([P|Ps], Mod) -> case erlang:check_process_code(P, Mod) of - true -> throw(false); + true -> false; false -> do_soft_purge(Ps, Mod) end; do_soft_purge([], Mod) -> diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index 004f03f231..c9a849eca7 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -34,54 +34,84 @@ -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 +-type option() :: + {active, true | false | once} | + {buffer, non_neg_integer()} | + {dontroute, boolean()} | + {linger, {boolean(), non_neg_integer()}} | + {mode, list | binary} | list | binary | + {priority, non_neg_integer()} | + {recbuf, non_neg_integer()} | + {reuseaddr, boolean()} | + {sctp_adaptation_layer, #sctp_setadaptation{}} | + {sctp_associnfo, #sctp_assocparams{}} | + {sctp_autoclose, non_neg_integer()} | + {sctp_default_send_param, #sctp_sndrcvinfo{}} | + {sctp_delayed_ack_time, #sctp_assoc_value{}} | + {sctp_disable_fragments, boolean()} | + {sctp_events, #sctp_event_subscribe{}} | + {sctp_get_peer_addr_info, #sctp_paddrinfo{}} | + {sctp_i_want_mapped_v4_addr, boolean()} | + {sctp_initmsg, #sctp_initmsg{}} | + {sctp_maxseg, non_neg_integer()} | + {sctp_nodelay, boolean()} | + {sctp_peer_addr_params, #sctp_paddrparams{}} | + {sctp_primary_addr, #sctp_prim{}} | + {sctp_rtoinfo, #sctp_rtoinfo{}} | + {sctp_set_peer_primary_addr, #sctp_setpeerprim{}} | + {sctp_status, #sctp_status{}} | + {sndbuf, non_neg_integer()} | + {tos, non_neg_integer()}. +-type option_name() :: + active | + buffer | + dontroute | + linger | + mode | + priority | + recbuf | + reuseaddr | + sctp_adaptation_layer | + sctp_associnfo | + sctp_autoclose | + sctp_default_send_param | + sctp_delayed_ack_time | + sctp_disable_fragments | + sctp_events | + sctp_get_peer_addr_info | + sctp_i_want_mapped_v4_addr | + sctp_initmsg | + sctp_maxseg | + sctp_nodelay | + sctp_peer_addr_params | + sctp_primary_addr | + sctp_rtoinfo | + sctp_set_peer_primary_addr | + sctp_status | + sndbuf | + tos. +-type sctp_socket() :: port(). + +-export_type([option/0, option_name/0]). + +-spec open() -> {ok, Socket} | {error, inet:posix()} when Socket :: sctp_socket(). open() -> open([]). --spec open(Port) -> {ok, Socket} | {error, posix()} when - Port :: port_number(), +-spec open(Port) -> {ok, Socket} | {error, inet:posix()} when + Port :: inet:port_number(), Socket :: sctp_socket(); - (Opts) -> {ok, Socket} | {error, posix()} when + (Opts) -> {ok, Socket} | {error, inet:posix()} when Opts :: [Opt], - Opt :: {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option(), - IP :: ip_address() | any | loopback, - Port :: port_number(), + Opt :: {ip,IP} + | {ifaddr,IP} + | inet:address_family() + | {port,Port} + | option(), + IP :: inet:ip_address() | any | loopback, + Port :: inet:port_number(), Socket :: sctp_socket(). open(Opts) when is_list(Opts) -> @@ -98,11 +128,15 @@ open(Port) when is_integer(Port) -> open(X) -> erlang:error(badarg, [X]). --spec open(Port, Opts) -> {ok, Socket} | {error, posix()} when +-spec open(Port, Opts) -> {ok, Socket} | {error, inet:posix()} when Opts :: [Opt], - Opt :: {ip,IP} | {ifaddr,IP} | {port,Port} | sctp_option(), - IP :: ip_address() | any | loopback, - Port :: port_number(), + Opt :: {ip,IP} + | {ifaddr,IP} + | inet:address_family() + | {port,Port} + | option(), + IP :: inet:ip_address() | any | loopback, + Port :: inet:port_number(), Socket :: sctp_socket(). open(Port, Opts) when is_integer(Port), is_list(Opts) -> @@ -110,7 +144,7 @@ open(Port, Opts) when is_integer(Port), is_list(Opts) -> open(Port, Opts) -> erlang:error(badarg, [Port,Opts]). --spec close(Socket) -> ok | {error, posix()} when +-spec close(Socket) -> ok | {error, inet:posix()} when Socket :: sctp_socket(). close(S) when is_port(S) -> @@ -138,22 +172,22 @@ 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 +-spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, inet:posix()} when Socket :: sctp_socket(), - Addr :: ip_address() | hostname(), - Port :: port_number(), - Opts :: [Opt :: sctp_option()], + Addr :: inet:ip_address() | inet:hostname(), + Port :: inet:port_number(), + Opts :: [Opt :: 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 + {ok, Assoc} | {error, inet:posix()} when Socket :: sctp_socket(), - Addr :: ip_address() | hostname(), - Port :: port_number(), - Opts :: [Opt :: sctp_option()], + Addr :: inet:ip_address() | inet:hostname(), + Port :: inet:port_number(), + Opts :: [Opt :: option()], Timeout :: timeout(), Assoc :: #sctp_assoc_change{}. @@ -166,21 +200,21 @@ connect(S, Addr, Port, Opts, Timeout) -> end. -spec connect_init(Socket, Addr, Port, Opts) -> - ok | {error, posix()} when + ok | {error, inet:posix()} when Socket :: sctp_socket(), - Addr :: ip_address() | hostname(), - Port :: port_number(), - Opts :: [sctp_option()]. + Addr :: inet:ip_address() | inet:hostname(), + Port :: inet:port_number(), + Opts :: [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 + ok | {error, inet:posix()} when Socket :: sctp_socket(), - Addr :: ip_address() | hostname(), - Port :: port_number(), - Opts :: [sctp_option()], + Addr :: inet:ip_address() | inet:hostname(), + Port :: inet:port_number(), + Opts :: [option()], Timeout :: timeout(). connect_init(S, Addr, Port, Opts, Timeout) -> @@ -232,7 +266,7 @@ eof(S, #sctp_assoc_change{assoc_id=AssocId}) when is_port(S) -> eof(S, Assoc) -> erlang:error(badarg, [S,Assoc]). --spec abort(Socket, Assoc) -> ok | {error, posix()} when +-spec abort(Socket, Assoc) -> ok | {error, inet:posix()} when Socket :: sctp_socket(), Assoc :: #sctp_assoc_change{}. @@ -294,13 +328,13 @@ send(S, AssocChange, Stream, Data) -> -spec recv(Socket) -> {ok, {FromIP, FromPort, AncData, Data}} | {error, Reason} when Socket :: sctp_socket(), - FromIP :: ip_address(), - FromPort :: port_number(), + FromIP :: inet:ip_address(), + FromPort :: inet: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{} + Reason :: inet:posix() | #sctp_send_failed{} | #sctp_paddr_change{} | #sctp_pdapi_event{} | #sctp_remote_error{} | #sctp_shutdown_event{}. @@ -311,13 +345,13 @@ recv(S) -> | {error, Reason} when Socket :: sctp_socket(), Timeout :: timeout(), - FromIP :: ip_address(), - FromPort :: port_number(), + FromIP :: inet:ip_address(), + FromPort :: inet: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{} + Reason :: inet:posix() | #sctp_send_failed{} | #sctp_paddr_change{} | #sctp_pdapi_event{} | #sctp_remote_error{} | #sctp_shutdown_event{}. diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index bee61ca84a..df326b59d6 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -28,34 +28,108 @@ -include("inet_int.hrl"). --type hostname() :: inet:hostname(). --type ip_address() :: inet:ip_address(). --type port_number() :: 0..65535. --type posix() :: inet:posix(). +-type option() :: + {active, true | false | once} | + {bit8, clear | set | on | off} | + {buffer, non_neg_integer()} | + {delay_send, boolean()} | + {deliver, port | term} | + {dontroute, boolean()} | + {exit_on_close, boolean()} | + {header, non_neg_integer()} | + {high_watermark, non_neg_integer()} | + {keepalive, boolean()} | + {linger, {boolean(), non_neg_integer()}} | + {low_watermark, non_neg_integer()} | + {mode, list | binary} | list | binary | + {nodelay, boolean()} | + {packet, + 0 | 1 | 2 | 4 | raw | sunrm | asn1 | + cdr | fcgi | line | tpkt | http | httph | http_bin | httph_bin } | + {packet_size, non_neg_integer()} | + {priority, non_neg_integer()} | + {raw, + Protocol :: non_neg_integer(), + OptionNum :: non_neg_integer(), + ValueBin :: binary()} | + {recbuf, non_neg_integer()} | + {reuseaddr, boolean()} | + {send_timeout, non_neg_integer() | infinity} | + {send_timeout_close, boolean()} | + {sndbuf, non_neg_integer()} | + {tos, non_neg_integer()}. +-type option_name() :: + active | + bit8 | + buffer | + delay_send | + deliver | + dontroute | + exit_on_close | + header | + high_watermark | + keepalive | + linger | + low_watermark | + mode | + nodelay | + packet | + packet_size | + priority | + {raw, + Protocol :: non_neg_integer(), + OptionNum :: non_neg_integer(), + ValueSpec :: (ValueSize :: non_neg_integer()) | + (ValueBin :: binary())} | + recbuf | + reuseaddr | + send_timeout | + send_timeout_close | + sndbuf | + tos. +-type connect_option() :: + {ip, inet:ip_address()} | + {fd, Fd :: non_neg_integer()} | + {ifaddr, inet:ip_address()} | + inet:address_family() | + {port, inet:port_number()} | + {tcp_module, module()} | + option(). +-type listen_option() :: + {ip, inet:ip_address()} | + {fd, Fd :: non_neg_integer()} | + {ifaddr, inet:ip_address()} | + inet:address_family() | + {port, inet:port_number()} | + {backlog, B :: non_neg_integer()} | + {tcp_module, module()} | + option(). -type socket() :: port(). +-export_type([option/0, option_name/0, connect_option/0, listen_option/0]). + %% %% Connect a socket %% -spec connect(Address, Port, Options) -> {ok, Socket} | {error, Reason} when - Address :: ip_address() | hostname(), - Port :: port_number(), - Options :: [Opt :: term()], + Address :: inet:ip_address() | inet:hostname(), + Port :: inet:port_number(), + Options :: [connect_option()], Socket :: socket(), - Reason :: posix(). + Reason :: inet: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()], + Address :: inet:ip_address() | inet:hostname(), + Port :: inet:port_number(), + Options :: [connect_option()], Timeout :: timeout(), Socket :: socket(), - Reason :: posix(). + Reason :: inet:posix(). connect(Address, Port, Opts, Time) -> Timer = inet:start_timer(Time), @@ -97,10 +171,10 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) -> %% -spec listen(Port, Options) -> {ok, ListenSocket} | {error, Reason} when - Port :: port_number(), - Options :: [Opt :: term()], + Port :: inet:port_number(), + Options :: [listen_option()], ListenSocket :: socket(), - Reason :: posix(). + Reason :: inet:posix(). listen(Port, Opts) -> Mod = mod(Opts, undefined), @@ -119,7 +193,7 @@ listen(Port, Opts) -> -spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when ListenSocket :: socket(), Socket :: socket(), - Reason :: closed | timeout | posix(). + Reason :: closed | timeout | inet:posix(). accept(S) -> case inet_db:lookup_socket(S) of @@ -133,7 +207,7 @@ accept(S) -> ListenSocket :: socket(), Timeout :: timeout(), Socket :: socket(), - Reason :: closed | timeout | posix(). + Reason :: closed | timeout | inet:posix(). accept(S, Time) when is_port(S) -> case inet_db:lookup_socket(S) of @@ -150,7 +224,7 @@ accept(S, Time) when is_port(S) -> -spec shutdown(Socket, How) -> ok | {error, Reason} when Socket :: socket(), How :: read | write | read_write, - Reason :: posix(). + Reason :: inet:posix(). shutdown(S, How) when is_port(S) -> case inet_db:lookup_socket(S) of @@ -177,7 +251,7 @@ close(S) -> -spec send(Socket, Packet) -> ok | {error, Reason} when Socket :: socket(), Packet :: string() | binary(), - Reason :: posix(). + Reason :: inet:posix(). send(S, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of @@ -195,7 +269,7 @@ send(S, Packet) when is_port(S) -> Socket :: socket(), Length :: non_neg_integer(), Packet :: string() | binary() | HttpPacket, - Reason :: closed | posix(), + Reason :: closed | inet:posix(), HttpPacket :: term(). recv(S, Length) when is_port(S) -> @@ -211,7 +285,7 @@ recv(S, Length) when is_port(S) -> Length :: non_neg_integer(), Timeout :: timeout(), Packet :: string() | binary() | HttpPacket, - Reason :: closed | posix(), + Reason :: closed | inet:posix(), HttpPacket :: term(). recv(S, Length, Time) when is_port(S) -> @@ -237,7 +311,7 @@ unrecv(S, Data) when is_port(S) -> -spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: socket(), Pid :: pid(), - Reason :: closed | not_owner | posix(). + Reason :: closed | not_owner | inet:posix(). controlling_process(S, NewOwner) -> case inet_db:lookup_socket(S) of diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 7d14615c04..554f4ece4a 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -25,25 +25,74 @@ -include("inet_int.hrl"). --type hostname() :: inet:hostname(). --type ip_address() :: inet:ip_address(). --type port_number() :: 0..65535. --type posix() :: inet:posix(). +-type option() :: + {active, true | false | once} | + {add_membership, {inet:ip_address(), inet:ip_address()}} | + {broadcast, boolean()} | + {buffer, non_neg_integer()} | + {deliver, port | term} | + {dontroute, boolean()} | + {drop_membership, {inet:ip_address(), inet:ip_address()}} | + {header, non_neg_integer()} | + {mode, list | binary} | list | binary | + {multicast_if, inet:ip_address()} | + {multicast_loop, boolean()} | + {multicast_ttl, non_neg_integer()} | + {priority, non_neg_integer()} | + {raw, + Protocol :: non_neg_integer(), + OptionNum :: non_neg_integer(), + ValueBin :: binary()} | + {read_packets, non_neg_integer()} | + {recbuf, non_neg_integer()} | + {reuseaddr, boolean()} | + {sndbuf, non_neg_integer()} | + {tos, non_neg_integer()}. +-type option_name() :: + active | + broadcast | + buffer | + deliver | + dontroute | + header | + mode | + multicast_if | + multicast_loop | + multicast_ttl | + priority | + {raw, + Protocol :: non_neg_integer(), + OptionNum :: non_neg_integer(), + ValueSpec :: (ValueSize :: non_neg_integer()) | + (ValueBin :: binary())} | + read_packets | + recbuf | + reuseaddr | + sndbuf | + tos. -type socket() :: port(). +-export_type([option/0, option_name/0]). + -spec open(Port) -> {ok, Socket} | {error, Reason} when - Port :: port_number(), + Port :: inet:port_number(), Socket :: socket(), - Reason :: posix(). + Reason :: inet:posix(). open(Port) -> open(Port, []). -spec open(Port, Opts) -> {ok, Socket} | {error, Reason} when - Port :: port_number(), - Opts :: [Opt :: term()], + Port :: inet:port_number(), + Opts :: [Option], + Option :: {ip, inet:ip_address()} + | {fd, non_neg_integer()} + | {ifaddr, inet:ip_address()} + | inet:address_family() + | {port, inet:port_number()} + | option(), Socket :: socket(), - Reason :: posix(). + Reason :: inet:posix(). open(Port, Opts) -> Mod = mod(Opts, undefined), @@ -58,10 +107,10 @@ close(S) -> -spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when Socket :: socket(), - Address :: ip_address() | hostname(), - Port :: port_number(), + Address :: inet:ip_address() | inet:hostname(), + Port :: inet:port_number(), Packet :: string() | binary(), - Reason :: not_owner | posix(). + Reason :: not_owner | inet:posix(). send(S, Address, Port, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of @@ -92,10 +141,10 @@ send(S, Packet) when is_port(S) -> {ok, {Address, Port, Packet}} | {error, Reason} when Socket :: socket(), Length :: non_neg_integer(), - Address :: ip_address(), - Port :: port_number(), + Address :: inet:ip_address(), + Port :: inet:port_number(), Packet :: string() | binary(), - Reason :: not_owner | posix(). + Reason :: not_owner | inet:posix(). recv(S,Len) when is_port(S), is_integer(Len) -> case inet_db:lookup_socket(S) of @@ -110,10 +159,10 @@ recv(S,Len) when is_port(S), is_integer(Len) -> Socket :: socket(), Length :: non_neg_integer(), Timeout :: timeout(), - Address :: ip_address(), - Port :: port_number(), + Address :: inet:ip_address(), + Port :: inet:port_number(), Packet :: string() | binary(), - Reason :: not_owner | posix(). + Reason :: not_owner | inet:posix(). recv(S,Len,Time) when is_port(S) -> case inet_db:lookup_socket(S) of diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 5649188c38..48a6f3db65 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -63,8 +63,9 @@ %% timer interface -export([start_timer/1, timeout/1, timeout/2, stop_timer/1]). --export_type([family_option/0, hostent/0, hostname/0, ip4_address/0, - ip6_address/0, ip_address/0, posix/0, socket/0]). +-export_type([address_family/0, hostent/0, hostname/0, ip4_address/0, + ip6_address/0, ip_address/0, posix/0, socket/0, + port_number/0]). %% imports -import(lists, [append/1, duplicate/2, filter/2, foldl/3]). @@ -87,98 +88,15 @@ -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 port_number() :: 0..65535. -type posix() :: exbadport | exbadseq | file:posix(). -type socket() :: port(). -type socket_setopt() :: - {'raw', non_neg_integer(), non_neg_integer(), binary()} | - %% TCP/UDP options - {'reuseaddr', boolean()} | - {'keepalive', boolean()} | - {'dontroute', boolean()} | - {'linger', {boolean(), non_neg_integer()}} | - {'broadcast', boolean()} | - {'sndbuf', non_neg_integer()} | - {'recbuf', non_neg_integer()} | - {'priority', non_neg_integer()} | - {'tos', non_neg_integer()} | - {'nodelay', boolean()} | - {'multicast_ttl', non_neg_integer()} | - {'multicast_loop', boolean()} | - {'multicast_if', ip_address()} | - {'add_membership', {ip_address(), ip_address()}} | - {'drop_membership', {ip_address(), ip_address()}} | - {'header', non_neg_integer()} | - {'buffer', non_neg_integer()} | - {'active', boolean() | 'once'} | - {'packet', - 0 | 1 | 2 | 4 | 'raw' | 'sunrm' | 'asn1' | - 'cdr' | 'fcgi' | 'line' | 'tpkt' | 'http' | 'httph' | 'http_bin' | 'httph_bin' } | - {'mode', 'list' | 'binary'} | - {'port', 'port', 'term'} | - {'exit_on_close', boolean()} | - {'low_watermark', non_neg_integer()} | - {'high_watermark', non_neg_integer()} | - {'bit8', 'clear' | 'set' | 'on' | 'off'} | - {'send_timeout', non_neg_integer() | 'infinity'} | - {'send_timeout_close', boolean()} | - {'delay_send', boolean()} | - {'packet_size', non_neg_integer()} | - {'read_packets', non_neg_integer()} | - %% SCTP options - {'sctp_rtoinfo', #sctp_rtoinfo{}} | - {'sctp_associnfo', #sctp_assocparams{}} | - {'sctp_initmsg', #sctp_initmsg{}} | - {'sctp_nodelay', boolean()} | - {'sctp_autoclose', non_neg_integer()} | - {'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{}}. + gen_sctp:option() | gen_tcp:option() | gen_udp:option(). -type socket_getopt() :: - {'raw', - non_neg_integer(), non_neg_integer(), binary()|non_neg_integer()} | - %% TCP/UDP options - 'reuseaddr' | 'keepalive' | 'dontroute' | 'linger' | - 'broadcast' | 'sndbuf' | 'recbuf' | 'priority' | 'tos' | 'nodelay' | - 'multicast_ttl' | 'multicast_loop' | 'multicast_if' | - 'add_membership' | 'drop_membership' | - 'header' | 'buffer' | 'active' | 'packet' | 'mode' | 'port' | - 'exit_on_close' | 'low_watermark' | 'high_watermark' | 'bit8' | - 'send_timeout' | 'send_timeout_close' | - 'delay_send' | 'packet_size' | 'read_packets' | - %% SCTP options - {'sctp_status', #sctp_status{}} | - 'sctp_get_peer_addr_info' | - {'sctp_get_peer_addr_info', #sctp_status{}} | - 'sctp_rtoinfo' | - {'sctp_rtoinfo', #sctp_rtoinfo{}} | - 'sctp_associnfo' | - {'sctp_associnfo', #sctp_assocparams{}} | - 'sctp_initmsg' | - {'sctp_initmsg', #sctp_initmsg{}} | - 'sctp_nodelay' | 'sctp_autoclose' | 'sctp_disable_fragments' | - 'sctp_i_want_mapped_v4_addr' | 'sctp_maxseg' | - {'sctp_primary_addr', #sctp_prim{}} | - {'sctp_set_peer_primary_addr', #sctp_setpeerprim{}} | - 'sctp_adaptation_layer' | - {'sctp_adaptation_layer', #sctp_setadaptation{}} | - {'sctp_peer_addr_params', #sctp_paddrparams{}} | - 'sctp_default_send_param' | - {'sctp_default_send_param', #sctp_sndrcvinfo{}} | - 'sctp_events' | - {'sctp_events', #sctp_event_subscribe{}} | - 'sctp_delayed_ack_time' | - {'sctp_delayed_ack_time', #sctp_assoc_value{}}. - + gen_sctp:option_name() | gen_tcp:option_name() | gen_udp:option_name(). -type ether_address() :: [0..255]. -type if_setopt() :: @@ -196,7 +114,7 @@ 'addr' | 'broadaddr' | 'dstaddr' | 'mtu' | 'netmask' | 'flags' |'hwaddr'. --type family_option() :: 'inet' | 'inet6'. +-type address_family() :: 'inet' | 'inet6'. -type protocol_option() :: 'tcp' | 'udp' | 'sctp'. -type stat_option() :: 'recv_cnt' | 'recv_max' | 'recv_avg' | 'recv_oct' | 'recv_dvi' | @@ -229,7 +147,7 @@ close(Socket) -> peername(Socket) -> prim_inet:peername(Socket). --spec setpeername(Socket :: socket(), Address :: {ip_address(), ip_port()}) -> +-spec setpeername(Socket :: socket(), Address :: {ip_address(), port_number()}) -> 'ok' | {'error', any()}. setpeername(Socket, {IP,Port}) -> @@ -246,7 +164,7 @@ setpeername(Socket, undefined) -> sockname(Socket) -> prim_inet:sockname(Socket). --spec setsockname(Socket :: socket(), Address :: {ip_address(), ip_port()}) -> +-spec setsockname(Socket :: socket(), Address :: {ip_address(), port_number()}) -> 'ok' | {'error', any()}. setsockname(Socket, {IP,Port}) -> @@ -254,7 +172,9 @@ setsockname(Socket, {IP,Port}) -> setsockname(Socket, undefined) -> prim_inet:setsockname(Socket, undefined). --spec port(Socket :: socket()) -> {'ok', ip_port()} | {'error', any()}. +-spec port(Socket) -> {'ok', Port} | {'error', any()} when + Socket :: socket(), + Port :: port_number(). port(Socket) -> case prim_inet:sockname(Socket) of @@ -268,16 +188,18 @@ port(Socket) -> send(Socket, Packet) -> prim_inet:send(Socket, Packet). --spec setopts(Socket :: socket(), Opts :: [socket_setopt()]) -> - 'ok' | {'error', posix()}. +-spec setopts(Socket, Options) -> ok | {error, posix()} when + Socket :: socket(), + Options :: [socket_setopt()]. setopts(Socket, Opts) -> prim_inet:setopts(Socket, Opts). -spec getopts(Socket, Options) -> - {'ok', [socket_setopt()]} | {'error', posix()} when + {'ok', OptionValues} | {'error', posix()} when Socket :: socket(), - Options :: [socket_getopt()]. + Options :: [socket_getopt()], + OptionValues :: [socket_setopt()]. getopts(Socket, Opts) -> prim_inet:getopts(Socket, Opts). @@ -419,14 +341,19 @@ gethostname() -> gethostname(Socket) -> prim_inet:gethostname(Socket). --spec getstat(Socket :: socket()) -> - {'ok', [{stat_option(), integer()}]} | {'error', posix()}. +-spec getstat(Socket) -> + {ok, OptionValues} | {error, posix()} when + Socket :: socket(), + OptionValues :: [{stat_option(), integer()}]. getstat(Socket) -> prim_inet:getstat(Socket, stats()). --spec getstat(Socket :: socket(), Statoptions :: [stat_option()]) -> - {'ok', [{stat_option(), integer()}]} | {'error', posix()}. +-spec getstat(Socket, Options) -> + {ok, OptionValues} | {error, posix()} when + Socket :: socket(), + Options :: [stat_option()], + OptionValues :: [{stat_option(), integer()}]. getstat(Socket,What) -> prim_inet:getstat(Socket, What). @@ -441,14 +368,14 @@ gethostbyname(Name) -> -spec gethostbyname(Hostname, Family) -> {ok, Hostent} | {error, posix()} when Hostname :: hostname(), - Family :: family_option(), + Family :: address_family(), Hostent :: hostent(). gethostbyname(Name,Family) -> gethostbyname_tm(Name, Family, false). -spec gethostbyname(Name :: hostname(), - Family :: family_option(), + Family :: address_family(), Timeout :: non_neg_integer() | 'infinity') -> {'ok', #hostent{}} | {'error', posix()}. @@ -527,14 +454,14 @@ getfd(Socket) -> -spec getaddr(Host, Family) -> {ok, Address} | {error, posix()} when Host :: ip_address() | hostname(), - Family :: family_option(), + Family :: address_family(), Address :: ip_address(). getaddr(Address, Family) -> getaddr(Address, Family, infinity). -spec getaddr(Host :: ip_address() | hostname(), - Family :: family_option(), + Family :: address_family(), Timeout :: non_neg_integer() | 'infinity') -> {'ok', ip_address()} | {'error', posix()}. @@ -553,14 +480,14 @@ getaddr_tm(Address, Family, Timer) -> -spec getaddrs(Host, Family) -> {ok, Addresses} | {error, posix()} when Host :: ip_address() | hostname(), - Family :: family_option(), + Family :: address_family(), Addresses :: [ip_address()]. getaddrs(Address, Family) -> getaddrs(Address, Family, infinity). -spec getaddrs(Host :: ip_address() | string() | atom(), - Family :: family_option(), + Family :: address_family(), Timeout :: non_neg_integer() | 'infinity') -> {'ok', [ip_address()]} | {'error', posix()}. @@ -570,7 +497,7 @@ getaddrs(Address, Family, Timeout) -> stop_timer(Timer), Res. --spec getservbyport(Port :: ip_port(), Protocol :: atom() | string()) -> +-spec getservbyport(Port :: port_number(), Protocol :: atom() | string()) -> {'ok', string()} | {'error', posix()}. getservbyport(Port, Proto) -> @@ -584,7 +511,7 @@ getservbyport(Port, Proto) -> -spec getservbyname(Name :: atom() | string(), Protocol :: atom() | string()) -> - {'ok', ip_port()} | {'error', posix()}. + {'ok', port_number()} | {'error', posix()}. getservbyname(Name, Protocol) when is_atom(Name) -> case inet_udp:open(0, []) of @@ -1067,7 +994,7 @@ gethostbyaddr_tm_native(Addr, Timer, Opts) -> -spec open(Fd :: integer(), Addr :: ip_address(), - Port :: ip_port(), + Port :: port_number(), Opts :: [socket_setopt()], Protocol :: protocol_option(), Family :: 'inet' | 'inet6', @@ -1108,7 +1035,7 @@ open(Fd, _Addr, _Port, Opts, Protocol, Family, Module) -> -spec fdopen(Fd :: non_neg_integer(), Opts :: [socket_setopt()], Protocol :: protocol_option(), - Family :: family_option(), + Family :: address_family(), Module :: atom()) -> {'ok', socket()} | {'error', posix()}. diff --git a/lib/kernel/src/inet_res.erl b/lib/kernel/src/inet_res.erl index d1f5644ff7..59ba408d7a 100644 --- a/lib/kernel/src/inet_res.erl +++ b/lib/kernel/src/inet_res.erl @@ -407,7 +407,7 @@ gethostbyname(Name) -> -spec gethostbyname(Name, Family) -> {ok, Hostent} | {error, Reason} when Name :: dns_name(), Hostent :: inet:hostent(), - Family :: inet:family_option(), + Family :: inet:address_family(), Reason :: inet:posix() | res_error(). gethostbyname(Name,Family) -> @@ -418,7 +418,7 @@ gethostbyname(Name,Family) -> Name :: dns_name(), Hostent :: inet:hostent(), Timeout :: timeout(), - Family :: inet:family_option(), + Family :: inet:address_family(), Reason :: inet:posix() | res_error(). gethostbyname(Name,Family,Timeout) -> diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index d8a5519195..b734d7fd98 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -400,6 +400,7 @@ open_fd(Config) when is_list(Config) -> {ok,S1} = gen_udp:open(0), {ok,P2} = inet:port(S1), {ok,FD} = prim_inet:getfd(S1), + {error,einval} = gen_udp:open(P2, [inet6, {fd,FD}]), {ok,S2} = gen_udp:open(P2, [{fd,FD}]), {ok,S3} = gen_udp:open(0), {ok,P3} = inet:port(S3), diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index 3dd7da08b9..11e311d72d 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -90,7 +90,7 @@ Datatype - USER_INT | USER_SMALL_INT | {USER_DECIMAL, Precision, Scale} | {USER_NMERIC, Precision, Scale} | {USER_CHAR, Max} | {USER_VARCHAR, Max} | {USER_WVARCHAR, Max} | {USER_FLOAT, Precision} | USER_REAL | USER_DOUBLE | - USER_TIMESTAMP + USER_TIMESTAMP | {USER_WLONGVARCHAR, Max} Scale - integer Precision - integer Max - integer @@ -1228,7 +1228,7 @@ static db_result_msg encode_column_name_list(SQLSMALLINT num_of_columns, &nullable))) DO_EXIT(EXIT_DESC); - if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY) + if(sql_type == SQL_LONGVARCHAR || sql_type == SQL_LONGVARBINARY || sql_type == SQL_WLONGVARCHAR) size = MAXCOLSIZE; (columns(state)[i]).type.decimal_digits = dec_digits; @@ -1537,6 +1537,11 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, case SQL_LONGVARCHAR: ei_x_encode_atom(&dynamic_buffer(state), "SQL_LONGVARCHAR"); break; + case SQL_WLONGVARCHAR: + ei_x_encode_tuple_header(&dynamic_buffer(state), 2); + ei_x_encode_atom(&dynamic_buffer(state), "sql_wlongvarchar"); + ei_x_encode_long(&dynamic_buffer(state), size); + break; case SQL_VARBINARY: ei_x_encode_atom(&dynamic_buffer(state), "SQL_VARBINARY"); break; @@ -2139,10 +2144,14 @@ static void init_param_column(param_array *params, byte *buffer, int *index, break; case USER_WCHAR: case USER_WVARCHAR: - if(user_type == USER_WCHAR) { - params->type.sql = SQL_WCHAR; - } else { - params->type.sql = SQL_WVARCHAR; + case USER_WLONGVARCHAR: + switch (user_type) { + case USER_WCHAR: + params->type.sql = SQL_WCHAR; break; + case USER_WVARCHAR: + params->type.sql = SQL_WVARCHAR; break; + default: + params->type.sql = SQL_WLONGVARCHAR; break; } ei_decode_long(buffer, index, &length); /* Max string length + string terminator */ @@ -2308,6 +2317,7 @@ static db_result_msg map_sql_2_c_column(db_column* column) break; case SQL_WCHAR: case SQL_WVARCHAR: + case SQL_WLONGVARCHAR: column -> type.len = (column -> type.col_size + 1)*sizeof(SQLWCHAR); column -> type.c = SQL_C_WCHAR; column -> type.strlen_or_indptr = SQL_NTS; diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h index 3e2b22ab7d..314fbf32c6 100644 --- a/lib/odbc/c_src/odbcserver.h +++ b/lib/odbc/c_src/odbcserver.h @@ -115,6 +115,7 @@ #define USER_WCHAR 12 #define USER_WVARCHAR 13 #define USER_TIMESTAMP 14 +#define USER_WLONGVARCHAR 15 /*------------------------ TYPDEFS ----------------------------------*/ diff --git a/lib/odbc/doc/src/databases.xml b/lib/odbc/doc/src/databases.xml index a6ba0e5245..09f5a5af5b 100644 --- a/lib/odbc/doc/src/databases.xml +++ b/lib/odbc/doc/src/databases.xml @@ -205,6 +205,10 @@ when p >= 16 </cell> <cell align="left" valign="middle">String | Binary (configurable)</cell> </row> <row> + <cell align="left" valign="middle">SQL_WLONGVARCHAR(size) </cell> + <cell align="left" valign="middle">Unicode binary encoded as UTF16 little endian.</cell> + </row> + <row> <cell align="left" valign="middle">SQL_BINARY</cell> <cell align="left" valign="middle">String | Binary (configurable)</cell> </row> diff --git a/lib/odbc/doc/src/odbc.xml b/lib/odbc/doc/src/odbc.xml index 70d8cfbe22..11ca97f743 100644 --- a/lib/odbc/doc/src/odbc.xml +++ b/lib/odbc/doc/src/odbc.xml @@ -102,7 +102,7 @@ {sql_decimal, precision(), scale()} | {sql_numeric, precision(), scale()} | {sql_char, size()} | {sql_wchar, size()} | {sql_varchar, size()} | {sql_wvarchar, size()}| {sql_float, precision()} | - {sql_float, precision()} | sql_real | sql_double | sql_bit | atom() + {sql_wlongvarchar, size()} | {sql_float, precision()} | sql_real | sql_double | sql_bit | atom() </code> <code type="none"> precision() = integer() </code> diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src index 2a6667ccd3..f3a3af8c29 100644 --- a/lib/odbc/src/odbc.appup.src +++ b/lib/odbc/src/odbc.appup.src @@ -1,8 +1,10 @@ %% -*- erlang -*- {"%VSN%", [ - {"2.10.9", [{restart_application, ssl}]} + {"2.10.10", [{restart_application, odbc}]}, + {"2.10.9", [{restart_application, odbc}]} ], [ - {"2.10.9", [{restart_application, ssl}]} + {"2.10.10", [{restart_application, odbc}]}, + {"2.10.9", [{restart_application, odbc}]} ]}. diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl index 2634450d4a..68497292db 100644 --- a/lib/odbc/src/odbc.erl +++ b/lib/odbc/src/odbc.erl @@ -925,6 +925,9 @@ fix_params({{sql_wchar, Max}, InOut, Values}) -> fix_params({{sql_wvarchar, Max}, InOut, Values}) -> NewValues = string_terminate(Values), {?USER_WVARCHAR, Max, fix_inout(InOut), NewValues}; +fix_params({{sql_wlongvarchar, Max}, InOut, Values}) -> + NewValues = string_terminate(Values), + {?USER_WLONGVARCHAR, Max, fix_inout(InOut), NewValues}; fix_params({{sql_float, Precision}, InOut, Values}) -> {?USER_FLOAT, Precision, fix_inout(InOut), Values}; fix_params({sql_real, InOut, Values}) -> diff --git a/lib/odbc/src/odbc_internal.hrl b/lib/odbc/src/odbc_internal.hrl index aa60120f9a..c0e7d9657b 100644 --- a/lib/odbc/src/odbc_internal.hrl +++ b/lib/odbc/src/odbc_internal.hrl @@ -72,6 +72,7 @@ -define(USER_WCHAR, 12). -define(USER_WVARCHAR, 13). -define(USER_TIMESTAMP, 14). +-define(USER_WLONGVARCHAR, 15). %% INPUT & OUTPUT TYPE -define(IN, 0). diff --git a/lib/odbc/test/mysql.erl b/lib/odbc/test/mysql.erl index 76ffd3ecc9..c990793213 100644 --- a/lib/odbc/test/mysql.erl +++ b/lib/odbc/test/mysql.erl @@ -26,7 +26,22 @@ %------------------------------------------------------------------------- connection_string() -> - "DSN=MySQL;Database=odbctest;Uid=odbctest;Pwd=gurka;CHARSET=utf8;SSTMT=SET NAMES 'utf8';". + case test_server:os_type() of + {unix, linux} -> + "DSN=MySQL;Database=odbctest;Uid=odbctest;Pwd=gurka;CHARSET=utf8;SSTMT=SET NAMES 'utf8';"; + {unix, sunos} -> + solaris_str(); + {unix, darwin} -> + "DSN=MySQLMac;Database=odbctest;Uid=odbctest;Pwd=gurka;CHARSET=utf8;SSTMT=SET NAMES 'utf8';" + end. + +solaris_str() -> + case erlang:system_info(system_architecture) of + "sparc" ++ _ -> + "DSN=MySQLSolaris10;Database=odbctest;Uid=odbctest;Pwd=gurka;CHARSET=utf8;SSTMT=SET NAMES 'utf8';"; + "i386" ++ _ -> + "DSN=MySQLSolaris10i386;Database=odbctest;Uid=odbctest;Pwd=gurka;CHARSET=utf8;SSTMT=SET NAMES 'utf8';" + end. %------------------------------------------------------------------------- insert_result() -> @@ -98,10 +113,6 @@ create_text_table() -> " (FIELD text)". %------------------------------------------------------------------------- -create_unicode_table() -> - " (FIELD text)". - -%------------------------------------------------------------------------- create_timestamp_table() -> " (FIELD TIMESTAMP)". diff --git a/lib/odbc/test/odbc_connect_SUITE.erl b/lib/odbc/test/odbc_connect_SUITE.erl index e59be772e3..a076c4dfff 100644 --- a/lib/odbc/test/odbc_connect_SUITE.erl +++ b/lib/odbc/test/odbc_connect_SUITE.erl @@ -76,20 +76,26 @@ end_per_group(_GroupName, 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_suite(Config) -> - case (catch odbc:start()) of - ok -> - case catch odbc:connect(?RDBMS:connection_string(), - [{auto_commit, off}] ++ odbc_test_lib:platform_options()) of - {ok, Ref} -> - odbc:disconnect(Ref), - [{tableName, odbc_test_lib:unique_table_name()} | Config]; - _ -> - {skip, "ODBC is not properly setup"} - end; - _ -> - {skip,"ODBC not startable"} - end. +init_per_suite(Config) when is_list(Config) -> + case odbc_test_lib:skip() of + true -> + {skip, "ODBC not supported"}; + false -> + case (catch odbc:start()) of + ok -> + case catch odbc:connect(?RDBMS:connection_string(), + [{auto_commit, off}] ++ odbc_test_lib:platform_options()) of + {ok, Ref} -> + odbc:disconnect(Ref), + [{tableName, odbc_test_lib:unique_table_name()} | Config]; + _ -> + {skip, "ODBC is not properly setup"} + end; + _ -> + {skip,"ODBC not startable"} + end + end. + %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] diff --git a/lib/odbc/test/odbc_data_type_SUITE.erl b/lib/odbc/test/odbc_data_type_SUITE.erl index 84c99e183b..d61a91f973 100644 --- a/lib/odbc/test/odbc_data_type_SUITE.erl +++ b/lib/odbc/test/odbc_data_type_SUITE.erl @@ -45,7 +45,7 @@ all() -> case odbc_test_lib:odbc_check() of ok -> [{group, char},{group, fixed_char}, {group, binary_char}, - {group, fixed_binary_char}, + {group, fixed_binary_char}, {group, unicode}, {group, int}, {group, floats}, {group, dec_and_num}, timestamp]; Other -> {skip, Other} @@ -55,18 +55,18 @@ groups() -> [{char, [], [varchar_lower_limit, varchar_upper_limit, varchar_no_padding, - text_lower_limit, text_upper_limit, unicode]}, + text_lower_limit, text_upper_limit]}, {fixed_char, [], [char_fixed_lower_limit, char_fixed_upper_limit, char_fixed_padding]}, {binary_char, [], [binary_varchar_lower_limit, binary_varchar_upper_limit, binary_varchar_no_padding, - binary_text_lower_limit, binary_text_upper_limit, - unicode]}, + binary_text_lower_limit, binary_text_upper_limit]}, {fixed_binary_char, [], [binary_char_fixed_lower_limit, binary_char_fixed_upper_limit, binary_char_fixed_padding]}, + {unicode, [], [utf8, nchar, nvarchar]}, {int, [], [tiny_int_lower_limit, tiny_int_upper_limit, small_int_lower_limit, small_int_upper_limit, @@ -88,14 +88,22 @@ init_per_group(GroupName, Config) when GroupName == fixed_char; Config end; +init_per_group(unicode, Config) -> + case {os:type(), erlang:system_info({wordsize, external})} of + {{unix, _}, 4} -> + Config; + {{unix, _}, _} -> + {skip, "Postgres drivers pre version psqlODBC 08.04.0200 have utf8-problems"}; + _ -> + Config + end; + init_per_group(_GroupName, Config) -> Config. end_per_group(_GroupName, Config) -> Config. - - %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -105,14 +113,18 @@ end_per_group(_GroupName, 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_suite(Config) -> - case (catch odbc:start()) of - ok -> - [{tableName, odbc_test_lib:unique_table_name()} | Config]; - _ -> - {skip, "ODBC not startable"} +init_per_suite(Config) when is_list(Config) -> + case odbc_test_lib:skip() of + true -> + {skip, "ODBC not supported"}; + false -> + case (catch odbc:start()) of + ok -> + [{tableName, odbc_test_lib:unique_table_name()}| Config]; + _ -> + {skip, "ODBC not startable"} + end end. - %%-------------------------------------------------------------------- %% Function: end_per_suite(Config) -> _ %% Config - [tuple()] @@ -164,6 +176,16 @@ init_per_testcase(param_insert_tiny_int = Case, Config) -> false -> {skip, "Not supported by driver"} end; + +init_per_testcase(Case, Config) when Case == nchar; + Case == nvarchar -> + case ?RDBMS of + sqlserver -> + common_init_per_testcase(Case, Config); + _ -> + {skip, "Not supported by driver"} + end; + init_per_testcase(Case, Config) -> common_init_per_testcase(Case, Config). @@ -173,7 +195,9 @@ common_init_per_testcase(Case, Config) -> "binary" ++ _ -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{binary_strings, on}] ++ PlatformOptions); - "unicode" -> + LCase when LCase == "utf8"; + LCase == "nchar"; + LCase == "nvarchar" -> {ok, Ref} = odbc:connect(?RDBMS:connection_string(), [{binary_strings, on}] ++ PlatformOptions); _ -> @@ -1435,17 +1459,16 @@ num_bignum(Config) when is_list(Config) -> ["FIELD"] = odbc_test_lib:to_upper(Fields1). %%------------------------------------------------------------------------ -unicode(doc) -> +utf8(doc) -> ["Test unicode support"]; -unicode(suit) -> +utf8(suit) -> []; -unicode(Config) when is_list(Config) -> +utf8(Config) when is_list(Config) -> Ref = ?config(connection_ref, Config), Table = ?config(tableName, Config), - {updated, _} = % Value == 0 || -1 driver dependent! - odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ - ?RDBMS:create_unicode_table()), + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ "(FIELD text)"), + Latin1Data = ["���������", "testasdf", "Row 3", @@ -1458,41 +1481,7 @@ unicode(Config) when is_list(Config) -> "Row 10", "Row 11", "Row 12"], - - case ?RDBMS of - sqlserver -> - w_char_support_win(Ref, Table, Latin1Data); - postgres -> - direct_utf8(Ref, Table, Latin1Data); - mysql -> - direct_utf8(Ref, Table, Latin1Data); - oracle -> - {skip, "not currently supported"} - end. - -w_char_support_win(Ref, Table, Latin1Data) -> - UnicodeIn = lists:map(fun(S) -> - unicode:characters_to_binary(S,latin1,{utf16,little}) - end, - Latin1Data), - - test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), - {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)", - [{{sql_wvarchar,50},UnicodeIn}]), - - {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), - - test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), - - Result = lists:map(fun({Unicode}) -> - unicode:characters_to_list(Unicode,{utf16,little}) - end, - UnicodeOut), - Latin1Data = Result. - - -direct_utf8(Ref, Table, Latin1Data) -> UnicodeIn = lists:map(fun(String) -> unicode:characters_to_binary(String,latin1,utf8) end, @@ -1513,6 +1502,37 @@ direct_utf8(Ref, Table, Latin1Data) -> test_server:format("Result: ~p ~n", [Result]), Latin1Data = Result. +%%------------------------------------------------------------------------ + +nchar(doc) -> + ["Test unicode nchar support in sqlserver"]; +nchar(suit) -> + []; +nchar(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD nchar(50))"), + + w_char_support(Ref, Table, sql_wvarchar, 50). + +%%------------------------------------------------------------------------ + +nvarchar(doc) -> + ["Test 'unicode' nvarchar support"]; +nvarchar(suit) -> + []; +nvarchar(Config) when is_list(Config) -> + Ref = ?config(connection_ref, Config), + Table = ?config(tableName, Config), + + {updated, _} = % Value == 0 || -1 driver dependent! + odbc:sql_query(Ref, "CREATE TABLE " ++ Table ++ + "(FIELD nvarchar(50))"), + + w_char_support(Ref, Table, sql_wlongvarchar, 50). %%------------------------------------------------------------------------ timestamp(doc) -> @@ -1541,3 +1561,43 @@ timestamp(Config) when is_list(Config) -> TimeStamps = lists:map(fun(Value) -> {Value} end, Data), {selected,_, TimeStamps} = odbc:sql_query(Ref, "SELECT * FROM " ++ Table). +%%------------------------------------------------------------------------ + +w_char_support(Ref, Table, CharType, Size) -> + Latin1Data = ["���������", + "testasdf", + "Row 3", + "Row 4", + "Row 5", + "Row 6", + "Row 7", + "Row 8", + "Row 9", + "Row 10", + "Row 11", + "Row 12"], + + UnicodeIn = lists:map(fun(S) -> + unicode:characters_to_binary(S,latin1,{utf16,little}) + end, + Latin1Data), + + test_server:format("UnicodeIn (utf 16): ~p ~n",[UnicodeIn]), + + {updated, _} = odbc:param_query(Ref, "INSERT INTO " ++ Table ++ "(FIELD) values(?)", + [{{CharType, Size},UnicodeIn}]), + + {selected,_,UnicodeOut} = odbc:sql_query(Ref,"SELECT * FROM " ++ Table), + + test_server:format("UnicodeOut: ~p~n", [UnicodeOut]), + + PadResult = lists:map(fun({Unicode}) -> + unicode:characters_to_list(Unicode,{utf16,little}) + end, + UnicodeOut), + + test_server:format("Result: ~p~n", [PadResult]), + + Result = lists:map(fun(Str) -> string:strip(Str) end, PadResult), + + Latin1Data = Result. diff --git a/lib/odbc/test/odbc_query_SUITE.erl b/lib/odbc/test/odbc_query_SUITE.erl index 76a214d553..1852678b4b 100644 --- a/lib/odbc/test/odbc_query_SUITE.erl +++ b/lib/odbc/test/odbc_query_SUITE.erl @@ -89,6 +89,7 @@ init_per_group(scrollable_cursors, Config) -> _ -> Config end; + init_per_group(_,Config) -> Config. @@ -105,11 +106,16 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) when is_list(Config) -> - case (catch odbc:start()) of - ok -> - [{tableName, odbc_test_lib:unique_table_name()}| Config]; - _ -> - {skip, "ODBC not startable"} + case odbc_test_lib:skip() of + true -> + {skip, "ODBC not supported"}; + false -> + case (catch odbc:start()) of + ok -> + [{tableName, odbc_test_lib:unique_table_name()}| Config]; + _ -> + {skip, "ODBC not startable"} + end end. %%-------------------------------------------------------------------- diff --git a/lib/odbc/test/odbc_start_SUITE.erl b/lib/odbc/test/odbc_start_SUITE.erl index 440c0ca921..e3a3440559 100644 --- a/lib/odbc/test/odbc_start_SUITE.erl +++ b/lib/odbc/test/odbc_start_SUITE.erl @@ -39,11 +39,18 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - case code:which(odbc) of - non_existing -> - {skip, "No ODBC built"}; - _ -> - [{tableName, odbc_test_lib:unique_table_name()} | Config] + case odbc_test_lib:skip() of + true -> + {skip, "ODBC not supported"}; + false -> + case code:which(odbc) of + non_existing -> + {skip, "No ODBC built"}; + _ -> + %% Make sure odbc is not already started + odbc:stop(), + [{tableName, odbc_test_lib:unique_table_name()} | Config] + end end. %%-------------------------------------------------------------------- diff --git a/lib/odbc/test/odbc_test.hrl b/lib/odbc/test/odbc_test.hrl index 397d04756b..f7bb338a7f 100644 --- a/lib/odbc/test/odbc_test.hrl +++ b/lib/odbc/test/odbc_test.hrl @@ -25,14 +25,16 @@ -define(RDBMS, case os:type() of {unix, sunos} -> - postgres; + mysql; {unix,linux} -> - case erlang:system_info(wordsize) of + case erlang:system_info({wordsize, external}) of 4 -> mysql; _ -> postgres end; + {unix, darwin} -> + mysql; {win32, _} -> sqlserver end). diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl index 3e78105cf3..2d6bf5fcac 100644 --- a/lib/odbc/test/odbc_test_lib.erl +++ b/lib/odbc/test/odbc_test_lib.erl @@ -36,7 +36,7 @@ match_float(Float, Match, Delta) -> (Float < Match + Delta) and (Float > Match - Delta). odbc_check() -> - case erlang:system_info(wordsize) of + case erlang:system_info({wordsize, external}) of 4 -> ok; Other -> @@ -71,9 +71,36 @@ strict(_,_) -> ok. platform_options() -> + []. + +skip() -> case os:type() of + {unix, linux} -> + Issue = linux_issue(), + is_sles9(Issue); {unix, sunos} -> - [{scrollable_cursors, off}]; + not supported_solaris(); + _ -> + false + end. + +supported_solaris() -> + case os:version() of + {_,10,_} -> + true; _ -> - [] + false end. + +linux_issue() -> + {ok, Binary} = file:read_file("/etc/issue"), + string:tokens(binary_to_list(Binary), " "). + +is_sles11(IssueTokens) -> + lists:member(11, IssueTokens). + +is_sles10(IssueTokens) -> + lists:member(10, IssueTokens). + +is_sles9(IssueTokens) -> + lists:member(9, IssueTokens). diff --git a/lib/odbc/test/oracle.erl b/lib/odbc/test/oracle.erl index 786280701d..d74863d8c1 100644 --- a/lib/odbc/test/oracle.erl +++ b/lib/odbc/test/oracle.erl @@ -108,10 +108,6 @@ create_text_table() -> " (FIELD long)". %Oracle long is variable length char data %------------------------------------------------------------------------- -create_unicode_table() -> - " (FIELD nvarchar(50))". - -%------------------------------------------------------------------------- create_timestamp_table() -> " (FIELD DATETIME)". diff --git a/lib/odbc/test/postgres.erl b/lib/odbc/test/postgres.erl index 9c7eed271f..d564dbd5ff 100644 --- a/lib/odbc/test/postgres.erl +++ b/lib/odbc/test/postgres.erl @@ -30,7 +30,7 @@ connection_string() -> {unix, sunos} -> "DSN=Postgres;UID=odbctest"; {unix, linux} -> - Size = erlang:system_info(wordsize), + Size = erlang:system_info({wordsize, external}), linux_dist_connection_string(Size) end. @@ -43,7 +43,12 @@ linux_dist_connection_string(4) -> end; linux_dist_connection_string(_) -> - "DSN=PostgresLinux64;UID=odbctest". + case linux_dist() of + "ubuntu" -> + "DSN=PostgresLinux64Ubuntu;UID=odbctest"; + _ -> + "DSN=PostgresLinux64;UID=odbctest" + end. linux_dist() -> case file:read_file("/etc/issue") of @@ -135,10 +140,6 @@ create_text_table() -> " (FIELD text)". %------------------------------------------------------------------------- -create_unicode_table() -> - " (FIELD text)". - -%------------------------------------------------------------------------- create_timestamp_table() -> " (FIELD TIMESTAMP)". diff --git a/lib/odbc/test/sqlserver.erl b/lib/odbc/test/sqlserver.erl index 13930cd5ae..59252d4276 100644 --- a/lib/odbc/test/sqlserver.erl +++ b/lib/odbc/test/sqlserver.erl @@ -123,10 +123,6 @@ create_text_table() -> " (FIELD text)". %------------------------------------------------------------------------- -create_unicode_table() -> - " (FIELD nvarchar(50))". - -%------------------------------------------------------------------------- create_timestamp_table() -> " (FIELD DATETIME)". diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk index 42a51be33e..120ed9ee3d 100644 --- a/lib/odbc/vsn.mk +++ b/lib/odbc/vsn.mk @@ -1 +1 @@ -ODBC_VSN = 2.10.10 +ODBC_VSN = 2.10.11 diff --git a/lib/parsetools/doc/src/yecc.xml b/lib/parsetools/doc/src/yecc.xml index c712609cf4..1d2a985d7d 100644 --- a/lib/parsetools/doc/src/yecc.xml +++ b/lib/parsetools/doc/src/yecc.xml @@ -425,9 +425,9 @@ myparser:parse_and_scan({Mod, Tokenizer, Args}) </code> Nonterminals E T F. Terminals '+' '*' '(' ')' number. Rootsymbol E. -E -> E '+' T: ['$1', '$2', '$3']. +E -> E '+' T: ['$2', '$1', '$3']. E -> T : '$1'. -T -> T '*' F: ['$1', '$2', '$3']. +T -> T '*' F: ['$2', '$1', '$3']. T -> F : '$1'. F -> '(' E ')' : '$2'. F -> number : '$1'. </code> @@ -438,8 +438,8 @@ Terminals '+' '*' '(' ')' number. Rootsymbol E. Left 100 '+'. Left 200 '*'. -E -> E '+' E : ['$1', '$2', '$3']. -E -> E '*' E : ['$1', '$2', '$3']. +E -> E '+' E : ['$2', '$1', '$3']. +E -> E '*' E : ['$2', '$1', '$3']. E -> '(' E ')' : '$2'. E -> number : '$1'. </code> <p>3. An overloaded minus operator:</p> diff --git a/lib/percept/src/percept_db.erl b/lib/percept/src/percept_db.erl index 52e9afb78f..61b68ce44f 100644 --- a/lib/percept/src/percept_db.erl +++ b/lib/percept/src/percept_db.erl @@ -92,7 +92,7 @@ restart(PerceptDB)-> stop_sync(PerceptDB), do_start(). -%% @spec do_start(pid()) -> pid() +%% @spec do_start() -> pid() %% @private %% @doc starts the percept database. @@ -131,6 +131,7 @@ stop_sync(Pid)-> {'DOWN', MonitorRef, _Type, Pid, _Info}-> true after ?STOP_TIMEOUT-> + erlang:demonitor(MonitorRef, [flush]), exit(Pid, kill) end. @@ -166,14 +167,14 @@ insert(Trace) -> select(Query) -> percept_db ! {select, self(), Query}, - receive Match -> Match end. + receive {result, Match} -> Match end. %% @spec select(atom(), list()) -> Result %% @equiv select({Table,Options}) select(Table, Options) -> percept_db ! {select, self(), {Table, Options}}, - receive Match -> Match end. + receive {result, Match} -> Match end. %% @spec consolidate() -> Result %% @doc Checks timestamp and state-flow inconsistencies in the @@ -213,7 +214,7 @@ loop_percept_db() -> insert_trace(clean_trace(Trace)), loop_percept_db(); {select, Pid, Query} -> - Pid ! select_query(Query), + Pid ! {result, select_query(Query)}, loop_percept_db(); {action, stop} -> stopped; @@ -222,7 +223,7 @@ loop_percept_db() -> loop_percept_db(); {operate, Pid, {Table, {Fun, Start}}} -> Result = ets:foldl(Fun, Start, Table), - Pid ! Result, + Pid ! {result, Result}, loop_percept_db(); Unhandled -> io:format("loop_percept_db, unhandled query: ~p~n", [Unhandled]), diff --git a/lib/public_key/asn1/README b/lib/public_key/asn1/README index 5fb8cf9725..2a880e2d51 100644 --- a/lib/public_key/asn1/README +++ b/lib/public_key/asn1/README @@ -46,6 +46,6 @@ diff -r1.1 PKIXAttributeCertificate.asn1 --- > version AttCertVersion, -- version is v2 -4. Defenitions of publuic keys from PKCS-1.asn1 present in +4. Definitions of public keys from PKCS-1.asn1 present in PKIX1Algorithms88.asn1 where removed as we take them directly from PKCS-1.asn1
\ No newline at end of file diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index d60d91cd83..9a3832c68b 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -63,7 +63,7 @@ <p><code>pki_asn1_type() = 'Certificate' | 'RSAPrivateKey'| 'RSAPublicKey' 'DSAPrivateKey' | 'DSAPublicKey' | 'DHParameter' | 'SubjectPublicKeyInfo'</code></p> - <p><code>pem_entry () = {pki_asn1_type(), binary() %% DER or encrypted DER + <p><code>pem_entry () = {pki_asn1_type(), binary(), %% DER or encrypted DER not_encrypted | {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}}.</code></p> <p><code>rsa_public_key() = #'RSAPublicKey'{}</code></p> @@ -72,8 +72,6 @@ <p><code>dsa_public_key() = {integer(), #'Dss-Parms'{}} </code></p> - <p><code>rsa_private_key() = #'RSAPrivateKey'{} </code></p> - <p><code>dsa_private_key() = #'DSAPrivateKey'{}</code></p> <p><code> public_crypt_options() = [{rsa_pad, rsa_padding()}]. </code></p> @@ -149,7 +147,7 @@ <name>der_decode(Asn1type, Der) -> term()</name> <fsummary> Decodes a public key asn1 der encoded entity.</fsummary> <type> - <v>Asn1Type = atom() -</v> + <v>Asn1Type = atom()</v> <d> ASN.1 type present in the public_key applications asn1 specifications.</d> <v>Der = der_encoded()</v> @@ -166,7 +164,8 @@ <v>Asn1Type = atom()</v> <d> Asn1 type present in the public_key applications ASN.1 specifications.</d> - <v>Entity = term() - The erlang representation of <c> Asn1Type</c></v> + <v>Entity = term()</v> + <d>The erlang representation of <c>Asn1Type</c></d> </type> <desc> <p> Encodes a public key entity with ASN.1 DER encoding.</p> @@ -218,12 +217,13 @@ <fsummary> Creates a pem entry that can be fed to pem_encode/1.</fsummary> <type> <v>Asn1Type = pki_asn1_type()</v> - <v>Entity = term() - The Erlang representation of + <v>Entity = term()</v> + <d>The Erlang representation of <c>Asn1Type</c>. If <c>Asn1Type</c> is 'SubjectPublicKeyInfo' then <c>Entity</c> must be either an rsa_public_key() or a dsa_public_key() and this function will create the appropriate 'SubjectPublicKeyInfo' entry. - </v> + </d> <v>CipherInfo = {"DES-CBC" | "DES-EDE3-CBC", crypto:rand_bytes(8)}</v> <v>Password = string()</v> </type> @@ -281,7 +281,7 @@ <desc> <p>Der encodes a pkix x509 certificate or part of such a certificate. This function must be used for encoding certificates or parts of certificates - that are decoded/created on the otp format, whereas for the plain format this + that are decoded/created in the otp format, whereas for the plain format this function will directly call der_encode/2. </p> </desc> </func> diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 2901020e83..33fcce2c44 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -488,9 +488,10 @@ pkix_path_validation(PathErr, [Cert | Chain], Options0) when is_atom(PathErr)-> _:_ -> {error, Reason} end; -pkix_path_validation(TrustedCert, CertChain, Options) when - is_binary(TrustedCert) -> OtpCert = pkix_decode_cert(TrustedCert, - otp), pkix_path_validation(OtpCert, CertChain, Options); +pkix_path_validation(TrustedCert, CertChain, Options) + when is_binary(TrustedCert) -> + OtpCert = pkix_decode_cert(TrustedCert, otp), + pkix_path_validation(OtpCert, CertChain, Options); pkix_path_validation(#'OTPCertificate'{} = TrustedCert, CertChain, Options) when is_list(CertChain), is_list(Options) -> diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 76c064f1e7..8b0f64eb45 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -54,7 +54,9 @@ whitelist, blacklist, derived, - fgraph_wins + fgraph_wins, + app_box, + mod_box }). -define(WIN_WIDTH, 800). @@ -86,6 +88,11 @@ -define(blacklist, "Excluded"). -define(derived, "Derived"). +-define(safe_config,{sys,[{incl_cond,exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}]}). + -record(root_data, {dir}). -record(lib_data, {dir, tree, item}). -record(escript_data, {file, tree, item}). @@ -102,7 +109,7 @@ start_link(Opts) -> proc_lib:start_link(?MODULE, init, - [[{parent, self()} | Opts]], + [[{safe_config, false}, {parent, self()} | Opts]], infinity, []). @@ -126,53 +133,73 @@ init(Options) -> exit({Reason, erlang:get_stacktrace()}) end. -do_init([{parent, Parent} | Options]) -> +do_init([{safe_config, Safe}, {parent, Parent} | Options]) -> case reltool_server:start_link(Options) of {ok, ServerPid, C, Sys} -> process_flag(trap_exit, C#common.trap_exit), - S = #state{parent_pid = Parent, - server_pid = ServerPid, - common = C, - config_file = filename:absname("config.reltool"), - target_dir = filename:absname("reltool_target_dir"), - app_wins = [], - sys = Sys, - fgraph_wins = []}, wx:new(), wx:debug(C#common.wx_debug), - S2 = create_window(S), %% wx_misc:beginBusyCursor(), case reltool_server:get_status(ServerPid) of {ok, Warnings} -> exit_dialog(Warnings), - {ok, Sys2} = reltool_server:get_sys(ServerPid), - S3 = S2#state{sys = Sys2}, + {ok, Sys} = reltool_server:get_sys(ServerPid), + S = #state{parent_pid = Parent, + server_pid = ServerPid, + common = C, + config_file = filename:absname("config.reltool"), + target_dir = filename:absname("reltool_target_dir"), + app_wins = [], + sys = Sys, + fgraph_wins = []}, + S2 = create_window(S), S5 = wx:batch(fun() -> Title = atom_to_list(?APPLICATION), - wxFrame:setTitle(S3#state.frame, + wxFrame:setTitle(S2#state.frame, Title), %% wxFrame:setMinSize(Frame, %% {?WIN_WIDTH, ?WIN_HEIGHT}), wxStatusBar:setStatusText( - S3#state.status_bar, + S2#state.status_bar, "Done."), - S4 = redraw_apps(S3), - redraw_libs(S4) + S3 = redraw_apps(S2), + S4 = redraw_libs(S3), + redraw_config_page(S4) end), %% wx_misc:endBusyCursor(), %% wxFrame:destroy(Frame), proc_lib:init_ack(S#state.parent_pid, {ok, self()}), loop(S5); {error, Reason} -> - io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason) + restart_server_safe_config(Safe,Parent,Reason) end; {error, Reason} -> io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), exit(Reason) end. +restart_server_safe_config(true,_Parent,Reason) -> + io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), + exit(Reason); +restart_server_safe_config(false,Parent,Reason) -> + Strings = + [{?wxBLACK,"Could not start reltool server:\n\n"}, + {?wxRED,Reason++"\n\n"}, + {?wxBLACK, + io_lib:format( + "Resetting the configuration to:~n~n ~p~n~n" + "Do you want to continue with this configuration?", + [?safe_config])}], + + case question_dialog_2("Reltool server start error", Strings) of + ?wxID_OK -> + do_init([{safe_config,true},{parent,Parent},?safe_config]); + ?wxID_CANCEL -> + io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), + exit(Reason) + end. + exit_dialog([]) -> ok; exit_dialog(Warnings) -> @@ -606,6 +633,13 @@ create_config_page(#state{sys = Sys, book = Book} = S) -> {proportion, 1}]), wxPanel:setSizer(Panel, Sizer), wxNotebook:addPage(Book, Panel, ?SYS_PAGE, []), + S#state{app_box = AppBox, mod_box = ModBox}. + +redraw_config_page(#state{sys = Sys, app_box = AppBox, mod_box = ModBox} = S) -> + AppChoice = reltool_utils:incl_cond_to_index(Sys#sys.incl_cond), + wxRadioBox:setSelection(AppBox, AppChoice), + ModChoice = reltool_utils:mod_cond_to_index(Sys#sys.mod_cond), + wxRadioBox:setSelection(ModBox, ModChoice), S. create_main_release_page(#state{book = Book} = S) -> @@ -640,15 +674,15 @@ create_main_release_page(#state{book = Book} = S) -> add_release_page(Book, #rel{name = RelName, rel_apps = RelApps}) -> Panel = wxPanel:new(Book, []), Sizer = wxBoxSizer:new(?wxHORIZONTAL), - RelBox = wxRadioBox:new(Panel, - ?wxID_ANY, - "Applications included in the release " ++ RelName, - ?wxDefaultPosition, - ?wxDefaultSize, - [atom_to_list(RA#rel_app.name) || RA <- RelApps], - []), - %% wxRadioBox:setSelection(RelBox, 2), % mandatory - wxEvtHandler:connect(RelBox, command_radiobox_selected, + AppNames = [kernel, stdlib | + [RA#rel_app.name || RA <- RelApps] -- [kernel, stdlib]], + RelBox = wxListBox:new( + Panel,?wxID_ANY, + [{pos,?wxDefaultPosition}, + {size,?wxDefaultSize}, + {choices,[[atom_to_list(AppName)] || AppName <- AppNames]}, + {style,?wxLB_EXTENDED}]), + wxEvtHandler:connect(RelBox, command_listbox_selected, [{userData, {config_rel_cond, RelName}}]), RelToolTip = "Choose which applications that shall " "be included in the release resource file.", @@ -1363,7 +1397,8 @@ refresh(S) -> [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- S#state.app_wins], S2 = S#state{sys = Sys}, S3 = redraw_libs(S2), - redraw_apps(S3). + S4 = redraw_apps(S3), + redraw_config_page(S4). question_dialog(Question, Details) -> %% Parent = S#state.frame, @@ -1420,6 +1455,44 @@ display_message(Message, Icon) -> wxMessageDialog:showModal(Dialog), wxMessageDialog:destroy(Dialog). +%% Strings = [{Color,String}] +question_dialog_2(DialogLabel, Strings) -> + %% Parent = S#state.frame, + Parent = wx:typeCast(wx:null(), wxWindow), + %% [{style, ?wxYES_NO bor ?wxICON_ERROR bor ?wx}]), + DialogStyle = ?wxRESIZE_BORDER bor ?wxCAPTION bor ?wxSYSTEM_MENU bor + ?wxMINIMIZE_BOX bor ?wxMAXIMIZE_BOX bor ?wxCLOSE_BOX, + Dialog = wxDialog:new(Parent, ?wxID_ANY, DialogLabel, + [{style, DialogStyle}]), + Color = wxWindow:getBackgroundColour(Dialog), + TextStyle = ?wxTE_READONLY bor ?wxTE_MULTILINE bor ?wxHSCROLL, + Text = wxTextCtrl:new(Dialog, ?wxID_ANY, + [{size, {600, 400}}, {style, TextStyle}]), + wxWindow:setBackgroundColour(Text, Color), + TextAttr = wxTextAttr:new(), + add_text(Text,TextAttr,Strings), + Sizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(Sizer, Text, [{border, 2}, {flag, ?wxEXPAND}, {proportion, 1}]), + ButtSizer = wxDialog:createStdDialogButtonSizer(Dialog, ?wxOK bor ?wxCANCEL), + wxSizer:add(Sizer, ButtSizer, [{border, 2}, {flag, ?wxEXPAND}]), + wxPanel:setSizer(Dialog, Sizer), + wxSizer:fit(Sizer, Dialog), + wxSizer:setSizeHints(Sizer, Dialog), + Answer = wxDialog:showModal(Dialog), + wxDialog:destroy(Dialog), + Answer. + +add_text(Text,Attr,[{Color,String}|Strings]) -> + wxTextAttr:setTextColour(Attr, Color), + wxTextCtrl:setDefaultStyle(Text, Attr), + wxTextCtrl:appendText(Text, String), + add_text(Text,Attr,Strings); +add_text(_,_,[]) -> + ok. + + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile index 4a4e04a536..c58f651696 100644 --- a/lib/sasl/examples/src/Makefile +++ b/lib/sasl/examples/src/Makefile @@ -66,7 +66,7 @@ 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 + chmod -R ug+w $(RELSYSDIR)/examples release_docs_spec: diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl index eb29787103..ab54b1d00b 100644 --- a/lib/sasl/src/release_handler.erl +++ b/lib/sasl/src/release_handler.erl @@ -291,7 +291,8 @@ check_script(Script, LibDirs) -> release_handler_1:check_script(Script, LibDirs). %%----------------------------------------------------------------- -%% eval_script(Script, Apps, LibDirs, Opts) -> {ok, UnPurged} | +%% eval_script(Script, Apps, LibDirs, NewLibs, Opts) -> +%% {ok, UnPurged} | %% restart_new_emulator | %% {error, Error} %% {'EXIT', Reason} @@ -299,9 +300,13 @@ check_script(Script, LibDirs) -> %% net_kernel:monitor_nodes(true) before calling this function. %% No! No other process than the release_handler can ever call this %% function, if sync_nodes is used. -%%----------------------------------------------------------------- -eval_script(Script, Apps, LibDirs, Opts) -> - catch release_handler_1:eval_script(Script, Apps, LibDirs, Opts). +%% +%% LibDirs is a list of all applications, while NewLibs is a list of +%% applications that have changed version between the current and the +%% new release. +%% ----------------------------------------------------------------- +eval_script(Script, Apps, LibDirs, NewLibs, Opts) -> + catch release_handler_1:eval_script(Script, Apps, LibDirs, NewLibs, Opts). %%----------------------------------------------------------------- %% Func: create_RELEASES(Root, RelFile, LibDirs) -> ok | {error, Reason} @@ -405,6 +410,7 @@ eval_appup_script(App, ToVsn, ToDir, Script) -> Res = release_handler_1:eval_script(Script, [], % [AppSpec] [{App, ToVsn, ToDir}], + [{App, ToVsn, ToDir}], []), % [Opt] case Res of {ok, _Unpurged} -> @@ -906,7 +912,9 @@ do_install_release(#state{start_prg = StartPrg, EnvBefore = application_controller:prep_config_change(), Apps = change_appl_data(RelDir, Release, Masters), LibDirs = Release#release.libs, - case eval_script(Script, Apps, LibDirs, Opts) of + NewLibs = get_new_libs(LatestRelease#release.libs, + Release#release.libs), + case eval_script(Script, Apps, LibDirs, NewLibs, Opts) of {ok, []} -> application_controller:config_change(EnvBefore), mon_nodes(false), @@ -1946,3 +1954,25 @@ safe_write_file_m(File, Data, Masters) -> filename:basename(File), filename:basename(Backup)}}) end. + +%%----------------------------------------------------------------- +%% Figure out which applications that have changed version between the +%% two releases. The paths for these applications must always be +%% updated, even if the relup script does not load any modules. See +%% OTP-9402. +%% +%% A different situation is when the same application version is used +%% in old and new release, but the path has changed. This is not +%% handled here - instead it must be explicitely indicated by the +%% 'update_paths' option to release_handler:install_release/2 if the +%% code path shall be updated then. +%% ----------------------------------------------------------------- +get_new_libs([{App,Vsn,LibDir}|CurrentLibs], NewLibs) -> + case lists:keyfind(App,1,NewLibs) of + {App,NewVsn,_} = LibInfo when NewVsn =/= Vsn -> + [LibInfo | get_new_libs(CurrentLibs,NewLibs)]; + _ -> + get_new_libs(CurrentLibs,NewLibs) + end; +get_new_libs([],_) -> + []. diff --git a/lib/sasl/src/release_handler_1.erl b/lib/sasl/src/release_handler_1.erl index 8d050fb7b0..ff62f847ac 100644 --- a/lib/sasl/src/release_handler_1.erl +++ b/lib/sasl/src/release_handler_1.erl @@ -19,7 +19,7 @@ -module(release_handler_1). %% External exports --export([eval_script/3, eval_script/4, check_script/2]). +-export([eval_script/1, eval_script/5, check_script/2]). -export([get_current_vsn/1]). %% exported because used in a test case -record(eval_state, {bins = [], stopped = [], suspended = [], apps = [], @@ -33,11 +33,11 @@ %% libdirs = [{Lib, LibVsn, LibDir}] - Maps Lib to Vsn and Directory %% unpurged = [{Mod, soft_purge | brutal_purge}] %% vsns = [{Mod, OldVsn, NewVsn}] - remember the old vsn of a mod -%% before it is removed/a new vsn is loaded; the new vsn +%% before a new vsn is loaded; the new vsn %% is kept in case of a downgrade, where the code_change %% function receives the vsn of the module to downgrade %% *to*. -%% newlibs = [{Lib, Dir}] - list of all new libs; used to change +%% newlibs = [{Lib, LibVsn, LibDir}] - list of all new libs; used to change %% the code path %% opts = [{Tag, Value}] - list of options %%----------------------------------------------------------------- @@ -63,10 +63,11 @@ check_script(Script, LibDirs) -> {error, {old_processes, Mod}} end. -eval_script(Script, Apps, LibDirs) -> - eval_script(Script, Apps, LibDirs, []). +%% eval_script/1 - For testing only - no apps added, just testing instructions +eval_script(Script) -> + eval_script(Script, [], [], [], []). -eval_script(Script, Apps, LibDirs, Opts) -> +eval_script(Script, Apps, LibDirs, NewLibs, Opts) -> case catch check_old_processes(Script) of ok -> {Before, After} = split_instructions(Script), @@ -75,6 +76,7 @@ eval_script(Script, Apps, LibDirs, Opts) -> end, #eval_state{apps = Apps, libdirs = LibDirs, + newlibs = NewLibs, opts = Opts}, Before) of EvalState2 when is_record(EvalState2, eval_state) -> @@ -214,16 +216,15 @@ check_old_code(Mod) -> %%----------------------------------------------------------------- eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) -> case lists:keysearch(Lib, 1, EvalState#eval_state.libdirs) of - {value, {Lib, LibVsn, LibDir}} -> - Ebin = filename:join(LibDir, "ebin"), + {value, {Lib, LibVsn, LibDir} = LibInfo} -> Ext = code:objfile_extension(), {NewBins, NewVsns} = lists:foldl(fun(Mod, {Bins, Vsns}) -> File = lists:concat([Mod, Ext]), - FName = filename:join(Ebin, File), + FName = filename:join([LibDir, "ebin", File]), case erl_prim_loader:get_file(FName) of {ok, Bin, FName2} -> - NVsns = add_new_vsn(Mod, Bin, Vsns), + NVsns = add_vsns(Mod, Bin, Vsns), {[{Mod, Bin, FName2} | Bins],NVsns}; error -> throw({error, {no_such_file,FName}}) @@ -232,7 +233,7 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) -> {EvalState#eval_state.bins, EvalState#eval_state.vsns}, Modules), - NewLibs = [{Lib, Ebin} | EvalState#eval_state.newlibs], + NewLibs = lists:keystore(Lib,1,EvalState#eval_state.newlibs,LibInfo), EvalState#eval_state{bins = NewBins, newlibs = NewLibs, vsns = NewVsns}; @@ -242,15 +243,14 @@ eval({load_object_code, {Lib, LibVsn, Modules}}, EvalState) -> eval(point_of_no_return, EvalState) -> Libs = case get_opt(update_paths, EvalState, false) of false -> - EvalState#eval_state.newlibs; % [{Lib, Path}] + EvalState#eval_state.newlibs; true -> - lists:map(fun({Lib, _LibVsn, LibDir}) -> - Ebin= filename:join(LibDir,"ebin"), - {Lib, Ebin} - end, - EvalState#eval_state.libdirs) + EvalState#eval_state.libdirs end, - lists:foreach(fun({Lib, Path}) -> code:replace_path(Lib, Path) end, + lists:foreach(fun({Lib, _LibVsn, LibDir}) -> + Ebin = filename:join(LibDir,"ebin"), + code:replace_path(Lib, Ebin) + end, Libs), EvalState; eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) -> @@ -258,32 +258,21 @@ eval({load, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) -> {value, {_Mod, Bin, File}} = lists:keysearch(Mod, 1, Bins), % load_binary kills all procs running old code % if soft_purge, we know that there are no such procs now - Vsns = EvalState#eval_state.vsns, - NewVsns = add_old_vsn(Mod, Vsns), code:load_binary(Mod, File, Bin), % Now, the prev current is old. There might be procs % running it. Find them. Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged), EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins), - unpurged = Unpurged, - vsns = NewVsns}; + unpurged = Unpurged}; eval({remove, {Mod, _PrePurgeMethod, PostPurgeMethod}}, EvalState) -> - % purge kills all procs running old code - % if soft_purge, we know that there are no such procs now - Vsns = EvalState#eval_state.vsns, - NewVsns = add_old_vsn(Mod, Vsns), + %% purge kills all procs running old code + %% if soft_purge, we know that there are no such procs now code:purge(Mod), code:delete(Mod), - % Now, the prev current is old. There might be procs - % running it. Find them. - Unpurged = - case code:soft_purge(Mod) of - true -> EvalState#eval_state.unpurged; - false -> [{Mod, PostPurgeMethod} | EvalState#eval_state.unpurged] - end, -%% Bins = EvalState#eval_state.bins, -%% EvalState#eval_state{bins = lists:keydelete(Mod, 1, Bins), - EvalState#eval_state{unpurged = Unpurged, vsns = NewVsns}; + %% Now, the prev current is old. There might be procs + %% running it. Find them. + Unpurged = do_soft_purge(Mod,PostPurgeMethod,EvalState#eval_state.unpurged), + EvalState#eval_state{unpurged = Unpurged}; eval({purge, Modules}, EvalState) -> % Now, if there are any processes still executing old code, OR % if some new processes started after suspend but before load, @@ -606,26 +595,20 @@ sync_nodes(Id, Nodes) -> end, NNodes). -add_old_vsn(Mod, Vsns) -> +add_vsns(Mod, NewBin, Vsns) -> + OldVsn = get_current_vsn(Mod), + NewVsn = get_vsn(NewBin), case lists:keysearch(Mod, 1, Vsns) of - {value, {Mod, undefined, NewVsn}} -> - OldVsn = get_current_vsn(Mod), - lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn}); - {value, {Mod, _OldVsn, _NewVsn}} -> - Vsns; + {value, {Mod, OldVsn0, NewVsn0}} -> + lists:keyreplace(Mod, 1, Vsns, {Mod, + replace_undefined(OldVsn0,OldVsn), + replace_undefined(NewVsn0,NewVsn)}); false -> - OldVsn = get_current_vsn(Mod), - [{Mod, OldVsn, undefined} | Vsns] + [{Mod, OldVsn, NewVsn} | Vsns] end. -add_new_vsn(Mod, Bin, Vsns) -> - NewVsn = get_vsn(Bin), - case lists:keysearch(Mod, 1, Vsns) of - {value, {Mod, OldVsn, undefined}} -> - lists:keyreplace(Mod, 1, Vsns, {Mod, OldVsn, NewVsn}); - false -> - [{Mod, undefined, NewVsn} | Vsns] - end. +replace_undefined(undefined,Vsn) -> Vsn; +replace_undefined(Vsn,_) -> Vsn. %%----------------------------------------------------------------- %% Func: get_current_vsn/1 @@ -645,7 +628,9 @@ get_current_vsn(Mod) -> {ok, Bin, _File2} -> get_vsn(Bin); error -> - throw({error, {no_such_file, File}}) + %% This is the case when a new module is added, there will + %% be no current version of it at the time of this call. + undefined end. %%----------------------------------------------------------------- diff --git a/lib/sasl/src/systools_lib.erl b/lib/sasl/src/systools_lib.erl index b652c109fe..f951647b79 100644 --- a/lib/sasl/src/systools_lib.erl +++ b/lib/sasl/src/systools_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 @@ -176,21 +176,26 @@ add_dirs(RegName, Dirs, Root) -> regexp_match(RegName, D0, Root) -> case file:list_dir(D0) of {ok, Files} when length(Files) > 0 -> - FR = fun(F) -> - case regexp:match(F, RegName) of - {match,1,N} when N == length(F) -> - DirF = join(D0, F, Root), - case dir_p(DirF) of - true -> - {true, DirF}; + case re:compile(RegName) of + {ok, MP} -> + FR = fun(F) -> + case re:run(F, MP) of + {match,[{0,N}]} when N == length(F) -> + DirF = join(D0, F, Root), + case dir_p(DirF) of + true -> + {true, DirF}; + _ -> + false + end; _ -> false - end; - _ -> - false - end - end, - {true,lists:zf(FR, Files)}; + end + end, + {true,lists:zf(FR, Files)}; + _ -> + false + end; _ -> false end. diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile index 0bdb79a06a..65be134462 100644 --- a/lib/sasl/test/Makefile +++ b/lib/sasl/test/Makefile @@ -86,7 +86,7 @@ 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) + chmod -R u+w $(RELSYSDIR) @tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl index 16267ba0d4..9c7733b7ec 100644 --- a/lib/sasl/test/release_handler_SUITE.erl +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -55,7 +55,7 @@ win32_cases() -> %% Cases that can be run on all platforms cases() -> - [otp_2740, otp_2760, otp_5761, instructions, eval_appup]. + [otp_2740, otp_2760, otp_5761, otp_9402, otp_9417, instructions, eval_appup]. groups() -> [{release,[], @@ -393,7 +393,7 @@ instructions(Conf) when is_list(Conf) -> {stop, [aa]}, {apply, {?MODULE, no_cc, []}}, {start, [aa]}], - {ok, _} = release_handler_1:eval_script(S1, [], []), + {ok, _} = release_handler_1:eval_script(S1), case whereis(cc) of Pid2 when is_pid(Pid2) -> ok; @@ -403,17 +403,17 @@ instructions(Conf) when is_list(Conf) -> %% 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, [], []), + {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,[],[]), + {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, []} = release_handler_1:eval_script(S3), ok = wait_for(bb), check_bstate("second", []), SecondBB = whereis(bb), @@ -446,7 +446,7 @@ instructions(Conf) when is_list(Conf) -> %% 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, HopefullyEmpty} = release_handler_1:eval_script(S4), ok = wait_for(bb), FourthBB = whereis(bb), @@ -566,8 +566,7 @@ otp_2760(Conf) -> %% 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, [], []]), + {ok, []} = rpc:call(Node, release_handler_1, eval_script, [Script]), false = rpc:call(Node, code, is_loaded, [app1]), true = stop_node(Node), @@ -658,6 +657,126 @@ otp_5761(Conf) when is_list(Conf) -> true = stop_node(Node), ok. + +%% When a new version of an application is added, but no module is +%% changed - the path was not updated - i.e. code:priv_dir would point +%% to the old location. +otp_9402(Conf) when is_list(Conf) -> + %% Set some paths + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_9402"), + LibDir = filename:join(?config(data_dir, Conf), "lib"), + + %% Create the releases + Rel1 = create_and_install_fake_first_release(Dir, + [{a,"1.1",LibDir}]), + Rel2 = create_fake_upgrade_release(Dir, + "2", + [{a,"1.2",LibDir}], + {Rel1,Rel1,[LibDir]}), + Rel1Dir = filename:dirname(Rel1), + Rel2Dir = filename:dirname(Rel2), + + %% Start a slave node + {ok, Node} = t_start_node(otp_9402, Rel1, filename:join(Rel1Dir,"sys.config")), + + %% Check path + Dir1 = filename:join([LibDir, "a-1.1"]), + Dir1 = rpc:call(Node, code, lib_dir, [a]), + ABeam = rpc:call(Node, code, which, [a]), + + %% Install second release, with no changed modules + {ok, RelVsn2} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{a,"1.2",LibDir}]]), + 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")]), + + {ok, RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn2]), + + %% Check path + Dir2 = filename:join([LibDir, "a-1.2"]), + Dir2 = rpc:call(Node, code, lib_dir, [a]), + APrivDir2 = rpc:call(Node, code, priv_dir, [a]), + true = filelib:is_regular(filename:join(APrivDir2,"file")), + + %% Just to make sure no modules have been re-loaded + ABeam = rpc:call(Node, code, which, [a]), + + %% Install RelVsn1 again + {ok, _OtherVsn, []} = + rpc:call(Node, release_handler, install_release, [RelVsn1]), + + %% Check path + Dir1 = rpc:call(Node, code, lib_dir, [a]), + APrivDir1 = rpc:call(Node, code, priv_dir, [a]), + false = filelib:is_regular(filename:join(APrivDir1,"file")), + + %% Just to make sure no modules have been re-loaded + ABeam = rpc:call(Node, code, which, [a]), + + ok. + + +%% When a module is deleted in an appup instruction, the upgrade +%% failed if the module was not loaded. +otp_9417(Conf) when is_list(Conf) -> + %% Set some paths + PrivDir = priv_dir(Conf), + Dir = filename:join(PrivDir,"otp_9417"), + LibDir = filename:join(?config(data_dir, Conf), "lib"), + + %% Create the releases + Rel1 = create_and_install_fake_first_release(Dir, + [{b,"1.0",LibDir}]), + Rel2 = create_fake_upgrade_release(Dir, + "2", + [{b,"2.0",LibDir}], + {Rel1,Rel1,[LibDir]}), + Rel1Dir = filename:dirname(Rel1), + Rel2Dir = filename:dirname(Rel2), + + %% Start a slave node + {ok, Node} = t_start_node(otp_9417, Rel1, filename:join(Rel1Dir,"sys.config")), + + %% Check paths + Dir1 = filename:join([LibDir, "b-1.0"]), + Dir1 = rpc:call(Node, code, lib_dir, [b]), + BLibBeam = filename:join([Dir1,"ebin","b_lib.beam"]), + BLibBeam = rpc:call(Node,code,which,[b_lib]), + false = rpc:call(Node,code,is_loaded,[b_lib]), + false = rpc:call(Node,code,is_loaded,[b_server]), + + %% Install second release, which removes b_lib module + {ok, RelVsn2} = + rpc:call(Node, release_handler, set_unpacked, + [Rel2++".rel", [{b,"2.0",LibDir}]]), + 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")]), + + {ok, _RelVsn1, []} = + rpc:call(Node, release_handler, install_release, [RelVsn2]), + + %% Check that the module does no longer exist + false = rpc:call(Node, code, is_loaded, [b_lib]), + non_existing = rpc:call(Node, code, which, [b_lib]), + + %% And check some paths + Dir2 = filename:join([LibDir, "b-2.0"]), + Dir2 = rpc:call(Node, code, lib_dir, [b]), + BServerBeam = filename:join([Dir2,"ebin","b_server.beam"]), + {file,BServerBeam} = rpc:call(Node,code,is_loaded,[b_server]), + ok. + %% Test upgrade and downgrade of applications eval_appup(Conf) when is_list(Conf) -> diff --git a/lib/sasl/test/release_handler_SUITE_data/Makefile.src b/lib/sasl/test/release_handler_SUITE_data/Makefile.src index 85e25fdc2f..a12e526d2e 100644 --- a/lib/sasl/test/release_handler_SUITE_data/Makefile.src +++ b/lib/sasl/test/release_handler_SUITE_data/Makefile.src @@ -1,14 +1,19 @@ EFLAGS=+debug_info P2B= \ - P2B/a-2.0/ebin/a.beam \ - P2B/a-2.0/ebin/a_sup.beam + P2B/a-2.0/ebin/a.@EMULATOR@ \ + P2B/a-2.0/ebin/a_sup.@EMULATOR@ 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 \ + lib/a-1.2/ebin/a.@EMULATOR@ \ + lib/a-1.2/ebin/a_sup.@EMULATOR@ \ + lib/a-1.1/ebin/a.@EMULATOR@ \ + lib/a-1.1/ebin/a_sup.@EMULATOR@ \ + lib/a-1.0/ebin/a.@EMULATOR@ \ + lib/a-1.0/ebin/a_sup.@EMULATOR@ \ + lib/b-1.0/ebin/b_server.@EMULATOR@ \ + lib/b-1.0/ebin/b_lib.@EMULATOR@ \ + lib/b-2.0/ebin/b_server.@EMULATOR@ APP= \ app1_app2/lib1/app1-1.0/ebin/app1_sup.@EMULATOR@ \ @@ -57,6 +62,21 @@ 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 +lib/a-1.2/ebin/a.@EMULATOR@: lib/a-1.2/src/a.erl + erlc $(EFLAGS) -olib/a-1.2/ebin lib/a-1.2/src/a.erl +lib/a-1.2/ebin/a_sup.@EMULATOR@: lib/a-1.2/src/a_sup.erl + erlc $(EFLAGS) -olib/a-1.2/ebin lib/a-1.2/src/a_sup.erl + +lib/b-1.0/ebin/b_server.@EMULATOR@: lib/b-1.0/src/b_server.erl + erlc $(EFLAGS) -olib/b-1.0/ebin lib/b-1.0/src/b_server.erl +lib/b-1.0/ebin/b_lib.@EMULATOR@: lib/b-1.0/src/b_lib.erl + erlc $(EFLAGS) -olib/b-1.0/ebin lib/b-1.0/src/b_lib.erl + +lib/b-2.0/ebin/b_server.@EMULATOR@: lib/b-2.0/src/b_server.erl + erlc $(EFLAGS) -olib/b-2.0/ebin lib/b-2.0/src/b_server.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 diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/README b/lib/sasl/test/release_handler_SUITE_data/lib/README new file mode 100644 index 0000000000..667d21d4cf --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/README @@ -0,0 +1,16 @@ +a-1.0: +start version + +a-1.1: +can be upgraded to from a-1.0. Module a has changed + +a-1.2: +can be upgraded to from a-1.1. +No module have changed, but priv dir is added including one 'file' + +b-1.0: +start version, includes b_lib and b_server + +b-2.0: +can be upgraded to from b-1.0. +Removes b_lib and updates b_server 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.2/ebin/a.app index e938137f67..b38722f06d 100644 --- 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.2/ebin/a.app @@ -1,7 +1,7 @@ {application, a, [{description, "A CXC 138 11"}, - {vsn, "1.0"}, - {modules, [{a, 1}, {a_sup,1}]}, + {vsn, "1.2"}, + {modules, [{a, 2}, {a_sup,1}]}, {registered, [a_sup]}, {applications, [kernel, stdlib]}, {env, [{key1, val1}]}, diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.appup b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.appup new file mode 100644 index 0000000000..3df0546316 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/ebin/a.appup @@ -0,0 +1,3 @@ +{"1.2", + [{"1.1",[]}], + [{"1.1",[]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/priv/file b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/priv/file new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/priv/file diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a.erl new file mode 100644 index 0000000000..c082ad5339 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/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.2/src/a_sup.erl b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/a-1.2/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/b-1.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app new file mode 100644 index 0000000000..00347b2754 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/ebin/b.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, b, + [{description, "B CXC 138 12"}, + {vsn, "1.0"}, + {modules, [{b_server, 1},{b_lib, 1}]}, + {registered, [b_server]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_lib.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_lib.erl new file mode 100644 index 0000000000..7e8a308a5e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_lib.erl @@ -0,0 +1,3 @@ +-module(b_lib). +-compile(export_all). +foo() -> ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_server.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_server.erl new file mode 100644 index 0000000000..e1a80a076f --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-1.0/src/b_server.erl @@ -0,0 +1,37 @@ +-module(b_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]). + +-define(SERVER, ?MODULE). + +-record(state, {}). + +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +init([]) -> + {ok, #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) -> + file:write_file("/tmp/b_server",io_lib:format("~p~n",[{"1.0",OldVsn,Extra}])), + {ok, State}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app new file mode 100644 index 0000000000..73c8e42b32 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.app @@ -0,0 +1,7 @@ +%% -*- erlang -*- +{application, b, + [{description, "B CXC 138 12"}, + {vsn, "2.0"}, + {modules, [{b_server, 1}]}, + {registered, [b_server]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.appup b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.appup new file mode 100644 index 0000000000..d261a37732 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/ebin/b.appup @@ -0,0 +1,3 @@ +{"2.0", + [{"1.0",[{delete_module,b_lib}, {update,b_server,{advanced,[]}}]}], + [{"1.0",[{add_module,b_lib},{update,b_server,{advanced,[]}}]}]}. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_lib.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_lib.erl new file mode 100644 index 0000000000..7e8a308a5e --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_lib.erl @@ -0,0 +1,3 @@ +-module(b_lib). +-compile(export_all). +foo() -> ok. diff --git a/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_server.erl b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_server.erl new file mode 100644 index 0000000000..f8bfbdaff7 --- /dev/null +++ b/lib/sasl/test/release_handler_SUITE_data/lib/b-2.0/src/b_server.erl @@ -0,0 +1,37 @@ +-module(b_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]). + +-define(SERVER, ?MODULE). + +-record(state, {}). + +start_link() -> + gen_server:start_link({local, ?SERVER}, ?MODULE, [], []). + +init([]) -> + {ok, #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) -> + file:write_file("/tmp/b_server",io_lib:format("~p~n",[{"2.0",OldVsn,Extra}])), + {ok, State}. diff --git a/lib/snmp/Makefile b/lib/snmp/Makefile index 20e3d4692a..c55eff04c6 100644 --- a/lib/snmp/Makefile +++ b/lib/snmp/Makefile @@ -67,7 +67,7 @@ do_configure: configure configure: configure.in autoconf -.PHONY: info +.PHONY: info gclean info: @echo "OS: $(OS)" @@ -76,6 +76,9 @@ info: @echo "SNMP_VSN: $(SNMP_VSN)" @echo "APP_VSN: $(APP_VSN)" +gclean: + git clean -fXd + # ---------------------------------------------------- # Application (source) release targets diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile index 35ed63e103..aa9431477c 100644 --- a/lib/snmp/doc/src/Makefile +++ b/lib/snmp/doc/src/Makefile @@ -152,6 +152,7 @@ $(TOP_PDF_FILE): $(XML_FILES) pdf: $(TOP_PDF_FILE) html: gifs $(HTML_REF_MAN_FILE) +html2: html $(INDEX_TARGET) clean clean_docs: clean_html clean_man clean_pdf rm -f errs core *~ @@ -228,6 +229,7 @@ clean_man: clean_html: @echo "cleaning html:" rm -rf $(HTMLDIR)/* + rm -f $(INDEX_TARGET) $(MAN7DIR)/%.7: $(MIBSDIR)/%.mib @echo "processing $*" @@ -286,7 +288,7 @@ release_docs_spec: docs $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man + $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 6a20d8ee3a..4178192120 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -33,6 +33,136 @@ </header> <section> + <title>SNMP Development Toolkit 4.21</title> + <p>Version 4.21 supports code replacement in runtime from/to + version 4.20.1, 4.20 and 4.19. </p> + + <section> + <title>Improvements and new features</title> +<!-- + <p>-</p> +--> + <list type="bulleted"> + <item> + <p>[manager] There was no way to specify transport domain. + The transport domains was assumed to be IPv4 (transportDomainUdpIpv4). + This has now been changed so that it can also be IPv6 + (transportDomainUdpIpv6). + To facilitate this, the transport domain, <c>tdomain</c>, + is now a (new) valid option when + <seealso marker="snmpm#register_agent">registering</seealso> + a new agent (and + <seealso marker="snmpm#update_agent_info">updating</seealso> + agent info). </p> + <p>This also mean that the transport behaviour has changed. </p> + <p>Own Id: OTP-9305</p> + <p>Aux Id: Seq 11847</p> + </item> + + <item> + <p>[compiler] Added the option + <seealso marker="snmpc#compile">warnings_as_errors</seealso> + (for the SNMP MIB compiler (escript) frontend, the option + <seealso marker="snmpc(command)#option_wae">--wae</seealso> is used) + which specifies whether warnings should be treated as errors. </p> + <p>Tuncer Ayaz</p> + <p>Own Id: OTP-9437</p> + </item> + </list> + + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>The snmp config tool could not handle (manager) audit trail config + because the option seqno was not handled. </p> + <p>Own Id: OTP-9354</p> + </item> + + <item> + <p>[agent] The SNMP ACM cache was not properly updated when + changes where made to the VACM security-to-group, access and + view-tree-family tables. </p> + <p>Own Id: OTP-9367</p> + <p>Aux Id: Seq 11858</p> + </item> + + <item> + <p>Fixed install directory typo for man3. </p> + <p>Peter Lemenkov</p> + <p>Hans Ulrich Niedermann</p> + <p>Own Id: OTP-9442</p> + </item> + + </list> + </section> + + + <section> + <title>Incompatibilities</title> + <p>-</p> + </section> + + </section> <!-- 4.21 --> + + + <section> + <title>SNMP Development Toolkit 4.20.1</title> + <p>Version 4.20.1 supports code replacement in runtime from/to + version 4.20, 4.19 and 4.18.</p> + + <section> + <title>Improvements and new features</title> + <p>-</p> +<!-- + <list type="bulleted"> + <item> + <p>Added type specs for functions that do not return. </p> + <p>Kostis Sagonas</p> + <p>Own Id: OTP-9208</p> + </item> + </list> +--> + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + <list type="bulleted"> + <item> + <p>[agent] Did not handle transport domains properly in some cases, + for instance trap sending. </p> + <p>Own Id: OTP-9400</p> + </item> + + <item> + <p>[agent] Wrong default transport domain, snmpUDPDomain, instead + of transportDomainUdpIpv4. </p> + <p>Own Id: OTP-9425</p> + <p>Aux Id: Seq 11874</p> + </item> + + </list> + </section> + + + <section> + <title>Incompatibilities</title> + <p>-</p> + </section> + + </section> <!-- 4.20.1 --> + + + <section> <title>SNMP Development Toolkit 4.20</title> <p>Version 4.20 supports code replacement in runtime from/to version 4.19 and 4.18.</p> diff --git a/lib/snmp/doc/src/snmpc.xml b/lib/snmp/doc/src/snmpc.xml index 771629492d..61d19251c5 100644 --- a/lib/snmp/doc/src/snmpc.xml +++ b/lib/snmp/doc/src/snmpc.xml @@ -48,7 +48,11 @@ <type> <v>File = string()</v> <v>Options = [opt()]</v> - <v>opt() = db() | relaxed_row_name_assign_check() | deprecated() | description() | reference() | group_check() | i() | il() | imports() | module() | module_identity() | module_compliance() | agent_capabilities() | outdir() | no_defs() | verbosity() | warnings()</v> + <v>opt() = db() | relaxed_row_name_assign_check() | deprecated() | + description() | reference() | group_check() | i() | il() | + imports() | module() | module_identity() | module_compliance() | + agent_capabilities() | outdir() | no_defs() | verbosity() | + warnings() | warnings_as_errors()</v> <v>db() = {db, volatile|persistent|mnesia}</v> <v>deprecated() = {deprecated, bool()}</v> <v>relaxed_row_name_assign_check() = relaxed_row_name_assign_check</v> @@ -66,6 +70,7 @@ <v>outdir() = {outdir, dir()}</v> <v>verbosity() = {verbosity, silence|warning|info|log|debug|trace}</v> <v>warnings() = {warnings, bool()}</v> + <v>warnings_as_errors() = warnings_as_errors</v> <v>dir() = string()</v> <v>BinFileName = string()</v> </type> @@ -200,11 +205,17 @@ <item> <p>The option <c>warnings</c> specifies whether warning - messages should be shown. </p> + messages should be shown. </p> <p>Default is <c>true</c>. </p> </item> + <item> + <p>The option <c>warnings_as_errors</c>, if present, specifies + whether warnings should be treated as errors.</p> + </item> + </list> + <p>The MIB compiler understands both SMIv1 and SMIv2 MIBs. It uses the <c>MODULE-IDENTITY</c> statement to determine if the MIB is version 1 or 2. diff --git a/lib/snmp/doc/src/snmpc_cmd.xml b/lib/snmp/doc/src/snmpc_cmd.xml index 9358382a10..72116f8981 100644 --- a/lib/snmp/doc/src/snmpc_cmd.xml +++ b/lib/snmp/doc/src/snmpc_cmd.xml @@ -50,6 +50,8 @@ with definitions of Erlang constants for the objects in the MIB, see <seealso marker="snmpc#mib_to_hrl">mib_to_hrl/1</seealso>. </p> + + <marker id="options"></marker> </desc> </func> </funcs> @@ -58,15 +60,18 @@ <title>Compiler options</title> <p>The following options are supported (note that most of these relate to the compilation of the MIB file):</p> + <marker id="option_help"></marker> <taglist> <tag>--help</tag> <item> <p>Prints help info.</p> + <marker id="option_version"></marker> </item> <tag>--version</tag> <item> <p>Prints application and mib format version.</p> + <marker id="option_verbosity"></marker> </item> <tag>--verbosity <em>verbosity</em></tag> @@ -74,11 +79,20 @@ <p>Print debug info. </p> <p><c>verbosity</c> = <c>trace</c> | <c>debug</c> | <c>log</c> | <c>info</c> | <c>silence</c></p> <p>Defaults to <c>silence</c>.</p> + <marker id="option_warnings"></marker> </item> <tag>--warnings</tag> <item> <p>Print warning messages. </p> + <marker id="option_wae"></marker> + </item> + + <tag>--wae</tag> + <item> + <p>Warnings as errors. + Indicates that warnings shall be treated as errors. </p> + <marker id="option_odir"></marker> </item> <tag>--o <em>directory</em></tag> @@ -86,6 +100,7 @@ <p>The directory where the compiler should place the output files. If not specified, output files will be placed in the current working directory.</p> + <marker id="option_idir"></marker> </item> <tag>--i <em>Directory</em></tag> @@ -94,6 +109,7 @@ By default, the current working directory is always included. </p> <p>This option can be present several times, each time specifying <em>one</em> path. </p> + <marker id="option_ildir"></marker> </item> <tag>--il <em>Directory</em></tag> @@ -106,6 +122,7 @@ the current version may be in the system). The current directory and the "snmp-home"/priv/mibs/ are always listed last in the include path. </p> + <marker id="option_sgc"></marker> </item> <tag>--sgc</tag> @@ -114,42 +131,50 @@ group check of the mib compiler. That is, should the OBJECT-GROUP and the NOTIFICATION-GROUP macro(s) be checked for correctness or not. </p> + <marker id="option_dep"></marker> </item> <tag>--dep</tag> <item> <p>Keep deprecated definition(s). If not specified the compiler will ignore deprecated definitions. </p> + <marker id="option_desc"></marker> </item> <tag>--desc</tag> <item> <p>The DESCRIPTION field will be included. </p> + <marker id="option_ref"></marker> </item> <tag>--ref</tag> <item> <p>The REFERENCE field will be included. </p> + <marker id="option_imp"></marker> </item> <tag>--imp</tag> <item> <p>The IMPORTS field will be included. </p> + <marker id="option_mi"></marker> </item> <tag>--mi</tag> <item> <p>The MODULE-IDENTITY field will be included. </p> + <marker id="option_mc"></marker> </item> <tag>--mc</tag> <item> <p>The MODULE-COMPLIANCE field will be included. </p> + <marker id="option_ac"></marker> </item> <tag>--ac</tag> <item> <p>The AGENT-CAPABILITIES field will be included. </p> + <marker id="option_mod"></marker> </item> <tag>--mod <em>module</em></tag> @@ -157,6 +182,7 @@ <p>The module which implements all the instrumentation functions. </p> <p>The name of all instrumentation functions must be the same as the corresponding managed object it implements. </p> + <marker id="option_nd"></marker> </item> <tag>--nd</tag> @@ -165,6 +191,7 @@ used if a managed object have no instrumentation function. Instead this will be reported as an error, and the compilation aborts. </p> + <marker id="option_rrnac"></marker> </item> <tag>--rrnac</tag> @@ -176,6 +203,7 @@ This means that the error will be converted to a warning. </p> <p>By default it is not included, but if this option is present it will be. </p> + <marker id="see_also"></marker> </item> </taglist> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index b527d171b0..c36a1b2a24 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -283,27 +283,27 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv <v>TargetName = target_name()</v> <v>Config = [agent_config()]</v> <v>agent_config() = {Item, Val}</v> - <v>Item = engine_id | address | port | community | timeout | max_message_size | version | sec_model | sec_name | sec_level</v> + <v>Item = engine_id | address | port | community | timeout | max_message_size | version | sec_model | sec_name | sec_level | tdomain</v> <v>Val = term()</v> <v>Reason = term()</v> </type> <desc> <p>Explicitly instruct the manager to handle this agent, with - <c>UserId</c> as the responsible user. </p> - <p>Called to instruct the manager that this agent - shall be handled. This function is used when - the user knows in advance which agents the - manager shall handle. - Note that there is an alternate way to do the same thing: - Add the agent to the manager config files (see - <seealso marker="snmp_manager_config_files#agents">agents.conf</seealso>).</p> + <c>UserId</c> as the responsible user. </p> + <p>Called to instruct the manager that this agent shall be handled. + This function is used when the user knows in advance which agents + the manager shall handle. + Note that there is an alternate way to do the same thing: + Add the agent to the manager config files (see + <seealso marker="snmp_manager_config_files#agents">agents.conf</seealso>).</p> <p><c>TargetName</c> is a non-empty string, - uniquely identifying the agent. </p> - <p>The type of <c>Val</c> depends on <c>Item</c>: </p> + uniquely identifying the agent. </p> + <p>The type of <c>Val</c> depends on <c>Item</c>: </p> <code type="none"><![CDATA[ [mandatory] engine_id = string() [mandatory] address = ip_address() [optional] port = integer() +[optional] tdomain = transportDomainUdpIpv4 | transportDomainUdpIpv6 [optional] community = string() [optional] timeout = integer() | snmp_timer() [optional] max_message_size = integer() @@ -312,7 +312,9 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv [optional] sec_name = string() [optional] sec_level = noAuthNoPriv | authNoPriv | authPriv ]]></code> - <p>Note that if no <c>Port</c> is given, the default value is used.</p> + <p>Note that if no <c>tdomain</c> is given, the default value, + <c>transportDomainUdpIpv4</c>, is used.</p> + <p>Note that if no <c>port</c> is given, the default value is used.</p> <marker id="unregister_agent"></marker> </desc> @@ -348,17 +350,25 @@ sec_level = noAuthNoPriv | authNoPriv | authPriv </func> <func> + <name>update_agent_info(UserId, TargetName, Info) -> ok | {error, Reason}</name> <name>update_agent_info(UserId, TargetName, Item, Val) -> ok | {error, Reason}</name> <fsummary>Update agent config</fsummary> <type> <v>UserId = term()</v> <v>TargetName = target_name()</v> - <v>Item = atom()</v> - <v>Val = term()</v> + <v>Info = [{item(), item_value()}]</v> + <v>Item = item()</v> + <v>item() = atom()</v> + <v>Val = item_value()</v> + <v>item_value() = term()</v> <v>Reason = term()</v> </type> <desc> - <p>Update agent config.</p> + <p>Update agent config. The function <c>update_agent_info/3</c> + should be used when several values needs to be updated atomically. </p> + <p>See function + <seealso marker="#register_agent">register_agent</seealso>) + for more info about what kind of items are allowed. </p> <marker id="which_agents"></marker> </desc> diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index 28469a7b4e..37f6dd3f26 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -247,6 +247,7 @@ add_sec2group(SecModel, SecName, GroupName) -> Key = [Key1, length(Key2) | Key2], case table_cre_row(vacmSecurityToGroupTable, Key, Row) of true -> + snmpa_agent:invalidate_ca_cache(), {ok, Key}; false -> {error, create_failed} @@ -260,6 +261,7 @@ add_sec2group(SecModel, SecName, GroupName) -> delete_sec2group(Key) -> case table_del_row(vacmSecurityToGroupTable, Key) of true -> + snmpa_agent:invalidate_ca_cache(), ok; false -> {error, delete_failed} @@ -279,6 +281,7 @@ add_access(GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV) -> Key3 = [SM, SL], Key = Key1 ++ Key2 ++ Key3, snmpa_vacm:insert([{Key, Row}], false), + snmpa_agent:invalidate_ca_cache(), {ok, Key}; {error, Reason} -> {error, Reason}; @@ -287,6 +290,7 @@ add_access(GroupName, Prefix, SecModel, SecLevel, Match, RV, WV, NV) -> end. delete_access(Key) -> + snmpa_agent:invalidate_ca_cache(), snmpa_vacm:delete(Key). @@ -299,6 +303,7 @@ add_view_tree_fam(ViewIndex, SubTree, Status, Mask) -> Key = [length(Key1) | Key1] ++ [length(Key2) | Key2], case table_cre_row(vacmViewTreeFamilyTable, Key, Row) of true -> + snmpa_agent:invalidate_ca_cache(), {ok, Key}; false -> {error, create_failed} @@ -312,6 +317,7 @@ add_view_tree_fam(ViewIndex, SubTree, Status, Mask) -> delete_view_tree_fam(Key) -> case table_del_row(vacmViewTreeFamilyTable, Key) of true -> + snmpa_agent:invalidate_ca_cache(), ok; false -> {error, delete_failed} diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index 82a7ec647b..6322f0f21d 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -1626,7 +1626,7 @@ invalidate_ca_cache() -> MasterAgent ! invalidate_ca_cache; false -> %% This is running on a sub-agent node, - %% so sent skip it + %% so skip it ok end; _ -> % Not on this node diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl index 4b88eb69f7..c17a6abbd7 100644 --- a/lib/snmp/src/agent/snmpa_conf.erl +++ b/lib/snmp/src/agent/snmpa_conf.erl @@ -424,7 +424,8 @@ target_addr_entry(Name, EngineId, TMask) -> target_addr_entry(Name, Ip, 162, TagList, - ParamsName, EngineId, TMask, 2048). + ParamsName, EngineId, + TMask, 2048). target_addr_entry(Name, Ip, @@ -435,7 +436,8 @@ target_addr_entry(Name, TMask, MaxMessageSize) -> target_addr_entry(Name, Ip, Udp, 1500, 3, TagList, - ParamsName, EngineId, TMask, MaxMessageSize). + ParamsName, EngineId, + TMask, MaxMessageSize). target_addr_entry(Name, Ip, @@ -448,7 +450,8 @@ target_addr_entry(Name, TMask, MaxMessageSize) -> target_addr_entry(Name, snmp_target_mib:default_domain(), Ip, Udp, - Timeout, RetryCount, TagList, ParamsName, + Timeout, RetryCount, TagList, + ParamsName, EngineId, TMask, MaxMessageSize). target_addr_entry(Name, diff --git a/lib/snmp/src/agent/snmpa_mpd.erl b/lib/snmp/src/agent/snmpa_mpd.erl index 14f62b12f3..4f50b1a674 100644 --- a/lib/snmp/src/agent/snmpa_mpd.erl +++ b/lib/snmp/src/agent/snmpa_mpd.erl @@ -32,6 +32,7 @@ -include("SNMP-MPD-MIB.hrl"). -include("SNMPv2-TM.hrl"). -include("SNMP-FRAMEWORK-MIB.hrl"). +-include("TRANSPORT-ADDRESS-MIB.hrl"). -define(VMODULE,"MPD"). -include("snmp_verbosity.hrl"). @@ -981,12 +982,15 @@ generate_discovery_msg2(NoteStore, Pdu, discovery_note_timeout(Timeout) -> (Timeout div 100) + 1. -generate_discovery_msg(NoteStore, {?snmpUDPDomain, [A,B,C,D,U1,U2]}, +generate_discovery_msg(NoteStore, {TDomain, TAddress}, Pdu, ScopedPduBytes, ContextEngineID, ManagerEngineID, SecModel, SecName, SecLevelFlag, InitialUserName, ContextName, Timeout) -> + + {ok, {_Domain, Address}} = transform_taddr(TDomain, TAddress), + %% 7.1.7 ?vdebug("generate_discovery_msg -> 7.1.7 (~w)", [ManagerEngineID]), MsgID = generate_msg_id(), @@ -1027,7 +1031,7 @@ generate_discovery_msg(NoteStore, {?snmpUDPDomain, [A,B,C,D,U1,U2]}, %% Log(Packet), inc_snmp_out_vars(Pdu), ?vdebug("generate_discovery_msg -> done", []), - {Packet, {{A,B,C,D}, U1 bsl 8 + U2}}; + {Packet, Address}; Error -> throw(Error) @@ -1057,6 +1061,34 @@ generate_sec_discovery_msg(Message, SecModule, end. +transform_taddr(?snmpUDPDomain, TAddress) -> + transform_taddr(?transportDomainUdpIpv4, TAddress); +transform_taddr(?transportDomainUdpIpv4, [A, B, C, D, P1, P2]) -> + Domain = transportDomainUdpIpv4, + Addr = {A,B,C,D}, + Port = P1 bsl 8 + P2, + Address = {Addr, Port}, + {ok, {Domain, Address}}; +transform_taddr(?transportDomainUdpIpv4, BadAddr) -> + {error, {bad_transportDomainUdpIpv4_address, BadAddr}}; +transform_taddr(?transportDomainUdpIpv6, + [A1, A2, A3, A4, A5, A6, A7, A8, P1, P2]) -> + Domain = transportDomainUdpIpv6, + Addr = {A1, A2, A3, A4, A5, A6, A7, A8}, + Port = P1 bsl 8 + P2, + Address = {Addr, Port}, + {ok, {Domain, Address}}; +transform_taddr(?transportDomainUdpIpv6, BadAddr) -> + {error, {bad_transportDomainUdpIpv6_address, BadAddr}}; +transform_taddr(BadTDomain, TAddress) -> + case lists:member(BadTDomain, snmp_conf:all_tdomains()) of + true -> + {error, {unsupported_tdomain, BadTDomain, TAddress}}; + false -> + {error, {unknown_tdomain, BadTDomain, TAddress}} + end. + + process_taddrs(Dests) -> ?vtrace("process_taddrs -> entry with" "~n Dests: ~p", [Dests]), @@ -1066,46 +1098,44 @@ process_taddrs([], Acc) -> ?vtrace("process_taddrs -> entry when done with" "~n Acc: ~p", [Acc]), lists:reverse(Acc); - + %% v3 -process_taddrs([{{?snmpUDPDomain, [A,B,C,D,U1,U2]}, SecData} | T], Acc) -> +process_taddrs([{{TDomain, TAddress}, SecData} | T], Acc) -> ?vtrace("process_taddrs -> entry when v3 with" - "~n A: ~p" - "~n B: ~p" - "~n C: ~p" - "~n D: ~p" - "~n U1: ~p" - "~n U2: ~p" - "~n SecData: ~p", [A, B, C, D, U1, U2, SecData]), - Entry = {{snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}}, SecData}, - process_taddrs(T, [Entry | Acc]); -%% Bad v3 -process_taddrs([{{TDomain, TAddr}, _SecData} | T], Acc) -> - ?vtrace("process_taddrs -> entry when bad v3 with" - "~n TDomain: ~p" - "~n TAddr: ~p", [TDomain, TAddr]), - user_err("Bad TDomain/TAddr: ~w/~w", [TDomain, TAddr]), - process_taddrs(T, Acc); + "~n TDomain: ~p" + "~n TAddress: ~p" + "~n SecData: ~p", [TDomain, TAddress, SecData]), + case transform_taddr(TDomain, TAddress) of + {ok, DestAddr} -> + ?vtrace("process_taddrs -> transformed: " + "~n DestAddr: ~p", [DestAddr]), + Entry = {DestAddr, SecData}, + process_taddrs(T, [Entry | Acc]); + {error, Reason} -> + ?vinfo("Failed transforming v3 domain and address" + "~n Reason: ~p", [Reason]), + user_err("Bad TDomain/TAddress: ~w/~w", [TDomain, TAddress]), + process_taddrs(T, Acc) + end; %% v1 & v2 -process_taddrs([{?snmpUDPDomain, [A,B,C,D,U1,U2]} | T], Acc) -> +process_taddrs([{TDomain, TAddress} | T], Acc) -> ?vtrace("process_taddrs -> entry when v1/v2 with" - "~n A: ~p" - "~n B: ~p" - "~n C: ~p" - "~n D: ~p" - "~n U1: ~p" - "~n U2: ~p", [A, B, C, D, U1, U2]), - Entry = {snmpUDPDomain, {{A,B,C,D}, U1 bsl 8 + U2}}, - process_taddrs(T, [Entry | Acc]); -%% Bad v1 or v2 -process_taddrs([{TDomain, TAddr} | T], Acc) -> - ?vtrace("process_taddrs -> entry when bad v1/v2 with" - "~n TDomain: ~p" - "~n TAddr: ~p", [TDomain, TAddr]), - user_err("Bad TDomain/TAddr: ~w/~w", [TDomain, TAddr]), - process_taddrs(T, Acc); + "~n TDomain: ~p" + "~n TAddress: ~p", [TDomain, TAddress]), + case transform_taddr(TDomain, TAddress) of + {ok, DestAddr} -> + ?vtrace("process_taddrs -> transformed: " + "~n DestAddr: ~p", [DestAddr]), + Entry = DestAddr, + process_taddrs(T, [Entry | Acc]); + {error, Reason} -> + ?vinfo("Failed transforming v1/v2 domain and address: " + "~n Reason: ~p", [Reason]), + user_err("Bad TDomain/TAddress: ~w/~w", [TDomain, TAddress]), + process_taddrs(T, Acc) + end; process_taddrs(Crap, Acc) -> - throw({error, {taddrs_crap, Crap, Acc}}). + throw({error, {bad_taddrs, Crap, Acc}}). mk_v1_v2_packet_list(To, Packet, Len, Pdu) -> diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index 5deb40be0f..8e1855b4df 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,82 +22,82 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.20.1", + [ + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, + [snmpm_server, snmpm_config, snmp_config]}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config, snmpm_config]}, + {load_module, snmpa_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, + {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_config]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_mpd]}, + {update, snmpm_config, soft, soft_purge, soft_purge, [snmp_conf]}, + {update, snmpm_server, soft, soft_purge, soft_purge, + [snmpm_net_if, snmpm_mpd, snmpm_config]}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpm_mpd, snmpm_config]} + ] + }, + {"4.20", + [ + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmpm, soft_purge, soft_purge, + [snmpm_server, snmpm_config, snmp_config]}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config, snmpm_config]}, + {load_module, snmpa_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, + {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_config]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_mpd]}, + {update, snmpm_config, soft, soft_purge, soft_purge, [snmp_conf]}, + {update, snmpm_server, soft, soft_purge, soft_purge, + [snmpm_net_if, snmpm_mpd, snmpm_config]}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpm_mpd, snmpm_config]} + ] + }, {"4.19", [ {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, + {load_module, snmpm, soft_purge, soft_purge, + [snmpm_server, snmpm_config, snmp_config]}, {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_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, snmpa_conf, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, {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_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, + {load_module, snmpm_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config, snmpm_config]}, {load_module, snmpa_trap, soft_purge, soft_purge, [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, - [snmp_notification_mib]}, + [snmp_config, snmp_notification_mib]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf, snmp_target_mib]}, {load_module, snmp_community_mib, soft_purge, soft_purge, []}, {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, - [snmpa_acm, snmpa_mpd, snmpa_trap]} - ] - }, - {"4.18", - [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {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_misc, soft_purge, soft_purge, []}, - {load_module, snmp_log, 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, []}, - {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_net_if]}, - {load_module, snmpa_acm, soft_purge, soft_purge, - [snmp_conf, snmpa_mpd, snmp_target_mib]}, - {load_module, snmpa, soft_purge, soft_purge, - [snmp_community_mib, - snmp_framework_mib, - snmp_standard_mib, - snmp_target_mib, - snmp_user_based_sm_mib, - snmp_view_based_acm_mib]}, - {load_module, snmp_notification_mib, soft_purge, soft_purge, - [snmp_conf, snmp_target_mib, snmpa_mib_lib]}, - {load_module, snmp_community_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_framework_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_standard_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_target_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, - [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, snmpm_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpm_mpd, snmpm_config]}, + {update, snmpm_config, soft, soft_purge, soft_purge, [snmp_conf]}, + {update, snmpm_server, soft, soft_purge, soft_purge, + [snmpm_net_if, snmpm_mpd, snmpm_config]}, {update, snmpa_net_if, soft, soft_purge, soft_purge, [snmp_conf, snmpa_mpd]}, {update, snmpa_agent, soft, soft_purge, soft_purge, @@ -109,84 +109,82 @@ %% ------D o w n g r a d e --------------------------------------------------- [ + {"4.20.1", + [ + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmpm, soft_purge, soft_purge, + [snmpm_server, snmpm_config, snmp_config]}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config, snmpm_config]}, + {load_module, snmpa_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, + {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_config]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_mpd]}, + {update, snmpm_config, soft, soft_purge, soft_purge, [snmp_conf]}, + {update, snmpm_server, soft, soft_purge, soft_purge, + [snmpm_net_if, snmpm_mpd, snmpm_config]}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpm_mpd, snmpm_config]} + ] + }, + {"4.20", + [ + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmpm, soft_purge, soft_purge, + [snmpm_server, snmpm_config, snmp_config]}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config, snmpm_config]}, + {load_module, snmpa_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, + {load_module, snmpa_conf, soft_purge, soft_purge, [snmp_config]}, + {update, snmpa_agent, soft, soft_purge, soft_purge, [snmpa_mpd]}, + {update, snmpm_config, soft, soft_purge, soft_purge, [snmp_conf]}, + {update, snmpm_server, soft, soft_purge, soft_purge, + [snmpm_net_if, snmpm_mpd, snmpm_config]}, + {update, snmpm_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpm_mpd, snmpm_config]} + ] + }, {"4.19", [ {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmpm, soft_purge, soft_purge, [snmpm_server]}, + {load_module, snmpm, soft_purge, soft_purge, + [snmpm_server, snmpm_config, snmp_config]}, {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_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, snmpa_conf, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, {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_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config]}, + {load_module, snmpm_mpd, soft_purge, soft_purge, + [snmp_conf, snmp_config, snmpm_config]}, {load_module, snmpa_trap, soft_purge, soft_purge, [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, - [snmp_notification_mib]}, + [snmp_config, snmp_notification_mib]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf, snmp_target_mib]}, {load_module, snmp_community_mib, soft_purge, soft_purge, []}, {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, - [snmpa_acm, snmpa_mpd, snmpa_trap]} - ] - }, - {"4.18", - [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {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_misc, soft_purge, soft_purge, []}, - {load_module, snmp_log, 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, []}, - {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_net_if]}, - {load_module, snmpa_acm, soft_purge, soft_purge, - [snmp_conf, snmpa_mpd, snmp_target_mib]}, - {load_module, snmpa, soft_purge, soft_purge, - [snmp_community_mib, - snmp_framework_mib, - snmp_standard_mib, - snmp_target_mib, - snmp_user_based_sm_mib, - snmp_view_based_acm_mib]}, - {load_module, snmp_notification_mib, soft_purge, soft_purge, - [snmp_conf, snmp_target_mib, snmpa_mib_lib]}, - {load_module, snmp_community_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_framework_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_standard_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_target_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, - [snmpa_mib_lib]}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, - [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, snmpm_net_if, soft, soft_purge, soft_purge, + [snmp_conf, snmpm_mpd, snmpm_config]}, + {update, snmpm_config, soft, soft_purge, soft_purge, [snmp_conf]}, + {update, snmpm_server, soft, soft_purge, soft_purge, + [snmpm_net_if, snmpm_mpd, snmpm_config]}, {update, snmpa_net_if, soft, soft_purge, soft_purge, [snmp_conf, snmpa_mpd]}, {update, snmpa_agent, soft, soft_purge, soft_purge, diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile index 0ceaf276a6..627af6f185 100644 --- a/lib/snmp/src/compile/Makefile +++ b/lib/snmp/src/compile/Makefile @@ -45,11 +45,10 @@ RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN) include modules.mk -ESCRIPT_BIN = $(ESCRIPT_SRC:%.src=$(BIN)/%) - -ERL_FILES = $(MODULES:%=%.erl) - -TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(ESCRIPT_BIN) +ESCRIPT_BIN = $(ESCRIPT_SRC:%.src=$(BIN)/%) +ERL_FILES = $(MODULES:%=%.erl) +EBIN_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +TARGET_FILES = $(EBIN_FILES) $(ESCRIPT_BIN) GENERATED_PARSER = $(PARSER_MODULE:%=%.erl) @@ -125,7 +124,7 @@ release_spec: opt $(INSTALL_DIR) $(RELSYSDIR)/src/compiler $(INSTALL_DATA) $(ESCRIPT_SRC) $(PARSER_SRC) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/compiler $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DATA) $(EBIN_FILES) $(RELSYSDIR)/ebin $(INSTALL_DIR) $(RELSYSDIR)/bin $(INSTALL_SCRIPT) $(ESCRIPT_BIN) $(RELSYSDIR)/bin diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl index 195c238184..5e6b81f1ec 100644 --- a/lib/snmp/src/compile/snmpc.erl +++ b/lib/snmp/src/compile/snmpc.erl @@ -108,6 +108,7 @@ compile(FileName) -> %% {i, [import_dir_string()]} ["./"] %% {il, [import_lib_dir_string()]} [] %% {warnings, bool()} true +%% warnings_as_errors %% {outdir, string()} "./" %% description %% reference @@ -199,6 +200,8 @@ get_options([reference|Opts], Formats, Args) -> get_options(Opts, ["~n reference"|Formats], Args); get_options([{warnings, Val}|Opts], Formats, Args) -> get_options(Opts, ["~n warnings: ~w"|Formats], [Val|Args]); +get_options([warnings_as_errors|Opts], Formats, Args) -> + get_options(Opts, ["~n warnings_as_errors"|Formats], Args); get_options([{verbosity, Val}|Opts], Formats, Args) -> get_options(Opts, ["~n verbosity: ~w"|Formats], [Val|Args]); get_options([imports|Opts], Formats, Args) -> @@ -261,6 +264,8 @@ check_options([{group_check, Atom} | T]) when is_atom(Atom) -> check_options([{warnings, Bool} | T]) -> check_bool(warnings, Bool), check_options(T); +check_options([warnings_as_errors | T]) -> + check_options(T); check_options([{db, volatile} | T]) -> check_options(T); check_options([{db, persistent} | T]) -> @@ -331,6 +336,9 @@ get_agent_capabilities(Options) -> get_module_compliance(Options) -> get_bool_option(module_compliance, Options). +get_warnings_as_errors(Options) -> + lists:member(warnings_as_errors, Options). + get_relaxed_row_name_assign_check(Options) -> lists:member(relaxed_row_name_assign_check, Options). @@ -409,6 +417,7 @@ init(From, MibFileName, Options) -> put(reference, get_reference(Options)), put(agent_capabilities, get_agent_capabilities(Options)), put(module_compliance, get_module_compliance(Options)), + put(warnings_as_errors, get_warnings_as_errors(Options)), File = filename:rootname(MibFileName, ".mib"), put(filename, filename:basename(File ++ ".mib")), R = case catch c_impl(File) of diff --git a/lib/snmp/src/compile/snmpc.src b/lib/snmp/src/compile/snmpc.src index 5f9b154bfa..4e91ae9a03 100644 --- a/lib/snmp/src/compile/snmpc.src +++ b/lib/snmp/src/compile/snmpc.src @@ -50,7 +50,8 @@ %% The default verbosity (silence) will be filled in %% during argument processing. verbosity, - warnings = false + warnings = false, + warnings_as_errors = false }). @@ -74,6 +75,7 @@ %% --version %% --verbosity V %% --warnings +%% --wae main(Args) when is_list(Args) -> case (catch process_args(Args)) of ok -> @@ -156,7 +158,8 @@ mk_mib_options(#state{outdir = OutDir, %% The default verbosity (silence) will be filled in %% during argument processing. verbosity = V, - warnings = W}) -> + warnings = W, + warnings_as_errors = WAE}) -> [{outdir, OutDir}, {db, DB}, {i, IDs}, @@ -178,7 +181,8 @@ mk_mib_options(#state{outdir = OutDir, maybe_option(Imp, imports) ++ maybe_option(MI, module_identity) ++ maybe_option(MC, module_compliance) ++ - maybe_option(AC, agent_capabilities). + maybe_option(AC, agent_capabilities) ++ + maybe_option(WE, warnings_as_errors). maybe_option(true, Opt) -> [Opt]; maybe_option(_, _) -> []. @@ -292,6 +296,8 @@ process_args(["--nd"|Args], State) -> process_args(Args, State#state{no_defaults = true}); process_args(["--rrnac"|Args], State) -> process_args(Args, State#state{relaxed_row_name_assigne_check = true}); +process_args(["--wae"|Args], State) -> + process_args(Args, State#state{warnings_as_errors = true}); process_args([MIB], State) -> Ext = filename:extension(MIB), if @@ -371,6 +377,8 @@ usage() -> "~n a warning. " "~n By default it is not included, but if this option is " "~n present it will be. " + "~n --wae - Warnings as errors. " + "~n Indicates that warnings shall be treated as errors. " "~n " "~n", []), halt(1). diff --git a/lib/snmp/src/compile/snmpc_lib.hrl b/lib/snmp/src/compile/snmpc_lib.hrl index 000486e728..35ec9abd03 100644 --- a/lib/snmp/src/compile/snmpc_lib.hrl +++ b/lib/snmp/src/compile/snmpc_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009. 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 @@ -20,8 +20,17 @@ -ifndef(snmpc_lib). -define(snmpc_lib, true). --define(vwarning(F, A), ?verbosity(warning, F, A, ignore)). --define(vwarning2(F, A, MibLine), ?verbosity(warning, F, A, MibLine)). +-define(vwarning(F, A), + case get(warnings_as_errors) of + true -> snmpc_lib:error(F, A); + _ -> ?verbosity(warning, F, A, ignore) + end). + +-define(vwarning2(F, A, MibLine), + case get(warnings_as_errors) of + true -> snmpc_lib:error(F, A, MibLine); + _ -> ?verbosity(warning, F, A, MibLine) + end). -define(vinfo(F, A), ?verbosity(info, F, A, ignore)). -define(vinfo2(F, A, MibLine), ?verbosity(info, F, A, MibLine)). -define(vlog(F, A), ?verbosity(log, F, A, ignore)). diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 0d084332de..6d2ac8d747 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -50,7 +50,7 @@ register_agent/2, register_agent/3, register_agent/4, unregister_agent/2, unregister_agent/3, which_agents/0, which_agents/1, - agent_info/2, update_agent_info/4, + agent_info/2, update_agent_info/3, update_agent_info/4, register_usm_user/3, unregister_usm_user/2, which_usm_users/0, which_usm_users/1, @@ -167,6 +167,7 @@ -include_lib("snmp/include/snmp_types.hrl"). -include("snmpm_atl.hrl"). -include("snmpm_internal.hrl"). +-include("snmp_verbosity.hrl"). -define(DEFAULT_AGENT_PORT, 161). @@ -447,8 +448,11 @@ agent_info(Addr, Port, Item) -> Error end. +update_agent_info(UserId, TargetName, Info) when is_list(Info) -> + snmpm_config:update_agent_info(UserId, TargetName, Info). + update_agent_info(UserId, TargetName, Item, Val) -> - snmpm_config:update_agent_info(UserId, TargetName, Item, Val). + update_agent_info(UserId, TargetName, [{Item, Val}]). %% Backward compatibility functions update_agent_info(UserId, Addr, Port, Item, Val) -> diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl index fd6da3e71a..c2e57abddb 100644 --- a/lib/snmp/src/manager/snmpm_config.erl +++ b/lib/snmp/src/manager/snmpm_config.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 @@ -36,7 +36,8 @@ user_info/0, user_info/1, user_info/2, register_agent/3, unregister_agent/2, - agent_info/0, agent_info/2, agent_info/3, update_agent_info/4, + agent_info/0, agent_info/2, agent_info/3, + update_agent_info/3, update_agent_info/4, which_agents/0, which_agents/1, is_known_engine_id/2, @@ -84,7 +85,9 @@ backup/1, - mk_target_name/3 + mk_target_name/3, + + default_transport_domain/0 ]). @@ -127,23 +130,24 @@ %% Macros and Constants: --define(SERVER, ?MODULE). --define(BACKUP_DB, snmpm_config_backup). --define(CONFIG_DB, snmpm_config_db). +-define(SERVER, ?MODULE). +-define(BACKUP_DB, snmpm_config_backup). +-define(CONFIG_DB, snmpm_config_db). -define(DEFAULT_USER, default_user). -define(DEFAULT_AGENT_PORT, 161). --define(IRB_DEFAULT, auto). -%% -define(IRB_DEFAULT, {user, timer:seconds(15)}). +-define(IRB_DEFAULT, auto). +%% -define(IRB_DEFAULT, {user, timer:seconds(15)}). --define(USER_MOD_DEFAULT, snmpm_user_default). --define(USER_DATA_DEFAULT, undefined). +-define(USER_MOD_DEFAULT, snmpm_user_default). +-define(USER_DATA_DEFAULT, undefined). %% -define(DEF_ADDR_TAG, default_addr_tag). -define(DEFAULT_TARGETNAME, default_agent). --define(DEF_PORT_TAG, default_port_tag). +-define(DEF_PORT_TAG, default_port_tag). +-define(SUPPORTED_DOMAINS, [transportDomainUdpIpv4, transportDomainUdpIpv6]). -ifdef(snmp_debug). -define(GS_START_LINK(Opts), @@ -159,6 +163,11 @@ %%%------------------------------------------------------------------- %%% API %%%------------------------------------------------------------------- + +default_transport_domain() -> + transportDomainUdpIpv4. + + start_link(Opts) -> ?d("start_link -> entry with" "~n Opts: ~p", [Opts]), @@ -269,9 +278,10 @@ do_user_info(_UserId, BadItem) -> error({not_found, BadItem}). -%% A target-name constructed in this way is a string with the following +%% A target-name constructed in this way is a string with the following: %% <IP-address>:<Port>-<Version> -%% +%% This is intended for backward compatibility and therefor has +%% only support for IPv4 addresses and *no* other transport domain. mk_target_name(Addr0, Port, Config) when is_list(Config) -> Version = case lists:keysearch(version, 1, Config) of @@ -280,7 +290,6 @@ mk_target_name(Addr0, Port, Config) when is_list(Config) -> false -> select_lowest_supported_version() end, -%% p("mk_target_name -> Version: ~p", [Version]), case normalize_address(Addr0) of {A, B, C, D} -> lists:flatten( @@ -308,57 +317,99 @@ select_lowest_supported_version([H|T], Versions) -> end. -register_agent(UserId, _TargetName, _Config) when (UserId =:= user_id) -> +register_agent(UserId, _TargetName, _Config0) when (UserId =:= user_id) -> {error, {bad_user_id, UserId}}; -register_agent(UserId, TargetName, Config) +register_agent(UserId, TargetName, Config0) when (is_list(TargetName) andalso (length(TargetName) > 0) andalso - is_list(Config)) -> + is_list(Config0)) -> -%% p("register_agent -> entry with" -%% "~n UserId: ~p" -%% "~n TargetName: ~p" -%% "~n Config: ~p", [UserId, TargetName, Config]), + ?vtrace("register_agent -> entry with" + "~n UserId: ~p" + "~n TargetName: ~p" + "~n Config0: ~p", [UserId, TargetName, Config0]), %% Check: %% 1) That the mandatory configs are present - %% 2) That the illegal config user_id (used internally) is - %% not present + %% 2) That no illegal config, e.g. user_id (used internally), + %% is not present %% 3) Check that there are no invalid or erroneous configs - %% 4) Chack that the manager is capable to use the selected version - case verify_agent_config(Config) of - ok -> + %% 4) Check that the manager is capable of using the selected version + case verify_agent_config(Config0) of + {ok, Config} -> call({register_agent, UserId, TargetName, Config}); Error -> Error end. -verify_agent_config(Conf) -> - ok = verify_mandatory(Conf, [engine_id, address, reg_type]), - case verify_invalid(Conf, [user_id]) of - ok -> - case verify_agent_config2(Conf) of - ok -> - {ok, Vsns} = system_info(versions), - Vsn = - case lists:keysearch(version, 1, Conf) of - {value, {version, V}} -> - V; - false -> - v1 - end, - case lists:member(Vsn, Vsns) of - true -> - ok; - false -> - {error, {version_not_supported_by_manager, Vsn, Vsns}} - end - end; - Error -> +verify_agent_config(Conf0) -> + try + begin + verify_mandatory(Conf0, [engine_id, address, reg_type]), + verify_invalid(Conf0, [user_id]), + Conf = verify_agent_config3(Conf0), + Vsns = versions(), + Vsn = which_version(Conf), + verify_version(Vsn, Vsns), + {ok, Conf} + end + catch + throw:Error -> Error end. +versions() -> + case system_info(versions) of + {ok, Vsns} -> + Vsns; + {error, _} = ERROR -> + throw(ERROR) + end. + +which_version(Conf) -> + case lists:keysearch(version, 1, Conf) of + {value, {version, V}} -> + V; + false -> + v1 + end. + +verify_version(Vsn, Vsns) -> + case lists:member(Vsn, Vsns) of + true -> + ok; + false -> + Reason = {version_not_supported_by_manager, Vsn, Vsns}, + throw({error, Reason}) + end. + +verify_agent_config3(Conf0) -> + %% Fix (transport) address and domain + {TDomain, Conf1} = + case lists:keysearch(tdomain, 1, Conf0) of + {value, {tdomain, Dom}} -> + {Dom, Conf0}; + false -> + Dom = default_transport_domain(), + {Dom, [{tdomain, Dom} | Conf0]} + end, + Conf2 = case lists:keysearch(address, 1, Conf1) of + {value, {address, Address}} -> + lists:keyreplace(address, 1, Conf1, + {address, {TDomain, Address}}); + false -> + %% This is a mandatory config option, + %% a later test will detect this + Conf1 + end, + case verify_agent2(Conf2) of + {ok, Conf} -> + Conf; + {error, _} = ERROR -> + throw(ERROR) + end. + verify_agent_config2(Conf) -> verify_agent2(Conf). @@ -366,6 +417,7 @@ verify_agent_config2(Conf) -> unregister_agent(UserId, TargetName) -> call({unregister_agent, UserId, TargetName}). +%% This is the old style agent unregistration (using Addr and Port). unregister_agent(UserId, Addr0, Port) -> Addr = normalize_address(Addr0), case do_agent_info(Addr, Port, target_name) of @@ -421,17 +473,51 @@ which_agents(UserId) -> Agents = ets:match(snmpm_agent_table, Pat), [TargetName || [TargetName] <- Agents]. - -update_agent_info(UserId, TargetName, Item, Val0) - when (Item =/= user_id) -> - case (catch verify_val(Item, Val0)) of - {ok, Val} -> - call({update_agent_info, UserId, TargetName, Item, Val}); - Error -> + +verify_agent_info(TargetName, Info0) -> + try + begin + verify_invalid(Info0, [user_id]), + %% Check if address is part of the list and + %% if so update it with the domain info. + Info = + case lists:keysearch(address, 1, Info0) of + {value, {address, Addr}} -> + %% If domain is part of the info, then use it. + %% If not, lookup what is already stored for + %% this agent and use that. + Domain = + case lists:keysearch(tdomain, 1, Info0) of + {value, {tdomain, Dom}} -> + Dom; + false -> + {ok, Dom} = + agent_info(TargetName, tdomain), + Dom + end, + Addr2 = {Domain, Addr}, + lists:keyreplace(address, 1, Info0, {address, Addr2}); + false -> + Info0 + end, + verify_agent2(Info) + end + catch + throw:Error -> Error end. -%% Backward compatibillity +update_agent_info(UserId, TargetName, Info) -> + call({update_agent_info, UserId, TargetName, Info}). + +%% <BACKWARD-COMPAT-2> +%% This is wrapped in the interface module, so this function is +%% only here to catch code-upgrade problems. +update_agent_info(UserId, TargetName, Item, Val) -> + update_agent_info(UserId, TargetName, [{Item, Val}]). +%% </BACKWARD-COMPAT-2> + +%% <BACKWARD-COMPAT-1> update_agent_info(UserId, Addr, Port, Item, Val) -> case agent_info(Addr, Port, target_name) of {ok, TargetName} -> @@ -439,6 +525,7 @@ update_agent_info(UserId, Addr, Port, Item, Val) -> Error -> Error end. +%% </BACKWARD-COMPAT-1> is_known_engine_id(EngineID, TargetName) -> case agent_info(TargetName, engine_id) of @@ -650,22 +737,14 @@ unregister_usm_user(EngineID, Name) call({unregister_usm_user, EngineID, Name}). verify_usm_user_config(EngineID, Name, Config) -> - %% case verify_mandatory(Config, []) of - %% ok -> - %% case verify_invalid(Config, [engine_id, name]) of - %% ok -> - %% verify_usm_user_config2(EngineID, Name, Config); - %% Error -> - %% Error - %% end; - %% Error -> - %% Error - %% end. - ok = verify_mandatory(Config, []), - case verify_invalid(Config, [engine_id, name]) of - ok -> - verify_usm_user_config2(EngineID, Name, Config); - Error -> + try + begin + verify_mandatory(Config, []), + verify_invalid(Config, [engine_id, name]), + verify_usm_user_config2(EngineID, Name, Config) + end + catch + throw:Error -> Error end. @@ -1590,6 +1669,7 @@ check_agent_config2(Agent) -> throw(Err) end. +%% For backward compatibility check_agent_config({UserId, TargetName, Community, @@ -1597,10 +1677,27 @@ check_agent_config({UserId, EngineId, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel}) -> + TDomain = default_transport_domain(), + check_agent_config({UserId, + TargetName, + Community, + TDomain, Ip, Port, + EngineId, + Timeout, MaxMessageSize, + Version, SecModel, SecName, SecLevel}); + +check_agent_config({UserId, + TargetName, + Community, + TDomain, Ip, Port, + EngineId, + Timeout, MaxMessageSize, + Version, SecModel, SecName, SecLevel}) -> ?vtrace("check_agent_config -> entry with" "~n UserId: ~p" "~n TargetName: ~p" "~n Community: ~p" + "~n TDomain: ~p" "~n Ip: ~p" "~n Port: ~p" "~n EngineId: ~p" @@ -1610,15 +1707,16 @@ check_agent_config({UserId, "~n SecModel: ~p" "~n SecName: ~p" "~n SecLevel: ~p", - [UserId, TargetName, Community, Ip, Port, + [UserId, TargetName, Community, + TDomain, Ip, Port, EngineId, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel]), - Addr = normalize_address(Ip), + Addr = normalize_address(TDomain, Ip), ?vtrace("check_agent_config -> Addr: ~p", [Addr]), Agent = {UserId, TargetName, Community, - Addr, Port, + TDomain, Addr, Port, EngineId, Timeout, MaxMessageSize, Version, SecModel, SecName, SecLevel}, @@ -1644,6 +1742,7 @@ init_agent_config({UserId, TargetName, Config}) -> end. +%% For backward compatibility verify_agent({UserId, TargetName, Comm, @@ -1651,48 +1750,68 @@ verify_agent({UserId, EngineId, Timeout, MMS, Version, SecModel, SecName, SecLevel}) -> - ?vtrace("verify_agent -> entry with" + TDomain = default_transport_domain(), + verify_agent({UserId, + TargetName, + Comm, + TDomain, Ip, Port, + EngineId, + Timeout, MMS, + Version, SecModel, SecName, SecLevel}); + +verify_agent({UserId, + TargetName, + Comm, + TDomain, Ip, Port, + EngineId, + Timeout, MMS, + Version, SecModel, SecName, SecLevel}) -> + ?vdebug("verify_agent -> entry with" "~n UserId: ~p" "~n TargetName: ~p", [UserId, TargetName]), snmp_conf:check_string(TargetName, {gt, 0}), - case verify_val(address, Ip) of - {ok, Addr} -> - snmp_conf:check_integer(Port, {gt, 0}), - Conf = - [{reg_type, target_name}, - {address, Addr}, - {port, Port}, - {community, Comm}, - {engine_id, EngineId}, - {timeout, Timeout}, - {max_message_size, MMS}, - {version, Version}, - {sec_model, SecModel}, - {sec_name, SecName}, - {sec_level, SecLevel} - ], - case verify_agent2(Conf) of - ok -> - {UserId, TargetName, Conf, Version}; - Err -> - throw(Err) - end; - - Error -> - ?vlog("verify_agent -> failed: ~n ~p", [Error]), - throw(Error) + snmp_conf:check_integer(Port, {gt, 0}), + %% Note that the order of Conf *is* important. + %% Some properties may depend on others, so that + %% in order to verify one property, another must + %% be already verified (and present). An example + %% of this is the property 'address', for which + %% the property tdomain is needed. + Conf0 = + [{reg_type, target_name}, + {tdomain, TDomain}, + %% This should be taddress, but what the*... + {address, {TDomain, Ip}}, + {port, Port}, + {community, Comm}, + {engine_id, EngineId}, + {timeout, Timeout}, + {max_message_size, MMS}, + {version, Version}, + {sec_model, SecModel}, + {sec_name, SecName}, + {sec_level, SecLevel} + ], + case verify_agent2(Conf0) of + {ok, Conf} -> + {UserId, TargetName, Conf, Version}; + Err -> + throw(Err) end. -verify_agent2([]) -> - ok; -verify_agent2([{Item, Val}|Items]) -> - case verify_val(Item, Val) of - {ok, _Val} -> - verify_agent2(Items); +verify_agent2(Conf) -> + verify_agent2(Conf, []). + +verify_agent2([], VerifiedConf) -> + {ok, VerifiedConf}; +verify_agent2([{Item, Val0}|Items], VerifiedConf) -> + case verify_val(Item, Val0) of + {ok, Val} -> + verify_agent2(Items, [{Item, Val} | VerifiedConf]); Err -> Err end; -verify_agent2([Bad|_]) -> +verify_agent2([Bad|_], _VerifiedConf) -> {error, {bad_agent_config, Bad}}. @@ -1708,14 +1827,28 @@ read_users_config_file(Dir) -> check_user_config({Id, Mod, Data}) -> + ?vtrace("check_user_config -> entry with" + "~n Id: ~p" + "~n Mod: ~p" + "~n Data: ~p", [Id, Mod, Data]), check_user_config({Id, Mod, Data, []}); -check_user_config({Id, Mod, _Data, DefaultAgentConfig} = User) +check_user_config({Id, Mod, Data, DefaultAgentConfig} = _User) when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) -> + ?vtrace("check_user_config -> entry with" + "~n Id: ~p" + "~n Mod: ~p" + "~n Data: ~p" + "~n DefaultAgentConfig: ~p", + [Id, Mod, Data, DefaultAgentConfig]), case (catch verify_user_behaviour(Mod)) of ok -> + ?vtrace("check_user_config -> user behaviour verified", []), case verify_user_agent_config(DefaultAgentConfig) of - ok -> - {ok, User}; + {ok, DefAgentConf} -> + ?vtrace("check_user_config -> " + "user agent (default) config verified", []), + User2 = {Id, Mod, Data, DefAgentConf}, + {ok, User2}; {error, Reason} -> error({bad_default_agent_config, Reason}) end; @@ -1756,16 +1889,16 @@ verify_user({Id, UserMod, UserData}) -> verify_user({Id, UserMod, UserData, DefaultAgentConfig}) when (Id =/= ?DEFAULT_USER) andalso is_list(DefaultAgentConfig) -> ?d("verify_user -> entry with" - "~n Id: ~p" - "~n UserMod: ~p" - "~n UserData: ~p" + "~n Id: ~p" + "~n UserMod: ~p" + "~n UserData: ~p" "~n DefaultAgentConfig: ~p", [Id, UserMod, UserData, DefaultAgentConfig]), case (catch verify_user_behaviour(UserMod)) of ok -> case verify_user_agent_config(DefaultAgentConfig) of - ok -> - Config = default_agent_config(DefaultAgentConfig), + {ok, DefAgentConf} -> + Config = default_agent_config(DefAgentConf), {ok, #user{id = Id, mod = UserMod, data = UserData, @@ -1783,10 +1916,15 @@ verify_user({Id, _, _, _}) -> {error, {bad_user_id, Id}}. verify_user_agent_config(Conf) -> - case verify_invalid(Conf, [user_id, engine_id, address]) of - ok -> - verify_agent_config2(Conf); - Error -> + try + begin + verify_invalid(Conf, [user_id, engine_id, address]), + verify_agent_config2(Conf) + end + catch + throw:Error -> + ?vdebug("verify_user_agent_config -> throw" + "~n Error: ~p", [Error]), Error end. @@ -2147,6 +2285,16 @@ handle_call({unregister_agent, UserId, TargetName}, _From, State) -> Reply = handle_unregister_agent(UserId, TargetName), {reply, Reply, State}; +handle_call({update_agent_info, UserId, TargetName, Info}, + _From, State) -> + ?vlog("received update_agent_info request: " + "~n UserId: ~p" + "~n TargetName: ~p" + "~n Info: ~p", [UserId, TargetName, Info]), + Reply = handle_update_agent_info(UserId, TargetName, Info), + {reply, Reply, State}; + +%% <BACKWARD-COMPAT> handle_call({update_agent_info, UserId, TargetName, Item, Val}, _From, State) -> ?vlog("received update_agent_info request: " @@ -2156,6 +2304,7 @@ handle_call({update_agent_info, UserId, TargetName, Item, Val}, "~n Val: ~p", [UserId, TargetName, Item, Val]), Reply = handle_update_agent_info(UserId, TargetName, Item, Val), {reply, Reply, State}; +%% </BACKWARD-COMPAT> handle_call({register_usm_user, User}, _From, State) -> ?vlog("received register_usm_user request: " @@ -2517,16 +2666,27 @@ handle_register_agent(UserId, TargetName, Config) -> "~n Config: ~p", [UserId, TargetName, Config]), case (catch agent_info(TargetName, user_id)) of {error, _} -> + ?vtrace("handle_register_agent -> user_id not found in config", []), case ets:lookup(snmpm_user_table, UserId) of [#user{default_agent_config = DefConfig}] -> + ?vtrace("handle_register_agent -> " + "~n DefConfig: ~p", [DefConfig]), + %% First, insert this users default config + ?vtrace("handle_register_agent -> store default config", []), do_handle_register_agent(TargetName, DefConfig), + %% Second, insert the config for this agent + ?vtrace("handle_register_agent -> store config", []), do_handle_register_agent(TargetName, [{user_id, UserId}|Config]), %% <DIRTY-BACKWARD-COMPATIBILLITY> %% And now for some (backward compatibillity) %% dirty crossref stuff + ?vtrace("handle_register_agent -> lookup address", []), {ok, Addr} = agent_info(TargetName, address), + ?vtrace("handle_register_agent -> Addr: ~p, lookup Port", + [Addr]), {ok, Port} = agent_info(TargetName, port), + ?vtrace("handle_register_agent -> register cross-ref fix", []), ets:insert(snmpm_agent_table, {{Addr, Port, target_name}, TargetName}), %% </DIRTY-BACKWARD-COMPATIBILLITY> @@ -2551,10 +2711,18 @@ handle_register_agent(UserId, TargetName, Config) -> do_handle_register_agent(_TargetName, []) -> ok; do_handle_register_agent(TargetName, [{Item, Val}|Rest]) -> + ?vtrace("handle_register_agent -> entry with" + "~n TargetName: ~p" + "~n Item: ~p" + "~n Val: ~p" + "~n Rest: ~p", [TargetName, Item, Val, Rest]), case (catch do_update_agent_info(TargetName, Item, Val)) of ok -> do_handle_register_agent(TargetName, Rest); {error, Reason} -> + ?vtrace("handle_register_agent -> failed updating ~p" + "~n Item: ~p" + "~n Reason: ~p", [Item, Reason]), ets:match_delete(snmpm_agent_table, {TargetName, '_'}), {error, Reason} end; @@ -2589,41 +2757,61 @@ handle_unregister_agent(UserId, TargetName) -> end. -handle_update_agent_info(UserId, TargetName, Item, Val) -> +handle_update_agent_info(UserId, TargetName, Info) -> ?vdebug("handle_update_agent_info -> entry with" "~n UserId: ~p" "~n TargetName: ~p" - "~n Item: ~p" - "~n Val: ~p", [UserId, TargetName, Item, Val]), + "~n Info: ~p", [UserId, TargetName, Info]), + %% Verify ownership case (catch agent_info(TargetName, user_id)) of - {ok, UserId} -> - do_update_agent_info(TargetName, Item, Val); + {ok, UserId} -> + handle_update_agent_info(TargetName, Info); {ok, OtherUserId} -> {error, {not_owner, OtherUserId}}; Error -> Error end. -do_update_agent_info(TargetName, Item, Val0) -> -%% p("do_update_agent_info -> entry with" -%% "~n TargetName: ~p" -%% "~n Item: ~p" -%% "~n Val0: ~p", [TargetName, Item, Val0]), - case verify_val(Item, Val0) of - {ok, Val} -> -%% p("do_update_agent_info -> verified value" -%% "~n Val: ~p", [Val]), - ets:insert(snmpm_agent_table, {{TargetName, Item}, Val}), - ok; +handle_update_agent_info(TargetName, Info0) -> + ?vtrace("handle_update_agent_info -> entry with" + "~n TargetName: ~p" + "~n Info0: ~p", [TargetName, Info0]), + %% Verify info + try verify_agent_info(TargetName, Info0) of + {ok, Info} -> + do_update_agent_info(TargetName, Info); Error -> - ?vlog("do_update_agent_info -> verify value failed: " - "~n TargetName: ~p" - "~n Item: ~p" - "~n Val0: ~p" - "~n Error: ~p", [TargetName, Item, Val0, Error]), - {error, {bad_agent_val, TargetName, Item, Val0}} + Error + catch + throw:Error -> + Error; + T:E -> + {error, {failed_info_verification, Info0, T, E}} end. +handle_update_agent_info(UserId, TargetName, Item, Val) -> + ?vdebug("handle_update_agent_info -> entry with" + "~n UserId: ~p" + "~n TargetName: ~p" + "~n Item: ~p" + "~n Val: ~p", [UserId, TargetName, Item, Val]), + handle_update_agent_info(TargetName, [{Item, Val}]). + +do_update_agent_info(TargetName, Info) -> + InsertItem = + fun({Item, Val}) -> + ets:insert(snmpm_agent_table, {{TargetName, Item}, Val}) + end, + lists:foreach(InsertItem, Info). + +do_update_agent_info(TargetName, Item, Val) -> + ?vtrace("do_update_agent_info -> entry with" + "~n TargetName: ~p" + "~n Item: ~p" + "~n Val: ~p", [TargetName, Item, Val]), + ets:insert(snmpm_agent_table, {{TargetName, Item}, Val}), + ok. + handle_register_usm_user(#usm_user{engine_id = EngineID, name = Name} = User) -> @@ -2791,7 +2979,7 @@ verify_mandatory(Conf, [Mand|Mands]) -> true -> verify_mandatory(Conf, Mands); false -> - {error, {missing_mandatory_config, Mand}} + throw({error, {missing_mandatory_config, Mand}}) end. verify_invalid(_, []) -> @@ -2801,7 +2989,7 @@ verify_invalid(Conf, [Inv|Invs]) -> false -> verify_invalid(Conf, Invs); true -> - {error, {illegal_config, Inv}} + throw({error, {illegal_config, Inv}}) end. @@ -2810,10 +2998,26 @@ verify_val(user_id, UserId) -> verify_val(reg_type, RegType) when (RegType =:= addr_port) orelse (RegType =:= target_name) -> {ok, RegType}; -verify_val(address, Addr0) -> - case normalize_address(Addr0) of +verify_val(tdomain = Item, snmpUDPDomain = _Domain) -> + verify_val(Item, transportDomainUdpIpv4); +verify_val(tdomain, Domain) -> + case lists:member(Domain, ?SUPPORTED_DOMAINS) of + true -> + {ok, Domain}; + false -> + case lists:member(Domain, snmp_conf:all_domains()) of + true -> + error({unsupported_domain, Domain}); + false -> + error({unknown_domain, Domain}) + end + end; +verify_val(address, {Domain, Addr0}) -> + case normalize_address(Domain, Addr0) of {_A1, _A2, _A3, _A4} = Addr -> {ok, Addr}; + {_A1, _A2, _A3, _A4, _A5, _A6, _A7, _A8} = Addr -> + {ok, Addr}; _ when is_list(Addr0) -> case (catch snmp_conf:check_ip(Addr0)) of ok -> @@ -2824,6 +3028,8 @@ verify_val(address, Addr0) -> _ -> error({bad_address, Addr0}) end; +verify_val(address, BadAddress) -> + error({bad_address, BadAddress}); verify_val(port, Port) -> case (catch snmp_conf:check_integer(Port, {gt, 0})) of ok -> @@ -2875,7 +3081,7 @@ verify_val(sec_name, BadName) -> verify_val(sec_level, Level) -> (catch snmp_conf:check_sec_level(Level)); verify_val(Item, _) -> - {error, {no_such_item, Item}}. + {error, {unknown_item, Item}}. %%%------------------------------------------------------------------- @@ -3034,11 +3240,17 @@ init_mini_mib_elems(MibName, [_|T], Res) -> %%---------------------------------------------------------------------- normalize_address(Addr) -> - case inet:getaddr(Addr, inet) of + normalize_address(snmpUDPDomain, Addr). + +normalize_address(snmpUDPDomain, Addr) -> + normalize_address(transportDomainUdpIpv4, Addr); + +normalize_address(Domain, Addr) -> + case inet:getaddr(Addr, td2fam(Domain)) of {ok, Addr2} -> Addr2; _ when is_list(Addr) -> - case (catch snmp_conf:check_ip(Addr)) of + case (catch snmp_conf:check_ip(Domain, Addr)) of ok -> list_to_tuple(Addr); _ -> @@ -3048,6 +3260,9 @@ normalize_address(Addr) -> Addr end. +td2fam(transportDomainUdpIpv4) -> inet; +td2fam(transportDomainUdpIpv6) -> inet6. + %%---------------------------------------------------------------------- diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl index 7712370d28..627838e3d4 100644 --- a/lib/snmp/src/manager/snmpm_mpd.erl +++ b/lib/snmp/src/manager/snmpm_mpd.erl @@ -92,7 +92,7 @@ reset(#state{v3 = V3}) -> %% Purpose: This is the main Message Dispatching function. (see %% section 4.2.1 in rfc2272) %%----------------------------------------------------------------- -process_msg(Msg, TDomain, Addr, Port, State, NoteStore, Logger) -> +process_msg(Msg, Domain, Addr, Port, State, NoteStore, Logger) -> inc(snmpInPkts), @@ -102,18 +102,18 @@ process_msg(Msg, TDomain, Addr, Port, State, NoteStore, Logger) -> #message{version = 'version-1', vsn_hdr = Community, data = Data} when State#state.v1 =:= true -> HS = ?empty_msg_size + length(Community), - process_v1_v2c_msg('version-1', NoteStore, Msg, TDomain, - Addr, Port, + process_v1_v2c_msg('version-1', NoteStore, Msg, + Domain, Addr, Port, Community, Data, HS, Logger); %% Version 2 #message{version = 'version-2', vsn_hdr = Community, data = Data} when State#state.v2c =:= true -> HS = ?empty_msg_size + length(Community), - process_v1_v2c_msg('version-2', NoteStore, Msg, TDomain, - Addr, Port, - Community, Data, HS, Logger); - + (catch process_v1_v2c_msg('version-2', NoteStore, Msg, + Domain, Addr, Port, + Community, Data, HS, Logger)); + %% Version 3 #message{version = 'version-3', vsn_hdr = H, data = Data} when State#state.v3 =:= true -> @@ -148,17 +148,30 @@ process_msg(Msg, TDomain, Addr, Port, State, NoteStore, Logger) -> %%----------------------------------------------------------------- %% Handles a Community based message (v1 or v2c). %%----------------------------------------------------------------- -process_v1_v2c_msg(Vsn, _NoteStore, Msg, snmpUDPDomain, +process_v1_v2c_msg(Vsn, _NoteStore, Msg, Domain, Addr, Port, Community, Data, HS, Log) -> ?vdebug("process_v1_v2c_msg -> entry with" "~n Vsn: ~p" + "~n Domain: ~p" "~n Addr: ~p" "~n Port: ~p" "~n Community: ~p" - "~n HS: ~p", [Vsn, Addr, Port, Community, HS]), + "~n HS: ~p", [Vsn, Domain, Addr, Port, Community, HS]), + {TDomain, TAddress} = + try + begin + TD = snmp_conf:mk_tdomain(Domain), + TA = snmp_conf:mk_taddress(Domain, Addr, Port), + {TD, TA} + end + catch + throw:{error, TReason} -> + throw({discarded, {badarg, Domain, TReason}}) + end, + Max = get_max_message_size(), AgentMax = get_agent_max_message_size(Addr, Port), PduMS = pdu_ms(Max, AgentMax, HS), @@ -170,14 +183,14 @@ process_v1_v2c_msg(Vsn, _NoteStore, Msg, snmpUDPDomain, ?vtrace("process_v1_v2c_msg -> was a pdu", []), Log(Msg), inc_snmp_in(Pdu), - MsgData = {Community, sec_model(Vsn)}, + MsgData = {Community, sec_model(Vsn), TDomain, TAddress}, {ok, Vsn, Pdu, PduMS, MsgData}; Trap when is_record(Trap, trappdu) -> ?vtrace("process_v1_v2c_msg -> was a trap", []), Log(Msg), inc_snmp_in(Trap), - MsgData = {Community, sec_model(Vsn)}, + MsgData = {Community, sec_model(Vsn), TDomain, TAddress}, {ok, Vsn, Trap, PduMS, MsgData}; {'EXIT', Reason} -> @@ -185,11 +198,7 @@ process_v1_v2c_msg(Vsn, _NoteStore, Msg, snmpUDPDomain, "~n Reason: ~p", [Reason]), inc(snmpInASNParseErrs), {discarded, Reason} - end; -process_v1_v2c_msg(_Vsn, _NoteStore, _Msg, TDomain, - _Addr, _Port, - _Comm, _HS, _Data, _Log) -> - {discarded, {badarg, TDomain}}. + end. pdu_ms(MgrMMS, AgentMMS, HS) when AgentMMS < MgrMMS -> AgentMMS - HS; @@ -482,8 +491,8 @@ generate_msg('version-3', NoteStore, Pdu, generate_v3_msg(NoteStore, Pdu, SecModel, SecName, SecLevel, CtxEngineID, CtxName, TargetName, Log); -generate_msg(Vsn, _NoteStore, Pdu, {Community, _SecModel}, Log) -> - generate_v1_v2c_msg(Vsn, Pdu, Community, Log). +generate_msg(Vsn, _NoteStore, Pdu, {Comm, _SecModel}, Log) -> + generate_v1_v2c_msg(Vsn, Pdu, Comm, Log). generate_v3_msg(NoteStore, Pdu, @@ -627,6 +636,8 @@ generate_response_msg('version-3', Pdu, generate_v3_response_msg(Pdu, MsgID, SecModel, SecName, SecLevel, CtxEngineID, CtxName, SecData, Log); generate_response_msg(Vsn, Pdu, {Comm, _SecModel}, Log) -> + generate_v1_v2c_response_msg(Vsn, Pdu, Comm, Log); +generate_response_msg(Vsn, Pdu, {Comm, _SecModel, _TDomain, _TAddress}, Log) -> generate_v1_v2c_response_msg(Vsn, Pdu, Comm, Log). diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl index a116c9f26b..4d6bd9aa33 100644 --- a/lib/snmp/src/manager/snmpm_net_if.erl +++ b/lib/snmp/src/manager/snmpm_net_if.erl @@ -28,7 +28,8 @@ start_link/2, stop/1, send_pdu/6, % Backward compatibillity - send_pdu/7, + send_pdu/7, % Backward compatibillity + send_pdu/8, inform_response/4, @@ -101,16 +102,21 @@ stop(Pid) -> send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port) -> send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ?DEFAULT_EXTRA_INFO). -send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo) +send_pdu(Pid, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo) -> + Domain = snmpm_config:default_transport_domain(), + send_pdu(Pid, Pdu, Vsn, MsgData, Domain, Addr, Port, ExtraInfo). + +send_pdu(Pid, Pdu, Vsn, MsgData, Domain, Addr, Port, ExtraInfo) when is_record(Pdu, pdu) -> ?d("send_pdu -> entry with" "~n Pid: ~p" "~n Pdu: ~p" "~n Vsn: ~p" "~n MsgData: ~p" + "~n Domain: ~p" "~n Addr: ~p" - "~n Port: ~p", [Pid, Pdu, Vsn, MsgData, Addr, Port]), - cast(Pid, {send_pdu, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo}). + "~n Port: ~p", [Pid, Pdu, Vsn, MsgData, Domain, Addr, Port]), + cast(Pid, {send_pdu, Pdu, Vsn, MsgData, Domain, Addr, Port, ExtraInfo}). note_store(Pid, NoteStore) -> call(Pid, {note_store, NoteStore}). @@ -380,15 +386,17 @@ 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, Domain, Addr, Port, ExtraInfo}, + State) -> ?vlog("received send_pdu message with" "~n Pdu: ~p" "~n Vsn: ~p" "~n MsgData: ~p" + "~n Domain: ~p" "~n Addr: ~p" - "~n Port: ~p", [Pdu, Vsn, MsgData, Addr, Port]), + "~n Port: ~p", [Pdu, Vsn, MsgData, Domain, Addr, Port]), maybe_process_extra_info(ExtraInfo), - maybe_handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, State), + maybe_handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, Port, State), {noreply, State}; handle_cast({inform_response, Ref, Addr, Port}, State) -> @@ -545,8 +553,9 @@ handle_recv_msg(Addr, Port, Bytes, mpd_state = MpdState, sock = Sock, log = Log} = State) -> + Domain = snmp_conf:which_domain(Addr), % What the ****... Logger = logger(Log, read, Addr, Port), - case (catch snmpm_mpd:process_msg(Bytes, snmpUDPDomain, Addr, Port, + case (catch snmpm_mpd:process_msg(Bytes, Domain, Addr, Port, MpdState, NoteStore, Logger)) of {ok, Vsn, Pdu, MS, ACM} -> @@ -734,17 +743,17 @@ irgc_stop(Ref) -> (catch erlang:cancel_timer(Ref)). -maybe_handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, +maybe_handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, Port, #state{filter = FilterMod} = State) -> case (catch FilterMod:accept_send_pdu(Addr, Port, pdu_type_of(Pdu))) of false -> inc(netIfPduOutDrops), ok; _ -> - handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, State) + handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, Port, State) end. -handle_send_pdu(Pdu, Vsn, MsgData, Addr, Port, +handle_send_pdu(Pdu, Vsn, MsgData, _Domain, Addr, Port, #state{server = Pid, note_store = NoteStore, sock = Sock, diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index 58a58507d6..484954addb 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -161,7 +161,8 @@ {id, user_id, reg_type, - target, + target, + domain, addr, port, type, @@ -1175,11 +1176,12 @@ handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State) -> "~n From: ~p", [Pid, UserId, TargetName, Oids, SendOpts, From]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get -> send a ~p message", [Vsn]), Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_request(Oids, Vsn, MsgData, - Addr, Port, Extra, State), + Domain, Addr, Port, + Extra, State), ?vdebug("handle_sync_get -> ReqId: ~p", [ReqId]), Msg = {sync_timeout, ReqId, From}, Timeout = ?SYNC_GET_TIMEOUT(SendOpts), @@ -1190,6 +1192,7 @@ handle_sync_get(Pid, UserId, TargetName, Oids, SendOpts, From, State) -> user_id = UserId, reg_type = RegType, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = get, @@ -1227,11 +1230,12 @@ handle_sync_get_next(Pid, UserId, TargetName, Oids, SendOpts, "~n From: ~p", [Pid, UserId, TargetName, Oids, SendOpts, From]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_next -> send a ~p message", [Vsn]), Extra = ?GET_EXTRA(SendOpts), ReqId = send_get_next_request(Oids, Vsn, MsgData, - Addr, Port, Extra, State), + Domain, Addr, Port, + Extra, State), ?vdebug("handle_sync_get_next -> ReqId: ~p", [ReqId]), Msg = {sync_timeout, ReqId, From}, Timeout = ?SYNC_GET_NEXT_TIMEOUT(SendOpts), @@ -1242,6 +1246,7 @@ handle_sync_get_next(Pid, UserId, TargetName, Oids, SendOpts, user_id = UserId, reg_type = RegType, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = get_next, @@ -1285,10 +1290,11 @@ handle_sync_get_bulk(Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, "~n From: ~p", [Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, From]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_get_bulk -> send a ~p message", [Vsn]), Extra = ?GET_EXTRA(SendOpts), - ReqId = send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, + ReqId = send_get_bulk_request(Oids, Vsn, MsgData, + Domain, Addr, Port, NonRep, MaxRep, Extra, State), ?vdebug("handle_sync_get_bulk -> ReqId: ~p", [ReqId]), Msg = {sync_timeout, ReqId, From}, @@ -1300,6 +1306,7 @@ handle_sync_get_bulk(Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts, user_id = UserId, reg_type = RegType, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = get_bulk, @@ -1339,11 +1346,12 @@ handle_sync_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, From, State) -> "~n From: ~p", [Pid, UserId, TargetName, VarsAndVals, From]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, Addr, Port, Vsn, MsgData} -> ?vtrace("handle_sync_set -> send a ~p message", [Vsn]), Extra = ?GET_EXTRA(SendOpts), ReqId = send_set_request(VarsAndVals, Vsn, MsgData, - Addr, Port, Extra, State), + Domain, Addr, Port, + Extra, State), ?vdebug("handle_sync_set -> ReqId: ~p", [ReqId]), Msg = {sync_timeout, ReqId, From}, Timeout = ?SYNC_SET_TIMEOUT(SendOpts), @@ -1354,6 +1362,7 @@ handle_sync_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, From, State) -> user_id = UserId, reg_type = RegType, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = set, @@ -1391,10 +1400,11 @@ handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State) -> "~n SendOpts: ~p", [Pid, UserId, TargetName, Oids, SendOpts]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, 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, + ReqId = send_get_request(Oids, Vsn, MsgData, + Domain, Addr, Port, Extra, State), ?vdebug("handle_async_get -> ReqId: ~p", [ReqId]), Expire = ?ASYNC_GET_TIMEOUT(SendOpts), @@ -1402,6 +1412,7 @@ handle_async_get(Pid, UserId, TargetName, Oids, SendOpts, State) -> user_id = UserId, reg_type = RegType, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = get, @@ -1439,17 +1450,19 @@ handle_async_get_next(Pid, UserId, TargetName, Oids, SendOpts, State) -> "~n SendOpts: ~p", [Pid, UserId, TargetName, Oids, SendOpts]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, 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, Extra, State), + Domain, 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, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = get_next, @@ -1494,10 +1507,11 @@ handle_async_get_bulk(Pid, "~n SendOpts: ~p", [Pid, UserId, TargetName, NonRep, MaxRep, Oids, SendOpts]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, 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, + ReqId = send_get_bulk_request(Oids, Vsn, MsgData, + Domain, Addr, Port, NonRep, MaxRep, Extra, State), ?vdebug("handle_async_get_bulk -> ReqId: ~p", [ReqId]), Expire = ?ASYNC_GET_BULK_TIMEOUT(SendOpts), @@ -1505,6 +1519,7 @@ handle_async_get_bulk(Pid, user_id = UserId, reg_type = RegType, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = get_bulk, @@ -1541,17 +1556,19 @@ handle_async_set(Pid, UserId, TargetName, VarsAndVals, SendOpts, State) -> "~n SendOpts: ~p", [Pid, UserId, TargetName, VarsAndVals, SendOpts]), case agent_data(TargetName, SendOpts) of - {ok, RegType, Addr, Port, Vsn, MsgData} -> + {ok, RegType, Domain, 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, Extra, State), + Domain, 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, target = TargetName, + domain = Domain, addr = Addr, port = Port, type = set, @@ -2907,7 +2924,7 @@ do_gc(Key, Now) -> %% %%---------------------------------------------------------------------- -send_get_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo, +send_get_request(Oids, Vsn, MsgData, Domain, Addr, Port, ExtraInfo, #state{net_if = NetIf, net_if_mod = Mod, mini_mib = MiniMIB}) -> @@ -2918,34 +2935,39 @@ send_get_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo, "~n Pdu: ~p" "~n Vsn: ~p" "~n MsgData: ~p" + "~n Domain: ~p" "~n Addr: ~p" - "~n Port: ~p", [Mod, NetIf, Pdu, Vsn, MsgData, Addr, Port]), - (catch Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo)), + "~n Port: ~p", + [Mod, NetIf, Pdu, Vsn, MsgData, Domain, Addr, Port]), + Res = (catch Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, + Domain, Addr, Port, ExtraInfo)), + ?vtrace("send_get_request -> send result:" + "~n ~p", [Res]), Pdu#pdu.request_id. -send_get_next_request(Oids, Vsn, MsgData, Addr, Port, ExtraInfo, +send_get_next_request(Oids, Vsn, MsgData, Domain, Addr, Port, ExtraInfo, #state{mini_mib = MiniMIB, net_if = NetIf, net_if_mod = Mod}) -> Pdu = make_pdu(get_next, Oids, MiniMIB), - Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo), + Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Domain, Addr, Port, ExtraInfo), Pdu#pdu.request_id. -send_get_bulk_request(Oids, Vsn, MsgData, Addr, Port, +send_get_bulk_request(Oids, Vsn, MsgData, Domain, Addr, Port, NonRep, MaxRep, ExtraInfo, #state{mini_mib = MiniMIB, net_if = NetIf, net_if_mod = Mod}) -> Pdu = make_pdu(bulk, {NonRep, MaxRep, Oids}, MiniMIB), - Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo), + Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Domain, Addr, Port, ExtraInfo), Pdu#pdu.request_id. -send_set_request(VarsAndVals, Vsn, MsgData, Addr, Port, ExtraInfo, +send_set_request(VarsAndVals, Vsn, MsgData, Domain, Addr, Port, ExtraInfo, #state{mini_mib = MiniMIB, net_if = NetIf, net_if_mod = Mod}) -> Pdu = make_pdu(set, VarsAndVals, MiniMIB), - Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Addr, Port, ExtraInfo), + Mod:send_pdu(NetIf, Pdu, Vsn, MsgData, Domain, Addr, Port, ExtraInfo), Pdu#pdu.request_id. %% send_discovery(Vsn, MsgData, Addr, Port, ExtraInfo, @@ -3181,10 +3203,11 @@ agent_data(TargetName, SendOpts) -> {Comm, SecModel} end, + Domain = agent_data_item(tdomain, Info), Addr = agent_data_item(address, Info), Port = agent_data_item(port, Info), RegType = agent_data_item(reg_type, Info), - {ok, RegType, Addr, Port, version(Version), MsgData}; + {ok, RegType, Domain, Addr, Port, version(Version), MsgData}; Error -> Error end. diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 20f4455d10..7249def24e 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -37,7 +37,9 @@ check_timer/1, + all_domains/0, check_domain/1, + all_tdomains/0, check_tdomain/1, mk_tdomain/1, which_domain/1, @@ -345,6 +347,25 @@ check_sec_level(BadSecLevel) -> %% --------- +all_tdomains() -> + [ + ?transportDomainUdpIpv4, + ?transportDomainUdpIpv6, + ?transportDomainUdpIpv4z, + ?transportDomainUdpIpv6z, + ?transportDomainTcpIpv4, + ?transportDomainTcpIpv6, + ?transportDomainTcpIpv4z, + ?transportDomainTcpIpv6z, + ?transportDomainSctpIpv4, + ?transportDomainSctpIpv6, + ?transportDomainSctpIpv4z, + ?transportDomainSctpIpv6z, + ?transportDomainLocal, + ?transportDomainUdpDns, + ?transportDomainTcpDns, + ?transportDomainSctpDns + ]. check_tdomain(TDomain) -> SupportedTDomains = @@ -353,25 +374,7 @@ check_tdomain(TDomain) -> ?transportDomainUdpIpv4, ?transportDomainUdpIpv6 ], - AllTDomains = - [ - ?transportDomainUdpIpv4, - ?transportDomainUdpIpv6, - ?transportDomainUdpIpv4z, - ?transportDomainUdpIpv6z, - ?transportDomainTcpIpv4, - ?transportDomainTcpIpv6, - ?transportDomainTcpIpv4z, - ?transportDomainTcpIpv6z, - ?transportDomainSctpIpv4, - ?transportDomainSctpIpv6, - ?transportDomainSctpIpv4z, - ?transportDomainSctpIpv6z, - ?transportDomainLocal, - ?transportDomainUdpDns, - ?transportDomainTcpDns, - ?transportDomainSctpDns - ], + AllTDomains = all_tdomains(), case lists:member(TDomain, SupportedTDomains) of true -> ok; @@ -388,7 +391,7 @@ check_tdomain(TDomain) -> %% --------- mk_tdomain(snmpUDPDomain) -> - ?snmpUDPDomain; + mk_tdomain(transportDomainUdpIpv4); mk_tdomain(transportDomainUdpIpv4) -> ?transportDomainUdpIpv4; mk_tdomain(transportDomainUdpIpv6) -> @@ -474,6 +477,26 @@ do_check_timer(WaitFor, Factor, Incr, Retry) -> %% --------- +all_domains() -> + [ + transportDomainUdpIpv4, + transportDomainUdpIpv6, + transportDomainUdpIpv4z, + transportDomainUdpIpv6z, + transportDomainTcpIpv4, + transportDomainTcpIpv6, + transportDomainTcpIpv4z, + transportDomainTcpIpv6z, + transportDomainSctpIpv4, + transportDomainSctpIpv6, + transportDomainSctpIpv4z, + transportDomainSctpIpv6z, + transportDomainLocal, + transportDomainUdpDns, + transportDomainTcpDns, + transportDomainSctpDns + ]. + check_domain(Domain) -> SupportedDomains = [ @@ -481,25 +504,7 @@ check_domain(Domain) -> transportDomainUdpIpv4, transportDomainUdpIpv6 ], - AllDomains = - [ - transportDomainUdpIpv4, - transportDomainUdpIpv6, - transportDomainUdpIpv4z, - transportDomainUdpIpv6z, - transportDomainTcpIpv4, - transportDomainTcpIpv6, - transportDomainTcpIpv4z, - transportDomainTcpIpv6z, - transportDomainSctpIpv4, - transportDomainSctpIpv6, - transportDomainSctpIpv4z, - transportDomainSctpIpv6z, - transportDomainLocal, - transportDomainUdpDns, - transportDomainTcpDns, - transportDomainSctpDns - ], + AllDomains = all_domains(), case lists:member(Domain, SupportedDomains) of true -> ok; diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index fcbc6a88c9..6ab20e3e48 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -337,7 +337,7 @@ config_agent_sys() -> {dir, ATLDir}, {size, ATLSize}, {repair, ATLRepair}, - {seqno, ATLSeqNo}]}]; + {seqno, ATLSeqNo}]}]; no -> [] end, @@ -568,7 +568,7 @@ config_agent_snmp(Dir, Vsns) -> false -> ok end, - i("The following agent files were written: agent.conf, " + i("The following agent files where written: agent.conf, " "community.conf,~n" "standard.conf, target_addr.conf, " "target_params.conf, ~n" @@ -776,7 +776,7 @@ config_manager_snmp(Dir, Vsns) -> Users, Agents, Usms)) of ok -> i("~n- - - - - - - - - - - - -"), - i("The following manager files were written: " + i("The following manager files where written: " "manager.conf, agents.conf " ++ case lists:member(v3, Vsns) of true -> @@ -2350,7 +2350,9 @@ write_sys_config_file_manager_atl_opt(Fid, {type, Type}) -> write_sys_config_file_manager_atl_opt(Fid, {size, Size}) -> ok = io:format(Fid, "{size, ~w}", [Size]); write_sys_config_file_manager_atl_opt(Fid, {repair, Rep}) -> - ok = io:format(Fid, "{repair, ~w}", [Rep]). + ok = io:format(Fid, "{repair, ~w}", [Rep]); +write_sys_config_file_manager_atl_opt(Fid, {seqno, SeqNo}) -> + ok = io:format(Fid, "{seqno, ~w}", [SeqNo]). header() -> diff --git a/lib/snmp/test/snmp_compiler_test.erl b/lib/snmp/test/snmp_compiler_test.erl index 2e6020ae7a..cee11ba97a 100644 --- a/lib/snmp/test/snmp_compiler_test.erl +++ b/lib/snmp/test/snmp_compiler_test.erl @@ -47,6 +47,7 @@ module_identity/1, agent_capabilities/1, module_compliance/1, + warnings_as_errors/1, otp_6150/1, otp_8574/1, @@ -97,9 +98,10 @@ all() -> description, oid_conflicts, imports, - module_identity, - agent_capabilities, - module_compliance, + module_identity, + agent_capabilities, + module_compliance, + warnings_as_errors, {group, tickets} ]. @@ -152,6 +154,8 @@ description(Config) when is_list(Config) -> ok. +%%====================================================================== + oid_conflicts(suite) -> []; oid_conflicts(Config) when is_list(Config) -> put(tname,oid_conflicts), @@ -165,18 +169,24 @@ oid_conflicts(Config) when is_list(Config) -> ok. +%%====================================================================== + imports(suite) -> []; imports(Config) when is_list(Config) -> ?SKIP(not_yet_implemented). +%%====================================================================== + module_identity(suite) -> []; module_identity(Config) when is_list(Config) -> ?SKIP(not_yet_implemented). +%%====================================================================== + agent_capabilities(suite) -> []; agent_capabilities(Config) when is_list(Config) -> @@ -218,6 +228,8 @@ agent_capabilities(Config) when is_list(Config) -> ok. +%%====================================================================== + module_compliance(suite) -> []; module_compliance(Config) when is_list(Config) -> @@ -259,6 +271,31 @@ module_compliance(Config) when is_list(Config) -> ok. +%%====================================================================== + +warnings_as_errors(suite) -> + ["OTP-9437"]; +warnings_as_errors(Config) when is_list(Config) -> + put(tname,warnings_as_errors), + p("starting with Config: ~p~n", [Config]), + Dir = ?config(comp_dir, Config), + MibDir = ?config(mib_dir, Config), + MibFile = join(MibDir, "OTP8574-MIB.mib"), + OutFile = join(Dir, "OTP8574-MIB.bin"), + Opts = [{group_check, false}, + {outdir, Dir}, + {verbosity, trace}, + relaxed_row_name_assign_check], + {error, compilation_failed} = + snmpc:compile(MibFile, [warnings_as_errors|Opts]), + false = filelib:is_regular(OutFile), + {ok, _} = snmpc:compile(MibFile, Opts), + true = filelib:is_regular(OutFile), + ok. + + +%%====================================================================== + otp_6150(suite) -> []; otp_6150(Config) when is_list(Config) -> @@ -273,6 +310,8 @@ otp_6150(Config) when is_list(Config) -> ok. +%%====================================================================== + otp_8574(suite) -> []; otp_8574(Config) when is_list(Config) -> @@ -304,6 +343,8 @@ otp_8574(Config) when is_list(Config) -> end. +%%====================================================================== + otp_8595(suite) -> []; otp_8595(Config) when is_list(Config) -> diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 0b536748fb..d18f20d359 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -61,6 +61,7 @@ register_agent1/1, register_agent2/1, + register_agent3/1, info/1, @@ -383,12 +384,12 @@ end_per_testcase2(Case, Config) -> all() -> [ {group, start_and_stop_tests}, - {group, misc_tests}, + {group, misc_tests}, {group, user_tests}, - {group, agent_tests}, + {group, agent_tests}, {group, request_tests}, {group, event_tests}, - discovery, + discovery, {group, tickets} ]. @@ -417,7 +418,8 @@ groups() -> {agent_tests, [], [ register_agent1, - register_agent2 + register_agent2, + register_agent3 ] }, {request_tests, [], @@ -477,14 +479,14 @@ groups() -> }, {event_tests, [], [ - trap1, - trap2, - inform1, - inform2, - inform3, - inform4, - inform_swarm, - report + trap1%% , + %% trap2, + %% inform1, + %% inform2, + %% inform3, + %% inform4, + %% inform_swarm, + %% report ] }, {tickets, [], @@ -1134,6 +1136,7 @@ register_agent1(suite) -> register_agent1(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname,ra1), + p("starting with Config: ~p~n", [Config]), ManagerNode = start_manager_node(), @@ -1164,7 +1167,7 @@ register_agent1(Config) when is_list(Config) -> p("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register user(s) calvin & hobbe"), + p("register user(s) user_alfa & user_beta"), ?line ok = mgr_register_user(ManagerNode, user_alfa, snmpm_user_default, []), ?line ok = mgr_register_user(ManagerNode, user_beta, snmpm_user_default, []), p("manager info: ~p~n", [mgr_info(ManagerNode)]), @@ -1293,7 +1296,7 @@ register_agent2(Config) when is_list(Config) -> p("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("register user(s) calvin & hobbe"), + p("register user(s) user_alfa & user_beta"), ?line ok = mgr_register_user(ManagerNode, user_alfa, snmpm_user_default, []), ?line ok = mgr_register_user(ManagerNode, user_beta, snmpm_user_default, []), p("manager info: ~p~n", [mgr_info(ManagerNode)]), @@ -1348,7 +1351,7 @@ register_agent2(Config) when is_list(Config) -> end, p("manager info: ~p~n", [mgr_info(ManagerNode)]), - + p("unregister user user_alfa"), ?line ok = mgr_unregister_user(ManagerNode, user_alfa), @@ -1377,7 +1380,157 @@ register_agent2(Config) when is_list(Config) -> p("manager info: ~p~n", [mgr_info(ManagerNode)]), - p("unregister user hobbe"), + p("unregister user user_beta"), + ?line ok = mgr_unregister_user(ManagerNode, user_beta), + + p("manager info: ~p~n", [mgr_info(ManagerNode)]), + + ?SLEEP(1000), + + p("stop snmp application (with only manager)"), + ?line ok = stop_snmp(ManagerNode), + + ?SLEEP(1000), + + stop_node(ManagerNode), + + ?SLEEP(1000), + + p("end"), + ok. + + +%%====================================================================== + +register_agent3(doc) -> + ["Test registration of agents with the NEW interface functions " + "and specifying transport domain"]; +register_agent3(suite) -> + []; +register_agent3(Config) when is_list(Config) -> + process_flag(trap_exit, true), + put(tname, ra3), + p("starting with Config: ~p~n", [Config]), + + ManagerNode = start_manager_node(), + + ConfDir = ?config(manager_conf_dir, Config), + DbDir = ?config(manager_db_dir, Config), + LocalHost = snmp_test_lib:localhost(), + + + write_manager_conf(ConfDir), + + Opts = [{server, [{verbosity, trace}]}, + {net_if, [{verbosity, trace}]}, + {note_store, [{verbosity, trace}]}, + {config, [{verbosity, trace}, {dir, ConfDir}, {db_dir, DbDir}]}], + + + p("load snmp application"), + ?line ok = load_snmp(ManagerNode), + + p("set manager env for the snmp application"), + ?line ok = set_mgr_env(ManagerNode, Opts), + + p("starting snmp application (with only manager)"), + ?line ok = start_snmp(ManagerNode), + + p("started"), + + ?SLEEP(1000), + + p("manager info: ~p~n", [mgr_info(ManagerNode)]), + + p("register user(s) user_alfa & user_beta"), + ?line ok = mgr_register_user(ManagerNode, user_alfa, snmpm_user_default, []), + ?line ok = mgr_register_user(ManagerNode, user_beta, snmpm_user_default, []), + p("manager info: ~p~n", [mgr_info(ManagerNode)]), + + p("register agent(s)"), + TargetName1 = "agent2", + ?line ok = mgr_register_agent(ManagerNode, user_alfa, TargetName1, + [{tdomain, transportDomainUdpIpv4}, + {address, LocalHost}, + {port, 5001}, + {engine_id, "agentEngineId-1"}]), + TargetName2 = "agent3", + ?line ok = mgr_register_agent(ManagerNode, user_alfa, TargetName2, + [{tdomain, transportDomainUdpIpv6}, + {address, LocalHost}, + {port, 5002}, + {engine_id, "agentEngineId-2"}]), + TargetName3 = "agent4", + ?line {error, {unsupported_domain, _} = Reason4} = + mgr_register_agent(ManagerNode, user_beta, TargetName3, + [{tdomain, transportDomainTcpIpv4}, + {address, LocalHost}, + {port, 5003}, + {engine_id, "agentEngineId-3"}]), + p("Expected registration failure: ~p", [Reason4]), + TargetName4 = "agent5", + ?line {error, {unknown_domain, _} = Reason5} = + mgr_register_agent(ManagerNode, user_beta, TargetName4, + [{tdomain, transportDomainUdpIpv4_bad}, + {address, LocalHost}, + {port, 5004}, + {engine_id, "agentEngineId-4"}]), + p("Expected registration failure: ~p", [Reason5]), + + p("verify all agent(s): expect 2"), + case mgr_which_agents(ManagerNode) of + Agents1 when length(Agents1) =:= 2 -> + p("all agents: ~p~n", [Agents1]), + ok; + Agents1 -> + ?FAIL({agent_registration_failure, Agents1}) + end, + + p("verify user_alfa agent(s)"), + case mgr_which_agents(ManagerNode, user_alfa) of + Agents2 when length(Agents2) =:= 2 -> + p("calvin agents: ~p~n", [Agents2]), + ok; + Agents2 -> + ?FAIL({agent_registration_failure, Agents2}) + end, + + p("verify user_beta agent(s)"), + case mgr_which_agents(ManagerNode, user_beta) of + Agents3 when length(Agents3) =:= 0 -> + p("hobbe agents: ~p~n", [Agents3]), + ok; + Agents3 -> + ?FAIL({agent_registration_failure, Agents3}) + end, + + p("manager info: ~p~n", [mgr_info(ManagerNode)]), + + p("unregister user user_alfa"), + ?line ok = mgr_unregister_user(ManagerNode, user_alfa), + + p("verify all agent(s): expect 0"), + case mgr_which_agents(ManagerNode) of + Agents4 when length(Agents4) =:= 0 -> + p("all agents: ~p~n", [Agents4]), + ok; + Agents4 -> + ?FAIL({agent_unregistration_failure, Agents4}) + end, + p("manager info: ~p~n", [mgr_info(ManagerNode)]), + + p("verify all agent(s): expect 0"), + case mgr_which_agents(ManagerNode) of + [] -> + ok; + Agents5 -> + p("all agents: ~p~n", [Agents5]), + ?FAIL({agent_unregistration_failure, Agents5}) + end, + + p("manager info: ~p~n", [mgr_info(ManagerNode)]), + + p("unregister user user_beta"), ?line ok = mgr_unregister_user(ManagerNode, user_beta), p("manager info: ~p~n", [mgr_info(ManagerNode)]), diff --git a/lib/snmp/test/test_config/Makefile b/lib/snmp/test/test_config/Makefile index d7bebbc431..d65bb8abe2 100644 --- a/lib/snmp/test/test_config/Makefile +++ b/lib/snmp/test/test_config/Makefile @@ -155,23 +155,23 @@ release_spec: release_tests_spec: clean opt $(INSTALL_DIR) $(RELSYSDIR) - chmod -f -R u+w $(RELSYSDIR) + chmod -R u+w $(RELSYSDIR) $(INSTALL_DIR) $(RELSYSDIR)/agent - chmod -f -R u+w $(RELSYSDIR)/agent + chmod -R u+w $(RELSYSDIR)/agent $(INSTALL_DIR) $(RELSYSDIR)/agent/conf - chmod -f -R u+w $(RELSYSDIR)/agent/conf + chmod -R u+w $(RELSYSDIR)/agent/conf $(INSTALL_DIR) $(RELSYSDIR)/agent/db - chmod -f -R u+w $(RELSYSDIR)/agent/db + chmod -R u+w $(RELSYSDIR)/agent/db $(INSTALL_DIR) $(RELSYSDIR)/agent/log - chmod -f -R u+w $(RELSYSDIR)/agent/log + chmod -R u+w $(RELSYSDIR)/agent/log $(INSTALL_DIR) $(RELSYSDIR)/manager - chmod -f -R u+w $(RELSYSDIR)/manager + chmod -R u+w $(RELSYSDIR)/manager $(INSTALL_DIR) $(RELSYSDIR)/manager/conf - chmod -f -R u+w $(RELSYSDIR)/manager/conf + chmod -R u+w $(RELSYSDIR)/manager/conf $(INSTALL_DIR) $(RELSYSDIR)/manager/db - chmod -f -R u+w $(RELSYSDIR)/manager/db + chmod -R u+w $(RELSYSDIR)/manager/db $(INSTALL_DIR) $(RELSYSDIR)/manager/log - chmod -f -R u+w $(RELSYSDIR)/manager/log + chmod -R u+w $(RELSYSDIR)/manager/log $(INSTALL_DATA) $(SYS_CONFIG_FILES) $(RELSYSDIR) $(INSTALL_DATA) $(AGENT_CONFIG_FILES) $(RELSYSDIR)/agent/conf $(INSTALL_DATA) $(MANAGER_CONFIG_FILES) $(RELSYSDIR)/manager/conf diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index 29228fc59b..08251ab9ea 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -17,6 +17,6 @@ # # %CopyrightEnd% -SNMP_VSN = 4.20 +SNMP_VSN = 4.21 PRE_VSN = APP_VSN = "snmp-$(SNMP_VSN)$(PRE_VSN)" diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 71f3941577..6fc4fdc43d 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -29,6 +29,20 @@ <file>notes.xml</file> </header> +<section><title>Ssh 2.0.8</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Calling ssh_sftp:stop_channel/1 resulted in that the trap_exit flag was + set to true for the invoking process.</p> + <p> + Own Id: OTP-9386 Aux Id: seq11865</p> + </item> + </list> + </section> +</section> + <section><title>Ssh 2.0.7</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 974145836c..150b7d86dd 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -19,13 +19,19 @@ {"%VSN%", [ - {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}]}, + {"2.0.7", [{load_module, ssh_sftp, soft_purge, soft_purge, []}]}, + {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, + {load_module, ssh_sftp, soft_purge, soft_purge, []}]}, {"2.0.5", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, + {load_module, ssh_sftp, soft_purge, soft_purge, []}, {load_module, ssh_connection_handler, soft_purge, soft_purge, [ssh_userreg]}]} ], [ - {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}]}, + {"2.0.7", [{load_module, ssh_sftp, soft_purge, soft_purge, []}]}, + {"2.0.6", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, + {load_module, ssh_sftp, soft_purge, soft_purge, []}]}, {"2.0.5", [{load_module, ssh_userreg, soft_purge, soft_purge, []}, + {load_module, ssh_sftp, soft_purge, soft_purge, []}, {load_module, ssh_connection_handler, soft_purge, soft_purge, [ssh_userreg]}]} ] }. diff --git a/lib/ssh/src/ssh_sftp.erl b/lib/ssh/src/ssh_sftp.erl index 59e09fdd0f..f000558100 100755 --- a/lib/ssh/src/ssh_sftp.erl +++ b/lib/ssh/src/ssh_sftp.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 @@ -130,9 +130,9 @@ start_channel(Host, Port, Opts) -> end. stop_channel(Pid) -> - case process_info(Pid, [trap_exit]) of - [{trap_exit, Bool}] -> - process_flag(trap_exit, true), + case is_process_alive(Pid) of + true -> + OldValue = process_flag(trap_exit, true), link(Pid), exit(Pid, ssh_sftp_stop_channel), receive @@ -145,9 +145,9 @@ stop_channel(Pid) -> ok end end, - process_flag(trap_exit, Bool), + process_flag(trap_exit, OldValue), ok; - undefined -> + false -> ok end. diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile index 5a2a6de24a..1820924ed6 100644 --- a/lib/ssh/test/Makefile +++ b/lib/ssh/test/Makefile @@ -115,7 +115,7 @@ release_tests_spec: opt $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) $(INSTALL_DATA) ssh.spec ssh.cover $(RELSYSDIR) $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) $(RELSYSDIR) - chmod -f -R u+w $(RELSYSDIR) + chmod -R u+w $(RELSYSDIR) @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index d79038df29..fe2b915d17 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,5 +1,5 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 2.0.7 +SSH_VSN = 2.0.8 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/c_src/esock_openssl.c b/lib/ssl/c_src/esock_openssl.c index 2621c9934e..0bc42958f0 100644 --- a/lib/ssl/c_src/esock_openssl.c +++ b/lib/ssl/c_src/esock_openssl.c @@ -1024,7 +1024,7 @@ static void info_callback(const SSL *ssl, int where, int ret) } } -/* This function is called whenever a SSL_CTX *ctx structure is +/* This function is called whenever an SSL_CTX *ctx structure is * freed. */ static void callback_data_free(void *parent, void *ptr, CRYPTO_EX_DATA *ad, diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index b2d17925fd..e090b4e1ef 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -554,7 +554,7 @@ Own Id: OTP-8224</p> </item> <item> - <p>A ssl:ssl_accept/3 could crash a connection if the + <p>An ssl:ssl_accept/3 could crash a connection if the timing was wrong.</p> <p>Removed info message if the socket closed without a proper disconnect from the ssl layer. </p> <p>ssl:send/2 is now blocking until the @@ -770,7 +770,7 @@ <item> <p> The new ssl implementation released as a alfa in this - version supports upgrading of a tcp connection to a ssl + version supports upgrading of a tcp connection to an ssl connection so that http client and servers may implement RFC 2817.</p> <p> @@ -789,7 +789,7 @@ very crippled as the control of the ssl-socket was deep down in openssl making it hard if not impossible to support all inet options, ipv6 and upgrade of a tcp - connection to a ssl connection. The alfa version has a + connection to an ssl connection. The alfa version has a few limitations that will be removed before the ssl-4.0 release. Main differences and limitations in the alfa are listed below.</p> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 0da6bbee5b..0c4c8796be 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -35,7 +35,7 @@ <title>SSL</title> <list type="bulleted"> - <item>ssl requires the crypto an public_key applications.</item> + <item>ssl requires the crypto and public_key applications.</item> <item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item> <item>For security reasons sslv2 is not supported.</item> <item>Ephemeral Diffie-Hellman cipher suites are supported @@ -216,7 +216,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | application is encountered. Additionally it will be called when a certificate is considered valid by the path validation to allow access to each certificate in the path to the user - application. Note that the it will differentiate between the + application. Note that it will differentiate between the peer certificate and CA certificates by using valid_peer or valid as the second argument to the verify fun. See <seealso marker="public_key:cert_records">the public_key User's @@ -326,10 +326,10 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | </item> <tag>{fail_if_no_peer_cert, boolean()}</tag> - <item>Used together with {verify, verify_peer} by a ssl server. + <item>Used together with {verify, verify_peer} by an ssl server. If set to true, the server will fail if the client does not have a certificate to send, i.e. sends a empty certificate, if set to - false it will only fail if the client sends a invalid + false it will only fail if the client sends an invalid certificate (an empty certificate is considered valid). </item> @@ -343,10 +343,10 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | PeerCert, Compression, CipherSuite) -> boolean()}</tag> <item>Enables the ssl server to have a local policy for deciding if a session should be reused or not, - only meaning full if <c>reuse_sessions</c> is set to true. + only meaningful if <c>reuse_sessions</c> is set to true. SuggestedSessionId is a binary(), PeerCert is a DER encoded certificate, Compression is an enumeration integer - and CipherSuite of type ciphersuite(). + and CipherSuite is of type ciphersuite(). </item> </taglist> @@ -355,7 +355,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <section> <title>General</title> - <p>When a ssl socket is in active mode (the default), data from the + <p>When an ssl socket is in active mode (the default), data from the socket is delivered to the owner of the socket in the form of messages: </p> @@ -396,7 +396,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <name>connect(Socket, SslOptions, Timeout) -> {ok, SslSocket} | {error, Reason}</name> <fsummary> Upgrades a gen_tcp, or - equivalent, connected socket to a ssl socket. </fsummary> + equivalent, connected socket to an ssl socket. </fsummary> <type> <v>Socket = socket()</v> <v>SslOptions = [ssloption()]</v> @@ -405,7 +405,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <v>Reason = term()</v> </type> <desc> <p>Upgrades a gen_tcp, or equivalent, - connected socket to a ssl socket i.e. performs the + connected socket to an ssl socket i.e. performs the client-side ssl handshake.</p> </desc> </func> @@ -428,12 +428,12 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <func> <name>close(SslSocket) -> ok | {error, Reason}</name> - <fsummary>Close a ssl connection</fsummary> + <fsummary>Close an ssl connection</fsummary> <type> <v>SslSocket = sslsocket()</v> <v>Reason = term()</v> </type> - <desc><p>Close a ssl connection.</p> + <desc><p>Close an ssl connection.</p> </desc> </func> @@ -450,7 +450,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <v>Reason = term()</v> </type> <desc><p>Assigns a new controlling process to the ssl-socket. A - controlling process is the owner of a ssl-socket, and receives + controlling process is the owner of an ssl-socket, and receives all messages from the socket.</p> </desc> </func> @@ -480,7 +480,6 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | </func> <func> - <name>getopts(Socket) -> </name> <name>getopts(Socket, OptionNames) -> {ok, [socketoption()]} | {error, Reason}</name> <fsummary>Get the value of the specified options.</fsummary> @@ -489,8 +488,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <v>OptionNames = [atom()]</v> </type> <desc> - <p>Get the value of the specified socket options, if no - options are specified all options are returned. + <p>Get the value of the specified socket options. </p> </desc> </func> @@ -498,14 +496,14 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | <func> <name>listen(Port, Options) -> {ok, ListenSocket} | {error, Reason}</name> - <fsummary>Creates a ssl listen socket.</fsummary> + <fsummary>Creates an ssl listen socket.</fsummary> <type> <v>Port = integer()</v> <v>Options = options()</v> <v>ListenSocket = sslsocket()</v> </type> <desc> - <p>Creates a ssl listen socket.</p> + <p>Creates an ssl listen socket.</p> </desc> </func> @@ -589,6 +587,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | the socket is closed.</p> </desc> </func> + <func> <name>setopts(Socket, Options) -> ok | {error, Reason}</name> <fsummary>Set socket options.</fsummary> @@ -648,7 +647,7 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | </type> <desc> <p> Upgrades a gen_tcp, or - equivalent, socket to a ssl socket i.e. performs the + equivalent, socket to an ssl socket i.e. performs the ssl server-side handshake.</p> <p><warning>Note that the listen socket should be in {active, false} mode before telling the client that the server is ready to upgrade diff --git a/lib/ssl/doc/src/ssl_protocol.xml b/lib/ssl/doc/src/ssl_protocol.xml index 6936408881..ca5cc8bc7a 100644 --- a/lib/ssl/doc/src/ssl_protocol.xml +++ b/lib/ssl/doc/src/ssl_protocol.xml @@ -31,11 +31,11 @@ </p> <p>By default erlang ssl is run over the TCP/IP protocol even - though you could plug in an other reliable transport protocol + though you could plug in any other reliable transport protocol with the same API as gen_tcp.</p> <p>If a client and server wants to use an upgrade mechanism, such as - defined by RFC2817, to upgrade a regular TCP/IP connection to a ssl + defined by RFC2817, to upgrade a regular TCP/IP connection to an ssl connection the erlang ssl API supports this. This can be useful for things such as supporting HTTP and HTTPS on the same port and implementing virtual hosting. diff --git a/lib/ssl/doc/src/using_ssl.xml b/lib/ssl/doc/src/using_ssl.xml index 605290b6f9..ab837a156a 100644 --- a/lib/ssl/doc/src/using_ssl.xml +++ b/lib/ssl/doc/src/using_ssl.xml @@ -56,7 +56,7 @@ <code type="erl">1 server> ssl:start(). ok</code> - <p>Create a ssl listen socket</p> + <p>Create an ssl listen socket</p> <code type="erl">2 server> {ok, ListenSocket} = ssl:listen(9999, [{certfile, "cert.pem"}, {keyfile, "key.pem"},{reuseaddr, true}]). {ok,{sslsocket, [...]}}</code> @@ -90,7 +90,7 @@ ok</code> <section> <title>Upgrade example</title> - <note><p> To upgrade a TCP/IP connection to a ssl connection the + <note><p> To upgrade a TCP/IP connection to an ssl connection the client and server have to aggre to do so. Agreement may be accompliced by using a protocol such the one used by HTTP specified in RFC 2817.</p> </note> @@ -114,7 +114,7 @@ ok</code> <code type="erl">2 client> {ok, Socket} = gen_tcp:connect("localhost", 9999, [], infinity).</code> <p>Make sure active is set to false before trying - to upgrade a connection to a ssl connection, otherwhise + to upgrade a connection to an ssl connection, otherwhise ssl handshake messages may be deliverd to the wrong process.</p> <code type="erl">4 server> inet:setopts(Socket, [{active, false}]). ok</code> @@ -124,7 +124,7 @@ ok</code> {certfile, "cert.pem"}, {keyfile, "key.pem"}]). {ok,{sslsocket,[...]}}</code> - <p> Upgrade to a ssl connection. Note that the client and server + <p> Upgrade to an ssl connection. Note that the client and server must agree upon the upgrade and the server must call ssl:accept/2 before the client calls ssl:connect/3.</p> <code type="erl">3 client>{ok, SSLSocket} = ssl:connect(Socket, [{cacertfile, "cacerts.pem"}, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index a5e8e7e5c2..74900936a3 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -49,9 +49,12 @@ inet_ssl, %% inet options for internal ssl socket cb %% Callback info }). --type option() :: socketoption() | ssloption() | transportoption(). --type socketoption() :: term(). %% See gen_tcp and inet, import spec later when there is one to import --type ssloption() :: {verify, verify_type()} | +-type connect_option() :: socket_connect_option() | ssl_option() | transport_option(). +-type socket_connect_option() :: gen_tcp:connect_option(). +-type listen_option() :: socket_listen_option() | ssl_option() | transport_option(). +-type socket_listen_option() :: gen_tcp:listen_option(). + +-type ssl_option() :: {verify, verify_type()} | {verify_fun, {fun(), InitialUserState::term()}} | {fail_if_no_peer_cert, boolean()} | {depth, integer()} | {cert, Der::binary()} | {certfile, path()} | {key, Der::binary()} | @@ -66,7 +69,7 @@ string(). % (according to old API) -type ssl_imp() :: new | old. --type transportoption() :: {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}. +-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}}. %%-------------------------------------------------------------------- @@ -96,15 +99,15 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- --spec connect(host() | port(), [option()]) -> {ok, #sslsocket{}} | +-spec connect(host() | inet:port_num(), [connect_option()]) -> {ok, #sslsocket{}} | {error, reason()}. --spec connect(host() | port(), [option()] | port_num(), timeout() | list()) -> +-spec connect(host() | inet:port_num(), [connect_option()] | inet:port_num(), timeout() | list()) -> {ok, #sslsocket{}} | {error, reason()}. --spec connect(host() | port(), port_num(), list(), timeout()) -> +-spec connect(host() | inet:port_num(), inet:port_num(), list(), timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% -%% Description: Connect to a ssl server. +%% Description: Connect to an ssl server. %%-------------------------------------------------------------------- connect(Socket, SslOptions) when is_port(Socket) -> connect(Socket, SslOptions, infinity). @@ -112,7 +115,7 @@ connect(Socket, SslOptions) when is_port(Socket) -> connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> EmulatedOptions = emulated_options(), {ok, InetValues} = inet:getopts(Socket, EmulatedOptions), - inet:setopts(Socket, internal_inet_values()), + ok = inet:setopts(Socket, internal_inet_values()), try handle_options(SslOptions0 ++ InetValues, client) of {ok, #config{cb=CbInfo, ssl=SslOptions, emulated=EmOpts}} -> case inet:peername(Socket) of @@ -148,10 +151,10 @@ connect(Host, Port, Options0, Timeout) -> end. %%-------------------------------------------------------------------- --spec listen(port_num(), [option()]) ->{ok, #sslsocket{}} | {error, reason()}. +-spec listen(inet:port_num(), [listen_option()]) ->{ok, #sslsocket{}} | {error, reason()}. %% -%% Description: Creates a ssl listen socket. +%% Description: Creates an ssl listen socket. %%-------------------------------------------------------------------- listen(_Port, []) -> {error, enooptions}; @@ -177,7 +180,7 @@ listen(Port, Options0) -> -spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% -%% Description: Performs transport accept on a ssl listen socket +%% Description: Performs transport accept on an ssl listen socket %%-------------------------------------------------------------------- transport_accept(ListenSocket) -> transport_accept(ListenSocket, infinity). @@ -214,11 +217,11 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> %%-------------------------------------------------------------------- -spec ssl_accept(#sslsocket{}) -> ok | {error, reason()}. --spec ssl_accept(#sslsocket{} | port(), timeout()| [option()]) -> +-spec ssl_accept(#sslsocket{} | port(), timeout()| [ssl_option() | transport_option()]) -> ok | {ok, #sslsocket{}} | {error, reason()}. --spec ssl_accept(port(), [option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(port(), [ssl_option()| transport_option()], timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% -%% Description: Performs accept on a ssl listen socket. e.i. performs +%% Description: Performs accept on an ssl listen socket. e.i. performs %% ssl handshake. %%-------------------------------------------------------------------- ssl_accept(ListenSocket) -> @@ -238,7 +241,7 @@ ssl_accept(#sslsocket{} = Socket, Timeout) -> ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> EmulatedOptions = emulated_options(), {ok, InetValues} = inet:getopts(Socket, EmulatedOptions), - inet:setopts(Socket, internal_inet_values()), + ok = inet:setopts(Socket, internal_inet_values()), try handle_options(SslOptions ++ InetValues, server) of {ok, #config{cb=CbInfo,ssl=SslOpts, emulated=EmOpts}} -> {ok, Port} = inet:port(Socket), @@ -252,7 +255,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> %%-------------------------------------------------------------------- -spec close(#sslsocket{}) -> term(). %% -%% Description: Close a ssl connection +%% Description: Close an ssl connection %%-------------------------------------------------------------------- close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}) -> CbMod:close(ListenSocket); @@ -373,7 +376,7 @@ select_part(plain, Cert, Opts) -> end. %%-------------------------------------------------------------------- --spec peername(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. +-spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_num()}} | {error, reason()}. %% %% Description: same as inet:peername/1. %%-------------------------------------------------------------------- @@ -402,29 +405,56 @@ cipher_suites(openssl) -> [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]. %%-------------------------------------------------------------------- --spec getopts(#sslsocket{}, [atom()]) -> {ok, [{atom(), term()}]} | {error, reason()}. +-spec getopts(#sslsocket{}, [gen_tcp:option_name()]) -> + {ok, [gen_tcp:option()]} | {error, reason()}. %% %% Description: Gets options %%-------------------------------------------------------------------- -getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptTags) when is_pid(Pid) -> - ssl_connection:get_opts(Pid, OptTags); -getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) -> - inet:getopts(ListenSocket, OptTags); -getopts(#sslsocket{} = Socket, Options) -> +getopts(#sslsocket{fd = new_ssl, pid = Pid}, OptionTags) when is_pid(Pid), is_list(OptionTags) -> + ssl_connection:get_opts(Pid, OptionTags); +getopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptionTags) when is_list(OptionTags) -> + try inet:getopts(ListenSocket, OptionTags) of + {ok, _} = Result -> + Result; + {error, InetError} -> + {error, {eoptions, {inet_options, OptionTags, InetError}}} + catch + _:_ -> + {error, {eoptions, {inet_options, OptionTags}}} + end; +getopts(#sslsocket{fd = new_ssl}, OptionTags) -> + {error, {eoptions, {inet_options, OptionTags}}}; +getopts(#sslsocket{} = Socket, OptionTags) -> ensure_old_ssl_started(), - ssl_broker:getopts(Socket, Options). + ssl_broker:getopts(Socket, OptionTags). %%-------------------------------------------------------------------- --spec setopts(#sslsocket{}, [proplists:property()]) -> ok | {error, reason()}. +-spec setopts(#sslsocket{}, [gen_tcp:option()]) -> ok | {error, reason()}. %% %% Description: Sets options %%-------------------------------------------------------------------- -setopts(#sslsocket{fd = new_ssl, pid = Pid}, Opts0) when is_pid(Pid) -> - Opts = proplists:expand([{binary, [{mode, binary}]}, - {list, [{mode, list}]}], Opts0), - ssl_connection:set_opts(Pid, Opts); -setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) -> - inet:setopts(ListenSocket, OptTags); +setopts(#sslsocket{fd = new_ssl, pid = Pid}, Options0) when is_pid(Pid), is_list(Options0) -> + try proplists:expand([{binary, [{mode, binary}]}, + {list, [{mode, list}]}], Options0) of + Options -> + ssl_connection:set_opts(Pid, Options) + catch + _:_ -> + {error, {eoptions, {not_a_proplist, Options0}}} + end; + +setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, Options) when is_list(Options) -> + try inet:setopts(ListenSocket, Options) of + ok -> + ok; + {error, InetError} -> + {error, {eoptions, {inet_options, Options, InetError}}} + catch + _:Error -> + {error, {eoptions, {inet_options, Options, Error}}} + end; +setopts(#sslsocket{fd = new_ssl}, Options) -> + {error, {eoptions,{not_a_proplist, Options}}}; setopts(#sslsocket{} = Socket, Options) -> ensure_old_ssl_started(), ssl_broker:setopts(Socket, Options). @@ -440,7 +470,7 @@ shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) -> ssl_connection:shutdown(Pid, How). %%-------------------------------------------------------------------- --spec sockname(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. +-spec sockname(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_num()}} | {error, reason()}. %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 8c0c2bfa5d..422ea6404b 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -30,9 +30,9 @@ -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([trusted_cert_and_path/2, - certificate_chain/2, - file_to_certificats/1, +-export([trusted_cert_and_path/3, + certificate_chain/3, + file_to_certificats/2, validate_extension/3, is_valid_extkey_usage/2, is_valid_key_usage/2, @@ -46,14 +46,14 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec trusted_cert_and_path([der_cert()], certdb_ref()) -> +-spec trusted_cert_and_path([der_cert()], db_handle(), certdb_ref()) -> {der_cert() | unknown_ca, [der_cert()]}. %% %% Description: Extracts the root cert (if not presents tries to %% look it up, if not found {bad_cert, unknown_ca} will be added verification %% errors. Returns {RootCert, Path, VerifyErrors} %%-------------------------------------------------------------------- -trusted_cert_and_path(CertChain, CertDbRef) -> +trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef) -> Path = [Cert | _] = lists:reverse(CertChain), OtpCert = public_key:pkix_decode_cert(Cert, otp), SignedAndIssuerID = @@ -66,7 +66,7 @@ trusted_cert_and_path(CertChain, CertDbRef) -> {ok, IssuerId} -> {other, IssuerId}; {error, issuer_not_found} -> - case find_issuer(OtpCert, no_candidate) of + case find_issuer(OtpCert, no_candidate, CertDbHandle) of {ok, IssuerId} -> {other, IssuerId}; Other -> @@ -82,7 +82,7 @@ trusted_cert_and_path(CertChain, CertDbRef) -> {self, _} when length(Path) == 1 -> {selfsigned_peer, Path}; {_ ,{SerialNr, Issuer}} -> - case ssl_manager:lookup_trusted_cert(CertDbRef, SerialNr, Issuer) of + case ssl_manager:lookup_trusted_cert(CertDbHandle, CertDbRef, SerialNr, Issuer) of {ok, {BinCert,_}} -> {BinCert, Path}; _ -> @@ -92,23 +92,23 @@ trusted_cert_and_path(CertChain, CertDbRef) -> end. %%-------------------------------------------------------------------- --spec certificate_chain(undefined | binary(), certdb_ref()) -> +-spec certificate_chain(undefined | binary(), db_handle(), certdb_ref()) -> {error, no_cert} | {ok, [der_cert()]}. %% %% Description: Return the certificate chain to send to peer. %%-------------------------------------------------------------------- -certificate_chain(undefined, _CertsDbRef) -> +certificate_chain(undefined, _, _) -> {error, no_cert}; -certificate_chain(OwnCert, CertsDbRef) -> +certificate_chain(OwnCert, CertDbHandle, CertsDbRef) -> ErlCert = public_key:pkix_decode_cert(OwnCert, otp), - certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]). + certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]). %%-------------------------------------------------------------------- --spec file_to_certificats(string()) -> [der_cert()]. +-spec file_to_certificats(string(), term()) -> [der_cert()]. %% %% Description: Return list of DER encoded certificates. %%-------------------------------------------------------------------- -file_to_certificats(File) -> - {ok, List} = ssl_manager:cache_pem_file(File), +file_to_certificats(File, DbHandle) -> + {ok, List} = ssl_manager:cache_pem_file(File, DbHandle), [Bin || {'Certificate', Bin, not_encrypted} <- List]. %%-------------------------------------------------------------------- -spec validate_extension(term(), #'Extension'{} | {bad_cert, atom()} | valid, @@ -180,7 +180,7 @@ signature_type(?'id-dsa-with-sha1') -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) -> +certificate_chain(OtpCert, _Cert, CertDbHandle, CertsDbRef, Chain) -> IssuerAndSelfSigned = case public_key:pkix_is_self_signed(OtpCert) of true -> @@ -191,11 +191,11 @@ certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) -> case IssuerAndSelfSigned of {_, true = SelfSigned} -> - certificate_chain(CertsDbRef, Chain, ignore, ignore, SelfSigned); + certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned); {{error, issuer_not_found}, SelfSigned} -> - case find_issuer(OtpCert, no_candidate) of + case find_issuer(OtpCert, no_candidate, CertDbHandle) of {ok, {SerialNr, Issuer}} -> - certificate_chain(CertsDbRef, Chain, + certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned); _ -> %% Guess the the issuer must be the root @@ -205,19 +205,19 @@ certificate_chain(OtpCert, _Cert, CertsDbRef, Chain) -> {ok, lists:reverse(Chain)} end; {{ok, {SerialNr, Issuer}}, SelfSigned} -> - certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, SelfSigned) + certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned) end. -certificate_chain(_CertsDbRef, Chain, _SerialNr, _Issuer, true) -> +certificate_chain(_,_, Chain, _SerialNr, _Issuer, true) -> {ok, lists:reverse(Chain)}; -certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> - case ssl_manager:lookup_trusted_cert(CertsDbRef, +certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> + case ssl_manager:lookup_trusted_cert(CertDbHandle, CertsDbRef, SerialNr, Issuer) of {ok, {IssuerCert, ErlCert}} -> ErlCert = public_key:pkix_decode_cert(IssuerCert, otp), certificate_chain(ErlCert, IssuerCert, - CertsDbRef, [IssuerCert | Chain]); + CertDbHandle, CertsDbRef, [IssuerCert | Chain]); _ -> %% The trusted cert may be obmitted from the chain as the %% counter part needs to have it anyway to be able to @@ -227,8 +227,8 @@ certificate_chain(CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned) -> {ok, lists:reverse(Chain)} end. -find_issuer(OtpCert, PrevCandidateKey) -> - case ssl_manager:issuer_candidate(PrevCandidateKey) of +find_issuer(OtpCert, PrevCandidateKey, CertDbHandle) -> + case ssl_manager:issuer_candidate(PrevCandidateKey, CertDbHandle) of no_more_candidates -> {error, issuer_not_found}; {Key, {_Cert, ErlCertCandidate}} -> @@ -236,7 +236,7 @@ find_issuer(OtpCert, PrevCandidateKey) -> true -> public_key:pkix_issuer_id(ErlCertCandidate, self); false -> - find_issuer(OtpCert, Key) + find_issuer(OtpCert, Key, CertDbHandle) end end. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index 3eceefa304..0560a02110 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -26,8 +26,8 @@ -include_lib("public_key/include/public_key.hrl"). -export([create/0, remove/1, add_trusted_certs/3, - remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1, - lookup_cached_certs/1, cache_pem_file/4, uncache_pem_file/2, lookup/2]). + remove_trusted_certs/2, lookup_trusted_cert/4, issuer_candidate/2, + lookup_cached_certs/2, cache_pem_file/4, uncache_pem_file/2, lookup/2]). -type time() :: {non_neg_integer(), non_neg_integer(), non_neg_integer()}. @@ -36,19 +36,19 @@ %%==================================================================== %%-------------------------------------------------------------------- --spec create() -> certdb_ref(). +-spec create() -> [db_handle()]. %% %% Description: Creates a new certificate db. -%% Note: lookup_trusted_cert/3 may be called from any process but only +%% Note: lookup_trusted_cert/4 may be called from any process but only %% the process that called create may call the other functions. %%-------------------------------------------------------------------- create() -> - [ets:new(certificate_db_name(), [named_table, set, protected]), - ets:new(ssl_file_to_ref, [named_table, set, protected]), + [ets:new(ssl_otp_certificate_db, [set, protected]), + ets:new(ssl_file_to_ref, [set, protected]), ets:new(ssl_pid_to_file, [bag, private])]. %%-------------------------------------------------------------------- --spec remove(certdb_ref()) -> term(). +-spec remove([db_handle()]) -> term(). %% %% Description: Removes database db %%-------------------------------------------------------------------- @@ -56,7 +56,7 @@ remove(Dbs) -> lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). %%-------------------------------------------------------------------- --spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> +-spec lookup_trusted_cert(db_handle(), certdb_ref(), serialnumber(), issuer()) -> undefined | {ok, {der_cert(), #'OTPCertificate'{}}}. %% @@ -64,19 +64,19 @@ remove(Dbs) -> %% <SerialNumber, Issuer>. Ref is used as it is specified %% for each connection which certificates are trusted. %%-------------------------------------------------------------------- -lookup_trusted_cert(Ref, SerialNumber, Issuer) -> - case lookup({Ref, SerialNumber, Issuer}, certificate_db_name()) of +lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> + case lookup({Ref, SerialNumber, Issuer}, DbHandle) of undefined -> undefined; [Certs] -> {ok, Certs} end. -lookup_cached_certs(File) -> - ets:lookup(certificate_db_name(), {file, File}). +lookup_cached_certs(DbHandle, File) -> + ets:lookup(DbHandle, {file, File}). %%-------------------------------------------------------------------- --spec add_trusted_certs(pid(), string() | {der, list()}, certdb_ref()) -> {ok, certdb_ref()}. +-spec add_trusted_certs(pid(), string() | {der, list()}, [db_handle()]) -> {ok, [db_handle()]}. %% %% Description: Adds the trusted certificates from file <File> to the %% runtime database. Returns Ref that should be handed to lookup_trusted_cert @@ -100,7 +100,7 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> insert(Pid, File, PidToFileDb), {ok, Ref}. %%-------------------------------------------------------------------- --spec cache_pem_file(pid(), string(), time(), certdb_ref()) -> term(). +-spec cache_pem_file(pid(), string(), time(), [db_handle()]) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- @@ -112,7 +112,7 @@ cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) -> {ok, Content}. %-------------------------------------------------------------------- --spec uncache_pem_file(string(), certdb_ref()) -> no_return(). +-spec uncache_pem_file(string(), [db_handle()]) -> no_return(). %% %% Description: If a cached file is no longer valid (changed on disk) %% we must terminate the connections using the old file content, and @@ -130,7 +130,7 @@ uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) -> %%-------------------------------------------------------------------- --spec remove_trusted_certs(pid(), certdb_ref()) -> term(). +-spec remove_trusted_certs(pid(), [db_handle()]) -> term(). %% %% Description: Removes trusted certs originating from @@ -161,7 +161,7 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> end. %%-------------------------------------------------------------------- --spec issuer_candidate(no_candidate | cert_key() | {file, term()}) -> +-spec issuer_candidate(no_candidate | cert_key() | {file, term()}, term()) -> {cert_key(),{der_cert(), #'OTPCertificate'{}}} | no_more_candidates. %% %% Description: If a certificat does not define its issuer through @@ -169,32 +169,30 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> %% try to find the issuer in the database over known %% certificates. %%-------------------------------------------------------------------- -issuer_candidate(no_candidate) -> - Db = certificate_db_name(), +issuer_candidate(no_candidate, Db) -> case ets:first(Db) of '$end_of_table' -> no_more_candidates; {file, _} = Key -> - issuer_candidate(Key); + issuer_candidate(Key, Db); Key -> [Cert] = lookup(Key, Db), {Key, Cert} end; -issuer_candidate(PrevCandidateKey) -> - Db = certificate_db_name(), +issuer_candidate(PrevCandidateKey, Db) -> case ets:next(Db, PrevCandidateKey) of '$end_of_table' -> no_more_candidates; {file, _} = Key -> - issuer_candidate(Key); + issuer_candidate(Key, Db); Key -> [Cert] = lookup(Key, Db), {Key, Cert} end. %%-------------------------------------------------------------------- --spec lookup(term(), term()) -> term() | undefined. +-spec lookup(term(), db_handle()) -> term() | undefined. %% %% Description: Looks up an element in a certificat <Db>. %%-------------------------------------------------------------------- @@ -212,9 +210,6 @@ lookup(Key, Db) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -certificate_db_name() -> - ssl_otp_certificate_db. - insert(Key, Data, Db) -> true = ets:insert(Db, {Key, Data}). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 2c452837f8..0ae39689cc 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -70,6 +70,7 @@ %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) tls_handshake_hashes, % see above tls_cipher_texts, % list() received but not deciphered yet + cert_db, % session, % #session{} from ssl_handshake.hrl session_cache, % session_cache_cb, % @@ -126,11 +127,11 @@ send(Pid, Data) -> recv(Pid, Length, Timeout) -> sync_send_all_state_event(Pid, {recv, Length}, Timeout). %%-------------------------------------------------------------------- --spec connect(host(), port_num(), port(), {#ssl_options{}, #socket_options{}}, +-spec connect(host(), inet:port_num(), port(), {#ssl_options{}, #socket_options{}}, pid(), tuple(), timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% -%% Description: Connect to a ssl server. +%% Description: Connect to an ssl server. %%-------------------------------------------------------------------- connect(Host, Port, Socket, Options, User, CbInfo, Timeout) -> try start_fsm(client, Host, Port, Socket, Options, User, CbInfo, @@ -140,11 +141,11 @@ connect(Host, Port, Socket, Options, User, CbInfo, Timeout) -> {error, ssl_not_started} end. %%-------------------------------------------------------------------- --spec ssl_accept(port_num(), port(), {#ssl_options{}, #socket_options{}}, +-spec ssl_accept(inet:port_num(), port(), {#ssl_options{}, #socket_options{}}, pid(), tuple(), timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% -%% Description: Performs accept on a ssl listen socket. e.i. performs +%% Description: Performs accept on an ssl listen socket. e.i. performs %% ssl handshake. %%-------------------------------------------------------------------- ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> @@ -184,7 +185,7 @@ socket_control(Socket, Pid, CbModule) -> %%-------------------------------------------------------------------- -spec close(pid()) -> ok | {error, reason()}. %% -%% Description: Close a ssl connection +%% Description: Close an ssl connection %%-------------------------------------------------------------------- close(ConnectionPid) -> case sync_send_all_state_event(ConnectionPid, close) of @@ -211,14 +212,14 @@ shutdown(ConnectionPid, How) -> new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). %%-------------------------------------------------------------------- --spec sockname(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. +-spec sockname(pid()) -> {ok, {inet:ip_adress(), inet:port_num()}} | {error, reason()}. %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, sockname). %%-------------------------------------------------------------------- --spec peername(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. +-spec peername(pid()) -> {ok, {inet:ip_adress(), inet:port_num()}} | {error, reason()}. %% %% Description: Same as inet:peername/1 %%-------------------------------------------------------------------- @@ -276,7 +277,7 @@ renegotiation(ConnectionPid) -> %%==================================================================== %%-------------------------------------------------------------------- --spec start_link(atom(), host(), port_num(), port(), list(), pid(), tuple()) -> +-spec start_link(atom(), host(), inet:port_num(), port(), list(), pid(), tuple()) -> {ok, pid()} | ignore | {error, reason()}. %% %% Description: Creates a gen_fsm process which calls Module:init/1 to @@ -305,12 +306,13 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, Hashes0 = ssl_handshake:init_hashes(), try ssl_init(SSLOpts0, Role) of - {ok, Ref, CacheRef, OwnCert, Key, DHParams} -> + {ok, Ref, CertDbHandle, CacheHandle, OwnCert, Key, DHParams} -> Session = State0#state.session, State = State0#state{tls_handshake_hashes = Hashes0, session = Session#session{own_certificate = OwnCert}, cert_db_ref = Ref, - session_cache = CacheRef, + cert_db = CertDbHandle, + session_cache = CacheHandle, private_key = Key, diffie_hellman_params = DHParams}, {ok, hello, State, get_timeout(State)} @@ -500,9 +502,10 @@ certify(#certificate{asn1_certificates = []}, certify(#certificate{} = Cert, #state{negotiated_version = Version, role = Role, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, ssl_options = Opts} = State) -> - case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth, + case ssl_handshake:certify(Cert, CertDbHandle, CertDbRef, Opts#ssl_options.depth, Opts#ssl_options.verify, Opts#ssl_options.verify_fun, Role) of {PeerCert, PublicKeyInfo} -> @@ -859,23 +862,23 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, #state{socket_options = Opts1, socket = Socket, user_data_buffer = Buffer} = State0) -> - Opts = set_socket_opts(Socket, Opts0, Opts1, []), + {Reply, Opts} = set_socket_opts(Socket, Opts0, Opts1, []), State1 = State0#state{socket_options = Opts}, if Opts#socket_options.active =:= false -> - {reply, ok, StateName, State1, get_timeout(State1)}; + {reply, Reply, StateName, State1, get_timeout(State1)}; Buffer =:= <<>>, Opts1#socket_options.active =:= false -> %% Need data, set active once {Record, State2} = next_record_if_active(State1), case next_state(StateName, Record, State2) of {next_state, StateName, State, Timeout} -> - {reply, ok, StateName, State, Timeout}; + {reply, Reply, StateName, State, Timeout}; {stop, Reason, State} -> {stop, Reason, State} end; Buffer =:= <<>> -> %% Active once already set - {reply, ok, StateName, State1, get_timeout(State1)}; + {reply, Reply, StateName, State1, get_timeout(State1)}; true -> case application_data(<<>>, State1) of Stop = {stop,_,_} -> @@ -883,7 +886,7 @@ handle_sync_event({set_opts, Opts0}, _From, StateName, {Record, State2} -> case next_state(StateName, Record, State2) of {next_state, StateName, State, Timeout} -> - {reply, ok, StateName, State, Timeout}; + {reply, Reply, StateName, State, Timeout}; {stop, Reason, State} -> {stop, Reason, State} end @@ -1044,19 +1047,19 @@ start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo, end. ssl_init(SslOpts, Role) -> - {ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role), + {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = - init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, + init_private_key(CertDbHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, SslOpts#ssl_options.password, Role), - DHParams = init_diffie_hellman(SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), - {ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}. + DHParams = init_diffie_hellman(CertDbHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), + {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert, PrivateKey, DHParams}. init_certificates(#ssl_options{cacerts = CaCerts, cacertfile = CACertFile, certfile = CertFile, cert = Cert}, Role) -> - {ok, CertDbRef, CacheRef} = + {ok, CertDbRef, CertDbHandle, CacheHandle} = try Certs = case CaCerts of undefined -> @@ -1064,44 +1067,44 @@ init_certificates(#ssl_options{cacerts = CaCerts, _ -> {der, CaCerts} end, - {ok, _, _} = ssl_manager:connection_init(Certs, Role) + {ok, _, _, _} = ssl_manager:connection_init(Certs, Role) catch Error:Reason -> handle_file_error(?LINE, Error, Reason, CACertFile, ecacertfile, erlang:get_stacktrace()) end, - init_certificates(Cert, CertDbRef, CacheRef, CertFile, Role). + init_certificates(Cert, CertDbRef, CertDbHandle, CacheHandle, CertFile, Role). -init_certificates(undefined, CertDbRef, CacheRef, "", _) -> - {ok, CertDbRef, CacheRef, undefined}; +init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, "", _) -> + {ok, CertDbRef, CertDbHandle, CacheHandle, undefined}; -init_certificates(undefined, CertDbRef, CacheRef, CertFile, client) -> +init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, CertFile, client) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile), - {ok, CertDbRef, CacheRef, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), + {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} catch _Error:_Reason -> - {ok, CertDbRef, CacheRef, undefined} + {ok, CertDbRef, CertDbHandle, CacheHandle, undefined} end; -init_certificates(undefined, CertDbRef, CacheRef, CertFile, server) -> +init_certificates(undefined, CertDbRef, CertDbHandle, CacheRef, CertFile, server) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile), - {ok, CertDbRef, CacheRef, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), + {ok, CertDbRef, CertDbHandle, CacheRef, OwnCert} catch Error:Reason -> handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, erlang:get_stacktrace()) end; -init_certificates(Cert, CertDbRef, CacheRef, _, _) -> - {ok, CertDbRef, CacheRef, Cert}. +init_certificates(Cert, CertDbRef, CertDbHandle, CacheRef, _, _) -> + {ok, CertDbRef, CertDbHandle, CacheRef, Cert}. -init_private_key(undefined, "", _Password, _Client) -> +init_private_key(_, undefined, "", _Password, _Client) -> undefined; -init_private_key(undefined, KeyFile, Password, _) -> +init_private_key(DbHandle, undefined, KeyFile, Password, _) -> try - {ok, List} = ssl_manager:cache_pem_file(KeyFile), + {ok, List} = ssl_manager:cache_pem_file(KeyFile, DbHandle), [PemEntry] = [PemEntry || PemEntry = {PKey, _ , _} <- List, - PKey =:= 'RSAPrivateKey' orelse + PKey =:= 'RSAPrivateKey' orelse PKey =:= 'DSAPrivateKey'], public_key:pem_entry_decode(PemEntry, Password) catch @@ -1110,9 +1113,9 @@ init_private_key(undefined, KeyFile, Password, _) -> erlang:get_stacktrace()) end; -init_private_key({rsa, PrivateKey}, _, _,_) -> +init_private_key(_,{rsa, PrivateKey}, _, _,_) -> public_key:der_decode('RSAPrivateKey', PrivateKey); -init_private_key({dsa, PrivateKey},_,_,_) -> +init_private_key(_,{dsa, PrivateKey},_,_,_) -> public_key:der_decode('DSAPrivateKey', PrivateKey). -spec(handle_file_error(_,_,_,_,_,_) -> no_return()). @@ -1128,15 +1131,15 @@ file_error(Line, Error, Reason, File, Throw, Stack) -> error_logger:error_report(Report), throw(Throw). -init_diffie_hellman(Params, _,_) when is_binary(Params)-> +init_diffie_hellman(_,Params, _,_) when is_binary(Params)-> public_key:der_decode('DHParameter', Params); -init_diffie_hellman(_,_, client) -> +init_diffie_hellman(_,_,_, client) -> undefined; -init_diffie_hellman(_,undefined, _) -> +init_diffie_hellman(_,_,undefined, _) -> ?DEFAULT_DIFFIE_HELLMAN_PARAMS; -init_diffie_hellman(_, DHParamFile, server) -> +init_diffie_hellman(DbHandle,_, DHParamFile, server) -> try - {ok, List} = ssl_manager:cache_pem_file(DHParamFile), + {ok, List} = ssl_manager:cache_pem_file(DHParamFile,DbHandle), case [Entry || Entry = {'DHParameter', _ , _} <- List] of [Entry] -> public_key:pem_entry_decode(Entry); @@ -1180,11 +1183,12 @@ certify_client(#state{client_certificate_requested = true, role = client, connection_states = ConnectionStates0, transport_cb = Transport, negotiated_version = Version, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}, socket = Socket, tls_handshake_hashes = Hashes0} = State) -> - Certificate = ssl_handshake:certificate(OwnCert, CertDbRef, client), + Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), {BinCert, ConnectionStates1, Hashes1} = encode_handshake(Certificate, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinCert), @@ -1365,9 +1369,10 @@ certify_server(#state{transport_cb = Transport, negotiated_version = Version, connection_states = ConnectionStates, tls_handshake_hashes = Hashes, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}} = State) -> - case ssl_handshake:certificate(OwnCert, CertDbRef, server) of + case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of CertMsg = #certificate{} -> {BinCertMsg, NewConnectionStates, NewHashes} = encode_handshake(CertMsg, Version, ConnectionStates, Hashes), @@ -1454,12 +1459,13 @@ rsa_key_exchange(_, _) -> request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, connection_states = ConnectionStates0, + cert_db = CertDbHandle, cert_db_ref = CertDbRef, tls_handshake_hashes = Hashes0, negotiated_version = Version, socket = Socket, transport_cb = Transport} = State) -> - Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbRef), + Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), Transport:send(Socket, BinMsg), @@ -2040,31 +2046,67 @@ get_socket_opts(Socket, [active | Tags], SockOpts, Acc) -> get_socket_opts(Socket, Tags, SockOpts, [{active, SockOpts#socket_options.active} | Acc]); get_socket_opts(Socket, [Tag | Tags], SockOpts, Acc) -> - case inet:getopts(Socket, [Tag]) of + try inet:getopts(Socket, [Tag]) of {ok, [Opt]} -> get_socket_opts(Socket, Tags, SockOpts, [Opt | Acc]); {error, Error} -> - {error, Error} - end. + {error, {eoptions, {inet_option, Tag, Error}}} + catch + %% So that inet behavior does not crash our process + _:Error -> {error, {eoptions, {inet_option, Tag, Error}}} + end; +get_socket_opts(_,Opts, _,_) -> + {error, {eoptions, {inet_option, Opts, function_clause}}}. set_socket_opts(_, [], SockOpts, []) -> - SockOpts; + {ok, SockOpts}; set_socket_opts(Socket, [], SockOpts, Other) -> %% Set non emulated options - inet:setopts(Socket, Other), - SockOpts; -set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) -> + try inet:setopts(Socket, Other) of + ok -> + {ok, SockOpts}; + {error, InetError} -> + {{error, {eoptions, {inet_options, Other, InetError}}}, SockOpts} + catch + _:Error -> + %% So that inet behavior does not crash our process + {{error, {eoptions, {inet_options, Other, Error}}}, SockOpts} + end; + +set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) when Mode == list; Mode == binary -> set_socket_opts(Socket, Opts, SockOpts#socket_options{mode = Mode}, Other); -set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) -> +set_socket_opts(_, [{mode, _} = Opt| _], SockOpts, _) -> + {{error, {eoptions, {inet_opt, Opt}}}, SockOpts}; +set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) when Packet == raw; + Packet == 0; + Packet == 1; + Packet == 2; + Packet == 4; + Packet == asn1; + Packet == cdr; + Packet == sunrm; + Packet == fcgi; + Packet == tpkt; + Packet == line; + Packet == http; + Packet == http_bin -> set_socket_opts(Socket, Opts, SockOpts#socket_options{packet = Packet}, Other); -set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) -> +set_socket_opts(_, [{packet, _} = Opt| _], SockOpts, _) -> + {{error, {eoptions, {inet_opt, Opt}}}, SockOpts}; +set_socket_opts(Socket, [{header, Header}| Opts], SockOpts, Other) when is_integer(Header) -> set_socket_opts(Socket, Opts, SockOpts#socket_options{header = Header}, Other); -set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) -> +set_socket_opts(_, [{header, _} = Opt| _], SockOpts, _) -> + {{error,{eoptions, {inet_opt, Opt}}}, SockOpts}; +set_socket_opts(Socket, [{active, Active}| Opts], SockOpts, Other) when Active == once; + Active == true; + Active == false -> set_socket_opts(Socket, Opts, SockOpts#socket_options{active = Active}, Other); +set_socket_opts(_, [{active, _} = Opt| _], SockOpts, _) -> + {{error, {eoptions, {inet_opt, Opt}} }, SockOpts}; set_socket_opts(Socket, [Opt | Opts], SockOpts, Other) -> set_socket_opts(Socket, Opts, SockOpts, [Opt | Other]). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 1f4c44d115..2dfa1741c5 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -31,9 +31,9 @@ -include_lib("public_key/include/public_key.hrl"). -export([master_secret/4, client_hello/6, server_hello/4, hello/4, - hello_request/0, certify/6, certificate/3, + hello_request/0, certify/7, certificate/4, client_certificate_verify/5, certificate_verify/5, - certificate_request/2, key_exchange/2, server_key_exchange_hash/2, + certificate_request/3, key_exchange/2, server_key_exchange_hash/2, finished/4, verify_connection/5, get_tls_handshake/2, decode_client_key/3, server_hello_done/0, encode_handshake/2, init_hashes/0, update_hashes/2, @@ -48,7 +48,7 @@ %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- --spec client_hello(host(), port_num(), #connection_states{}, +-spec client_hello(host(), inet:port_num(), #connection_states{}, #ssl_options{}, boolean(), der_cert()) -> #client_hello{}. %% %% Description: Creates a client hello message. @@ -106,7 +106,7 @@ hello_request() -> %%-------------------------------------------------------------------- -spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, - #connection_states{} | {port_num(), #session{}, cache_ref(), + #connection_states{} | {inet:port_num(), #session{}, db_handle(), atom(), #connection_states{}, binary()}, boolean()) -> {tls_version(), session_id(), #connection_states{}}| {tls_version(), {resumed | new, #session{}}, @@ -173,13 +173,13 @@ hello(#client_hello{client_version = ClientVersion, random = Random, end. %%-------------------------------------------------------------------- --spec certify(#certificate{}, term(), integer() | nolimit, +-spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit, verify_peer | verify_none, {fun(), term}, client | server) -> {der_cert(), public_key_info()} | #alert{}. %% %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- -certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, +certify(#certificate{asn1_certificates = ASN1Certs}, CertDbHandle, CertDbRef, MaxPathLen, _Verify, VerifyFunAndState, Role) -> [PeerCert | _] = ASN1Certs, @@ -208,7 +208,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, end, {TrustedErlCert, CertPath} = - ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef), + ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbHandle, CertDbRef), case public_key:pkix_path_validation(TrustedErlCert, CertPath, @@ -222,13 +222,13 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, end. %%-------------------------------------------------------------------- --spec certificate(der_cert(), term(), client | server) -> #certificate{} | #alert{}. +-spec certificate(der_cert(), db_handle(), certdb_ref(), client | server) -> #certificate{} | #alert{}. %% %% Description: Creates a certificate message. %%-------------------------------------------------------------------- -certificate(OwnCert, CertDbRef, client) -> +certificate(OwnCert, CertDbHandle, CertDbRef, client) -> Chain = - case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of + case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, CertChain} -> CertChain; {error, _} -> @@ -239,8 +239,8 @@ certificate(OwnCert, CertDbRef, client) -> end, #certificate{asn1_certificates = Chain}; -certificate(OwnCert, CertDbRef, server) -> - case ssl_certificate:certificate_chain(OwnCert, CertDbRef) of +certificate(OwnCert, CertDbHandle, CertDbRef, server) -> + case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, Chain} -> #certificate{asn1_certificates = Chain}; {error, _} -> @@ -302,17 +302,17 @@ certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams %%-------------------------------------------------------------------- --spec certificate_request(#connection_states{}, certdb_ref()) -> +-spec certificate_request(#connection_states{}, db_handle(), certdb_ref()) -> #certificate_request{}. %% %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- -certificate_request(ConnectionStates, CertDbRef) -> +certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> #connection_state{security_parameters = #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates, read), Types = certificate_types(CipherSuite), - Authorities = certificate_authorities(CertDbRef), + Authorities = certificate_authorities(CertDbHandle, CertDbRef), #certificate_request{ certificate_types = Types, certificate_authorities = Authorities @@ -383,8 +383,9 @@ master_secret(Version, #session{master_secret = Mastersecret}, ConnectionStates, Role) catch exit:Reason -> - error_logger:error_report("Key calculation failed due to ~p", - [Reason]), + Report = io_lib:format("Key calculation failed due to ~p", + [Reason]), + error_logger:error_report(Report), ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) end; @@ -400,8 +401,9 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> SecParams, ConnectionStates, Role) catch exit:Reason -> - error_logger:error_report("Master secret calculation failed" - " due to ~p", [Reason]), + Report = io_lib:format("Master secret calculation failed" + " due to ~p", [Reason]), + error_logger:error_report(Report), ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) end. @@ -1071,8 +1073,8 @@ certificate_types({KeyExchange, _, _, _}) certificate_types(_) -> <<?BYTE(?RSA_SIGN)>>. -certificate_authorities(CertDbRef) -> - Authorities = certificate_authorities_from_db(CertDbRef), +certificate_authorities(CertDbHandle, CertDbRef) -> + Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef), Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> OTPSubj = TBSCert#'OTPTBSCertificate'.subject, DNEncodedBin = public_key:pkix_encode('Name', OTPSubj, otp), @@ -1084,18 +1086,18 @@ certificate_authorities(CertDbRef) -> end, list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]). -certificate_authorities_from_db(CertDbRef) -> - certificate_authorities_from_db(CertDbRef, no_candidate, []). +certificate_authorities_from_db(CertDbHandle, CertDbRef) -> + certificate_authorities_from_db(CertDbHandle, CertDbRef, no_candidate, []). -certificate_authorities_from_db(CertDbRef, PrevKey, Acc) -> - case ssl_manager:issuer_candidate(PrevKey) of +certificate_authorities_from_db(CertDbHandle,CertDbRef, PrevKey, Acc) -> + case ssl_manager:issuer_candidate(PrevKey, CertDbHandle) of no_more_candidates -> lists:reverse(Acc); {{CertDbRef, _, _} = Key, Cert} -> - certificate_authorities_from_db(CertDbRef, Key, [Cert|Acc]); + certificate_authorities_from_db(CertDbHandle, CertDbRef, Key, [Cert|Acc]); {Key, _Cert} -> %% skip certs not from this ssl connection - certificate_authorities_from_db(CertDbRef, Key, Acc) + certificate_authorities_from_db(CertDbHandle, CertDbRef, Key, Acc) end. digitally_signed(Hash, #'RSAPrivateKey'{} = Key) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index c28daa271e..6bf1edc452 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -28,13 +28,12 @@ -type reply() :: term(). -type msg() :: term(). -type from() :: term(). --type host() :: string() | tuple(). --type port_num() :: integer(). +-type host() :: inet:ip_address() | inet:hostname(). -type session_id() :: 0 | binary(). -type tls_version() :: {integer(), integer()}. -type tls_atom_version() :: sslv3 | tlsv1. --type cache_ref() :: term(). --type certdb_ref() :: term(). +-type certdb_ref() :: reference(). +-type db_handle() :: term(). -type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. -type der_cert() :: binary(). -type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 541ca1e918..56c43a16d1 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -28,8 +28,8 @@ %% Internal application API -export([start_link/1, - connection_init/2, cache_pem_file/1, - lookup_trusted_cert/3, issuer_candidate/1, client_session_id/4, + connection_init/2, cache_pem_file/2, + lookup_trusted_cert/4, issuer_candidate/2, client_session_id/4, server_session_id/4, register_session/2, register_session/3, invalidate_session/2, invalidate_session/3]). @@ -50,7 +50,8 @@ session_cache_cb, session_lifetime, certificate_db, - session_validation_timer + session_validation_timer, + last_delay_timer %% Keep for testing purposes }). -define('24H_in_msec', 8640000). @@ -72,47 +73,47 @@ start_link(Opts) -> %%-------------------------------------------------------------------- -spec connection_init(string()| {der, list()}, client | server) -> - {ok, reference(), cache_ref()}. + {ok, certdb_ref(), db_handle(), db_handle()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init(Trustedcerts, Role) -> call({connection_init, Trustedcerts, Role}). %%-------------------------------------------------------------------- --spec cache_pem_file(string()) -> {ok, term()} | {error, reason()}. +-spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}. %% %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- -cache_pem_file(File) -> +cache_pem_file(File, DbHandle) -> try file:read_file_info(File) of {ok, #file_info{mtime = LastWrite}} -> - cache_pem_file(File, LastWrite) + cache_pem_file(File, LastWrite, DbHandle) catch _:Reason -> {error, Reason} end. %%-------------------------------------------------------------------- --spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> +-spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) -> undefined | {ok, {der_cert(), #'OTPCertificate'{}}}. %% %% Description: Lookup the trusted cert with Key = {reference(), %% serialnumber(), issuer()}. %% -------------------------------------------------------------------- -lookup_trusted_cert(Ref, SerialNumber, Issuer) -> - ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). +lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> + ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- --spec issuer_candidate(cert_key() | no_candidate) -> +-spec issuer_candidate(cert_key() | no_candidate, term()) -> {cert_key(), {der_cert(), #'OTPCertificate'{}}} | no_more_candidates. %% %% Description: Return next issuer candidate. %%-------------------------------------------------------------------- -issuer_candidate(PrevCandidateKey) -> - ssl_certificate_db:issuer_candidate(PrevCandidateKey). +issuer_candidate(PrevCandidateKey, DbHandle) -> + ssl_certificate_db:issuer_candidate(PrevCandidateKey, DbHandle). %%-------------------------------------------------------------------- --spec client_session_id(host(), port_num(), #ssl_options{}, +-spec client_session_id(host(), inet:port_num(), #ssl_options{}, der_cert() | undefined) -> session_id(). %% %% Description: Select a session id for the client. @@ -121,7 +122,7 @@ client_session_id(Host, Port, SslOpts, OwnCert) -> call({client_session_id, Host, Port, SslOpts, OwnCert}). %%-------------------------------------------------------------------- --spec server_session_id(host(), port_num(), #ssl_options{}, +-spec server_session_id(host(), inet:port_num(), #ssl_options{}, der_cert()) -> session_id(). %% %% Description: Select a session id for the server. @@ -130,8 +131,8 @@ server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) -> call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}). %%-------------------------------------------------------------------- --spec register_session(port_num(), #session{}) -> ok. --spec register_session(host(), port_num(), #session{}) -> ok. +-spec register_session(inet:port_num(), #session{}) -> ok. +-spec register_session(host(), inet:port_num(), #session{}) -> ok. %% %% Description: Make the session available for reuse. %%-------------------------------------------------------------------- @@ -141,8 +142,8 @@ register_session(Host, Port, Session) -> register_session(Port, Session) -> cast({register_session, Port, Session}). %%-------------------------------------------------------------------- --spec invalidate_session(port_num(), #session{}) -> ok. --spec invalidate_session(host(), port_num(), #session{}) -> ok. +-spec invalidate_session(inet:port_num(), #session{}) -> ok. +-spec invalidate_session(host(), inet:port_num(), #session{}) -> ok. %% %% Description: Make the session unavailable for reuse. After %% a the session has been marked "is_resumable = false" for some while @@ -192,19 +193,20 @@ init([Opts]) -> %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call({{connection_init, "", _Role}, Pid}, _From, - #state{session_cache = Cache} = State) -> + #state{certificate_db = [CertDb |_], + session_cache = Cache} = State) -> erlang:monitor(process, Pid), - Result = {ok, make_ref(), Cache}, + Result = {ok, make_ref(),CertDb, Cache}, {reply, Result, State}; handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, - #state{certificate_db = Db, + #state{certificate_db = [CertDb|_] =Db, session_cache = Cache} = State) -> erlang:monitor(process, Pid), Result = try {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db), - {ok, Ref, Cache} + {ok, Ref, CertDb, Cache} catch _:Reason -> {error, Reason} @@ -273,15 +275,17 @@ handle_cast({invalidate_session, Host, Port, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> CacheCb:update(Cache, {{Host, Port}, ID}, Session#session{is_resumable = false}), - timer:send_after(delay_time(), self(), {delayed_clean_session, {{Host, Port}, ID}}), - {noreply, State}; + TRef = + erlang:send_after(delay_time(), self(), {delayed_clean_session, {{Host, Port}, ID}}), + {noreply, State#state{last_delay_timer = TRef}}; handle_cast({invalidate_session, Port, #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> CacheCb:update(Cache, {Port, ID}, Session#session{is_resumable = false}), - timer:send_after(delay_time(), self(), {delayed_clean_session, {Port, ID}}), - {noreply, State}; + TRef = + erlang:send_after(delay_time(), self(), {delayed_clean_session, {Port, ID}}), + {noreply, State#state{last_delay_timer = TRef}}; handle_cast({recache_pem, File, LastWrite, Pid, From}, #state{certificate_db = [_, FileToRefDb, _]} = State0) -> @@ -408,8 +412,8 @@ session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. -cache_pem_file(File, LastWrite) -> - case ssl_certificate_db:lookup_cached_certs(File) of +cache_pem_file(File, LastWrite, DbHandle) -> + case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of [{_, {Mtime, Content}}] -> case LastWrite of Mtime -> diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 4c3c0b9c58..72091fdd5f 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -342,7 +342,7 @@ get_tls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer), get_tls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC, version = {MajVer, MinVer}, fragment = Data} | Acc]); -%% Matches a ssl v2 client hello message. +%% Matches an ssl v2 client hello message. %% The server must be able to receive such messages, from clients that %% are willing to use ssl v3 or higher, but have ssl v2 compatibility. get_tls_records_aux(<<1:1, Length0:15, Data0:Length0/binary, Rest/binary>>, diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index dc4b7a711c..a0643f616c 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -48,7 +48,7 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom(), +-spec id({host(), inet:port_num(), #ssl_options{}}, db_handle(), atom(), undefined | binary()) -> binary(). %% %% Description: Should be called by the client side to get an id @@ -63,7 +63,7 @@ id(ClientInfo, Cache, CacheCb, OwnCert) -> end. %%-------------------------------------------------------------------- --spec id(port_num(), binary(), #ssl_options{}, cache_ref(), +-spec id(inet:port_num(), binary(), #ssl_options{}, db_handle(), atom(), seconds(), binary()) -> binary(). %% %% Description: Should be called by the server side to get an id diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index c1be6691be..d026bf179e 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -28,10 +28,10 @@ -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, select_session/2]). --type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}. +-type key() :: {{host(), inet:port_num()}, session_id()} | {inet:port_num(), session_id()}. %%-------------------------------------------------------------------- --spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque) +-spec init(list()) -> db_handle(). %% Returns reference to the cache (opaque) %% %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- @@ -39,7 +39,7 @@ init(_) -> ets:new(cache_name(), [set, protected]). %%-------------------------------------------------------------------- --spec terminate(cache_ref()) -> any(). %% +-spec terminate(db_handle()) -> any(). %% %% Description: Handles cache table at termination of ssl manager. %%-------------------------------------------------------------------- @@ -47,7 +47,7 @@ terminate(Cache) -> ets:delete(Cache). %%-------------------------------------------------------------------- --spec lookup(cache_ref(), key()) -> #session{} | undefined. +-spec lookup(db_handle(), key()) -> #session{} | undefined. %% %% Description: Looks up a cach entry. Should be callable from any %% process. @@ -61,7 +61,7 @@ lookup(Cache, Key) -> end. %%-------------------------------------------------------------------- --spec update(cache_ref(), key(), #session{}) -> any(). +-spec update(db_handle(), key(), #session{}) -> any(). %% %% Description: Caches a new session or updates a already cached one. %% Will only be called from the ssl_manager process. @@ -70,7 +70,7 @@ update(Cache, Key, Session) -> ets:insert(Cache, {Key, Session}). %%-------------------------------------------------------------------- --spec delete(cache_ref(), key()) -> any(). +-spec delete(db_handle(), key()) -> any(). %% %% Description: Delets a cache entry. %% Will only be called from the ssl_manager process. @@ -79,7 +79,7 @@ delete(Cache, Key) -> ets:delete(Cache, Key). %%-------------------------------------------------------------------- --spec foldl(fun(), term(), cache_ref()) -> term(). +-spec foldl(fun(), term(), db_handle()) -> term(). %% %% Description: Calls Fun(Elem, AccIn) on successive elements of the %% cache, starting with AccIn == Acc0. Fun/2 must return a new @@ -91,7 +91,7 @@ foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). %%-------------------------------------------------------------------- --spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}]. +-spec select_session(db_handle(), {host(), inet:port_num()} | inet:port_num()) -> [#session{}]. %% %% Description: Selects a session that could be reused. Should be callable %% from any process. diff --git a/lib/ssl/src/ssl_ssl2.erl b/lib/ssl/src/ssl_ssl2.erl index b1005b1acb..30a3a5fc98 100644 --- a/lib/ssl/src/ssl_ssl2.erl +++ b/lib/ssl/src/ssl_ssl2.erl @@ -20,7 +20,7 @@ %% %%---------------------------------------------------------------------- %% Purpose: Handles sslv2 hello as clients supporting sslv2 and higher -%% will send a sslv2 hello. +%% will send an sslv2 hello. %%---------------------------------------------------------------------- -module(ssl_ssl2). diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index ecb5228a8b..37a021e7cf 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -25,7 +25,6 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). --include("test_server_line.hrl"). -include_lib("public_key/include/public_key.hrl"). -include("ssl_alert.hrl"). @@ -210,6 +209,10 @@ all() -> empty_protocol_versions, controlling_process, controller_dies, client_closes_socket, peercert, connect_dist, peername, sockname, socket_options, + invalid_inet_get_option, invalid_inet_get_option_not_list, + invalid_inet_get_option_improper_list, + invalid_inet_set_option, invalid_inet_set_option_not_list, + invalid_inet_set_option_improper_list, misc_ssl_options, versions, cipher_suites, upgrade, upgrade_with_timeout, tcp_connect, tcp_connect_big, ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown, @@ -254,7 +257,7 @@ all() -> %%different_ca_peer_sign, no_reuses_session_server_restart_new_cert, no_reuses_session_server_restart_new_cert_file, reuseaddr, - hibernate + hibernate, connect_twice ]. groups() -> @@ -810,8 +813,218 @@ socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]), ssl:setopts(Socket, [{nodelay, true}]), {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), + {ok, All} = ssl:getopts(Socket, []), + test_server:format("All opts ~p~n", [All]), ok. + + +%%-------------------------------------------------------------------- +invalid_inet_get_option(doc) -> + ["Test handling of invalid inet options in getopts"]; + +invalid_inet_get_option(suite) -> + []; + +invalid_inet_get_option(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +get_invalid_inet_option(Socket) -> + {error, {eoptions, {inet_option, foo, _}}} = ssl:getopts(Socket, [foo]), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_get_option_not_list(doc) -> + ["Test handling of invalid type in getopts"]; + +invalid_inet_get_option_not_list(suite) -> + []; + +invalid_inet_get_option_not_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option_not_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +get_invalid_inet_option_not_list(Socket) -> + {error, {eoptions, {inet_options, some_invalid_atom_here}}} + = ssl:getopts(Socket, some_invalid_atom_here), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_get_option_improper_list(doc) -> + ["Test handling of invalid type in getopts"]; + +invalid_inet_get_option_improper_list(suite) -> + []; + +invalid_inet_get_option_improper_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option_improper_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +get_invalid_inet_option_improper_list(Socket) -> + {error, {eoptions, {inet_option, foo,_}}} = ssl:getopts(Socket, [packet | foo]), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_set_option(doc) -> + ["Test handling of invalid inet options in setopts"]; + +invalid_inet_set_option(suite) -> + []; + +invalid_inet_set_option(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +set_invalid_inet_option(Socket) -> + {error, {eoptions, {inet_opt, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]), + {error, {eoptions, {inet_opt, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]), + {error, {eoptions, {inet_opt, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]), + {error, {eoptions, {inet_opt, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]), + ok. +%%-------------------------------------------------------------------- +invalid_inet_set_option_not_list(doc) -> + ["Test handling of invalid type in setopts"]; + +invalid_inet_set_option_not_list(suite) -> + []; + +invalid_inet_set_option_not_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option_not_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +set_invalid_inet_option_not_list(Socket) -> + {error, {eoptions, {not_a_proplist, some_invalid_atom_here}}} + = ssl:setopts(Socket, some_invalid_atom_here), + ok. + +%%-------------------------------------------------------------------- +invalid_inet_set_option_improper_list(doc) -> + ["Test handling of invalid tye in setopts"]; + +invalid_inet_set_option_improper_list(suite) -> + []; + +invalid_inet_set_option_improper_list(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option_improper_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +set_invalid_inet_option_improper_list(Socket) -> + {error, {eoptions, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} = + ssl:setopts(Socket, [{packet, 0} | {foo, 2}]), + ok. + %%-------------------------------------------------------------------- misc_ssl_options(doc) -> ["Test what happens when we give valid options"]; @@ -3338,6 +3551,7 @@ reuseaddr(Config) when is_list(Config) -> {options, [{active, false} | ClientOpts]}]), test_server:sleep(?SLEEP), ssl_test_lib:close(Server), + ssl_test_lib:close(Client), Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, @@ -3395,6 +3609,54 @@ hibernate(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- + +connect_twice(doc) -> + [""]; +connect_twice(suite) -> + []; +connect_twice(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{keepalive, true},{active, false} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{keepalive, true},{active, false} + | ClientOpts]}]), + Server ! listen, + + {Client1, #sslsocket{}} = + ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, send_recv_result, []}}, + {options, [{keepalive, true},{active, false} + | ClientOpts]}]), + + test_server:format("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:check_result(Server, ok, Client1, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + ssl_test_lib:close(Client1). + + +%%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- send_recv_result(Socket) -> @@ -3472,7 +3734,7 @@ session_cache_process_mnesia(suite) -> session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). -session_cache_process(Type,Config) when is_list(Config) -> +session_cache_process(_Type,Config) when is_list(Config) -> reuse_session(Config). init([Type]) -> diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 5d96b457ed..5ea45018e6 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -26,6 +26,7 @@ -include_lib("common_test/include/ct.hrl"). +-define(DELAY, 500). -define(SLEEP, 500). -define(TIMEOUT, 60000). -define(LONG_TIMEOUT, 600000). @@ -102,7 +103,7 @@ init_per_testcase(session_cleanup, Config0) -> ssl:stop(), application:load(ssl), application:set_env(ssl, session_lifetime, 5), - application:set_env(ssl, session_delay_cleanup_time, ?SLEEP), + application:set_env(ssl, session_delay_cleanup_time, ?DELAY), ssl:start(), [{watchdog, Dog} | Config]; @@ -178,7 +179,7 @@ end_per_group(_GroupName, Config) -> %%-------------------------------------------------------------------- session_cleanup(doc) -> ["Test that sessions are cleand up eventually, so that the session table " - "does grow and grow ..."]; + "does not grow and grow ..."]; session_cleanup(suite) -> []; session_cleanup(Config)when is_list(Config) -> @@ -211,6 +212,7 @@ session_cleanup(Config)when is_list(Config) -> [_, _,_, _, Prop] = StatusInfo, State = state(Prop), Cache = element(2, State), + SessionTimer = element(6, State), Id = proplists:get_value(session_id, SessionInfo), CSession = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}), @@ -220,8 +222,14 @@ session_cleanup(Config)when is_list(Config) -> true = SSession =/= undefined, %% Make sure session has expired and been cleaned up - test_server:sleep(5000), %% Expire time - test_server:sleep((?SLEEP*20), %% Clean up delay (very small in this test case) + some extra time + check_timer(SessionTimer), + test_server:sleep(?DELAY *2), %% Delay time + some extra time + + DelayTimer = get_delay_timer(), + + check_timer(DelayTimer), + + test_server:sleep(?SLEEP), %% Make sure clean has had to run undefined = ssl_session_cache:lookup(Cache, {{Hostname, Port}, Id}), undefined = ssl_session_cache:lookup(Cache, {Port, Id}), @@ -235,6 +243,27 @@ state([{data,[{"State", State}]} | _]) -> state([_ | Rest]) -> state(Rest). +check_timer(Timer) -> + case erlang:read_timer(Timer) of + false -> + {status, _, _, _} = sys:get_status(whereis(ssl_manager)), + ok; + Int -> + test_server:sleep(Int), + check_timer(Timer) + end. + +get_delay_timer() -> + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = state(Prop), + case element(7, State) of + undefined -> + test_server:sleep(?SLEEP), + get_delay_timer(); + DelayTimer -> + DelayTimer + end. %%-------------------------------------------------------------------- session_cache_process_list(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -252,7 +281,6 @@ session_cache_process_mnesia(suite) -> session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). - %%-------------------------------------------------------------------- %%% Session cache API callbacks %%-------------------------------------------------------------------- diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml index d15383c621..e35b5adace 100644 --- a/lib/stdlib/doc/src/gen_fsm.xml +++ b/lib/stdlib/doc/src/gen_fsm.xml @@ -438,7 +438,7 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <fsummary>Initialize process and internal state name and state data.</fsummary> <type> <v>Args = term()</v> - <v>Return = {ok,StateName,StateData} | {ok,StateName,StateData,Timeout}</v> + <v>Result = {ok,StateName,StateData} | {ok,StateName,StateData,Timeout}</v> <v> | {ok,StateName,StateData,hibernate}</v> <v> | {stop,Reason} | ignore</v> <v> StateName = atom()</v> @@ -639,9 +639,9 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 <v>StateName = atom()</v> <v>StateData = term()</v> <v>Result = {next_state,NextStateName,NewStateData}</v> - <v> > | {next_state,NextStateName,NewStateData,Timeout}</v> - <v> > | {next_state,NextStateName,NewStateData,hibernate}</v> - <v> > | {stop,Reason,NewStateData}</v> + <v> | {next_state,NextStateName,NewStateData,Timeout}</v> + <v> | {next_state,NextStateName,NewStateData,hibernate}</v> + <v> | {stop,Reason,NewStateData}</v> <v> NextStateName = atom()</v> <v> NewStateData = term()</v> <v> Timeout = int()>0 | infinity</v> diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index 009aa60faa..edd119d37a 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -150,9 +150,12 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules} <p><c>Restart</c> defines when a terminated child process should be restarted. A <c>permanent</c> child process should always be restarted, a <c>temporary</c> child process should - never be restarted and a <c>transient</c> child process - should be restarted only if it terminates abnormally, i.e. - with another exit reason than <c>normal</c>.</p> + never be restarted (even when the supervisor's restart strategy + is <c>rest_for_one</c> or <c>one_for_all</c> and a sibling's + death causes the temporary process to be terminated) and a + <c>transient</c> child process should be restarted only if + it terminates abnormally, i.e. with another exit reason + than <c>normal</c>.</p> </item> <item> <p><c>Shutdown</c> defines how a child process should be diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index 416df1f02c..b48ad8c1f3 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -52,7 +52,7 @@ <tag>UCS-4</tag> <item>Basically the same as UTF-32, but without some Unicode semantics, defined by IEEE and has little use as a separate encoding standard. For all normal (and possibly abnormal) usages, UTF-32 and UCS-4 are interchangeable.</item> </taglist> -<p>Certain ranges of characters are left unused and certain ranges are even deemed invalid. The most notable invalid range is 16#D800 - 16#DFFF, as the UTF-16 encoding does not allow for encoding of these numbers. It can be speculated that the UTF-16 encoding standard was, from the beginning, expected to be able to hold all Unicode characters in one 16-bit entity, but then had to be extended, leaving a whole in the Unicode range to cope with backward compatibility.</p> +<p>Certain ranges of characters are left unused and certain ranges are even deemed invalid. The most notable invalid range is 16#D800 - 16#DFFF, as the UTF-16 encoding does not allow for encoding of these numbers. It can be speculated that the UTF-16 encoding standard was, from the beginning, expected to be able to hold all Unicode characters in one 16-bit entity, but then had to be extended, leaving a hole in the Unicode range to cope with backward compatibility.</p> <p>Additionally, the codepoint 16#FEFF is used for byte order marks (BOM's) and use of that character is not encouraged in other contexts than that. It actually is valid though, as the character "ZWNBS" (Zero Width Non Breaking Space). BOM's are used to identify encodings and byte order for programs where such parameters are not known in advance. Byte order marks are more seldom used than one could expect, put their use is becoming more widely spread as they provide the means for programs to make educated guesses about the Unicode format of a certain file.</p> </section> <section> diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 718ca2e91a..10b2ed2e49 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -408,7 +408,12 @@ set_attr(line, {Line,Column}, Fun) when ?ALINE(Line), ?COLUMN(Column) -> end; set_attr(line=Tag, Attrs, Fun) when is_list(Attrs) -> {line,Line} = lists:keyfind(Tag, 1, Attrs), - lists:keyreplace(Tag, 1, Attrs, {line,Fun(Line)}); + case lists:keyreplace(Tag, 1, Attrs, {line,Fun(Line)}) of + [{line,Ln}] when ?ALINE(Ln) -> + Ln; + As -> + As + end; set_attr(T1, T2, T3) -> erlang:error(badarg, [T1,T2,T3]). diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 54c7283abf..165e03e506 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -100,7 +100,7 @@ fwrite(Format, Args) -> -spec fread(Format, String) -> Result when Format :: string(), String :: string(), - Result :: {'ok', InputList :: chars(), LeftOverChars :: string()} + Result :: {'ok', InputList :: [term()], LeftOverChars :: string()} | {'more', RestFormat :: string(), Nchars :: non_neg_integer(), InputStack :: chars()} @@ -115,7 +115,7 @@ fread(Chars, Format) -> Format :: string(), Return :: {'more', Continuation1 :: continuation()} | {'done', Result, LeftOverChars :: string()}, - Result :: {'ok', InputList :: chars()} + Result :: {'ok', InputList :: [term()]} | 'eof' | {'error', What :: term()}. diff --git a/lib/stdlib/src/io_lib_fread.erl b/lib/stdlib/src/io_lib_fread.erl index 52aa4d073c..ded1346097 100644 --- a/lib/stdlib/src/io_lib_fread.erl +++ b/lib/stdlib/src/io_lib_fread.erl @@ -24,6 +24,10 @@ -import(lists, [reverse/1,reverse/2]). +-define(is_whitespace(C), + ((C) =:= $\s orelse (C) =:= $\t + orelse (C) =:= $\r orelse (C) =:= $\n)). + %%----------------------------------------------------------------------- %% fread(Continuation, CharList, FormatString) @@ -106,31 +110,27 @@ fread_line(Format0, Line, N0, Results0, More, Newline) -> fread(Format, Line) -> fread(Format, Line, 0, []). -fread([$~|Format0], Line, N, Results) -> +fread([$~|Format0]=AllFormat, Line, N, Results) -> {Format,F,Sup,Unicode} = fread_field(Format0), - fread1(Format, F, Sup, Unicode, Line, N, Results, Format0); -fread([$\s|Format], Line, N, Results) -> - fread_skip_white(Format, Line, N, Results); -fread([$\t|Format], Line, N, Results) -> - fread_skip_white(Format, Line, N, Results); -fread([$\r|Format], Line, N, Results) -> - fread_skip_white(Format, Line, N, Results); -fread([$\n|Format], Line, N, Results) -> + fread1(Format, F, Sup, Unicode, Line, N, Results, AllFormat); +fread([C|Format], Line, N, Results) when ?is_whitespace(C) -> fread_skip_white(Format, Line, N, Results); fread([C|Format], [C|Line], N, Results) -> fread(Format, Line, N+1, Results); fread([_F|_Format], [_C|_Line], _N, _Results) -> fread_error(input); +fread([_|_]=Format, [], N, Results) -> + {more,Format,N,Results}; +fread([_|_], eof, 0, []) -> + %% This is at start of input so no error. + eof; +fread([_|_], eof, _N, _Results) -> + %% This is an error as there is no more input. + fread_error(input); fread([], Line, _N, Results) -> {ok,reverse(Results),Line}. -fread_skip_white(Format, [$\s|Line], N, Results) -> - fread_skip_white(Format, Line, N+1, Results); -fread_skip_white(Format, [$\t|Line], N, Results) -> - fread_skip_white(Format, Line, N+1, Results); -fread_skip_white(Format, [$\r|Line], N, Results) -> - fread_skip_white(Format, Line, N+1, Results); -fread_skip_white(Format, [$\n|Line], N, Results) -> +fread_skip_white(Format, [C|Line], N, Results) when ?is_whitespace(C) -> fread_skip_white(Format, Line, N+1, Results); fread_skip_white(Format, Line, N, Results) -> fread(Format, Line, N, Results). @@ -166,9 +166,9 @@ fread1([$l|Format], _F, Sup, _U, Line, N, Res, _AllFormat) -> fread(Format, Line, N, fread_result(Sup, N, Res)); fread1(_Format, _F, _Sup, _U, [], N, Res, AllFormat) -> %% Need more input here. - {more,[$~|AllFormat],N,Res}; -fread1(_Format, _F, _Sup, _U, eof, _N, [], _AllFormat) -> - %% This is at start of format string so no error. + {more,AllFormat,N,Res}; +fread1(_Format, _F, _Sup, _U, eof, 0, [], _AllFormat) -> + %% This is at start of input so no error. eof; fread1(_Format, _F, _Sup, _U, eof, _N, _Res, _AllFormat) -> %% This is an error as there is no more input. @@ -386,26 +386,16 @@ fread_string_cs(Line0, N0, true) -> %% fread_digits(Line, N, Base, Characters) %% Read segments of things, return "thing" characters in reverse order. -fread_skip_white([$\s|Line]) -> fread_skip_white(Line); -fread_skip_white([$\t|Line]) -> fread_skip_white(Line); -fread_skip_white([$\r|Line]) -> fread_skip_white(Line); -fread_skip_white([$\n|Line]) -> fread_skip_white(Line); +fread_skip_white([C|Line]) when ?is_whitespace(C) -> + fread_skip_white(Line); fread_skip_white(Line) -> Line. -fread_skip_white([$\s|Line], N) -> - fread_skip_white(Line, N+1); -fread_skip_white([$\t|Line], N) -> - fread_skip_white(Line, N+1); -fread_skip_white([$\r|Line], N) -> - fread_skip_white(Line, N+1); -fread_skip_white([$\n|Line], N) -> +fread_skip_white([C|Line], N) when ?is_whitespace(C) -> fread_skip_white(Line, N+1); fread_skip_white(Line, N) -> {Line,N}. -fread_skip_latin1_nonwhite([$\s|Line], N, Cs) -> {[$\s|Line],N,Cs}; -fread_skip_latin1_nonwhite([$\t|Line], N, Cs) -> {[$\t|Line],N,Cs}; -fread_skip_latin1_nonwhite([$\r|Line], N, Cs) -> {[$\r|Line],N,Cs}; -fread_skip_latin1_nonwhite([$\n|Line], N, Cs) -> {[$\n|Line],N,Cs}; +fread_skip_latin1_nonwhite([C|Line], N, Cs) when ?is_whitespace(C) -> + {[C|Line],N,Cs}; fread_skip_latin1_nonwhite([C|Line], N, []) when C > 255 -> {[C|Line],N,error}; fread_skip_latin1_nonwhite([C|Line], N, Cs) when C > 255 -> @@ -414,10 +404,8 @@ fread_skip_latin1_nonwhite([C|Line], N, Cs) -> fread_skip_latin1_nonwhite(Line, N+1, [C|Cs]); fread_skip_latin1_nonwhite([], N, Cs) -> {[],N,Cs}. -fread_skip_nonwhite([$\s|Line], N, Cs) -> {[$\s|Line],N,Cs}; -fread_skip_nonwhite([$\t|Line], N, Cs) -> {[$\t|Line],N,Cs}; -fread_skip_nonwhite([$\r|Line], N, Cs) -> {[$\r|Line],N,Cs}; -fread_skip_nonwhite([$\n|Line], N, Cs) -> {[$\n|Line],N,Cs}; +fread_skip_nonwhite([C|Line], N, Cs) when ?is_whitespace(C) -> + {[C|Line],N,Cs}; fread_skip_nonwhite([C|Line], N, Cs) -> fread_skip_nonwhite(Line, N+1, [C|Cs]); fread_skip_nonwhite([], N, Cs) -> {[],N,Cs}. diff --git a/lib/stdlib/src/proplists.erl b/lib/stdlib/src/proplists.erl index 68697d0da2..e3eda5d932 100644 --- a/lib/stdlib/src/proplists.erl +++ b/lib/stdlib/src/proplists.erl @@ -49,9 +49,10 @@ %% --------------------------------------------------------------------- --export_type([property/0]). +-export_type([property/0, proplist/0]). -type property() :: atom() | tuple(). +-type proplist() :: [property()]. %% --------------------------------------------------------------------- diff --git a/lib/stdlib/src/queue.erl b/lib/stdlib/src/queue.erl index 4c6b4d710b..afe917b151 100644 --- a/lib/stdlib/src/queue.erl +++ b/lib/stdlib/src/queue.erl @@ -56,16 +56,14 @@ new() -> {[],[]}. %{RearList,FrontList} %% O(1) --spec is_queue(Term) -> boolean() when - Term :: term(). +-spec is_queue(Term :: term()) -> boolean(). is_queue({R,F}) when is_list(R), is_list(F) -> true; is_queue(_) -> false. %% O(1) --spec is_empty(Q) -> boolean() when - Q :: queue(). +-spec is_empty(Q :: queue()) -> boolean(). is_empty({[],[]}) -> true; is_empty({In,Out}) when is_list(In), is_list(Out) -> @@ -74,16 +72,14 @@ is_empty(Q) -> erlang:error(badarg, [Q]). %% O(len(Q)) --spec len(Q) -> non_neg_integer() when - Q :: queue(). +-spec len(Q :: queue()) -> non_neg_integer(). 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(Q) -> list() when - Q :: queue(). +-spec to_list(Q :: queue()) -> list(). to_list({In,Out}) when is_list(In), is_list(Out) -> Out++lists:reverse(In, []); to_list(Q) -> @@ -92,8 +88,7 @@ to_list(Q) -> %% Create queue from list %% %% O(length(L)) --spec from_list(L) -> queue() when - L :: list(). +-spec from_list(L :: list()) -> queue(). from_list(L) when is_list(L) -> f2r(L); from_list(L) -> @@ -102,9 +97,7 @@ from_list(L) -> %% Return true or false depending on if element is in queue %% %% O(length(Q)) worst case --spec member(Item, Q) -> boolean() when - Item :: term(), - Q :: queue(). +-spec member(Item :: term(), Q :: queue()) -> boolean(). member(X, {R,F}) when is_list(R), is_list(F) -> lists:member(X, R) orelse lists:member(X, F); member(X, Q) -> @@ -117,10 +110,7 @@ member(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in(Item, Q1) -> Q2 when - Item :: term(), - Q1 :: queue(), - Q2 :: queue(). +-spec in(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). in(X, {[_]=In,[]}) -> {[X], In}; in(X, {In,Out}) when is_list(In), is_list(Out) -> @@ -132,10 +122,7 @@ in(X, Q) -> %% Put at least one element in each list, if it is cheap %% %% O(1) --spec in_r(Item, Q1) -> Q2 when - Item :: term(), - Q1 :: queue(), - Q2 :: queue(). +-spec in_r(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). in_r(X, {[],[_]=F}) -> {F,[X]}; in_r(X, {R,F}) when is_list(R), is_list(F) -> @@ -146,10 +133,9 @@ in_r(X, Q) -> %% Take from head/front %% %% O(1) amortized, O(len(Q)) worst case --spec out(Q1) -> Result when - Q1 :: queue(), - Q2 :: queue(), - Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. +-spec out(Q1 :: queue()) -> + {{value, Item :: term()}, Q2 :: queue()} | + {empty, Q1 :: queue()}. out({[],[]}=Q) -> {empty,Q}; out({[V],[]}) -> @@ -167,10 +153,9 @@ out(Q) -> %% Take from tail/rear %% %% O(1) amortized, O(len(Q)) worst case --spec out_r(Q1) -> Result when - Q1 :: queue(), - Q2 :: queue(), - Result :: {{value, Item :: term()}, Q2} | {empty, Q1}. +-spec out_r(Q1 :: queue()) -> + {{value, Item :: term()}, Q2 :: queue()} | + {empty, Q1 :: queue()}. out_r({[],[]}=Q) -> {empty,Q}; out_r({[],[V]}) -> @@ -191,9 +176,7 @@ out_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec get(Q) -> Item when - Q :: queue(), - Item :: term(). +-spec get(Q :: queue()) -> Item :: term(). get({[],[]}=Q) -> erlang:error(empty, [Q]); get({R,F}) when is_list(R), is_list(F) -> @@ -212,9 +195,7 @@ 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(Q) -> Item when - Q :: queue(), - Item :: term(). +-spec get_r(Q :: queue()) -> Item :: term(). get_r({[],[]}=Q) -> erlang:error(empty, [Q]); get_r({[H|_],F}) when is_list(F) -> @@ -229,9 +210,7 @@ get_r(Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek(Q) -> 'empty' | {'value',Item} when - Q :: queue(), - Item :: term(). +-spec peek(Q :: queue()) -> empty | {value,Item :: term()}. peek({[],[]}) -> empty; peek({R,[H|_]}) when is_list(R) -> @@ -246,9 +225,7 @@ peek(Q) -> %% Return the last element in the queue %% %% O(1) since the queue is supposed to be well formed --spec peek_r(Q) -> 'empty' | {'value',Item} when - Q :: queue(), - Item :: term(). +-spec peek_r(Q :: queue()) -> empty | {value,Item :: term()}. peek_r({[],[]}) -> empty; peek_r({[H|_],F}) when is_list(F) -> @@ -263,9 +240,7 @@ peek_r(Q) -> %% Remove the first element and return resulting queue %% %% O(1) amortized --spec drop(Q1) -> Q2 when - Q1 :: queue(), - Q2 :: queue(). +-spec drop(Q1 :: queue()) -> Q2 :: queue(). drop({[],[]}=Q) -> erlang:error(empty, [Q]); drop({[_],[]}) -> @@ -283,9 +258,7 @@ drop(Q) -> %% Remove the last element and return resulting queue %% %% O(1) amortized --spec drop_r(Q1) -> Q2 when - Q1 :: queue(), - Q2 :: queue(). +-spec drop_r(Q1 :: queue()) -> Q2 :: queue(). drop_r({[],[]}=Q) -> erlang:error(empty, [Q]); drop_r({[],[_]}) -> @@ -306,9 +279,7 @@ drop_r(Q) -> %% Return reversed queue %% %% O(1) --spec reverse(Q1) -> Q2 when - Q1 :: queue(), - Q2 :: queue(). +-spec reverse(Q1 :: queue()) -> Q2 :: queue(). reverse({R,F}) when is_list(R), is_list(F) -> {F,R}; reverse(Q) -> @@ -318,10 +289,7 @@ reverse(Q) -> %% %% Q2 empty: O(1) %% else: O(len(Q1)) --spec join(Q1, Q2) -> Q3 when - Q1 :: queue(), - Q2 :: queue(), - Q3 :: queue(). +-spec join(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) -> @@ -335,11 +303,8 @@ join(Q1, Q2) -> %% %% N = 0..len(Q) %% O(max(N, len(Q))) --spec split(N, Q1) -> {Q2,Q3} when - N :: non_neg_integer(), - Q1 :: queue(), - Q2 :: queue(), - Q3 :: queue(). +-spec split(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) -> @@ -380,10 +345,8 @@ split_r1_to_f2(N, [X|R1], F1, R2, F2) -> %% %% Fun(_) -> List: O(length(List) * len(Q)) %% else: O(len(Q) --spec filter(Fun, Q1) -> Q2 when - Fun :: fun((Item :: term()) -> boolean() | list()), - Q1 :: queue(), - Q2 :: queue(). +-spec filter(Fun, Q1 :: queue()) -> Q2 :: queue() when + Fun :: fun((Item :: term()) -> boolean() | list()). 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), @@ -459,10 +422,7 @@ filter_r(Fun, [X|R0]) -> %% Cons to head %% --spec cons(Item, Q1) -> Q2 when - Item :: term(), - Q1 :: queue(), - Q2 :: queue(). +-spec cons(Item :: term(), Q1 :: queue()) -> Q2 :: queue(). cons(X, Q) -> in_r(X, Q). @@ -471,9 +431,7 @@ cons(X, Q) -> %% Return the first element in the queue %% %% O(1) since the queue is supposed to be well formed --spec head(Q) -> Item when - Q :: queue(), - Item :: term(). +-spec head(Q :: queue()) -> Item :: term(). head({[],[]}=Q) -> erlang:error(empty, [Q]); head({R,F}) when is_list(R), is_list(F) -> @@ -483,9 +441,7 @@ head(Q) -> %% Remove head element and return resulting queue %% --spec tail(Q1) -> Q2 when - Q1 :: queue(), - Q2 :: queue(). +-spec tail(Q1 :: queue()) -> Q2 :: queue(). tail(Q) -> drop(Q). @@ -493,35 +449,22 @@ tail(Q) -> %% Cons to tail %% --spec snoc(Q1, Item) -> Q2 when - Q1 :: queue(), - Q2 :: queue(), - Item :: term(). +-spec snoc(Q1 :: queue(), Item :: term()) -> Q2 :: queue(). snoc(Q, X) -> in(X, Q). %% Return last element --spec daeh(Q) -> Item when - Q :: queue(), - Item :: term(). +-spec daeh(Q :: queue()) -> Item :: term(). daeh(Q) -> get_r(Q). --spec last(Q) -> Item when - Q :: queue(), - Item :: term(). +-spec last(Q :: queue()) -> Item :: term(). last(Q) -> get_r(Q). %% Remove last element and return resulting queue --spec liat(Q1) -> Q2 when - Q1 :: queue(), - Q2 :: queue(). +-spec liat(Q1 :: queue()) -> Q2 :: queue(). liat(Q) -> drop_r(Q). --spec lait(Q1) -> Q2 when - Q1 :: queue(), - Q2 :: queue(). +-spec lait(Q1 :: queue()) -> Q2 :: queue(). lait(Q) -> drop_r(Q). %% Oops, mis-spelled 'tail' reversed. Forget this one. --spec init(Q1) -> Q2 when - Q1 :: queue(), - Q2 :: queue(). +-spec init(Q1 :: queue()) -> Q2 :: queue(). init(Q) -> drop_r(Q). %%-------------------------------------------------------------------------- diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index e60706ed05..dc31647eb5 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -735,6 +735,13 @@ restart(one_for_all, Child, State) -> terminate_children(Children, SupName) -> terminate_children(Children, SupName, []). +%% Temporary children should not be restarted and thus should +%% be skipped when building the list of terminated children, although +%% we do want them to be shut down as many functions from this module +%% use this function to just clear everything. +terminate_children([Child = #child{restart_type=temporary} | Children], SupName, Res) -> + do_terminate(Child, SupName), + terminate_children(Children, SupName, Res); terminate_children([Child | Children], SupName, Res) -> NChild = do_terminate(Child, SupName), terminate_children(Children, SupName, [NChild | Res]); diff --git a/lib/stdlib/src/timer.erl b/lib/stdlib/src/timer.erl index e3d6c905b6..689e42051f 100644 --- a/lib/stdlib/src/timer.erl +++ b/lib/stdlib/src/timer.erl @@ -199,7 +199,7 @@ tc(M, F, A) -> %% Calculate the time difference (in microseconds) of two %% erlang:now() timestamps, T2-T1. %% --spec now_diff(T1, T2) -> Tdiff when +-spec now_diff(T2, T1) -> Tdiff when T1 :: erlang:timestamp(), T2 :: erlang:timestamp(), Tdiff :: integer(). diff --git a/lib/stdlib/src/zip.erl b/lib/stdlib/src/zip.erl index 524d709431..c82c8159b6 100644 --- a/lib/stdlib/src/zip.erl +++ b/lib/stdlib/src/zip.erl @@ -223,7 +223,7 @@ openzip_open(F, Options) -> do_openzip_open(F, Options) -> Opts = get_openzip_options(Options), #openzip_opts{output = Output, open_opts = OpO, cwd = CWD} = Opts, - Input = get_zip_input(F), + Input = get_input(F), In0 = Input({open, F, OpO -- [write]}, []), {[#zip_comment{comment = C} | Files], In1} = get_central_dir(In0, fun raw_file_info_etc/5, Input), @@ -489,7 +489,7 @@ do_list_dir(F, Options) -> %% Print zip directory in short form -spec(t(Archive) -> ok when - Archive :: file:name() | binary | ZipHandle, + Archive :: file:name() | binary() | ZipHandle, ZipHandle :: pid()). t(F) when is_pid(F) -> zip_t(F); @@ -513,7 +513,7 @@ do_t(F, RawPrint) -> %% Print zip directory in long form (like ls -l) -spec(tt(Archive) -> ok when - Archive :: file:name() | binary | ZipHandle, + Archive :: file:name() | binary() | ZipHandle, ZipHandle :: pid()). tt(F) when is_pid(F) -> zip_tt(F); @@ -1174,7 +1174,7 @@ zip_get(Pid) when is_pid(Pid) -> zip_close(Pid) when is_pid(Pid) -> request(self(), Pid, close). --spec(zip_get(FileName, ZipHandle) -> {ok, [Result]} | {error, Reason} when +-spec(zip_get(FileName, ZipHandle) -> {ok, Result} | {error, Reason} when FileName :: file:name(), ZipHandle :: pid(), Result :: file:name() | {file:name(), binary()}, @@ -1183,7 +1183,7 @@ zip_close(Pid) when is_pid(Pid) -> zip_get(FileName, Pid) when is_pid(Pid) -> request(self(), Pid, {get, FileName}). --spec(zip_list_dir(ZipHandle) -> Result | {error, Reason} when +-spec(zip_list_dir(ZipHandle) -> {ok, Result} | {error, Reason} when Result :: [zip_comment() | zip_file()], ZipHandle :: pid(), Reason :: term()). diff --git a/lib/stdlib/test/erl_scan_SUITE.erl b/lib/stdlib/test/erl_scan_SUITE.erl index 31a4f94294..4298b2c701 100644 --- a/lib/stdlib/test/erl_scan_SUITE.erl +++ b/lib/stdlib/test/erl_scan_SUITE.erl @@ -737,6 +737,10 @@ set_attribute() -> (catch {foo, erl_scan:set_attribute(line, [], F2)}), % type error ?line {'EXIT',{badarg,_}} = (catch {foo, erl_scan:set_attribute(column, [], F2)}), % type error + + %% OTP-9412 + ?line 8 = erl_scan:set_attribute(line, [{line,{nos,'X',8}}], + fun({nos,_V,VL}) -> VL end), ok. column_errors() -> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 9d348b5f1a..9341300f90 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -72,6 +72,7 @@ exit_many_many_tables_owner/1]). -export([write_concurrency/1, heir/1, give_away/1, setopts/1]). -export([bad_table/1, types/1]). +-export([otp_9423/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% Convenience for manual testing @@ -143,7 +144,8 @@ all() -> otp_8166, exit_large_table_owner, exit_many_large_table_owner, exit_many_tables_owner, exit_many_many_tables_owner, write_concurrency, heir, - give_away, setopts, bad_table, types]. + give_away, setopts, bad_table, types, + otp_9423]. groups() -> [{new, [], @@ -5420,7 +5422,39 @@ types_do(Opts) -> ?line verify_etsmem(EtsMem). - +otp_9423(doc) -> ["vm-deadlock caused by race between ets:delete and others on write_concurrency table"]; +otp_9423(Config) when is_list(Config) -> + InitF = fun(_) -> {0,0} end, + ExecF = fun({S,F}) -> + receive + stop -> + io:format("~p got stop\n", [self()]), + [end_of_work | {"Succeded=",S,"Failed=",F}] + after 0 -> + %%io:format("~p (~p) doing lookup\n", [self(), {S,F}]), + try ets:lookup(otp_9423, key) of + [] -> {S+1,F} + catch + error:badarg -> {S,F+1} + end + end + end, + FiniF = fun(R) -> R end, + case run_workers(InitF, ExecF, FiniF, infinite, 1) of + Pids when is_list(Pids) -> + %%[P ! start || P <- Pids], + repeat(fun() -> ets:new(otp_9423, [named_table, public, {write_concurrency,true}]), + ets:delete(otp_9423) + end, 10000), + [P ! stop || P <- Pids], + wait_pids(Pids), + ok; + + Skipped -> Skipped + end. + + + % % Utility functions: @@ -5434,21 +5468,30 @@ add_lists([E1|T1], [E2|T2], Acc) -> add_lists(T1, T2, [E1+E2 | Acc]). run_workers(InitF,ExecF,FiniF,Laps) -> + run_workers(InitF,ExecF,FiniF,Laps, 0). +run_workers(InitF,ExecF,FiniF,Laps, Exclude) -> case erlang:system_info(smp_support) of true -> - run_workers_do(InitF,ExecF,FiniF,Laps); + run_workers_do(InitF,ExecF,FiniF,Laps, Exclude); false -> {skipped,"No smp support"} end. - + run_workers_do(InitF,ExecF,FiniF,Laps) -> - NumOfProcs = erlang:system_info(schedulers), + run_workers_do(InitF,ExecF,FiniF,Laps, 0). +run_workers_do(InitF,ExecF,FiniF,Laps, Exclude) -> + ?line NumOfProcs = case erlang:system_info(schedulers) of + N when (N > Exclude) -> N - Exclude + end, io:format("smp starting ~p workers\n",[NumOfProcs]), Seeds = [{ProcN,random:uniform(9999)} || ProcN <- lists:seq(1,NumOfProcs)], Parent = self(), Pids = [spawn_link(fun()-> worker(Seed,InitF,ExecF,FiniF,Laps,Parent,NumOfProcs) end) || Seed <- Seeds], - wait_pids(Pids). + case Laps of + infinite -> Pids; + _ -> wait_pids(Pids) + end. worker({ProcN,Seed}, InitF, ExecF, FiniF, Laps, Parent, NumOfProcs) -> io:format("smp worker ~p, seed=~p~n",[self(),Seed]), @@ -5463,6 +5506,8 @@ worker_loop(0, _, State) -> State; worker_loop(_, _, [end_of_work|State]) -> State; +worker_loop(infinite, ExecF, State) -> + worker_loop(infinite,ExecF,ExecF(State)); worker_loop(N, ExecF, State) -> worker_loop(N-1,ExecF,ExecF(State)). @@ -5517,20 +5562,21 @@ etsmem() -> case erlang:system_info({allocator,ets_alloc}) of false -> undefined; MemInfo -> - MSBCS = lists:foldl( - fun ({instance, _, L}, Acc) -> - {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), - {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), - [MBCS,SBCS | Acc] - end, - [], - MemInfo), + CS = lists:foldl( + fun ({instance, _, L}, Acc) -> + {value,{_,SBMBCS}} = lists:keysearch(sbmbcs, 1, L), + {value,{_,MBCS}} = lists:keysearch(mbcs, 1, L), + {value,{_,SBCS}} = lists:keysearch(sbcs, 1, L), + [SBMBCS,MBCS,SBCS | Acc] + end, + [], + MemInfo), lists:foldl( fun(L, {Bl0,BlSz0}) -> {value,{_,Bl,_,_}} = lists:keysearch(blocks, 1, L), {value,{_,BlSz,_,_}} = lists:keysearch(blocks_size, 1, L), {Bl0+Bl,BlSz0+BlSz} - end, {0,0}, MSBCS) + end, {0,0}, CS) end}, {Mem,AllTabs}. diff --git a/lib/stdlib/test/io_SUITE.erl b/lib/stdlib/test/io_SUITE.erl index 54a98985cd..bb02a879c2 100644 --- a/lib/stdlib/test/io_SUITE.erl +++ b/lib/stdlib/test/io_SUITE.erl @@ -27,7 +27,7 @@ otp_6282/1, otp_6354/1, otp_6495/1, otp_6517/1, otp_6502/1, manpage/1, otp_6708/1, otp_7084/1, otp_7421/1, io_lib_collect_line_3_wb/1, cr_whitespace_in_string/1, - io_fread_newlines/1, otp_8989/1]). + io_fread_newlines/1, otp_8989/1, io_lib_fread_literal/1]). %-define(debug, true). @@ -62,7 +62,7 @@ all() -> otp_6282, otp_6354, otp_6495, otp_6517, otp_6502, manpage, otp_6708, otp_7084, otp_7421, io_lib_collect_line_3_wb, cr_whitespace_in_string, - io_fread_newlines, otp_8989]. + io_fread_newlines, otp_8989, io_lib_fread_literal]. groups() -> []. @@ -1995,3 +1995,29 @@ otp_8989(Suite) when is_list(Suite) -> ?line "Hel " = fmt("~-4.*s", [3,Hello]), ?line "Hel " = fmt("~*.*s", [-4,3,Hello]), ok. + +io_lib_fread_literal(doc) -> + "OTP-9439 io_lib:fread bug for literate at end"; +io_lib_fread_literal(Suite) when is_list(Suite) -> + ?line {more,"~d",0,""} = io_lib:fread("~d", ""), + ?line {error,{fread,integer}} = io_lib:fread("~d", " "), + ?line {more,"~d",1,""} = io_lib:fread(" ~d", " "), + ?line {ok,[17],"X"} = io_lib:fread(" ~d", " 17X"), + %% + ?line {more,"d",0,""} = io_lib:fread("d", ""), + ?line {error,{fread,input}} = io_lib:fread("d", " "), + ?line {more,"d",1,""} = io_lib:fread(" d", " "), + ?line {ok,[],"X"} = io_lib:fread(" d", " dX"), + %% + ?line {done,eof,_} = io_lib:fread([], eof, "~d"), + ?line {done,eof,_} = io_lib:fread([], eof, " ~d"), + ?line {more,C1} = io_lib:fread([], " \n", " ~d"), + ?line {done,{error,{fread,input}},_} = io_lib:fread(C1, eof, " ~d"), + ?line {done,{ok,[18]},""} = io_lib:fread(C1, "18\n", " ~d"), + %% + ?line {done,eof,_} = io_lib:fread([], eof, "d"), + ?line {done,eof,_} = io_lib:fread([], eof, " d"), + ?line {more,C2} = io_lib:fread([], " \n", " d"), + ?line {done,{error,{fread,input}},_} = io_lib:fread(C2, eof, " d"), + ?line {done,{ok,[]},[]} = io_lib:fread(C2, "d\n", " d"), + ok. diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl index c79a5002fb..b48450c151 100644 --- a/lib/stdlib/test/supervisor_SUITE.erl +++ b/lib/stdlib/test/supervisor_SUITE.erl @@ -42,7 +42,7 @@ -export([ permanent_normal/1, transient_normal/1, temporary_normal/1, permanent_abnormal/1, transient_abnormal/1, - temporary_abnormal/1]). + temporary_abnormal/1, temporary_bystander/1]). %% Restart strategy tests -export([ one_for_one/1, @@ -74,7 +74,7 @@ all() -> {group, abnormal_termination}, child_unlink, tree, count_children_memory, do_not_save_start_parameters_for_temporary_children, do_not_save_child_specs_for_temporary_children, - simple_one_for_one_scale_many_temporary_children]. + simple_one_for_one_scale_many_temporary_children, temporary_bystander]. groups() -> [{sup_start, [], @@ -114,10 +114,9 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(count_children_memory, Config) -> - MemoryState = erlang:system_info(allocator), - case count_children_allocator_test(MemoryState) of - true -> Config; - false -> + try erlang:memory() of + _ -> Config + catch error:notsup -> {skip, "+Meamin used during test; erlang:memory/1 not available"} end; init_per_testcase(_Case, Config) -> @@ -608,6 +607,37 @@ temporary_abnormal(Config) when is_list(Config) -> [0,0,0,0] = get_child_counts(sup_test). %%------------------------------------------------------------------------- +temporary_bystander(doc) -> + ["A temporary process killed as part of a rest_for_one or one_for_all " + "restart strategy should not be restarted given its args are not " + " saved. Otherwise the supervisor hits its limit and crashes."]; +temporary_bystander(suite) -> []; +temporary_bystander(_Config) -> + Child1 = {child1, {supervisor_1, start_child, []}, permanent, 100, + worker, []}, + Child2 = {child2, {supervisor_1, start_child, []}, temporary, 100, + worker, []}, + {ok, SupPid1} = supervisor:start_link(?MODULE, {ok, {{one_for_all, 2, 300}, []}}), + {ok, SupPid2} = supervisor:start_link(?MODULE, {ok, {{rest_for_one, 2, 300}, []}}), + unlink(SupPid1), % otherwise we crash with it + unlink(SupPid2), % otherwise we crash with it + {ok, CPid1} = supervisor:start_child(SupPid1, Child1), + {ok, _CPid2} = supervisor:start_child(SupPid1, Child2), + {ok, CPid3} = supervisor:start_child(SupPid2, Child1), + {ok, _CPid4} = supervisor:start_child(SupPid2, Child2), + terminate(SupPid1, CPid1, child1, normal), + terminate(SupPid2, CPid3, child1, normal), + timer:sleep(350), + catch link(SupPid1), + catch link(SupPid2), + %% The supervisor would die attempting to restart child2 + true = erlang:is_process_alive(SupPid1), + true = erlang:is_process_alive(SupPid2), + %% Child2 has not been restarted + [{child1, _, _, _}] = supervisor:which_children(SupPid1), + [{child1, _, _, _}] = supervisor:which_children(SupPid2). + +%%------------------------------------------------------------------------- one_for_one(doc) -> ["Test the one_for_one base case."]; one_for_one(suite) -> []; @@ -1032,17 +1062,6 @@ count_children_memory(Config) when is_list(Config) -> [terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3], [1,0,0,0] = get_child_counts(sup_test). -count_children_allocator_test(MemoryState) -> - Allocators = [temp_alloc, eheap_alloc, binary_alloc, ets_alloc, - driver_alloc, sl_alloc, ll_alloc, fix_alloc, std_alloc, - sys_alloc], - MemoryStateList = element(4, MemoryState), - AllocTypes = [lists:keyfind(Alloc, 1, MemoryStateList) - || Alloc <- Allocators], - AllocStates = [lists:keyfind(e, 1, AllocValue) - || {_Type, AllocValue} <- AllocTypes], - lists:all(fun(State) -> State == {e, true} end, AllocStates). - %%------------------------------------------------------------------------- do_not_save_start_parameters_for_temporary_children(doc) -> ["Temporary children shall not be restarted so they should not " diff --git a/lib/stdlib/test/zip_SUITE.erl b/lib/stdlib/test/zip_SUITE.erl index d5f2cd52d4..7233c061ef 100644 --- a/lib/stdlib/test/zip_SUITE.erl +++ b/lib/stdlib/test/zip_SUITE.erl @@ -375,7 +375,8 @@ zip_options(Config) when is_list(Config) -> ok = file:set_cwd(?config(data_dir, Config)), %% Create a zip archive - {ok, Zip} = zip:zip("filename_not_used.zip", Names, [memory, {cwd, PrivDir}]), + {ok, {_,Zip}} = + zip:zip("filename_not_used.zip", Names, [memory, {cwd, PrivDir}]), %% Open archive {ok, ZipSrv} = zip:zip_open(Zip, [memory]), diff --git a/lib/test_server/src/ts_install_cth.erl b/lib/test_server/src/ts_install_cth.erl index c5444a342f..0770190c36 100644 --- a/lib/test_server/src/ts_install_cth.erl +++ b/lib/test_server/src/ts_install_cth.erl @@ -49,8 +49,7 @@ -include_lib("kernel/include/file.hrl"). --type proplist() :: list({atom(),term()}). --type config() :: proplist(). +-type config() :: proplists:proplist(). -type reason() :: term(). -type skip_or_fail() :: {skip, reason()} | {auto_skip, reason()} | @@ -65,7 +64,7 @@ id(_Opts) -> ?MODULE. %% @doc Always called before any other callback function. --spec init(Id :: term(), Opts :: proplist()) -> +-spec init(Id :: term(), Opts :: proplists:proplist()) -> State :: #state{}. init(_Id, Opts) -> Nodenames = proplists:get_value(nodenames, Opts, 0), diff --git a/lib/tools/doc/src/xref.xml b/lib/tools/doc/src/xref.xml index 75ffa25311..17de66bb22 100644 --- a/lib/tools/doc/src/xref.xml +++ b/lib/tools/doc/src/xref.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2000</year><year>2010</year> + <year>2000</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -1465,8 +1465,8 @@ Evaluates a predefined analysis. <name>start(NameOrOptions) -> Return</name> <fsummary>Create an Xref server.</fsummary> <type> - <v>Name = atom()()</v> - <v>XrefOrOptions = Xref | Options</v> + <v>NameOrOptions = Name | Options</v> + <v>Name = atom()</v> <v>Options = [Option] | Option</v> <v>Option = {xref_mode, mode()} | term()</v> <v>Return = {ok, pid()} | {error, {already_started, pid()}}</v> @@ -1483,7 +1483,7 @@ Evaluates a predefined analysis. <name>start(Name, Options) -> Return</name> <fsummary>Create an Xref server.</fsummary> <type> - <v>Name = atom()()</v> + <v>Name = atom()</v> <v>Options = [Option] | Option</v> <v>Option = {xref_mode, mode()} | term()</v> <v>Return = {ok, pid()} | {error, {already_started, pid()}}</v> diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index 6728bef2a4..a8dd3ec3ac 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -523,6 +523,32 @@ This is an elisp list of options. Each option can be either: - a string Example: '(bin_opt_info (i . \"/path1/include\") (i . \"/path2/include\"))") +(defvar erlang-compile-command-function-alist + '((".erl\\'" . inferior-erlang-compute-erl-compile-command) + (".xrl\\'" . inferior-erlang-compute-leex-compile-command) + (".yrl\\'" . inferior-erlang-compute-yecc-compile-command) + ("." . inferior-erlang-compute-erl-compile-command)) + "*Alist of filename patterns vs corresponding compilation functions. +Each element looks like (REGEXP . FUNCTION). Compiling a file whose name +matches REGEXP specifies FUNCTION to use to compute the compilation +command. The FUNCTION will be called with two arguments: module name and +default compilation options, like output directory. The FUNCTION +is expected to return a string.") + +(defvar erlang-leex-compile-opts '() + "*Options to pass to leex when compiling xrl files. +This is an elisp list of options. Each option can be either: +- an atom +- a dotted pair +- a string") + +(defvar erlang-yecc-compile-opts '() + "*Options to pass to yecc when compiling yrl files. +This is an elisp list of options. Each option can be either: +- an atom +- a dotted pair +- a string") + (eval-and-compile (defvar erlang-regexp-modern-p (if (> erlang-emacs-major-version 21) t nil) @@ -5276,6 +5302,22 @@ unless the optional NO-DISPLAY is non-nil." (file-name-as-directory buffer-dir)))) (defun inferior-erlang-compute-compile-command (module-name opts) + (let ((ccfn erlang-compile-command-function-alist) + (res (inferior-erlang-compute-erl-compile-command module-name opts)) + ccfn-entry + done) + (if (not (null (buffer-file-name))) + (while (and (not done) (not (null ccfn))) + (setq ccfn-entry (car ccfn)) + (setq ccfn (cdr ccfn)) + (if (string-match (car ccfn-entry) (buffer-file-name)) + (let ((c-fn (cdr ccfn-entry))) + (setq done t) + (if (not (null c-fn)) + (setq result (funcall c-fn module-name opts))))))) + result)) + +(defun inferior-erlang-compute-erl-compile-command (module-name opts) (let* ((out-dir-opt (assoc 'outdir opts)) (out-dir (cdr out-dir-opt))) (if erlang-compile-use-outdir @@ -5299,6 +5341,48 @@ unless the optional NO-DISPLAY is non-nil." (remq out-dir-opt opts)) tmpvar tmpvar tmpvar2))))) +(defun inferior-erlang-compute-leex-compile-command (module-name opts) + (let ((file-name (buffer-file-name)) + (erl-compile-expr (inferior-erlang-remove-any-trailing-dot + (inferior-erlang-compute-erl-compile-command + module-name opts)))) + (format (concat "f(LErr1__), f(LErr2__), " + "case case leex:file(\"%s\", [%s]) of" + " ok -> ok;" + " {ok,_} -> ok;" + " {ok,_,_} -> ok;" + " LErr1__ -> LErr1__ " + "end of" + " ok -> %s;" + " LErr2__ -> LErr2__ " + "end.") + file-name + (inferior-erlang-format-comma-opts erlang-leex-compile-opts) + erl-compile-expr))) + +(defun inferior-erlang-compute-yecc-compile-command (module-name opts) + (let ((file-name (buffer-file-name)) + (erl-compile-expr (inferior-erlang-remove-any-trailing-dot + (inferior-erlang-compute-erl-compile-command + module-name opts)))) + (format (concat "f(YErr1__), f(YErr2__), " + "case case yecc:file(\"%s\", [%s]) of" + " {ok,_} -> ok;" + " {ok,_,_} -> ok;" + " YErr1__ -> YErr1__ " + "end of" + " ok -> %s;" + " YErr2__ -> YErr2__ " + "end.") + file-name + (inferior-erlang-format-comma-opts erlang-yecc-compile-opts) + erl-compile-expr))) + +(defun inferior-erlang-remove-any-trailing-dot (str) + (if (string= (substring str -1) ".") + (substring str 0 (1- (length str))) + str)) + (defun inferior-erlang-format-comma-opts (opts) (if (null opts) "" diff --git a/lib/xmerl/include/xmerl_xsd.hrl b/lib/xmerl/include/xmerl_xsd.hrl index b527accc8c..6dad7d8ff0 100644 --- a/lib/xmerl/include/xmerl_xsd.hrl +++ b/lib/xmerl/include/xmerl_xsd.hrl @@ -36,6 +36,7 @@ schema_name, vsn, schema_preprocessed=false, + external_xsd_base=false, xsd_base, xml_options=[], scope=[], diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl index 059c8f21b6..e598c5f56d 100644 --- a/lib/xmerl/src/xmerl_scan.erl +++ b/lib/xmerl/src/xmerl_scan.erl @@ -2276,7 +2276,7 @@ scan_att_chars([H|T], S0, H, Acc, TmpAcc,AttType,IsNorm) -> % End quote true -> normalize(Acc,S,IsNorm) end, - {lists:reverse(Acc2), T, S2,IsNorm2}; + {lists:flatten(lists:reverse(Acc2)), T, S2,IsNorm2}; scan_att_chars("&" ++ T, S0, Delim, Acc, TmpAcc,AT,IsNorm) -> % Reference ?bump_col(1), {ExpRef, T1, S1} = scan_reference(T, S), diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl index e56f1470c0..f003cc74ba 100644 --- a/lib/xmerl/src/xmerl_xsd.erl +++ b/lib/xmerl/src/xmerl_xsd.erl @@ -287,10 +287,19 @@ process_schema(Schema) -> %% error reason. The error reason may be a list of several errors %% or a single error encountered during the processing. process_schema(Schema,Options) when is_list(Options) -> - S = initiate_state(Options,Schema), - process_schema2(xmerl_scan:file(filename:join(S#xsd_state.xsd_base, Schema)),S,Schema); -process_schema(Schema,State) when is_record(State,xsd_state) -> - process_schema2(xmerl_scan:file(filename:join(State#xsd_state.xsd_base, Schema)),State,Schema). + State = initiate_state(Options,Schema), + process_schema(Schema, State); +process_schema(Schema, State=#xsd_state{fetch_fun=Fetch})-> + case Fetch(Schema, State) of + {ok,{file,File},_} -> + process_schema2(xmerl_scan:file(File), State, Schema); + {ok,{string,Str},_} -> + process_schema2(xmerl_scan:string(Str), State, Schema); + {ok,[],_} -> + {error,enoent}; + Err -> + Err + end. process_schema2(Err={error,_},_,_) -> Err; @@ -319,12 +328,9 @@ process_schemas(Schemas) -> %% error reason. The error reason may be a list of several errors %% or a single error encountered during the processing. process_schemas(Schemas=[{_,Schema}|_],Options) when is_list(Options) -> - process_schemas(Schemas,initiate_state(Options,Schema)); + State = initiate_state(Options,Schema), + process_schemas(Schemas, State); process_schemas([{_NS,Schema}|Rest],State=#xsd_state{fetch_fun=Fetch}) -> -%% case process_external_schema_once(Schema,if_list_to_atom(NS),State) of -%% S when is_record(S,xsd_state) -> -%% case process_schema(filename:join([State#xsd_state.xsd_base,Schema]),State) of -%% {ok,S} -> Res= case Fetch(Schema,State) of {ok,{file,File},_} -> @@ -345,20 +351,20 @@ process_schemas([{_NS,Schema}|Rest],State=#xsd_state{fetch_fun=Fetch}) -> process_schemas([],S) when is_record(S,xsd_state) -> {ok,S}. - initiate_state(Opts,Schema) -> XSDBase = filename:dirname(Schema), {{state,S},RestOpts}=new_state(Opts), S2 = create_tables(S), - initiate_state2(S2#xsd_state{schema_name = Schema, - xsd_base = XSDBase, - fetch_fun = fun fetch/2},RestOpts). + S3 = initiate_state2(S2#xsd_state{schema_name = Schema, xsd_base=XSDBase, + fetch_fun = fun fetch/2}, + RestOpts). + initiate_state2(S,[]) -> S; initiate_state2(S,[{tab2file,Bool}|T]) -> initiate_state2(S#xsd_state{tab2file=Bool},T); -initiate_state2(S,[{xsdbase,XSDBase}|T]) -> - initiate_state2(S#xsd_state{xsd_base=XSDBase},T); +initiate_state2(S,[{xsdbase, XSDBase}|T]) -> + initiate_state2(S#xsd_state{xsd_base=XSDBase, external_xsd_base=true},T); initiate_state2(S,[{fetch_fun,FetchFun}|T]) -> initiate_state2(S#xsd_state{fetch_fun=FetchFun},T); initiate_state2(S,[{fetch_path,FetchPath}|T]) -> @@ -5232,7 +5238,12 @@ fetch(URI,S) -> [] -> %% empty systemliteral []; _ -> - filename:join(S#xsd_state.xsd_base, URI) + case S#xsd_state.external_xsd_base of + true -> + filename:join(S#xsd_state.xsd_base, URI); + false -> + filename:join(S#xsd_state.xsd_base, filename:basename(URI)) + end end, Path = path_locate(S#xsd_state.fetch_path, Filename, Fullname), ?dbg("fetch(~p) -> {file, ~p}.~n", [URI, Path]), diff --git a/lib/xmerl/test/Makefile b/lib/xmerl/test/Makefile index 9715aa054a..5a2a585841 100644 --- a/lib/xmerl/test/Makefile +++ b/lib/xmerl/test/Makefile @@ -124,4 +124,4 @@ release_tests_spec: opt @tar cfh - xmerl_xsd_MS2002-01-16_SUITE_data | (cd $(RELSYSDIR); tar xf -) @tar cfh - xmerl_xsd_NIST2002-01-16_SUITE_data | (cd $(RELSYSDIR); tar xf -) @tar cfh - xmerl_xsd_Sun2002-01-16_SUITE_data | (cd $(RELSYSDIR); tar xf -) - chmod -f -R u+w $(RELSYSDIR) + chmod -R u+w $(RELSYSDIR) diff --git a/lib/xmerl/test/xmerl_SUITE.erl b/lib/xmerl/test/xmerl_SUITE.erl index 392b2522e8..0c809dbcb6 100644 --- a/lib/xmerl/test/xmerl_SUITE.erl +++ b/lib/xmerl/test/xmerl_SUITE.erl @@ -57,7 +57,8 @@ groups() -> {eventp_tests, [], [sax_parse_and_export]}, {ticket_tests, [], [ticket_5998, ticket_7211, ticket_7214, ticket_7430, - ticket_6873, ticket_7496, ticket_8156, ticket_8697]}, + ticket_6873, ticket_7496, ticket_8156, ticket_8697, + ticket_9411]}, {app_test, [], [{xmerl_app_test, all}]}, {appup_test, [], [{xmerl_appup_test, all}]}]. @@ -575,7 +576,17 @@ ticket_8697(Config) -> ?line [16#545C] = HexEntityText, ok. +ticket_9411(suite) -> []; +ticket_9411(doc) -> + ["Test that xmerl_scan handles attribute that contains for example ""]; +ticket_9411(Config) -> + DataDir = ?config(data_dir,Config), + ?line {ok, Schema} = xmerl_xsd:process_schema(filename:join([DataDir,"misc/ticket_9411.xsd"])), + ?line {ok, Bin} = file:read_file(filename:join([DataDir,"misc/ticket_9411.xml"])), + ?line Xml = erlang:binary_to_list(Bin), + ?line {E, _} = xmerl_scan:string(Xml), + ?line {E, _} = xmerl_xsd:validate(E, Schema). diff --git a/lib/xmerl/test/xmerl_SUITE_data/misc.tar.gz b/lib/xmerl/test/xmerl_SUITE_data/misc.tar.gz Binary files differindex c48a6f897b..fef7431845 100644 --- a/lib/xmerl/test/xmerl_SUITE_data/misc.tar.gz +++ b/lib/xmerl/test/xmerl_SUITE_data/misc.tar.gz diff --git a/lib/xmerl/test/xmerl_xsd_SUITE.erl b/lib/xmerl/test/xmerl_xsd_SUITE.erl index a0d3b1e667..421fa48054 100644 --- a/lib/xmerl/test/xmerl_xsd_SUITE.erl +++ b/lib/xmerl/test/xmerl_xsd_SUITE.erl @@ -62,7 +62,7 @@ groups() -> sis2, state2file_file2state, union]}, {ticket_tests, [], [ticket_6910, ticket_7165, ticket_7190, ticket_7288, - ticket_7736, ticket_8599]}, + ticket_7736, ticket_8599, ticket_9410]}, {facets, [], [length, minLength, maxLength, pattern, enumeration, whiteSpace, maxInclusive, maxExclusive, minExclusive, @@ -1146,3 +1146,8 @@ ticket_8599(Config) -> ?line {{xmlElement,persons,persons,_,_,_,_,_,_,_,_,_},_GlobalState} = xmerl_xsd:validate(E, S). + +ticket_9410(suite) -> []; +ticket_9410(Config) -> + file:set_cwd(filename:join([?config(data_dir,Config),".."])), + ?line {ok, _S} = xmerl_xsd:process_schema("xmerl_xsd_SUITE_data/small.xsd"). |